Gatsby: Добавьте официальное руководство по интернационализации веб-сайтов с помощью Gatsby.

Созданный на 4 февр. 2018  ·  74Комментарии  ·  Источник: gatsbyjs/gatsby

Увидев реакцию на мой комментарий по другому вопросу , я решил открыть этот вопрос.

Я думаю, что i18n намного сложнее, чем должно быть. Я не смог найти никакой официальной документации или плагина для интернационализации контента на сайтах, созданных Гэтсби. Я наткнулся на jsLingui , который, кажется, решает большинство проблем, но до сих пор нет руководств по поддержке, например, файлов/страниц уценки на разных языках.

documentation

Самый полезный комментарий

Ребята, прошел почти год 😅

Недавно я выпустил новый плагин gatsby gatsby-plugin-intl, который легко превращает ваш веб-сайт gatsby в интернациональную структуру из коробки.

ДЕМО: https://gatsby-starter-default-intl.netlify.com

  • Готовая платформа интернационализации на базе react-intl

  • Поддержка автоматического перенаправления на основе предпочтительного языка пользователя в браузере.

  • Поддержка многоязычных маршрутов URL-адресов в одном компоненте страницы. Это означает, что вам не нужно создавать отдельные страницы, такие как pages/en/index.js или pages/ko/index.js .

  • Как некоторые из вас, ребята, предложили выше, теперь он связывает только текущий язык во время сборки.

Кроме того, я хочу отметить, что многие из примеров/стартеров i18n на самом деле рендерятся на стороне клиента. Лучший способ проверить, отображается ли приложение как SSR, — просмотреть исходный код и проверить, существуют ли локализованные тексты. Пожалуйста, дважды проверьте этот вопрос, когда вы интернационализируете свой веб-сайт gatsby для SEO.

Все 74 Комментарий

Есть эта статья об использовании i18next с GatsbyJS , который на данный момент является самым продвинутым методом для меня.

Но я не чувствую, что i18next — это «статический путь».

Что касается сообщения в блоге, у меня были следующие вопросы/оговорки:
https://twitter.com/semdubois/status/930389055388508160

Существует https://github.com/angeloocana/gatsby-plugin-i18n, но у него есть несколько ограничений, и он не получает большой активности/внимания. Это может помочь переместить его в репозиторий Gatsby. Я тоже хотел бы получить правильное консолидированное решение.

Я также наткнулся на js lingui, и он кажется многообещающим, особенно с только что вышедшей версией 2.

Я тоже пытаюсь в этом разобраться. Использование метода i18next в посте является наиболее удобным и интуитивно понятным, но у меня осталось два вопроса....

  1. как я могу включить разные файлы уценки для языков, например, в решении gatsby-plugin-i18n?

  2. Это полностью исключает статическую визуализацию контента?

К вашему сведению @angeloocana

Я напишу краткий обзор того, как мы справляемся с этим на данный момент, используя react-intl . Это приложение еще не запущено, поэтому мы все еще можем найти некоторые проблемы с этой настройкой, однако пока оно работает нормально.

Мы храним почти весь наш контент (который был перенесен из нашего блога Wordpress) в Contentful . Мы не используем его функции перевода, но вместо этого у нас есть одно пространство (вид проекта или папки) для каждого языка. Мы используем плагин gatsby-source-contentful для извлечения этих данных, однако ранее мы сами извлекали и преобразовывали эти данные в файлы JSON и использовали плагин gatsby-source-filesystem (мы использовали структуру папок, подобную /en/blog/... , /de/blog/... ), поэтому на самом деле не имеет значения, используете ли вы Contentful или нет, если каждый узел знает свою локаль.

У нас также есть некоторый текст, такой как метки кнопок, некоторые ссылки или статический контент, который не поступает из Contentful, а вместо этого переводится в Transifex и синхронизируется с файлами JSON, которые хранятся в репозитории. Для этой части нам нужно было использовать некоторую библиотеку i18n, и мы решили использовать react-intl просто потому, что я уже знаю ее и знаю, что она также обрабатывает форматирование даты и числа. Вот как мы это настроили: https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment -362710469. Затем мы используем, например, intl.formatMessage при создании метатегов и компонентов <FormattedMessage /> , <FormattedDate /> и т. д. в шаблонах.

@szimek Если я вас правильно понял, то у вас react-intl обрабатывает перевод текста компонента, в то время как сообщения находятся в жестко закодированных маршрутах в каталоге страниц?

Это будет означать, что жестко закодированные маршруты являются единственными, которые отображаются статически? А межкомпонентные переводы i18n динамически рендерятся?

@deltaskelta Я не уверен, что понимаю ваш вопрос.

На данный момент у нас нет пользовательской маршрутизации на стороне клиента (как описано, например, здесь ), только статически сгенерированные страницы. При создании страницы post (мы также создаем страницы index и category с разбивкой на страницы с учетом локали, используя слегка модифицированную версию плагина gatsby-pagination ), мы используя следующий код:

posts.edges.map(({ node }) => {
  const id = node.contentfulid;
  const locale = node.node_locale;

  return createPage({
    path: `/${locale}/blog/posts/${id}`,
    layout: locale,
    component: path.resolve('./src/templates/post-page.jsx'),
    context: {
      id,
      locale,
    },
  });
});

Даже если у вас отключен JS, весь контент, включая метатеги, отображается на правильном языке.

С нашей стороны мы используем i18next с некоторыми изменениями.

Основные принципы следующие:

  • Локали доступны в виде файлов .json и просто перемещаются в каталог /dist/ во время сборки.
  • Мы работаем над одной единственной страницей, которая будет создавать столько статических версий, сколько у нас есть языков, используя onCreatePagegatsby-node.js ).
  • Информация о страницах (например, имя пути) централизована в одном файле.

Места

Переводы в основном сгруппированы по страницам с одним пространством имен (= файл JSON) на страницу + один глобальный файл (для общих текстов, таких как верхний и нижний колонтитулы).

|- src/
  |- locales/
    |- en/
      |- foo.json
      |- bar.json
    |- fr/
      |- foo.json
      |- bar.json

К сожалению, нет горячей перезагрузки на модификациях локалей 👎

i18next

i18next инициализируется со следующей конфигурацией:

import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import { reactI18nextModule } from "react-i18next";
import config from "config";  // Our custom configurations env-specifics

const NAMESPACES = [
  "foo",
  "bar",
  ...
];

export default function createI18n() {
  const options = {
    fallbackLng   : false,
    whitelist     : ["en", "fr"],
    ns            : NAMESPACES,
    debug         : config.debug,
    interpolation : {
      escapeValue : false
    },
    react         : {
      wait : true
    },
    backend       : {
      loadPath : `${config.pathPrefix}locales/{{lng}}/{{ns}}.json`
    },
    parseMissingKeyHandler : () => "",  // Display an empty string when missing/loading key
  };

  return i18n
    .use(Backend)
    .use(reactI18nextModule)
    .init(options);
}

Информация о страницах

Перед созданием всех i18n-версий наших страниц нам нужно знать некоторую информацию, которую мы сгруппировали в файле pagesInfos.js :

module.exports = {
  index : {
    id          : "index",
    namespace   : "home",
    path        : {
      fr : "/",
      en : "/en/"
    }
  },
  projects : {
    id          : "projects",
    namespace   : "projects",
    path        : {
      fr : "/nos-clients/",
      en : "/en/our-clients/"
    }
  },
  // etc...

Где ключи -- это имена файлов страниц , а пространства имен -- имена файлов локалей . Они могут быть разными 🚨

В таком случае:

|- src/
  |- pages/
    |- index.js
    |- projects.js
  |- locales/
    |- en/
      |- home.json
      |- projects.json
    |- fr/
      |- home.json
      |- projects.json

А где путь — это пути к нашим будущим версиям (языкам) наших страниц.

Создание страниц

Используя тот же пример, что и выше, наша цель здесь — создать версию домашней страницы и страницы проекта на FR + EN.

Для этого мы создали специальную функцию:

/**
 * Generate a custom page informations
 * <strong i="15">@param</strong>  {Object} defaultInfos  Default informations generated by Gatsby
 * <strong i="16">@return</strong> {Object}               Customized page object
 */
function generatePagesInfos(defaultInfos) {
  const pageId = defaultInfos.jsonName.slice(0, -5);  // NOTE: Get pageId from "pageName.json"
  const pageInfos = pagesInfos[pageId];

  const pageFR = {
    ...defaultInfos,
    context : {
      pageId      : pageInfos.id,
      namespace   : pageInfos.namespace,
      language    : "fr"
    },
    path : pageInfos.path.fr
  };

  const pageEN = {
    ...defaultInfos,
    context : {
      pageId      : pageInfos.id,
      namespace   : pageInfos.namespace,
      language    : "en"
    },
    path : pageInfos.path.en
  };

  return [pageFR, pageEN];
}

Затем этот помощник будет использоваться во время хука onCreatePage , выбирая каждую страницу с помощью регулярного выражения:

exports.onCreatePage = async ({ page, boundActionCreators }) => {
  const { createPage, deletePage } = boundActionCreators;

  return new Promise((resolve, reject) => {

    if (page.path.match(page.path.match(/^\/$/))) {
      const i18nPages = generatePagesInfos(page);
      deletePage(page);                         // Remove old default page
      i18nPages.map(page => createPage(page));  // Create custom i18n pages
    }

    if (page.path.match(/^\/projects\/?$/)) {
      const i18nPages = generatePagesInfos(page);
      deletePage(page);
      i18nPages.map(page => createPage(page));
    }

    // etc...

    resolve();
  });
}

Теперь у нас есть две версии каждой страницы с собственным путем (из файла с информацией о наших страницах). Вы могли заметить, что мы передаем информацию language на каждую страницу через pathContext . Это значение будет использоваться на каждой странице для отображения правильного языка.

Показать правильный язык

Мы используем класс React для страниц со следующим декоратором, который автоматически определяет язык текущей страницы и при необходимости обновляет i18n:

import React from "react";
import PropTypes from "prop-types";
import { i18n } from "context";    // Our custom context

/**
 * <strong i="10">@returns</strong> {React.PureComponent} Component with locales as proptypes
 */
export default function setLanguageFromPage() {

  return WrappedComponent => (
    class extends React.PureComponent {

      static propTypes = {
        pathContext : PropTypes.shape({
          language : PropTypes.string.isRequired
        })
      }

      componentDidMount() {
        const currentLanguage = i18n.language;
        const pageLanguage = this.props.pathContext.language;

        // First request
        if (!currentLanguage) {
          i18n.language = pageLanguage;
        }

        // Only update on language change
        if (currentLanguage !== pageLanguage) {
          i18n.changeLanguage(pageLanguage);
        }
      }

      render() {
        return <WrappedComponent {...this.props} />;
      }

    }
  );

}

Затем вызовите его на страницах:

@setLanguageFromPage()
export default class ProjectsPage extends React.PureComponent {
// ...

Pfew, все, что вам нужно сделать сейчас, это использовать функцию перевода i18next.

Вывод

👍 Единый исходный файл, который генерирует столько версий, сколько необходимо во время сборки.
👍 Простое управление выводом страниц
👍 Сначала хлопоты, а потом все просто

👎 Нет горячей перезагрузки для локалей

Я чувствую, что это не совсем «статичный образ жизни»… Но это лучшее, что нам удалось получить на данный момент.

Я хотел бы увидеть, что вы думаете об этом, и как вы, ребята, справляетесь с этим.

PS. Ткните @szimek , это то, что я хотел показать вам несколько дней назад :)

Я использовал @angeloocana https://github.com/angeloocana/gatsby-plugin-i18n + React-intl, и это не так сложно, как описанные выше методы, но есть некоторые ограничения (см. проблемы репо), и я не очень доволен React-intl. хотел бы попробовать https://github.com/lingui/js-lingui @tricoder42, просто не было времени.

@monsieurnebo какова цель запуска deletePage прямо перед createPage ? Мне нравится ваше решение, и я пытался его реализовать, но у меня есть некоторые ошибки.

  • Я либо получаю неверный pathContext.language без использования deletePage

  • или я получаю сообщение об ошибке сборки, когда включаю deletePage как в вашем примере. (он говорит TypeError: Cannot read property 'id' of undefined когда сборка достигает стадии run graphql queries )

Это лучшее решение, которое я видел до сих пор.

@deltaskelta Рад видеть, что вам нравится!

deletePage используется для отмены создания Gatsby страницы по умолчанию. Если вы не добавите это, вы получите свои собственные страницы и страницу по умолчанию.

Проверьте свой каталог public с этой строкой и без нее, вы заметите разницу;)

Про ваши ошибки без кода сложно догадаться. Не могли бы вы сделать репозиторий с вашим кодом? Я бы взглянул на него тогда.

РЕДАКТИРОВАТЬ: Вам будет интересен пример ?

О, это не было связано с вызовом deletePage. Это была проблема с тем, чтобы не копировать объект страницы, так как я изменил ваш пример. Я всегда спотыкаюсь с копированием объектов после того, как какое-то время не использовал js;)

@monsieurnebo Я react-i18next нужен javascript для рендеринга чего-либо в компоненте...

Можете ли вы подтвердить, так ли это работает с вашей стороны?

РЕДАКТИРОВАТЬ: кстати, я пропустил ваше упоминание о примере, если у вас есть время, я хотел бы увидеть полный пример.

@deltaskelta Я сделаю пример, когда у меня будет свободное время :)

@monsieurnebo, можете ли вы подтвердить, что ваш метод не отображает строки статически в HTML и требует JavaScript?

Да, именно поэтому это не совсем "статичный образ жизни" 😐

Мне бы хотелось плагин, который вместо этого генерирует статический текст.

МММ ясно. Статический рендеринг - это то, что мне нужно...

Я подумал, что, поскольку контексты уже переданы с именем языка для большинства страниц в вашем gatsby-node было бы не слишком сложно просто запросить ключи сообщений в запросах graphql для каждой страницы. и передавать их таким образом, но я бы предпочел использовать инструмент i18n полностью, если это возможно...

@szimek, как react-intl отображает переводы на этапе сборки и не использует js?

Как уже упоминалось, насколько я вижу, Gatsby-plugin-i18n генерирует статический текст. Вы проверяли его пример?

@deltaskelta Ну, все переводы доступны во время сборки (поэтому я использую несколько макетов), так что это «просто работает» ™️ ;) Я просто надеюсь, что оно продолжит работать в версии 2... Я могу подготовить образец приложения, если вы хотеть. Я еще не изучал gatsby-plugin-i18n , потому что работаю с Contentful, а не с файлами.

Я не читал подробностей, но есть обсуждение (или, скорее, монолог) о комбо gatsby-plugin-i18n + contentful здесь: https://github.com/angeloocana/gatsby-plugin-i18n/issues/31

@szimek Мне был бы очень интересен пример. @sedubois Я знаю, что gatsby-plugin-i18n также генерирует статический текст, но я не вижу, где происходит волшебство, отсутствующее в i18next для создания полностью статических файлов ....

Думаю, теперь я понимаю, почему... вы передаете react-intl сообщения через pathContext в gatsby-node во время рендеринга?

РЕДАКТИРОВАТЬ: если это так (что имеет смысл), то используемая библиотека i18n «несколько» не имеет значения, потому что сообщения передаются через контекст и рендерятся статически, что является чистой настройкой gatsby, а библиотека i18n обрабатывает только особые случаи, такие как даты и множественное число с js.

При дальнейшем тестировании как i18next и react-intl кажется, что HOC перевода i18next требует javascript для загрузки, поэтому даже если сообщения передаются через контекст и отображаются статически, любое использование перевода HOC будет отображать весь компонент, требующий javascript для запуска.

react-intl другой стороны, компонент FormattedMessage отображает сообщение по умолчанию (которое может быть основано на переданном ему контексте) и статически отображает в html при сборке.

По дизайну я думаю, что более естественная интеграция i18n будет с react-intl, если вы хотите добиться статических переводов в HTML. Если я неправильно понял поток, который вы, ребята, используете, пожалуйста, поправьте меня.

У @mattferderer была правильная идея, но я думаю, что ее нужно немного подправить. https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment -362715706

макеты отображаются перед страницами, поэтому без создания нескольких макетов нет возможности передавать сообщения через контекст из функции createPages (поправьте меня, если я ошибаюсь). Итак, чтобы упростить i18n, я бы подумал, что макет должен просто вызывать children() а затем разные макеты для каждого языкового эффекта будут выполняться за счет того, что gatsby-node создает разные пути для каждого языка. индексировать страницы из pages/index которым можно передавать сообщения через контекст

РЕДАКТИРОВАТЬ: извините за бессвязность, но все только что стало ясно, и я должен был записать это куда-то

EDIT2: я определенно ошибаюсь выше, верхние и нижние колонтитулы должны идти в макете, я просто не знаю, как получить к ним сообщения, не создавая несколько макетов. Единственный другой способ, который я могу придумать, - это разделить URL-адреса и регулярное выражение для локали ... но это похоже на хакерскую вещь.

@KyleAMathews с v2, имеющей только один макет, как мы можем передавать данные, такие как сообщения i18n, в корневой компонент макета через контекст на основе пути или массива языковых ключей?

Если бы это можно было сделать, то было бы легко реализовать i18n, но я не вижу, как это сделать, не создавая несколько макетов.

@deltaskelta Вот пример того, что мы делаем: https://github.com/szimek/gatsby-react-intl-example. Он показывает, как отображать отдельные сообщения, как создавать индексную страницу для каждой локали, как использовать компоненты react-intl , как использовать HOC react-intl injectIntl для установки заголовка (или любые другие метатеги) для индексных страниц с помощью помощника intl.formatMessage и т. д.

Сгенерированные страницы:

  • /en
  • /en/hello-world
  • /pl
  • /pl/witaj-swiecie

В реальном приложении мы используем модифицированную версию gatsby-pagination , потому что исходная версия не поддерживает параметр макета. Мы также устанавливаем поле post_id для каждого поста, что позволяет нам находить переводы одного и того же поста, например, в случае с этим демонстрационным приложением оба поста будут иметь одинаковые post_id .

КСТАТИ. Я только что понял, что нам, скорее всего, потребуется создать отдельные карты сайта для каждого языка, чтобы Swiftype (поисковая система, которую мы используем) знал, какие страницы мы получили.

Вкратце @deltaskelta у вас будут компоненты страницы для каждого языка, а затем компонент макета для каждого языка. Вот один из способов сделать это.

// French
import React from 'react'
import FrenchLayout from '../components/layouts/french'
import ImportantPage from '../components/pages/important-page'

export default ({ data }) => (
  <FrenchLayout>
    <ImportantPage {...data} />
  </FrenchLayout>
)

// French query here
// English
import React from 'react'
import EnglishLayout from '../components/layouts/english'
import ImportantPage from '../components/pages/important-page'

export default ({ data }) => (
  <EnglishLayout>
    <ImportantPage {...data} />
  </EnglishLayout>
)

// English query here

@KyleAMathews Эти файлы являются шаблонами, верно? Значит ли это, что если у меня 3 типа страниц и 7 языков, мне понадобится 21 шаблон? :)

Вышеуказанное является наиболее оптимизированным способом сделать это. Если каждый компонент макета не сильно отличается, вы можете объединить их в один компонент макета, а затем переключать макет в зависимости от того, какой язык активен.

Я не читал подробностей, но есть обсуждение (или, скорее, монолог) о комбо gatsby-plugin-i18n + contentful здесь: angeloocana/gatsby-plugin-i18n#31

@sedubois , ха-ха, да. Резюме: все заработало и включено стартовое репо gatsby-starter-contentful-i18n в документы Gatsby через этот PR: https://github.com/gatsbyjs/gatsby/pull/4138

Заинтересовано в другом решении выше, особенно в сравнении с плагином сообщества re: SEO и т. д.

@mccrodp Ваше решение очень похоже на мое, главное отличие состоит в том, что gatsby-plugin-i18n не требует, чтобы вы явно передавали параметр layout в createPage , но делает это за вас за кулисами. Тем не менее, он по-прежнему использует несколько макетов;)

Я согласился с предложением components/Layout который использует библиотеку i18n, и полностью удалил папку макета gatsby. Таким образом, в gatsby-node я могу создавать страницы для своих локалей, и они имеют доступ ко всему, к чему имеет доступ страница, включая пути.

Затем я могу передать локаль непосредственно компоненту макета, который передает ее i18n.

Немного неудобно обертывать каждый компонент уровня страницы макетом, но это прояснило так много путаницы и значительно упростило мой код.

Привет, @deltaskelta , у тебя есть пример решения? Хотелось бы узнать, можно ли что-то извлечь из этого, чтобы продвигать вверх по течению к плагину сообщества i18n. Спасибо.

весь мой проект сейчас в беспорядке, но я думаю, что могу изложить основы...

  1. Нет макета - потому что (если я правильно помню) макет вступает в игру перед путями или чем-то еще, что было очень важно, что мешало мне дать ему правильные объекты сообщений...

  2. Подайте правильные messages и locale в gatsby-node через контекст...

exports.onCreatePage = ({ page, boundActionCreators }) => {
  const { createPage, deletePage } = boundActionCreators;

  if (page.path.includes('404')) {
    return; // no need for localized 404 pages
  }

  return new Promise(resolve => {
    // if it is not the app page then I need localized static pages
    const pages = localizedPages(page);
    deletePage(page);
    pages.map(page => createPage(page));

    resolve();
  });
};

// to be passed to the localized pages so it can calculate the matchPath
const getMatchPath = lang => {
  return `${locales[lang]['path']}/app/:path`;
};

// this is a helper function that makes pages in each language.
const localizedPages = (page, matchPathFunc) => {
  var pages = [];
  Object.keys(locales).map(lang => {
    const path = locales[lang]['path'] + page.path;

    pages.push({
      ...page,
      path: path,
      matchPath: matchPathFunc ? matchPathFunc(lang) : undefined,
      context: {
        locale: lang,
        messages: locales[lang],
        pathRegex: `/.pages${page.path}./` // so pages can match markdown in their dir
      }
    });
  });

  return pages;
};
  1. Теперь создайте глобальный компонент макета, который нужно вызывать на каждом компоненте уровня страницы...
// this is the main entrypoint for the layout to the site
const GlobalLayout = ({ locale, children, path }) => {
  const theme = getTheme();
  return (
    <MuiThemeProvider theme={theme}>
      <CssBaseline>
        <IntlProvider locale={locale} messages={locales[locale]}>
          <div>
            <Header locale={locale} messages={locales[locale]} path={path} />
            {children}
          </div>
        </IntlProvider>
      </CssBaseline>
    </MuiThemeProvider>
  );
};
  1. Вызовите глобальный компонент макета с компонентами уровня вашей страницы, которые имеют правильную локаль и сообщения, переданные из контекста.
const BlogPost = ({ data, pathContext, location }) => {
  const { locale } = pathContext;
  return (
    <GlobalLayout locale={locale} path={location.pathname}>
      <FullWidth>
        <h1>{data.markdownRemark.frontmatter.title}</h1>
        <h3>{data.markdownRemark.frontmatter.date}</h3>
        <div dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }} />
      </FullWidth>
    </GlobalLayout>
  );
};

Я должен очистить и реорганизовать этот код, потому что я просто сделал это быстро, чтобы доказать, что он может работать, и я собираюсь вернуться к нему позже... Я надеюсь, что это дает вам суть.

Я создал стартер gatsby, который использует js-lingui для перевода сообщений:
https://github.com/dcroitoru/gatsby-starter-i18n-lingui

В реальном приложении мы используем модифицированную версию gatsby-pagination, потому что исходная версия не поддерживает параметр макета. Мы также устанавливаем поле post_id для каждого поста, что позволяет нам находить переводы одного и того же поста, например, в случае с этим демонстрационным приложением оба поста будут иметь одинаковый post_id.

@szimek , есть ли шанс, что вы могли бы поделиться своей измененной гэтсби-пагинацией? Очень хотелось бы посмотреть, так как у меня похожая проблема.

@martynhoyer Мой патч был объединен, поэтому я вернулся к исходной версии gatsby-pagination . Какая у вас проблема?

Привет @sgoudie
Я использую этот учебник: https://www.gatsbyjs.org/blog/2017-10-17-building-i18n-with-gatsby/, но я не могу найти ни одного из моих locales/{lang}/*.json . У кого-нибудь есть ключ?
2018-04-25 12_04_13-o intermedium agora e banco inter

Моя конфигурация:
gasbty-node.js
```javascriptexports.onPostBuild = () => {
console.log('Копирование локалей')
fs.copySync(
path.join(__dirname, '/src/locales'),
path.join(__dirname, '/public/locales')
)
}

```

Добавлять

exports.onPostBootstrap = () => {
    console.log("Copying locales");
    fs.copySync(
        path.join(__dirname, "/src/locales"),
        path.join(__dirname, "/public/locales")
    );
};

до gatsby-node.js

@ThiagoMiranda Я столкнулся с той же проблемой, когда понял, что gatsby develop не вызывает onPostBuild, а вызывает только gatsby build . onPostBootstrap вызывается каждый раз.

Кто-нибудь знает, как создать https://moz.com/learn/seo/hreflang-tag в макетах?

@RobinHerzog Мы создаем их в шаблонах с помощью Helmet. Они специфичны для типа страницы, поэтому, по крайней мере, в нашем случае не имело смысла создавать их в макете.

@szimek спасибо за ответ. Я понимаю, но в моем случае было бы интересно иметь его в макетах.

<link rel="alternate" href={Route['en-us'][this.props.data.prismicDocument.data.group]} hreflang="en-us" /> <link rel="alternate" href={Route['fr-fr'][this.props.data.prismicDocument.data.group]} hreflang="fr-fr" />

На данный момент, как вы сказали, скопируйте эти строки во все шаблоны.

Я только начинаю разработку с React/JavaScript, но все, что я видел для поддержки i18n, было слишком сложным. Вот моя собственная работа для наиболее частого использования: wise-starter

Динамическая перезагрузка, оптимизация для SEO и языки по умолчанию не используют ключ в URL-адресе.
Все страницы .js создаются для всех языков.
Все макеты и .md должны быть созданы для всех языков во избежание ошибок.
Компоненты LangSelect и Link умны i18n.

Если вы можете помочь мне и объяснить мне, как я могу улучшить свой код и стиль, я был бы признателен.

@ Tom-Pichaud, я считаю, что, поскольку вы не передаете сообщения компонентам страницы, они не будут отображаться там статически.

Настройка уценки i18n в gatsby-node выглядит похожей на то, что люди делали здесь, но мне любопытно, получаете ли вы статический рендеринг с отключенным javascript на компонентах вашей страницы?

Да, я получаю статический рендеринг, в любом случае это было моей целью, i18n-react делает свое дело!

@TomPichaud, не могли бы вы поделиться, чем упомянутый вами i18n -react лучше, чем

Одна вещь, которую я не совсем понимаю, - это фактическая необходимость внешних пакетов для загрузки переведенных сообщений (возможно, кроме множественного числа и родственников).

Для простого сайта со статическим содержимым я просто дублирую страницы для каждого языка onCreatePage и передаю локаль до context :

// some file with the locales
const locales = {
  en: {
    path: 'en',
    default: true,
  },
  pt: {
    path: 'pt',
  },
}
// gatsby-node.js
exports.onCreatePage = ({ page, boundActionCreators }) => {
  const { createPage, deletePage } = boundActionCreators

  return new Promise(resolve => {
    deletePage(page)

    Object.keys(locales).map(lang => {
      const localizedPath = locales[lang].default
        ? page.path
        : locales[lang].path + page.path

      return createPage({
        ...page,
        path: localizedPath,
        context: {
          locale: lang,
        },
      })
    })

    resolve()
  })
}

Затем на фактической странице я использую локаль в контексте для запроса содержимого с помощью фильтра graphql.

Допустим, у меня есть домашний контент в /data/home/en.js и /data/home/pt.js :

import React from 'react'

const IndexPage = ({ pathContext: { locale }, ...props }) => {
  const { childHomeJson: data } = props.data.allFile.edges[0].node

  return <div>{data.hello}</div>
}

export const query = graphql`
  query HomeContent($locale: String) {
    allFile(filter: { name: { eq: $locale } }) {
      edges {
        node {
          childHomeJson {
            hello
          }
        }
      }
    }
  }
`

export default IndexPage

отлично работает с netlifyCMS (хотя и немного многословно, пока они не поддерживают i18n) и изображениями в файлах JSON (хотя нам нужно создать новый NodeField с относительным путем, чтобы файловая система Гэтсби получила его)

Разве этого недостаточно для большинства случаев?

Я все еще тестирую и не использовал ничего из этого в производстве, но я думаю об использовании контекстного API реакции для локали, чтобы решить некоторые нерешенные проблемы, такие как локализованные ссылки.

@pbrandone Мне кажется, это отличный подход. Я думаю, что что-то подобное должно быть задокументировано официально.

Спасибо за все комментарии, количество обсуждаемых здесь идей ясно указывает на потребность в хорошо документированной поддержке i18n.

@pbrandone Означает ли это, что вы должны явно указывать все ключи, используемые любыми дочерними компонентами, в IndexPage в этом запросе и передавать переводы всем компонентам через реквизит?

Кроме того, я использую правила множественного числа и относительные даты, поэтому мне все равно приходится загружать дополнительные данные, специфичные для локали:/

Тем не менее, я согласен, что было бы здорово иметь официальную документацию, как сделать i18n без какой-либо библиотеки для простых случаев, а затем, как это сделать с наиболее популярными библиотеками.

@szimek хорошо, в данном случае да.

Добавить react-intl (или любую другую библиотеку i18n) очень просто:

// in src/components/layout/index.js

import React from 'react'
import { IntlProvider, addLocaleData } from 'react-intl'

// Locale data
import enData from 'react-intl/locale-data/en'
import ptData from 'react-intl/locale-data/pt'

// Messages
import en from '../../data/en.json'
import pt from '../../data/pt.json'

const messages = { en, pt }

addLocaleData([...enData, ...ptData])

const Layout = ({ locale, children }) => (
  <IntlProvider locale={locale} messages={messages[locale]}>
    {children}
  </IntlProvider>
)

export default Layout

а потом на страницах:

import React from 'react'
import { FormattedMessage } from 'react-intl'

import Layout from '../components/layouts'

const IndexPage = ({ pathContext: { locale } }) => (
  <Layout locale={locale}>
    <FormattedMessage id="hello" />
  </Layout>
)

export default IndexPage

Но тогда вы не сможете использовать несколько файлов JSON (например, на странице) или иметь все данные CMS в этих файлах для запроса и преобразования с помощью GraphQL.
С подходом graphql мы могли бы, например, иметь некоторые ключи с путями для изображений в этих файлах JSON, чтобы загружать разные изображения для каждой локали и по-прежнему иметь возможность использовать для них gatsby-image .
А затем добавьте netlify CMS для редактирования этих файлов JSON 😃

Не смотрел должным образом gatsby v2, но, по-видимому, там есть компонент StaticQuery который позволяет нам запрашивать дочерние компоненты (кто-то поправит меня, если я ошибаюсь!)

Если это так, мы могли бы создать контекст React, чтобы сделать локаль доступной в любом месте, а затем запросить необходимые ключи в каждом компоненте с фильтрацией локали.

@pbrandone, вы правы, он статически отображается именно так. Я помню, как тестировал его в прошлом и потерпел неудачу, но это было до того, как я хорошо понял, как работает Гэтсби, возможно, у меня была настройка react-intl в браузере Гэтсби, которая, похоже, не может отображаться без javascript. Мое решение теперь выглядит так же, как ваше

@KyleAMathews Я пытаюсь обновить нашу страницу с Gatsby до v2, и у меня возникла проблема с нашей настройкой react-intl и запросами graphql.

Ранее я объяснял, как я использую языковые макеты для загрузки языковых данных в Gatsby v1 — https://github.com/szimek/gatsby-react-intl-example. В Gatsby v2 у меня возникла идея заменить эти макеты языковыми компонентами страницы. У меня был бы компонент src/templates/Post.js зависящий от языка, а затем компоненты, зависящие от языка, такие как src/templates/Post.en.js , src/templates/Post.de.js , которые загружали бы только языковые данные и отображали компонент, не зависящий от языка.

В своем предыдущем комментарии (https://github.com/gatsbyjs/gatsby/issues/3853#issuecomment-367115380) вы показали пример, в котором каждый компонент страницы имеет запрос для конкретного языка.

Проблема в том, что при вызове createPage я передаю имена этих языковых компонентов (например, src/templates/Post.en.js ) как параметр component , но запрос graphql находится в компонент, не зависящий от языка, потому что он __одинаков для всех языков__ (это зависит от locale , но я передаю его в context ). Я хотел бы избежать повторения одного и того же запроса во всех этих языковых компонентах.

Любые идеи, как это решить? Могу ли я извлечь этот запрос в переменную? Когда я попробовал это, Гэтсби жалуется на то, что имена запросов и фрагментов совпадают...

Недавно я добавил стартер Gatsby по умолчанию с функциями многоязычных маршрутов URL и определения языка браузера. (демо)

gatsby-starter-default-intl

Функции:

  • Локализация (многоязычность) обеспечивается react-intl .

  • Автоматическое перенаправление на основе предпочитаемого пользователем языка в браузере, предоставляемое параметром browser-lang .

  • Поддержка многоязычных маршрутов URL-адресов в рамках одного компонента страницы. Это означает, что вам не нужно создавать отдельные страницы, такие как pages/en/index.js или pages/ko/index.js .

  • На основе gatsby-starter-default с наименьшими изменениями.

@wiziple Спасибо! Это выглядит действительно интересно. Я понятия не имел, что вы можете сделать что-то вроде этого: https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L38;) Надеюсь, это все еще работает в вебпаке 4...

Можно ли загрузить данные локали таким же образом здесь https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L6 ? Мы поддерживаем 6 (скоро 7) языков, поэтому было бы здорово, если бы я мог загружать только тот, для которого создаю страницу. ничего страшного, если это невозможно — к счастью, эти файлы данных локали относительно малы.

Мне также придется посмотреть, как вы создаете эти страницы, потому что в моем случае не каждая страница переведена на все языки (нет единого «исходного» языка), поэтому решение с onCreatePage , вероятно, не сработает. в моем случае.

Надеюсь, это решит мою проблему с одним и тем же запросом graphql в каждом компоненте страницы, зависящем от языка.

@szimek
Веб-сайт, которым я управляю, имеет 14 языков, и каждый языковой файл весит 12-15 КБ. Я почти уверен, что нам нужно предоставить правильный язык для каждого языкового маршрутизатора во время сборки, чтобы генерировать данные SEO. Поэтому я не уверен, как я могу справиться с этим, не предоставляя все языки.

Я понимаю, что иногда трудно обеспечить перевод каждой страницы на все языки. Возможно, вы сможете решить эту проблему, предоставив некоторое исключение для onCreatePage в gatsby-node.js . В моем случае я просто решил, не предлагая переведенный язык независимо от языкового маршрутизатора. 😆 Вы можете найти сайт-витрину на продакшне со стартового README.md и проверить его работоспособность.

@wiziple Большое спасибо!

Я использовал ваш компонент withIntl с динамическим трюком require для переводов (я понятия не имею, есть ли какие-либо недостатки в его использовании), и, похоже, он отлично работает. Это решило проблему, с которой я боролся — как обрабатывать один и тот же запрос graphql в нескольких языковых компонентах страницы — с помощью одного компонента страницы для всех языков.

@wiziple спасибо за

lingui кажется лучшей альтернативой . Я не думаю, что @dcroitoru получил должное признание за отличный пример. Просто нужно немного любви, чтобы подтолкнуть его к Гэтсби 2.0

Я согласен с тем, что Lingui действительно очень хорош, хотя все еще нуждается в полном стартере с последней версией Gatsby, а также Lingui. Упомянутый стартер является неофициальным, и в последний раз, когда я проверял, в нем отсутствовали некоторые функции (например, использование загрузчика для запуска компиляции lingui на лету). Автор Lingui @tricoder42 сказал , что предоставит документацию, когда выйдет Lingui v3 (что, похоже, произойдет в ближайшее время).

NB: я заметил, что моя потребность в библиотеке i18n уменьшилась после интеграции CMS (DatoCMS), но мне все еще нужен Lingui для некоторых строк, которые не находят своего места в CMS, а также для множественного числа и, возможно, других вещей позже, поэтому я определенно хочу сохранить его в моей кодовой базе.

В любом случае, в моем случае существование gatsby-plugin-i18n сделало вещи довольно запутанными, поскольку он не поддерживается, имеет запутанное имя и отвлекает внимание от других действительно хороших решений, таких как js-lingui и CMS, которые я затем взял на себя. пока разберемся и соберемся.

Только что сделал этот стартер, чтобы помочь вам, ребята: https://github.com/smakosh/gatsby-starter-i18n
Статья: https://dev.to/smakosh/implement-i18n-to-a-gatsby-site-5ala

Я сделал два примера интернационализации с интеграцией react-intl

Первый пример ориентирован на объединение только текущих переводов в куски js (чего я не смог найти в других плагинах, которые я проверил).

Второй пример фокусируется на использовании динамических запросов для предоставления только запрошенных переводов для данной комбинации страницы/языка.

Надеюсь, эти примеры будут кому-то полезны.

Также сделал быстрый средний пост (и забыл опубликовать его здесь) с почти тем, что находится в https://github.com/gatsbyjs/gatsby/issues/3853#issuecomment -395432693 (хотя и немного более подробно).

https://blog.significa.pt/i18n-with-gatsby-528607b4da81 для всех, кому интересно

Старые вопросы будут закрыты после 30 дней бездействия. Эта проблема не обсуждалась в течение 20 дней и помечается как устаревшая. Ответьте здесь или добавьте ярлык «не устарел», чтобы этот вопрос оставался открытым!

Ребята, прошел почти год 😅

Недавно я выпустил новый плагин gatsby gatsby-plugin-intl, который легко превращает ваш веб-сайт gatsby в интернациональную структуру из коробки.

ДЕМО: https://gatsby-starter-default-intl.netlify.com

  • Готовая платформа интернационализации на базе react-intl

  • Поддержка автоматического перенаправления на основе предпочтительного языка пользователя в браузере.

  • Поддержка многоязычных маршрутов URL-адресов в одном компоненте страницы. Это означает, что вам не нужно создавать отдельные страницы, такие как pages/en/index.js или pages/ko/index.js .

  • Как некоторые из вас, ребята, предложили выше, теперь он связывает только текущий язык во время сборки.

Кроме того, я хочу отметить, что многие из примеров/стартеров i18n на самом деле рендерятся на стороне клиента. Лучший способ проверить, отображается ли приложение как SSR, — просмотреть исходный код и проверить, существуют ли локализованные тексты. Пожалуйста, дважды проверьте этот вопрос, когда вы интернационализируете свой веб-сайт gatsby для SEO.

Ребята, прошел почти год 😅

Недавно я выпустил новый плагин gatsby gatsby-plugin-intl, который легко превращает ваш веб-сайт gatsby в интернациональную структуру из коробки.

ДЕМО: https://gatsby-starter-default-intl.netlify.com

  • Готовая платформа интернационализации на базе react-intl
  • Поддержка автоматического перенаправления на основе предпочтительного языка пользователя в браузере.
  • Поддержка многоязычных маршрутов URL-адресов в одном компоненте страницы. Это означает, что вам не нужно создавать отдельные страницы, такие как pages/en/index.js или pages/ko/index.js .
  • Как некоторые из вас, ребята, предложили выше, теперь он связывает только текущий язык во время сборки.

Кроме того, я хочу отметить, что многие из примеров/стартеров i18n на самом деле рендерятся на стороне клиента. Лучший способ проверить, отображается ли приложение как SSR, — просмотреть исходный код и проверить, существуют ли локализованные тексты. Пожалуйста, дважды проверьте этот вопрос, когда вы интернационализируете свой веб-сайт gatsby для SEO.

Эй, @wiziple, большое спасибо за это, я сходил с ума,
Может быть, я не понял, но есть ли у вас ВСЕ строки языка в одном файле?
Можно ли разделить JSON каждого языка на большее количество файлов, возможно, используя ту же структуру компонентов?

@ cant89 Я понимаю вашу точку зрения, но в настоящее время это невозможно без изменения кода плагина. Или вы можете создать скрипт, который анализирует каталог src и получает все языковые файлы компонентов. Объединитесь в один JSON, затем подключитесь к gatsby develop или gatsby build .
Как только вы получите все файлы JSON и объедините их как вложенный объект, вы также сможете преобразовать его в плоский объект.
https://github.com/yahoo/react-intl/wiki/Upgrade-Guide#flatten-messages-object

У нас есть хороший пример настройки для i18n. https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n. На самом деле у нас нет мнения о фреймворках i18n. Просто выберите один по своему вкусу.

У нас есть хороший пример настройки для i18n. https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n. На самом деле у нас нет мнения о фреймворках i18n. Просто выберите один по своему вкусу.

Круто, спасибо, попробую!
В любом случае, ссылка в ридми не работает, возможно, вы имеете в виду https://using-i18n.netlify.com/ ?

@wardpeet Генерирует ли ваш пример статические переведенные строки (во время сборки)? Или он генерирует текст во время выполнения?

@monsieurnebo похоже на время сборки

@ cant89 мы все еще обновляем DNS, так что это скоро произойдет, используя

@monsieurnebo сейчас время сборки. Он создает копию вашего веб-сайта для каждого языка, поэтому все можно построить статически. Это означает, что ваш веб-сайт остается быстрым, поскольку все это просто .html.

не уверен, где еще спросить об этом, но немного уместно. любой из этих плагинов поддерживает pathPrefix Гэтсби?

i.e. 
// gatsby-config.js

modules.exports = {
    pathPrefix: 'bar'
}

https://foo.com => https://foo.com/bar

but now my language locales will now be https://foo.com/bar/de-DE/
when I think I would prefer it be https://foo.com/de-DE/bar if that makes sense.

Хм, интересно, я думаю, что первое обычно имеет больше смысла, поскольку pathPrefix как бы делает ваш домен domain.com/prefix поэтому измените корень, когда вы установили Gatsby в подкаталог, если вы не установите его в подкаталог, который вам не нужен, если вы используете подкаталог, изменяющий префикс после того, как язык сломает его.

Теперь возникает вопрос, почему вы вообще используете pathPrefix ?

Ссылка: документы pathPrefix

Привет,

Большинство дискуссий здесь о том, как создать сайт Гэтсби. Но есть разница между работающей POC и оптимизированной готовой к производству системой.

Если вам интересно узнать больше о разделении кода и файлах i18n, а также о том, почему большинство решений в этой теме не оптимизированы, вы найдете этот выпуск полезным.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги