Gatsby&CosmicJSを使用した多言語Webサイトの作成
最近、友人から日本語版と英語版のウェブサイトを作るように言われました。 私は国際化されたウェブサイトを書いたことはありませんが、ローカリゼーションがどのように機能するかについて強い意見を持っています。 そこで、ヘッドレスCMSであるCosmicJSを使用してギャツビーの国際化に取り組む1つの方法を紹介します。
コンテキストのビット
ローカリゼーションに取り組む方法はたくさんあり、いつものように特効薬はありません。 それぞれのアプローチは、さまざまな方法で問題を解決します。 だからここに私の文脈があります:
- できるだけメンテナンスコストの少ないサイトを作りたいです。
- コンテンツライターにはプログラミングの経験がありません
- サイトのメンテナンスコストは可能な限り低くする必要があります。
- サイトは、サイトの1つのバージョンでユーザーを強制するべきではありません。
この最後の点は私にとってとても重要です。 あなたが外国にいるとき、いくつかのウェブサイトはあなたに彼らのサイトのローカルバージョンを使用することを強制します。 [array of multinational companies]
が私を[long array of languages I don't understand]
バージョンのWebサイトに強制すると、私は怒ります。 ページの自動翻訳でも同じ問題が発生します。 ウェブサイトの自動翻訳が必要な場合は、素晴らしいGoogle翻訳Chrome拡張機能を使用できます。
このウェブサイトは、日本語と英語の両方のユーザーを対象としています。 そのため、サイトのすべてのページに英語版と日本語版が必要です。 ユーザーがWebサイトの現在のバージョンを変更したい場合は、ナビゲーションバーの言語メニューをクリックできます。
私のアプローチ
GatsbyとReactは、ローカリゼーション(l10n)と国際化(i18n)にアプローチするための多くのツールを提供します。
私は最初にgatsby-plugin-i18nを使用してルートを簡単に生成しました。
たとえば、/page/team.ja.js
は次のURLを生成します:/ja/team
(ja
は日本の言語コードです)。
これは本当に素晴らしいプラグインですが、問題はそれがプログラム的ではないということです。 言語ごとに新しいファイルを作成する必要があります。 各ファイルで、データをフェッチするために特定のGraphQLクエリを作成する必要があります。 したがって、たとえば、CMSに新しい言語を導入した場合、新しい言語拡張機能を使用してすべてのルートを再度作成する必要があります。
そこで、代わりに、プラグインなしでl10nをビルドすることにしました。 このプロジェクトのすべてのコードは、https://github.com/alligatorio/kodouで入手できます。
このコンテキストでは、コンテンツライターはローカリゼーションに完全に責任があります。 彼女が日本語版のウェブサイトを書くとき、彼女は日付の形式が正しいことを確認する必要があります。 これが、Internationalization APIに依存し、今後の投稿のトピックとなるreact-intl
を使用しない理由です。
CosmicJSのセットアップ
優れたヘッドレスCMSオプションであるCosmicJSを使用すると、新しいオブジェクトタイプを作成するときにローカリゼーションをアクティブ化できます。
優先ロケールを選択することを忘れないでください。そうしないと、新しいオブジェクトが保存されません。
新しいサイトにはチームページがあるので、チームメンバーオブジェクトを作成します。 新しいチームメンバーを作成するときに、その言語を選択できるようになりました。
Gatsbyからそのデータにアクセスするには、gatsby-source-cosmicjs
ソースプラグインを追加する必要があります。
$ yarn add gatsby-source-cosmicjs
次に、plugins
に次のコードを追加して、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,
}
}
}
コードの残りの部分では、次を実行してCosmicJSからチームメンバーデータにアクセスできます。
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テンプレートを使用すると、再利用可能なコンテンツを作成し、プログラムでページを作成できます。 これはまさに私たちがしなければならないことです!
テンプレートファイルを見る前に、CosmicJSからデータをフェッチして新しいページを作成する方法を見てみましょう。
// 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
を使用して2つの新しいページを作成します(デフォルト言語として英語を設定しているため、/en
はありません)。
ご覧のとおり、createPage
は、path
、component
、context
の3つのフィールドを持つオブジェクトを引数として取ります。 パスは、新しいページに必要なパスです。 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
要約すると、上記のコードは、CosmicJSから取得したプロファイルの配列であるprofiles
プロップを取ります。 各プロファイルには、プロファイル画像オブジェクト、title
およびcontent
フィールドがあります。 content
は実際にはHTMLの文字列であるため、dangerouslySetInnerHTML
プロップを使用して設定する必要があります。
このテンプレートが機能するためには、一貫した結果を得るために事前にCSSファイルを準備することが重要です。 私の友人は、CosmicのWYSIWYGにクラス名またはIDを追加できなくなります。
言うことやすることはもっとたくさんあります:
- ナビゲーションバーと場所を意識したレイアウトの作成
- 国際化APIの使用方法
- ユーザーを自分のバージョンのサイトにソフトリダイレクトする方法
Githubリポジトリを調べて、これらの問題に対処する方法を確認し、kodou.meで結果を確認できます。 または、 Alligator.io を使用して、そのトピックに関する新しいコンテンツをアップロードしたかどうかを確認します。 しかし、1つの投稿で処理することはすでにたくさんあると思います。 上記で、これがあなた自身の国際化されたサイトを構築するのに少しまたは大いに役立つことを願っています。 😉