Увидев реакцию на мой комментарий по другому вопросу , я решил открыть этот вопрос.
Я думаю, что i18n намного сложнее, чем должно быть. Я не смог найти никакой официальной документации или плагина для интернационализации контента на сайтах, созданных Гэтсби. Я наткнулся на jsLingui , который, кажется, решает большинство проблем, но до сих пор нет руководств по поддержке, например, файлов/страниц уценки на разных языках.
Есть эта статья об использовании i18next с GatsbyJS , который на данный момент является самым продвинутым методом для меня.
Но я не чувствую, что i18next — это «статический путь».
Что касается сообщения в блоге, у меня были следующие вопросы/оговорки:
https://twitter.com/semdubois/status/930389055388508160
Существует https://github.com/angeloocana/gatsby-plugin-i18n, но у него есть несколько ограничений, и он не получает большой активности/внимания. Это может помочь переместить его в репозиторий Gatsby. Я тоже хотел бы получить правильное консолидированное решение.
Я также наткнулся на js lingui, и он кажется многообещающим, особенно с только что вышедшей версией 2.
Я тоже пытаюсь в этом разобраться. Использование метода i18next в посте является наиболее удобным и интуитивно понятным, но у меня осталось два вопроса....
как я могу включить разные файлы уценки для языков, например, в решении gatsby-plugin-i18n?
Это полностью исключает статическую визуализацию контента?
К вашему сведению @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/
во время сборки.onCreatePage
(в gatsby-node.js
).Переводы в основном сгруппированы по страницам с одним пространством имен (= файл JSON) на страницу + один глобальный файл (для общих текстов, таких как верхний и нижний колонтитулы).
|- src/
|- locales/
|- en/
|- foo.json
|- bar.json
|- fr/
|- foo.json
|- bar.json
К сожалению, нет горячей перезагрузки на модификациях локалей 👎
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. Спасибо.
весь мой проект сейчас в беспорядке, но я думаю, что могу изложить основы...
Нет макета - потому что (если я правильно помню) макет вступает в игру перед путями или чем-то еще, что было очень важно, что мешало мне дать ему правильные объекты сообщений...
Подайте правильные 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;
};
// 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>
);
};
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
. У кого-нибудь есть ключ?
Моя конфигурация:
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 и определения языка браузера. (демо)
Функции:
Локализация (многоязычность) обеспечивается 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, а также о том, почему большинство решений в этой теме не оптимизированы, вы найдете этот выпуск полезным.
Самый полезный комментарий
Ребята, прошел почти год 😅
Недавно я выпустил новый плагин 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.