一个朋友最近让我建立一个网站,有日语和英语版本。我从来没有写过一个国际化的网站,但我对本地化应该如何工作有强烈的意见。因此,这里有一种使用Cosmic JS(一种无头CMS)在Gatsby中实现国际化的方法。
一点上下文
本地化的方法有很多种,而且一如既往地没有灵丹妙药。每种方法都以不同的方式解决问题。因此,我的背景是:
- 我想建立一个网站,尽可能少的维护成本。
- 内容编写者没有任何编程经验
- 网站的维护成本应尽可能低。
- 网站不应该强迫用户使用网站的一个版本。
最后这一点对我来说非常重要。当你在国外时,一些网站会强迫你使用他们网站的当地版本。当[一大堆跨国公司]
强迫我使用`[一长串我不懂的语言]‘版本的网站时,我简直要疯了。我对页面的自动翻译也有同样的问题。如果我想要自动翻译我的网站,我可以使用非常棒的谷歌翻译Chrome extension.
该网站面向日语和英语用户。因此,网站的所有页面都应该有一个英文版本和一个日文版本。如果用户想要更改网站的当前版本,可以点击导航栏中的语言菜单。
我的做法
Gatsby和Reaction提供许多工具来接近本地化(L10n)和国际化(i18n).
我首先使用gatsby-plugin-i18n轻松生成路由。
例如,/page/Team.ja.js
会生成如下URL:/ja/Team
(ja
是日本的语言代码)。
这是一个非常好的插件,但问题是它不是程序化的。我必须为每种语言编写一个新文件。在每个文件中,我必须进行特定的GraphQL查询来获取数据。例如,如果我将一种新语言引入我的CMS,我必须使用新语言扩展重新创建所有路线。
所以,我决定在没有任何插件的情况下构建l10n。这个项目的所有代码都可以在https://github.com/alligatorio/kodou上找到。
在这种情况下,内容作者完全负责本地化。当她写日语版的网站时,她应该确保日期格式正确。这就是为什么我们没有使用act-intl
,它依赖于国际化API,并将成为未来帖子的主题。
设置宇宙JS
Cosmic JS是一个很棒的无头CMS选项,允许您在创建新对象类型时激活本地化。
中的L10N选项
不要忘记选择优先区域设置,否则新对象将不会保存。
在我们的新站点中,我们有一个团队页面,因此我们创建一个Team Members对象。当我们创建一个新的团队成员时,我们现在可以选择它的语言。
中的团队成员语言选择
现在,要从Gatsby访问该数据,我们需要添加`gatsby-source-Cosmicjs‘源插件:
1$ yarn add gatsby-source-cosmicjs
然后,我们需要在plugins
中添加以下代码,将gatsby-config.js
配置为使用gatsby-source-Cosmicjs
。
1[label Module: gatsby-config.js]
2{
3 resolve: "gatsby-source-cosmicjs",
4 options: {
5 bucketSlug: process.env.COSMIC_BUCKET,
6 // We add the 'team-members' object type to be able to fetch it later
7 objectTypes: ["team-members"],
8 // If you have enabled read_key to fetch data (optional).
9 apiAccess: {
10 read_key: process.env.COSMIC_ENV_KEY,
11 }
12 }
13}
在我们代码的其余部分中,我们可以通过运行以下命令从Cosic JS访问团队成员数据:
1graphql(`
2 {
3 allCosmicjsTeamMembers {
4 edges {
5 # Here we have the structure of out `team-members` object
6 node {
7 title
8 locale
9 content
10 metadata {
11 profile_picture {
12 imgix_url
13 }
14 }
15 }
16 }
17 }
18 }
19`)
现在,本地化魔法发生了。
生成本地化页面
我希望我的朋友能够自己做任何他想做的改变。因此,我完全放弃了/pages
目录,转而使用/temples
目录。Gatsby模板允许我们拥有可重用的内容并以编程方式创建页面;这正是我们需要做的!
在查看模板文件之前,让我们先看看如何从Cosic JS获取数据以创建新页面。
1[label Module: gatsby-node.js]
2// langs contains the languages of our blog and default langKey is the default language of the site
3// To be fully programmatic we could calculate langs
4// here langs = ['en', 'ja'] and defaultLangKey = 'en'
5const { langs, defaultLangKey } = require('../config/languages')
6const path = require(`path`)
7const { localizeUrl, createLanguagesObject } = require('../utils/localization')
8
9exports.createPages = async ({ actions, graphql }) => {
10 const { createPage } = actions
11
12 const result = await graphql(`
13 {
14 allCosmicjsTeamMembers {
15 edges {
16 node {
17 title
18 locale
19 content
20 metadata {
21 profile_picture {
22 imgix_url
23 }
24 }
25 }
26 }
27 }
28 }
29 `)
30
31 if (result.errors) {
32 console.error(result.errors)
33 }
34
35 // Creates a profiles object with out site's languages
36 const profiles = createLanguagesObject(langs)
37 // profiles = {
38 // 'en': [],
39 // 'ja': []
40 // }
41
42 // converting the raw cosmic data into a more useable data structure
43 result.data.allCosmicjsTeamMembers.edges.forEach(({ node }) => {
44 profiles[node.locale].push(node)
45 })
46 // profiles = {
47 // 'en': [...all English profiles],
48 // 'ja': [...all Japanese profiles]
49 // }
50
51 // we create a new page for each language
52 langs.forEach(lang =>{
53 createPage({
54 // the localizeUrl function creates a url which takes into consideration what the default language is
55 path: localizeUrl(lang, defaultLangKey, '/team'),
56 component: path.resolve(`src/templates/team.js`),
57 context: {
58 profiles: profiles[lang]
59 }
60 })
61 })
62}
此代码将创建两个新页面,路径分别为/ja/Team
和/Team
(没有/en
,因为我们将英语设置为默认语言)。
如您所见,createPage
以一个对象作为参数,该对象有三个字段路径
、组件
和上下文
。路径就是我们希望新页面具有的路径。Component
是我们要使用的模板。Conext
是我们想要传递给模板的数据。在这里,我们传递用我们想要的语言编写的配置文件。
模板
让我们来看看我们的团队模板。
1import React from "react"
2import Layout from "../components/layout"
3import SEO from "../components/seo"
4
5const TeamPage = (props) => {
6 // We will see about pageContext in the next section
7 const {profiles} = props.pageContext
8 return (
9 <Layout location={props.location}>
10 <SEO title="Team" />
11 <h1>Team</h1>
12 // Iterating trough the array of profiles
13 {profiles.map((profile,i)=>(
14 <div key={i} className="columns">
15 <div className="column">
16 // Here are some nice profile pictures of our team members
17 <div className="square-image" style={{backgroundImage: `url("${profile.metadata.profile_picture.imgix_url}")`}}/>
18 </div>
19 <div className="column is-two-thirds">
20 <div className="team-member-title">{profile.title}</div>
21 // Here is some html content we get from Cosmic
22 <div dangerouslySetInnerHTML={{ __html: profile.content }}/>
23 </div>
24 </div>
25 )
26 )}
27 </Layout>
28 )
29}
30
31export default TeamPage
综上所述,上面的代码接受了一个profiles
props,它是我们从Cosmic JS中获得的配置文件数组。每个配置文件都有一个配置文件图片对象、一个标题
和一个内容
字段。content
实际上是一个HTML字符串,所以我们必须使用construouslySetInnerHTML
属性来设置它。
为了使该模板起作用,提前准备好您的css文件以获得一致的结果是很重要的。我的朋友将不能在宇宙的所见即所得中添加类名或ID。
还有更多要说和做的事情:
- 创建导航栏和位置感知布局
- 如何使用国际化接口
- 如何温和地将用户重定向到他们的网站版本
你可以浏览Github repo),了解我是如何解决这些问题的,并在kodou.me.)上查看结果或者使用Alliator.io查看我们是否上载了关于该主题的一些新内容。但我认为,在一个帖子中处理这件事已经很多了。以上,我希望这将对建立你自己的国际化网站有一点或很大帮助,并期待更多的未来!😉