105
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Keycloak by OpenStandiaAdvent Calendar 2017

Day 11

OSSドキュメント翻訳を支える技術 (Keycloakを題材に)

Last updated at Posted at 2017-12-10

Keycloak by OpenStandiaの11日目は、ちょっとKeycloakそのものからは離れたトピックとして、OSSのIAMプロダクトであるKeycloakのドキュメント翻訳を題材に OSSドキュメントの翻訳を支える技術 について書きたいと思います。今後何かしらのOSSの翻訳をやってみたい:exclamation:という方には役立つネタかもしれません:thumbsup_tone2:

モダンOSSドキュメント翻訳フロー

はじめに、翻訳作業全体の流れについて説明します。

最近のOSSはソースをGitで管理し、変更をGitHubなどのプルリクエスト機能により反映しつつ、CIサービスを使って自動ビルド/リリースを行う開発スタイルが多いかと思います。ドキュメント翻訳についても同様のフローで行うことで、以下のようなメリットがあると考えています。

  • 翻訳リソースがバージョン管理される
  • プルリクエストなどでレビュープロセスをきちんと通して翻訳内容が取り込まれる
  • CircleCIやTravis CIと連携して、プルリクエスト時に自動的に静的チェックを走らせたり、プレビュー版を生成してレビューすることが可能
  • (GitHubの場合は)翻訳作業がGitHub Contributionとして加算され、貢献度が可視化されることで参加者のモチベーションアップにつながる
  • 最終的な翻訳版ドキュメントの公開まで自動化ができ、運用負荷を下げることができる

Keycloakドキュメントの翻訳では、今のところ下図のようなフロー/ツール・サービス群で翻訳作業を行っています。

image.png

  1. 翻訳者は、翻訳支援サービスであるTransifexを利用して翻訳作業を行います。
  2. 翻訳を終えると自動的にGitHubにプルリクエストが送られます(実際の例)。
  3. プルリクエストと同時にCircleCIにて翻訳版ドキュメントの自動ビルドが行われます。
  4. 完了後、ホスティングサイトにアップロードされプレビューが可能になります。
  5. 翻訳レビュアーはTransifex上で訳文のチェックを行います。必要であれば生成されたプレビュー版のドキュメントも参照しつつチェックします。指摘事項がある場合は翻訳者に修正してもらう、または直接修正を行い、レビュー完了とします。
  6. レビューが完了すると、レビュー指摘分がGitHubに追加pushされます。
  7. 再度CircleCIにて自動ビルドが行われます。
  8. レビュー完了版がホスティングサイトにアップロードされプレビューが可能になります。
  9. プルリクエストマージ担当はプレビュー版を確認しつつ、developブランチにマージを行います。
  10. 再度CircleCIにてdevelopブランチの自動ビルドが行われます。
  11. developブランチ版がホスティングサイトにアップロードされ参照可能になります。
  12. リリースの場合はmasterブランチへのプルリクエストを作成し、masterブランチにマージを行います。
  13. 再度CircleCIにてmasterブランチの自動ビルドが行われます。
  14. CIにより公開用サイトへのアップロードを行います。

以降では、各プロセスの詳細な取り組みであったり技術的な内容について紹介していきます。

翻訳方式について

次に、翻訳方式(具体的に何をソースとして翻訳していくのか)について軽く述べます。

一般的に、OSSのドキュメントはWebとの親和性からHTML形式で公開されていることが多いでしょう。また、ドキュメントのメンテナンス性を考慮して、直接HTMLを記述することはせずに、Markdownなどのテキストベースのフォーマットを利用してドキュメントを記述していき、各種ツールにより公開用のHTMLやPDFを生成しているケースが多いかと思います。

このようなドキュメントの場合、翻訳方式としては

  1. 最終的なアウトプットのHTMLの翻訳を行いHTMLを作成する。
  2. HTMLのソースとなるテキストベースのファイルの翻訳版を作成し、翻訳版のHTMLを生成する。
  3. ツールでソースのテキストベースのファイルからテキスト抽出を行い、翻訳後に元の文章にマージし、翻訳版を生成する。

のような方式があります。各方式の特徴を下表にまとめます。

方式 1. HTMLを直接翻訳 2. ソーステキストを翻訳 3. テキスト抽出して翻訳
ツールチェインの構築 不要 必要 大変
翻訳量 大 (目次等の自動生成箇所も対象) 小 (スタイル部分は極力除外される、同一文章は1回の翻訳だけで済む)
元ドキュメント変更への追随 大変1 大変1 容易 (構成やスタイルが変わっても影響を受けにくい)
PDF等他フォーマットへの対応 大変 容易 容易

Keycloakドキュメントの翻訳ではどうしているかというと、3つ目のテキスト抽出した部分のみを翻訳の方式を採用しています。というのも、現状、毎月のように最新版がリリースされており、ドキュメントもどんどん変わっていく恐れがあると思い、変更に追随しやすい(はず)翻訳者は翻訳作業だけに集中できる、という点から3つ目の方式を今のところは採用しています。

ソーステキストからのテキスト抽出について

方式3を取る場合、テキスト抽出を行い、翻訳後に元のテキストにマージする仕組みが重要になってきます。というわけで、次はテキスト抽出について述べます。

翻訳対象のソーステキストフォーマット

テキスト抽出を行うためには翻訳対象のソーステキストフォーマットが重要なポイントになってきます。OSSドキュメントで利用される代表的なテキストベースのドキュメント作成ツール/マークアップ言語としては以下があります。

Keycloakでは少し前(バージョン3.3系まで)は、GitBook(つまりMarkdown)で書かれており、HTML化されて公開されていました。しかしながら、開発スポンサーであるRed Hat社の方針により、バージョン3.4.0にてAsciiDoc(ツールはAsciidoctorを利用)に移行となっています。

なお、個人的にはMarkdown派だったのですが、AsciiDocのテーブル記法(Asciidoctor独自記法かも?)は非常に書きやすく、Markdownで書いているドキュメントをAsciiDocに鞍替えしたくなってきました:smirk:

テキスト抽出方式

ここは、前述のドキュメント作成ツールに何を使っているかによって方式が変わってきます。例えば、Sphinxを利用している場合は、Sphinxの国際化機能を使うことができます。

Keycloakドキュメントの場合は前述のとおりAsciidoctorを使っていますが、国際化機能のデザインドキュメントはあるものの、まだ実装はされていません。

そこで、AsciiDocからうまくテキスト抽出して翻訳できないかツールを探してみると、同じAsciiDoc形式を採用しているOSSのドキュメントとして、Clojureを発見しました。clojure-site 日本語訳プロジェクトを見ると、ここではpo4aというツールが使われています。Keycloakドキュメントの翻訳でも、Clojureに倣いpo4aを使ってテキスト抽出を行うようにしました。というわけで、以降はpo4aについて詳細を説明します。

なお、Sphinxの国際化機能もそうですがpo4aはgettextのPOファイルを利用した翻訳という昔からある技術で行われています。この方式は、豊富なgettext用ツールが使えるというメリットはあるものの、個人的にはとっつきにくいなぁという印象があります。ただし、po4aを使っておけばかなり隠蔽化してくれるので、最初のセットアップさえ乗り越えれば後は楽だと思います。

po4aについて

po4a(PO for anything)とは、gettextが想定していないドキュメントのような領域で、POファイルを使った翻訳管理を行うためのツールです。Supported formatsを見ると、MarkdownやAsciiDocにも対応しています。

gettext自体はソフトフェアの国際化のためのものであり、ドキュメントを対象としては想定していません。そこでpo4aを使うと、ドキュメントを対象に翻訳対象の文字列をPOTファイルとして抽出し、翻訳後のファイルとして翻訳者がPOファイルを作成することで、元の文章とマージして翻訳後のテキストファイルに変換することができます。po4aを使うことで、以下の流れで翻訳を行い最終的なHTMLを生成できるようになります。

  1. po4aでAsciiDoc形式のファイルからPOTファイルを生成
  2. POTファイルをコピーして翻訳言語用のPOファイルを作成
  3. POファイルに翻訳メッセージを記述
  4. po4aでAsciiDoc形式のファイルとPOファイルをマージして、翻訳後のAsciiDoc形式のファイルを生成
  5. Asciidoctorで翻訳後のHTMLファイルを生成

上記のうち、翻訳者には3番だけ実施してもらうように役割分担することができます。POファイルの編集ツールとしてはPoeditOmegaTがあります。または、最近だと翻訳支援サービスがSaaSで提供されており、POファイルに対応しているところもあります(TransifexCrowdinなど)。このようなツールを活用すれば、翻訳者は翻訳作業にだけ集中することができ、翻訳作業がスケールしやすいかと思います。

翻訳後に元ドキュメントが更新された場合は、po4aを再度実行することで、変更箇所のメッセージには fuzzy というキーワードでマーキングされます。fuzzy とされたメッセージに関しては、POファイルとのマージ時に翻訳されずに元のままとなります。また、新規に文章が追加されていた場合は、新たな翻訳対象メッセージとして追加されます。つまり、以下の作業フローで元ドキュメントの変更に追随ができます。

  1. po4aでPOファイルを更新
  2. POファイルに新たに新規メッセージがあれば、翻訳を追加する
  3. POファイルの fuzzy 箇所があれば、翻訳を修正する

po4aについては、Qiitaに nginx をネタに po4a で翻訳管理を始めてみる例 という記事が過去にあったりしますので、そちらを読むと理解が進むかと思います。

po4aの設定

po4aでは、po4a.cfg というファイルを作成して、翻訳対象のソースとその形式、POT/POファイルの出力先を設定します。以下はKeycloakドキュメント翻訳で使用している設定例です。

po4a.cfg
[po4a_langs] ja_JP
[po4a_paths] i18n/pot/$master.pot $lang:i18n/po/$lang/$master.$lang.po

[po4a_alias:myadoc] asciidoc opt:"-k 0 -M utf-8 -L utf-8"
[po4a_alias:mytext] text opt:"-k 0 -M utf-8 -L utf-8 -o asciidoc -o neverwrap"

# aggregation
[type: myadoc] source/aggregation/navbar.html $lang:translated/$lang/aggregation/navbar.html master:file=aggregation/navbar add_$lang:i18n/po/$lang/aggregation/navbar.$lang.add

# getting_started
[type: myadoc] source/getting_started/master.adoc $lang:translated/$lang/getting_started/master.adoc master:file=getting_started/master
[type: myadoc] source/getting_started/topics/first-boot/admin-console.adoc $lang:translated/$lang/getting_started/topics/first-boot/admin-console.adoc master:file=getting_started/topics/first-boot/admin-console
[type: myadoc] source/getting_started/topics/first-boot/boot.adoc $lang:translated/$lang/getting_started/topics/first-boot/boot.adoc master:file=getting_started/topics/first-boot/boot
...

上記のように、AsciiDoc(*.adoc)の1ファイルに対して1つずつ設定する必要があります。翻訳対象のソーステキストがたくさんあると設定を書くだけでも大変なので、シェルスクリプトなどで自動化しておくと良いでしょう。Keycloakドキュメントの翻訳作業では、update-po4acfg.shというシェルスクリプトを用意し、po4a.cfgを翻訳対象のAsciiDocファイルから自動生成するようにしています。

po4aのAsciiDoc対応は微妙...?

実際にKeycloakドキュメントの翻訳でpo4aを使ってみたところ、いくつか課題に遭遇しました。

  1. AsciiDocのマクロを使っていると正常にテキスト抽出できない
  2. Asciidoctorの拡張書式であるいくつかのMarkdown書式に対応していない
  3. AsciiDocのテーブル基本に対応しきれていない
    • AsciidoctorのTablesに記載のスタイルをKeycloakドキュメントでは使用しており、うまく抽出できない

上記課題対応でpo4aに修正パッチをプルリクエストしようかと思いましたが、ソースを見てみると **正規表現でひたすら頑張るという実装**になっており、これは辛い&さらなる負債を作り込んでしまうと思い、一旦断念しました。というわけで、Keycloakドキュメント翻訳では、独自パッチを当ててひとまず対応しています:sweat:

SaaSを利用した翻訳作業

po4aによりPOTファイルとしてテキストを抽出できましたので、次はこれを翻訳言語ごとにPOファイルを作成することになります。POT/POファイルは単なるテキストファイルなので、テキストエディタで翻訳作業を行うこともできますが、

  • POファイルのフォーマットを誤って壊さないように専用のエディタで安全に翻訳したい
  • 翻訳結果を翻訳メモリとして翻訳者間で共有したい
  • 用語集を翻訳者間で共有したい
  • 機械翻訳を活用して効率化したい
  • 翻訳結果のレビューを効率よく行いたい

という点から、翻訳支援サービスを使うのがオススメと思っています。特に、OSSの翻訳となると、ありがたいことに有償のサービスが無料で提供されています。今回、以下のサービスを試してみました。

とりあえず一通り試してみて、独断ですが以下の評価になりました。

評価項目 Crowdin GitLocalize Google Translator Toolkit Transifex Weblate(ホスティング版)
POファイル対応 :white_check_mark: :x: :white_check_mark: :white_check_mark: :white_check_mark:
翻訳メモリ :white_check_mark: :x: :white_check_mark: :white_check_mark: :white_check_mark:
機械翻訳 :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
用語集 :white_check_mark: :x: :white_check_mark: :white_check_mark: :white_check_mark:
GitHub連携 :white_check_mark: :white_check_mark: :x: :x: :white_check_mark:
REST API提供 :white_check_mark: :x: :x: :white_check_mark: :white_check_mark:
翻訳完了時のWebhookによる通知 :x: :white_check_mark: :x: :white_check_mark: :white_check_mark:
翻訳リソース制限なし :x: :x: :white_check_mark: :white_check_mark: :x:
レビュー機能 :white_check_mark: :white_check_mark: :x: :white_check_mark: :white_check_mark:
コメント機能 :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
翻訳に使用しているOSS例 Electron Vue.js, WebFundamentals ? VSCode Debian

以下、詳細な評価コメントです。

  • Crowdin
    • 一番インパクトがあるのが翻訳リソース制限でした。Keycloakドキュメントの翻訳対象ドキュメントはそこそこあり、Crowdinに全部インポートするとOSSプランの上限を超えてしまい、使えないことが判明しました。
    • GitHub連携機能はあるものの、なぜか1コミットずつpushしてくるため、CIサービスと連携しているとビルドジョブがたまりまくるという自体になり、正直いまいちでした(実際の例)。
    • また、GitコミットにはCrowdin-GitHub連携で使用するGitHubアカウントが使われるため、翻訳貢献者のContributionにならないのも悲しいところです。
  • Google Translator Toolkit
    • 制限はなさそうでしたが、残念ながらREST APIが提供されておらず、POファイルのアップロードや翻訳結果をGitHubに戻すといったことが自動化できなさそうでいまいちでした。
    • Googleはこのサービスはあんまりやる気はないのでしょうか...?
  • GitLocalize
    • OSS向けプランでは100,000セグメント(≒パラグラフ)で十分な容量でしたが、POファイルやAsciiDocには未対応であり、翻訳メモリや用語集の機能がなく今回は見送り。
    • ただ、コンセプトとしてGitHub連携を最初から想定したものなので、GitHub連携はすばらしく、まだまだ新しいサービスなので今後に期待しています:exclamation:
  • Transifex
    • OSS向けプランではリソース制限はなく、GitHub連携がない点だけを他の手段でカバーできれば良さそうです。
    • 幸いなことに、REST APIが提供されており、かつWebhookで翻訳時にTransifexから情報をpushすることもできるので、連携部分はカスタマイズでなんとかなりそうです。
  • Weblate
    • Weblateのホスティングのフリーソフトウェア向けプランでは、翻訳コンポーネント数(=翻訳対象のPOファイル数)が25と限られており、これもリソース制限となり駄目でした。
    • リソース制限が早々に判明したのであまり深く利用はしていませんが、Gitとの連携は良さそうな雰囲気でした。po4a自体のドキュメント翻訳でも使われているのですが、Weblateからプルリクエストが送られAuthorにちゃんと翻訳者が記録されていて良い感じ(実際の例)。

というわけで、Keycloakドキュメント翻訳では、WebhookでなんとかGitHub連携さえすればよさそうなTransifexを今は選択しています。GitHub連携部分については次に続きます。

Transifex-GitHub連携をなんとかする

自前でサーバを建てれるなら、TransifexがオフィシャルにOSSで提供しているGitHub連携ツールの github.com/transifex/txgh を使うのが簡単かもしれません。しかし、クラウドを使うにしてもずっとインスタンスを立ち上げる必要があるため、自腹を切るのは痛いところです。また、プルリクエストの発行まではやってくれなさそうで機能的にも不満があります。

txghの実装をみるとやっていることは単純で、

  • Transifexでの翻訳完了時に、Webhookで通知をHTTPで受けとる
  • 受け取った通知をもとに、GitHub APIを呼び出し翻訳結果をコミット(push)

というものでしたので、HTTPしか扱わないなら**Amazon API Gateway + AWS Lambdaでサーバレスで簡単に実装できるんでは?**と思い自作することにしました。

作ったもの: github.com/wadahiro/txgh-serverless

今回はServerless Frameworkを使用して開発してみました。使い方は、Node.jsをインストールした環境にて、READMEの手順でデプロイしてもらえれば即使えます。これを使うと、Transifex上で翻訳が完了するとGitHubにプルリクエストが自動発行されるようになります(実際の例)。なお、サボってGitHub側のソースが更新された場合のTransifex側への反映まではまだ実装しておりません:bow_tone1: 残念ながらこの部分は手動でTransifexのCLIツールを実行してPOTファイルをインポートしています(POTファイルをインポートしてTransifexで翻訳すると、言語ごとにPOファイルが出力されます)。

これなら運用にかかる費用もかなり安くおさえられそうです。参考までに東京リージョンだと

  • Amazon API Gateway
    • 100万回のAPI 呼び出しの受信につき4.25USDのレートに、データの送出の費用がギガバイト単位で加算
    • 最初の10TBにつき、0.14USD/GB
  • AWS Lambda
    • 1ヶ月に1,000,000件のリクエストおよび400GB-秒は無料

という料金です。翻訳作業の流量なんてたいしたことなく、またAWSは1年間無料枠があるので、余裕でしばらくは0円で運用できそうです:thumbsup_tone2:

CI環境について

po4aの実行やドキュメント生成をCIで動かすため、Dockerコンテナが使えるCIサービスがオススメです。ツールによっては結構環境依存問題がありますので...

Keycloakドキュメント翻訳では、CircleCIを使っており、po4aの実行のためコンテナを用意しています(Dockerfileはこちら)。po4aをソースビルドしていますので、最新版のpo4aを使いたい方は参考になるかもしれません。なお、KeycloakドキュメントはRuby製のAsciidoctorでHTMLを生成しますが、Java(JRuby)上で動かしておりMavenからキックするようになっているため、このコンテナにはMavenもインストールしています。

なお、現状まだできてはいませんが、CIで翻訳結果の静的チェックを機械的に実行することで、翻訳品質を高めることもできそうです。Keycloakドキュメント翻訳では、翻訳スタイルとして日本翻訳連盟で公開されているJTF日本語標準スタイルガイド(翻訳用)を参考にしていますが、textlintJTF用ルールセットを使うことでチェックを自動化できそうです。ここは今後改善したいポイントではあります:point_up_tone2:

翻訳ドキュメントのホスティングについて

CIによりようやく翻訳ドキュメントが生成されました。最後は公開用にホスティングですが、無料で済ませるならGItHub Pagesを使うといいかもしれません。

Keycloakドキュメント翻訳の場合は、公開先はNRI OpenStandiaのサイトを利用している関係上、OpenStandiaで利用しているAWSのS3にアップロードしてCloudFront経由で外部公開しています。アップロードはもちろんCIから自動的に行われるようにしています。aws-cliでS3と同期させるだけなので簡単ですね。

最後にKeycloakドキュメント翻訳についてご紹介

最後に、Keycloakドキュメントの翻訳について簡単に紹介しておきたいと思います。OSSのIAM(Identity Access Management)プロダクトであるKeycloakでは、各種ドキュメントがKeycloakのサイトのDocumentationページで読むことができます。最新バージョンである Keycloak 3.4.1.Final(2017/12/11時点) では、下記の構成になっています。

  • Guides
    • Getting Started - Keycloak入門編
    • Server Installation - インストール方法
    • Securing Apps - アプリケーションの保護方法
    • Server Admin - Keycloakサーバの管理方法
    • Server Development - Keycloakサーバーのカスタマイズ方法
    • Authorization Services - 認可サービスについて
    • Upgrading - アップグレード方法について
  • API Documentation
    • JavaDoc - KeycloakのAPIのJavaDoc
    • Administration REST API - 管理REST API仕様について

Keycloakドキュメント翻訳の活動では、上記のGuidesの部分の翻訳をターゲットとして活動しています。GitHubに翻訳用プロジェクトを用意していますので、興味がある方はContributingのWikiページをご覧になって、プロジェクトへ参加していただければと思います:grin:

  1. TransifexGitLocalizeのようなHTMLやMarkdownなどのファイルフォーマットに対応した翻訳支援サービスを活用することで、ある程度緩和はされるかもしれません。 2

105
63
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
105
63

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?