使用Gatsby和CosmicJS创建多语言网站

来自菜鸟教程
跳转至:导航、​搜索

最近有朋友让我建一个有日文版和英文版的网站。 我从来没有写过一个国际化的网站,但我对本地化应该如何工作有强烈的看法。 因此,这是使用 Cosmic JS(一种无头 CMS)在 Gatsby 中实现国际化的一种方法。

一点上下文

有很多方法可以实现本地化,但始终没有灵丹妙药。 每种方法都以不同的方式解决问题。 所以这是我的背景:

  • 我想建立一个维护成本尽可能低的网站。
  • 内容作者没有任何编程经验
  • 站点的维护成本应尽可能低。
  • 网站不应强迫用户使用网站的一个版本。

最后一点对我来说非常重要。 当您在国外时,某些网站会强制您使用其网站的本地版本。 当 [array of multinational companies] 强迫我访问网站的 [long array of languages I don't understand] 版本时,它让我发疯。 我对页面的自动翻译有同样的问题。 如果我想自动翻译我的网站,我可以使用出色的 Google 翻译 Chrome 扩展程序

本网站适用于日语和英语用户。 所以网站的所有页面都应该有英文版和日文版。 如果用户想要更改网站的当前版本,她可以单击导航栏中的语言菜单。

我的方法

Gatsby 和 React 提供 许多工具来处理本地化 (l10n) 和国际化 (i18n)

我首先使用 gatsby-plugin-i18n 来轻松生成路由。

例如,/page/team.ja.js 将生成以下 URL:/ja/teamja 是日本的语言代码)。

这是一个非常好的插件,但问题是它不是程序化的。 我必须为每种语言编写一个新文件。 在每个文件中,我必须进行特定的 GraphQL 查询来获取数据。 例如,如果我向我的 CMS 引入一种新语言,我必须使用新的语言扩展再次创建所有路由。

因此,我决定在没有任何插件的情况下构建 l10n。 这个项目的所有代码都可以在 https://github.com/alligatorio/kodou 上找到。

在这种情况下,内容作者完全负责本地化。 当她编写网站的日文版时,她应该确保日期格式正确。 这就是我们不使用依赖于国际化 API 的 react-intl 的原因,这将是未来帖子的主题。

设置 Cosmic JS

Cosmic JS 是一个很棒的无头 CMS 选项,允许您在创建新对象类型时激活本地化。

不要忘记选择优先区域设置,否则新对象将无法保存。

在我们的新站点中,我们有一个团队页面,因此我们创建了一个团队成员对象。 当我们创建一个新的团队成员时,我们现在可以选择它的语言。

现在要从 Gatsby 访问这些数据,我们需要添加 gatsby-source-cosmicjs 源插件:

$ yarn add gatsby-source-cosmicjs

然后我们需要通过在plugins中添加以下代码来配置gatsby-config.js使用gatsby-source-cosmicjs

模块:gatsby-config.js

{
  resolve: "gatsby-source-cosmicjs",
  options: {
    bucketSlug: process.env.COSMIC_BUCKET,
    // We add the 'team-members' object type to be able to fetch it later
    objectTypes: ["team-members"],
    // If you have enabled read_key to fetch data (optional).
    apiAccess: {
      read_key: process.env.COSMIC_ENV_KEY,
    }
  }
}

在我们的其余代码中,我们可以通过运行以下命令从 Cosmic JS 访问团队成员数据:

graphql(`
  {
    allCosmicjsTeamMembers  {
      edges {
      # Here we have the structure of out `team-members` object
        node {
          title
          locale
          content
          metadata {
            profile_picture {
              imgix_url
            }
          }
        }
      }
    }
  }
`)

现在本地化魔法发生了。

生成本地化页面

我希望我的朋友能够自己做任何他想做的改变。 所以我完全放弃了 /pages 目录,转而使用 /templates 目录。 Gatsby 模板允许我们拥有可重用的内容并以编程方式创建页面; 这正是我们需要做的!

在查看模板文件之前,让我们看看如何从 Cosmic JS 获取数据以创建新页面。

模块:gatsby-node.js

// langs contains the languages of our blog and default langKey is the default language of the site
// To be fully programmatic we could calculate langs
// here langs = ['en', 'ja'] and defaultLangKey = 'en'
const { langs, defaultLangKey } = require('../config/languages')
const path = require(`path`)
const { localizeUrl, createLanguagesObject } = require('../utils/localization')

exports.createPages = async ({ actions, graphql }) => {
  const { createPage } = actions

  const result = await graphql(`
    {
      allCosmicjsTeamMembers  {
        edges {
          node {
            title
            locale
            content
            metadata {
              profile_picture {
                imgix_url
              }
            }
          }
        }
      }
    }
  `)

  if (result.errors) {
    console.error(result.errors)
  }

  // Creates a profiles object with out site's languages 
  const profiles = createLanguagesObject(langs)
  // profiles = {
  // 'en': [],
  // 'ja': []  
  // }

  // converting the raw cosmic data into a more useable data structure
  result.data.allCosmicjsTeamMembers.edges.forEach(({ node }) => {
    profiles[node.locale].push(node)
  })
  // profiles = {
  // 'en': [...all English profiles],
  // 'ja': [...all Japanese profiles]  
  // }

  // we create a new page for each language
  langs.forEach(lang =>{
     createPage({
      // the localizeUrl function creates a url which takes into consideration what the default language is
      path: localizeUrl(lang, defaultLangKey, '/team'),
      component: path.resolve(`src/templates/team.js`),
      context: {
        profiles: profiles[lang]
      }
    })
  })
}

此代码将使用路径 /ja/team/team 创建两个新页面(没有 /en,因为我们将英语设置为默认语言)。

如您所见,createPage 将具有 3 个字段 pathcomponentcontext 的对象作为参数。 路径只是我们希望新页面拥有的路径。 component 是我们要使用的模板。 context 是我们要传递给模板的数据。 在这里,我们传递以所需语言编写的配置文件。

模板

让我们看一下我们的团队模板。

import React from "react"
import Layout from "../components/layout"
import SEO from "../components/seo"

const TeamPage = (props) => {
  // We will see about pageContext in the next section
  const {profiles} = props.pageContext
  return (
  <Layout location={props.location}>
    <SEO title="Team" />
    <h1>Team</h1>
    // Iterating trough the array of profiles
    {profiles.map((profile,i)=>(
      <div key={i} className="columns">
        <div className="column">
          // Here are some nice profile pictures of our team members
          <div className="square-image" style={{backgroundImage: `url("${profile.metadata.profile_picture.imgix_url}")`}}/>
        </div>
        <div className="column is-two-thirds">
          <div className="team-member-title">{profile.title}</div>
          // Here is some html content we get from Cosmic
          <div dangerouslySetInnerHTML={{ __html: profile.content }}/>
        </div>
      </div>
      )
    )}
  </Layout>
  )
}

export default TeamPage

总而言之,上面的代码采用 profiles 属性,它是我们从 Cosmic JS 获得的配置文件数组。 每个配置文件都有一个配置文件图片对象、一个 title 和一个 content 字段。 content 实际上是一个 HTML 字符串,所以我们必须使用 dangerouslySetInnerHTML 属性来设置它。

要使此模板正常工作,请务必提前准备 CSS 文件以获得一致的结果。 我的朋友将无法在 Cosmic 的 WYSIWYG 中添加类名或 ID。

还有很多话要说和做:

  • 创建导航栏和位置感知布局
  • 如何使用国际化 API
  • 如何将用户软重定向到他们的网站版本

您可以浏览 Github 存储库 以了解我如何解决这些问题并在 kodou.me 上查看结果。 或者使用 Alligator.io 查看我们是否上传了有关该主题的一些新内容。 但我认为在一篇文章中处理的内容已经很多。 以上,我希望这对您建立自己的国际化网站有所帮助,敬请期待更多! 😉