ククログ

株式会社クリアコード > ククログ > Jekyllで複数言語のコンテンツを継続してメンテナンスする方法

Jekyllで複数言語のコンテンツを継続してメンテナンスする方法

GitHub PagesのWebサイトジェネレーターであるJekyllで複数言語のコンテンツを管理する方法を紹介します。複数言語のコンテンツを管理するとは、例えば、英語と日本語のコンテンツを提供しているWebサイトにコンテンツを追加・修正・削除し、継続的にメンテナンスするということです。

最初に結論を書くと、jekyll-task-i18nを使えばGitHub Pagesの仕組みから逸脱せずに複数言語のコンテンツを継続的にメンテナンスできる仕組みができます。

複数言語のコンテンツを管理するときの問題

複数言語のコンテンツを管理するときに問題になることは、情報のズレです。一方の言語では提供されている情報が、他の言語では提供されていないという状態です。情報は提供されているが、古い情報のままということもあります。

「作った後は変更しない」というWebサイトではあまり問題になりませんが、作った後も継続して変更する場合は問題になります。継続して変更する場合はいかにコストを小さく複数言語のコンテンツを更新するかを考えなければいけません。コストが大きいと更新することが億劫になり、問題がより大きくなります。

複数言語のコンテンツを管理する方法

複数言語のコンテンツを管理する方法はいくつかあるので、それらをメリット・デメリット付きで紹介します。ここで紹介する方法は次の通りです。

  • 翻訳元コンテンツをコピーして翻訳
  • 翻訳箇所をマークアップし、コンテンツを処理する時に翻訳済みメッセージに置換
  • 翻訳元コンテンツから翻訳箇所を抽出し、翻訳済みメッセージと組みあわせて翻訳済みコンテンツを生成

コピーして翻訳

一番簡単で特に用意もいらない方法が翻訳コンテンツをコピーして翻訳する方法です。

どのような方法か例を使って説明します。

まず、en/index.mdという英語のコンテンツがあるとします。

en/index.md:

---
layout: en
title: Top page
---
# Hello!

これを翻訳し、ja/index.mdという日本語のコンテンツを作成することを考えます。

この方法では、次のように翻訳元のen/index.mdをコピーして、コピーしたファイルを編集し、翻訳します。

% mkdir -p ja
% cp en/index.md ja/
% editor ja/index.md
(↑で翻訳。)

ja/index.md:

---
layout: ja
title: トップページ
---
# こんにちは!

この方法のメリットは次の通りです。

  • 単純なのでわかりやすい。
  • 特にツールは必要ないのですぐに始められる。
  • Jekyllのプラグインも必要ないのでGitHub Pagesでも使える。

デメリットは次の通りです。

  • 翻訳元が変更された場合、変更点を見つけて追従することが大変。

この方法は、コンテンツの追加だけでコンテンツを追加するときは新規ファイルとして追加する、という運用であれば問題がありません。例えば、ブログのようなコンテンツです。

しかし、既存ファイルにコンテンツを追加したり、コンテンツの変更・削除がある場合はメンテナンスコストが大きくなります。このような場合は別の方法を検討したほうがよいでしょう。

Rabbitのサイト1や、おそらくwww.ruby-lang.org2もこの方法を使っています。

翻訳箇所をマークアップする方法

次は、翻訳箇所をマークアップし、コンテンツを処理する時に翻訳済みメッセージに置換する方法です。jekyll-localizationjekyll-i18n3jekyll-multiple-languages-pluginが使っている方法です。独立したプラグインにはなっていませんが、bitcoin.orgもこの方法です。

これも例を使って説明します。最近もコミットがあるjekyll-multiple-languages-pluginを使います4

まず、index.mdという英語のコンテンツがあるとします。

---
layout: en
title: Top page
---
# Hello!

これを翻訳し、ja/index.htmlという日本語のコンテンツを作成することを考えます。

まず、英語のコンテンツの翻訳対象のメッセージを{% t 翻訳メッセージのキー %}に置換します。今回の場合は「Hello!」を{% t top_page.title %}に置換します。

---
layout: en
title: Top page
---
# {% t top_page.title %}

英語のメッセージは_i18n/en.ymlに書きます。

top_page:
  title: Hello!

同様に、日本語のメッセージは_i18n/ja.ymlに書きます。

top_page:
  title: こんにちは!

_config.ymlに翻訳対象の言語を指定します。今回は英語と日本語なのでこうなります。

languages: ["en", "ja"]

HTMLを生成します。

% jekyll build

これで、_site/en/index.htmlに英語のコンテンツが生成され、_site/ja/index.htmlに日本語のコンテンツが生成されます。

_site/en/index.html:

...
<h1>Hello!</h1>
...

_site/ja/index.html:

...
<h1>こんにちは!</h1>
...

この方法ではRailsのi18n機能のように、元のコンテンツにはキーだけを記述し、YAMLに翻訳済みのメッセージを書きます。キーではなく翻訳対象のメッセージを使う方法や、YAML以外のファイルにメッセージを書く方法があってもよいはずですが、みんなこの方法を使っています。Railsの影響でしょう。

この方法のメリットは次の通りです。

  • 翻訳するメッセージだけに集中できる。「見出し」などの構造は気にしなくてもよい。
  • Railsのi18n機能を使っている人にはなじみがある。

デメリットは次の通りです。

  • キーだけ見てもどんなメッセージにすればよいかわかりにくいので翻訳が難しい。
  • オリジナルのメッセージ(今回の場合は英語のメッセージ)が変更されたことに気づくための仕組みがないので、翻訳済みのメッセージ(今回の場合は日本語のメッセージ)を追従することが難しい。
    • 例えば英語で「Hello!」を「Hello! This is a cool site!」に変更したときに気づく仕組みがないので、日本語は「こんにちは!」のままになってしまう。
  • Jekyllのプラグインとして動作するのでGitHub Pagesでは使えない。

Railsと同じような感覚で使いたい場合はこの方法がよいでしょう。

ただし、そのままではGitHub Pagesでは使えないので、GitHub Pagesで使いたい場合はローカルでHTMLを生成し、生成したHTMLをGitHub Pagesで使うようにする必要があります。

翻訳元コンテンツと翻訳済みメッセージから翻訳済みコンテンツを生成

最後の方法は最初の方法と2番目の方法を組み合わせたような方法で、2つの方法のよいところをとった方法です。

最初の方法では普通に書いたコンテンツそのものが翻訳対象でした。この方法でも普通に書いたコンテンツそのものが翻訳対象です。2番目の方法のように翻訳対象をマークアップしません。

翻訳済みコンテンツの作り方は2番目の方法と似ています。翻訳済みメッセージを準備することは2番目の方法と同じです。翻訳元コンテンツと翻訳済みメッセージを組みあわせて翻訳済みコンテンツを生成することも同じです。ただし、翻訳済みメッセージの管理方法が違います。

この方法を使っているのがjekyll-task-i18nです。

どのような方法か例を使って説明します。

まず、index.mdという英語のコンテンツがあるとします。

---
layout: en
title: Top page
---

# Hello!

これを翻訳し、ja/index.mdという日本語のコンテンツを作成することを考えます。2番目の方法のときはインストール方法を省略しましたが、この方法では省略すると何をしているかがわからないのでインストール方法も説明します。

まず、次の内容のGemfileとRakefileを作成します。

Gemfile:

source "https://rubygems.org/"

gem "jekyll-task-i18n"
gem "rake"

Rakefile:

require "bundler/setup"
require "jekyll/task/i18n"

Jekyll::Task::I18n.define do |task|
  task.locales = ["ja"]
  task.translator_name = "Kouhei Sutou"
  task.translator_email = "kou@clear-code.com"
  task.files = Rake::FileList["**/*.md"]
  task.files -= Rake::FileList["_*/**/*.md"]
  task.locales.each do |locale|
    task.files -= Rake::FileList["#{locale}/**/*.md"]
  end
end

task :default => "jekyll:i18n:translate"

初期化します。

% bundle install

ここからが実際の翻訳作業です。

まず、翻訳対象のメッセージを抽出します。

% bundle exec rake

これで次の内容の_po/ja/index.edit.poができます。

_po/ja/index.edit.po:

# 省略
msgid ""
msgstr ""
"省略"

#: ../../index.md:1
msgid ""
"---\n"
"layout: en\n"
"title: Top page\n"
"---"
msgstr ""

#: ../../index.md:6
msgid "# Hello!"
msgstr ""

この中のmsgid "..."となっている部分が翻訳対象のメッセージです。index.mdから自動抽出されたものです。抽出方法は単に2つ以上の連続する空行で区切っているだけです。Markdownをパースしていたりはしません。

msgid "..."の部分を翻訳し、msgstr ""に入れます。

_po/ja/index.edit.po:

# 省略
msgid ""
msgstr ""
"省略"

#: ../../index.md:1
msgid ""
"---\n"
"layout: en\n"
"title: Top page\n"
"---"
msgstr ""
"---\n"
"layout: ja\n"
"title: トップページ\n"
"---"

#: ../../index.md:6
msgid "# Hello!"
msgstr "# こんにちは!"

これで翻訳済みメッセージができました。翻訳元コンテンツと翻訳済みメッセージを使って翻訳済みコンテンツを生成します。

% bundle exec rake

これでja/index.mdに翻訳済みコンテンツができます。

ja/index.md:

---
layout: ja
title: トップページ
---

# こんにちは!

できあがるものはコピーして翻訳する方法と同じものです。Jekyllが処理済みのHTMLを生成するのではなく、Jekyllの入力ファイルを生成するので、ja/index.mdをpushすればGitHub Pagesでそのまま使えます。

この方法のメリットは次の通りです。

  • JekyllのプラグインではないのでGitHub Pagesでも使える。
  • 翻訳対象のメッセージがすぐ近くにあり、確認しながら翻訳できるので翻訳しやすい。
  • 翻訳対象のメッセージが変更されたらツールが検出してくれるので、追従しやすい。

デメリットは次の通りです。

  • gettextという仕組みになじみがない人5は最初の敷居が高い。
  • Jekyllだけでなく、RakeやBundlerも使うのでRubyになじみのない人は最初の敷居が高い。

jekyll-task-i18nを使ったサンプルがjekyll-task-i18n/sampleにあるので、それを使えば手元で試すことができます。

翻訳対象のメッセージの追加・削除に対するツールの支援があるのはこの方法だけなので、コンテンツの追加だけではない複数言語対応のWebサイトを継続してメンテナンスするならこの方法が適切です。

JekyllのプラグインではないためGitHub Pagesの仕組みから逸脱しません。そのため、2番目の方法と違い、GitHub Pagesでも使えます。

ただし、それほど頻繁に更新しないWebサイトには大げさです。その場合はコピーして翻訳する方法でも十分でしょう。

droonga.orgはこの方法を使っています。droonga.orgにはリファレンスマニュアルやチュートリアルがあり、コンテンツの追加だけでなく、変更・削除が多いからです。

まとめ

Jekyllを使っているWebサイトで複数言語のコンテンツを管理する方法について説明しました。説明した方法は次の3つです。

  • 翻訳元コンテンツをコピーして翻訳
  • 翻訳箇所をマークアップし、コンテンツを処理する時に翻訳済みメッセージに置換
  • 翻訳元コンテンツから翻訳箇所を抽出し、翻訳済みメッセージと組みあわせて翻訳済みコンテンツを生成

コンテンツを追加するだけだったり、それほど変更がない場合は最初の「翻訳元コンテンツをコピーして翻訳」方法で十分でしょう。

コンテンツの変更・削除がそれなりにあるようなら最後の方法がよいでしょう。

最後の方法に興味がある人はgettextとバージョン管理システムの相性の悪さを解消する案も参照してください。jekyll-task-i18nはこの案を実装しています。

  1. GitHub Pagesは使っていませんがJekyllは使っています。

  2. www.ruby-lang.orgもGitHub Pagesを使っていません。

  3. 開発は終了しています。

  4. 本質的なことではないのでjekyll-multiple-languages-pluginのインストール方法は省略して、翻訳する流れだけ説明しています。そのため、この通りやっても手順が足りず試すことはできません。

  5. 例えば、Railsのi18n機能しか知らない人。