使用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/team
(ja
是日本的语言代码)。
这是一个非常好的插件,但问题是它不是程序化的。 我必须为每种语言编写一个新文件。 在每个文件中,我必须进行特定的 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 个字段 path
、component
和 context
的对象作为参数。 路径只是我们希望新页面拥有的路径。 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 查看我们是否上传了有关该主题的一些新内容。 但我认为在一篇文章中处理的内容已经很多。 以上,我希望这对您建立自己的国际化网站有所帮助,敬请期待更多! 😉