Gatsby: 为使用 Gatsby 国际化网站添加官方指南

创建于 2018-02-04  ·  74评论  ·  资料来源: gatsbyjs/gatsby

看到我对另一个问题的评论的反应,我决定打开这个问题。

我认为 i18n 比它应该的要难得多。 我在 Gatsby 制作的网站上找不到任何用于国际化内容的官方文档或插件。 我遇到了jsLingui ,它似乎解决了大部分问题,但仍然没有关于维护不同语言的降价文件/页面的指南。

documentation

最有用的评论

大家好,快一年了😅

我最近发布了新的 gatsby 插件gatsby-plugin-intl ,它可以轻松地将您的 gatsby 网站作为一个开箱即用的国际化框架。

演示: https :

  • react-intl提供支持的开箱即用国际化框架

  • 支持根据用户在浏览器中的首选语言自动重定向

  • 在单个页面组件中支持多语言 url 路由。 这意味着您不必创建单独的页面,例如pages/en/index.jspages/ko/index.js

  • 正如上面的一些人所建议的那样,现在它在构建期间仅捆绑当前语言。

另外,我想提一下,许多 i18n 示例/启动器实际上是在客户端呈现的。 检查应用程序是否呈现为 SSR 的最佳方法是查看源代码并检查本地化文本是否存在。 当您将 gatsby 网站国际化以进行 SEO 时,请仔细检查此问题。

所有74条评论

这篇关于在 GatsbyJS 中使用 i18next 的文章,这对我来说是迄今为止最先进的方法。

但我不觉得 i18next 是“静态方式”。

关于博客文章,我有以下问题/保留意见:
https://twitter.com/semdubois/status/930389055388508160

https://github.com/angeloocana/gatsby-plugin-i18n但它有一些限制,并且没有受到太多活动/关注。 在 Gatsby 存储库中移动它可能会有所帮助。 我也希望有一个适当的综合解决方案。

我也偶然发现了 js lingui,它看起来很有希望,尤其是在 v2 刚刚发布的情况下。

我也想弄清楚这一点。 在帖子中使用 i18next 方法是最方便的,感觉也很直观,但我还有两个问题......

  1. 如何为 gatsby-plugin-i18n 解决方案中的语言合并不同的降价文件?

  2. 这是否完全放弃了内容的静态渲染?

仅供参考@angeloocana

我将用react-intl写一个简短的总结,说明我们目前如何处理它。 此应用程序尚未投入生产,因此我们可能仍会发现此设置存在一些问题,但到目前为止它似乎运行良好。

我们将几乎所有的内容(从我们的 Wordpress 博客迁移过来)保存在gatsby-source-contentful插件来获取这些数据,然而,之前我们自己获取这些数据并将其转换为 JSON 文件并使用了gatsby-source-filesystem插件(我们使用了像/en/blog/...这样的文件夹结构/de/blog/... ),所以是否使用 Contentful 并不重要,只要每个节点都知道它的语言环境。

我们还有一些文本,如按钮标签、一些链接或静态内容,它们不是来自 Contentful,而是在Transifex 中翻译并同步到存储在 repo 中的 JSON 文件。 对于这一部分,我们需要使用一些 i18n 库并决定使用react-intl ,因为我已经知道它并且我知道它也可以处理日期和数字格式。 以下是我们的设置方式: https : intl.formatMessage和模板中的<FormattedMessage /><FormattedDate />等组件。

@szimek如果我理解正确,那么您有react-intl处理组件文本翻译,而帖子位于 pages 目录下的硬编码路线中?

这意味着硬编码的路线是唯一静态渲染的路线吗? 并且 i18n 组件间翻译是动态呈现的?

@deltaskelta我不确定我是否理解您的问题。

我们没有任何自定义客户端路由(如例如描述这里的那一刻),仅静态生成的页面。 在生成post页面时(我们还使用gatsby-pagination插件的略微修改版本生成特定于区域设置和分页的indexcategory页面),我们是使用以下代码:

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

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

其中 path 是我们页面的未来版本(语言)的路径名。

构建页面

使用与上面相同的示例,我们的目标是构建主页和项目页面的 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();
  });
}

我们现在每个页面都有两个版本,带有一个自定义路径名(来自我们的页面信息文件)。 您可能已经注意到我们通过pathContextlanguage信息传递给每个页面。 此值将用于每个页面以显示正确的语言。

显示正确的语言

我们在页面上使用 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,它不像上面的方法那么复杂,但是有一些限制(参见 repo 问题),我是对 React-intl 不太满意。 很想试试@tricoder42 的https://github.com/lingui/js-lingui ,只是没时间。

@monsieurnebocreatePage之前运行deletePage的目标是什么? 我喜欢你的解决方案,我已经尝试实施它,但我有一些错误。

  • 我要么得到不正确的pathContext.language而不使用deletePage

  • 或者当我像您的示例一样包含deletePage时出现构建错误。 (它说, TypeError: Cannot read property 'id' of undefined当生成获取到run graphql queries期)

这是迄今为止我见过的最好的解决方案。

@deltaskelta很高兴看到你喜欢它!

deletePage用于取消 Gatsby 创建的默认页面。 如果您不添加此内容,您将获得自定义页面默认页面。

检查您的public目录有无此行,您会发现不同之处;)

关于您的错误,没有代码很难猜测。 你能用你的代码做一个存储库吗? 那我就去看看吧。

编辑:你会对一个例子感兴趣吗?

哦,这与 deletePage 调用无关。 自从我修改了您的示例以来,这是一个不复制 pages 对象的问题。 离开 js 一段时间后,我总是在对象复制方面绊倒;)

@monsieurnebo我一直在玩您的解决方案,看来您仍然需要javascript来从json文件加载语言环境,对吗? 最重要的是,似乎所有用react-i18next HOC 包装的组件都需要 javascript 来呈现组件中的任何内容......

您能否确认这是否是您的工作方式?

编辑:顺便说一句,我错过了你提到的一个例子,如果你有时间我很想看到一个完整的例子。

@deltaskelta我有空的时候会做一个例子:)

@monsieurnebo您能否确认您的方法不会将字符串静态呈现到html中并且需要javascript?

是的,这就是为什么它不完全是“静态的生活方式”😐

我想要一个生成静态文本的插件。

嗯,我明白了。 静态渲染将是我需要去的方式......

我在想,因为上下文已经与gatsby-node大多数页面的语言名称一起传递,那么在每个页面的 graphql 查询中询问他们的消息键并不会太难并以这种方式传递它们,但如果可能的话,我宁愿一直使用 i18n 工具...

@szimek react-intl如何在构建步骤中呈现翻译而不使用任何js?

如前所述,据我所知 Gatsby-plugin-i18n 生成静态文本。 你检查过它的例子吗?

@deltaskelta好吧,所有翻译都在构建期间可用(这就是我使用多种布局的原因),所以它“正常工作”™️ ;)我只是希望它在 v2 中继续工作......如果你可以准备一个示例应用程序想。 我还没有真正研究过gatsby-plugin-i18n ,因为我正在使用 Contentful,而不是文件。

我还没有阅读详细信息,但这里有关于 gatsby-plugin-i18n + 内容丰富的组合的讨论(或者更确切地说是独白): https :

@szimek我会对一个例子非常感兴趣。 @sedubois我知道 gatsby-plugin-i18n 也会生成静态文本,但我看不到 i18next 中为了生成完全静态文件而缺少的魔法发生在哪里......

我想我现在可能明白为什么...在渲染期间您是否通过pathContext in gatsby-node传递了react-intl消息?

编辑:如果是这种情况(这是有道理的),那么使用的 i18n 库“有点”无关紧要,因为一个是通过上下文传递消息并静态渲染,这是一个纯粹的 gatsby 设置,而 i18n 库只处理特殊情况,例如日期和复数与 js。

在进一步测试i18nextreact-intl ,似乎i18next的翻译 HOC 需要 javascript 才能加载,因此即使消息通过上下文传递并静态呈现,任何使用 translate HOC 都会渲染整个组件需要 javascript 才能运行。

另一方面, react-intlFormattedMessage组件呈现默认消息(可以基于传递给它的上下文)并在构建时静态呈现到 html 中。

按照设计,如果您想在 HTML 中实现静态渲染的翻译,我认为 i18n 更自然的集成将是与 react-intl。 如果我误解了你们使用的流程,请纠正我

@mattferderer在这里有正确的想法,但我认为它需要一些调整。 https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment -362715706

布局在页面之前呈现,因此如果不创建多个布局,就无法通过createPages函数通过上下文传递消息(如果我在这里错了,请纠正我)。 那么,为了使 i18n 最简单,我认为布局应该只调用children() ,然后通过gatsby-node为每种语言创建不同的路径来实现每种语言效果的不同布局pages/index索引页面,可以通过上下文传递消息

编辑:很抱歉漫无边际,但一切都变得清晰了,我不得不把它记录在某个地方

EDIT2:我上面肯定错了,页眉和页脚需要进入布局,我只是不知道如何在不创建多个布局的情况下将消息发送给他们。 我能想到的唯一另一种方法是拆分网址并为语言环境进行正则表达式...但这感觉像是一件很棘手的事情

@KyleAMathews v2 只有一个布局,我们如何通过基于路径或语言键数组的上下文将 i18n 消息等数据传递到根布局组件?

如果可以做到这一点,那么实现 i18n 将很容易,但是如果不制作多个布局,我看不到如何做到这一点

@deltaskelta这是我们正在做的一个例子: https : react-intl组件,如何使用react-intl injectIntl HOC 设置标题(或任何其他元标记)用于使用intl.formatMessage帮助器等的索引页面。

生成的页面是:

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

在实际应用中,我们使用的是gatsby-pagination的修改版本,因为原始版本不支持布局选项。 我们还为每个帖子设置了post_id字段,允许我们查找同一帖子的翻译,例如在这个演示应用程序的情况下,两个帖子将具有相同的post_id

顺便提一句。 我刚刚意识到我们很可能需要为每种语言生成单独的站点地图,以便 Swifttype(我们使用的搜索引擎)知道我们得到了哪些页面。

@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 + 内容丰富的组合的讨论(或者更确切地说是独白):angeloocana/gatsby-plugin-i18n#31

@sedubois ,哈哈,是的。 摘要:让它工作并通过此 PR 在 Gatsby 文档中包含gatsby-starter-contentful-i18n入门 repo: https :

对上面的其他解决方案感兴趣,尤其是与社区插件 re: SEO 等的比较。

@mccrodp您的解决方案看起来与我的非常相似,主要区别在于gatsby-plugin-i18n不需要您将layout选项显式传递给createPage ,而是为您完成在幕后。 但是,它仍在使用多种布局;)

我已经接受了@KyleAMathews 的建议,并制作了一个使用 i18n 库的components/Layout并完全删除了我的 gatsby 布局文件夹。 这样,在gatsby-node我可以为我的语言环境创建页面,并且他们可以访问页面可以访问的所有内容,包括路径。

然后我可以将语言环境直接传递给布局组件,该组件将其传递给 i18n。

必须用布局包装每个页面级组件有点不方便,但它消除了很多混乱并简化了我的代码。

@deltaskelta ,你有你的解决方案的例子吗? 很想看看是否可以从中学到任何东西来推动社区 i18n 插件的上游。 谢谢。

我的整个项目现在处于混乱状态,但我想我可以列出基础知识......

  1. 没有布局 - 因为(如果我没记错的话),布局在路径或其他非常重要的东西之前发挥作用,这阻止了我给它正确的消息对象......

  2. 通过上下文在gatsby-node输入正确的messageslocale ...

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 starter,它使用 js-lingui 来翻译消息:
https://github.com/dcroitoru/gatsby-starter-i18n-lingui

在实际应用中,我们使用的是 gatsby-pagination 的修改版本,因为原始版本不支持布局选项。 我们还为每个帖子设置了 post_id 字段,允许我们查找同一篇文章的翻译,例如,在这个演示应用程序的情况下,两个帖子将具有相同的 post_id。

@szimek你有没有机会分享你修改后的 gatsby-pagination? 我很想看到它,因为我自己也有类似的问题。

@martynhoyer我的patch已被合并,所以我切换回gatsby-pagination的原始版本。 你有什么问题?

嗨@sgoudie
我正在使用本教程: https : 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

@ThiagoMirandagatsby 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 smart。

如果您能帮助我并向我解释如何改进我的代码和风格,我将不胜感激。

@Tom-Pichaud,我相信由于您没有将消息传递给页面组件,因此它们不会在那里静态呈现。

gatsby-node的降价 i18n 设置看起来与人们在这里所做的类似,但我很好奇您是否在页面组件上禁用了 javascript 进行静态渲染?

是的,我确实得到了静态渲染,这就是我的目标, i18n-react就可以了!

@TomPichaud您是否可以分享您提到的 i18n-react 如何比js-lingui更好?

我不太明白的一件事是实际需要外部包来加载翻译的消息(可能除了复数和亲属之外)。

对于具有静态内容的简单网站,我只是复制每种语言的页面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 以便 Gatsby 的文件系统得到它)很好地工作

这对于大多数情况来说还不够吗?

我仍在测试并且没有在生产中使用任何这些,但我正在考虑使用 react 的上下文 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 Context 以使语言环境在任何地方都可用,然后使用语言环境过滤查询每个组件中的必要键

@pbrandone你是对的,它确实以这种方式静态渲染。 我记得过去测试过它并失败了,但那是在我很好地掌握 gatsby 的工作原理之前,我可能已经在 gatsby 浏览器中设置了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.jssrc/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.jspages/ko/index.js

  • 基于gatsby-starter-default修改最少。

@wiziple谢谢! 看起来真的很有趣。 我不知道你可以这样做:https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L38 ;) 希望它仍然有效在 Webpack 4...

是否可以在这里以相同的方式加载语言环境数据https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L6 ? 我们支持 6 种语言(很快会支持 7 种语言),所以如果我可以只加载我正在为其构建页面的语言,那就太好了。 如果不可能,那也没什么大不了的——幸运的是,这些语言环境数据文件相对较小。

我还必须研究您如何生成这些页面,因为在我的情况下,并非每个页面都被翻译成所有语言(没有单一的“源”语言),所以onCreatePage的解决方案可能不起作用就我而言。

希望这将解决我在每个特定语言页面组件中使用相同的 graphql 查询的问题。

@szimek
我管理的网站有 14 种语言,每种语言文件为 12-15 KB。 我很确定我们需要在构建时为每个语言路由器提供正确的语言,以便生成 SEO 数据。 所以我不确定在不提供所有语言的情况下如何处理这个问题。

我知道有时很难将每一页都翻译成所有语言。 您可以通过在gatsby-node.js onCreatePage上提供一些例外来解决此问题。 就我而言,我只是简单地通过不提供翻译语言来解决,而不管语言路由器如何。 😆 您可以从 starter README.md 中找到关于生产的展示网站并检查其性能。

@wiziple 非常感谢!

我使用您的withIntl组件和动态require技巧进行翻译(我不知道使用它是否有任何缺点),它似乎工作得很好。 它解决了我一直在努力解决的问题——如何在多个特定于语言的页面组件中处理相同的 graphql 查询——通过为所有语言提供一个页面组件。

@wiziple感谢您的回购分享。 让我走上正确的道路😄🎉

lingui 似乎是一个更好的选择。 我不认为@dcroitoru是一个很好的例子。 只需要一点爱就可以把它推到 Gatsby 2.0

我同意 Lingui 确实很好,虽然仍然需要一个完整的启动器,用最新版本的 Gatsby 和 Lingui。 提到的启动器是非官方的,并且在我上次检查时缺少一些功能(例如,使用加载器运行 lingui compile on the fly)。 Lingui 的作者@tricoder42表示,当 Lingui v3 发布时(似乎很快),他会提供文档。

注意:我注意到在集成 CMS (DatoCMS) 后我对 i18n 库的需求减少了,但我仍然需要 Lingui 来处理一些在 CMS 中找不到位置的字符串,以及复数化和以后可能的其他事情,所以我肯定想把它保存在我的代码库中。

无论如何,在我的情况下,gatsby-plugin-i18n 的存在使事情变得非常混乱,因为它没有维护,名称令人困惑,并且将注意力从这些其他非常好的解决方案(例如 js-lingui 和 CMSes)上移开,然后我采取了同时弄清楚并组装在一起。

刚刚制作了这个启动器来帮助你们: https :
文章: https :

我用 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 :

  • react-intl提供支持的开箱即用国际化框架

  • 支持根据用户在浏览器中的首选语言自动重定向

  • 在单个页面组件中支持多语言 url 路由。 这意味着您不必创建单独的页面,例如pages/en/index.jspages/ko/index.js

  • 正如上面的一些人所建议的那样,现在它在构建期间仅捆绑当前语言。

另外,我想提一下,许多 i18n 示例/启动器实际上是在客户端呈现的。 检查应用程序是否呈现为 SSR 的最佳方法是查看源代码并检查本地化文本是否存在。 当您将 gatsby 网站国际化以进行 SEO 时,请仔细检查此问题。

大家好,快一年了😅

我最近发布了新的 gatsby 插件gatsby-plugin-intl ,它可以轻松地将您的 gatsby 网站作为一个开箱即用的国际化框架。

演示: https :

  • react-intl提供支持的开箱即用国际化框架
  • 支持根据用户在浏览器中的首选语言自动重定向
  • 在单个页面组件中支持多语言 url 路由。 这意味着您不必创建单独的页面,例如pages/en/index.jspages/ko/index.js
  • 正如上面的一些人所建议的那样,现在它在构建期间仅捆绑当前语言。

另外,我想提一下,许多 i18n 示例/启动器实际上是在客户端呈现的。 检查应用程序是否呈现为 SSR 的最佳方法是查看源代码并检查本地化文本是否存在。 当您将 gatsby 网站国际化以进行 SEO 时,请仔细检查此问题。

@wiziple非常感谢它,我正在疯狂地寻找本地化 Gatsby 的解决方案。
也许我没有明白这一点,但是,您是否仅在一个文件中拥有一种语言的所有字符串?
是否可以将每种语言的 JSON 拆分为更多文件,也许使用相同的组件结构?

@ cant89我明白你的意思,但目前不更改插件代码是不可能的。 或者您可以制作一个解析 src 目录并获取所有组件语言文件的脚本。 合并到一个 JSON 中,然后挂钩到gatsby developgatsby 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,所以现在很快就可以使用-i18n.netlify.com/是正确的链接。

@monsieurnebo它在构建时。 它为每种语言创建您网站的副本,因此可以静态构建所有内容。 这意味着您的网站保持快速,因为一切都只是一个 .html。

不知道在哪里可以问这个,但有点相关。 这些插件中的任何一个都支持 gatsby 的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 文档

你好,

这里的大部分讨论都是关于如何创建一个 gatsby 网站。 但是,让 POC 工作与拥有优化的生产就绪系统是有区别的。

如果您有兴趣阅读有关代码拆分和 i18n 文件的更多信息,以及为什么此线程中的大多数解决方案未优化,您会发现此问题很有用

此页面是否有帮助?
0 / 5 - 0 等级