| PO4A.7(1) | User Contributed Perl Documentation | PO4A.7(1) |
po4a - ドキュメントやその他の素材の翻訳フレームワーク
po4a (PO for anything) は、従来の gettext ツールを使った文書翻訳の保守を容易にするものです。po4aの主な特徴は、内容の翻訳を文書構造から切り離すことです。
このドキュメントではpo4aプロジェクトの紹介を行いこのツールを使うかどうかを検討している潜在的な利用者と、このツールの仕組みのなりたちを理解したいという興味のある読者に焦点を当てます。
自由ソフトウェアの理念は、技術を真に誰もが利用できるようにすることです。しかし、ライセンスだけが考慮すべきことではありません。翻訳されていない自由ソフトウェアは、英語を母国語としない人々にとっては無用の長物なのです。ですから、ソフトウェアを誰もが利用できるようにするためにやるべきことはまだあります。
このような状況はほとんどのプロジェクトで知られており、誰もがあらゆるものを翻訳する必要性を確信しています。しかし、実際の翻訳は、多くの人の膨大な努力の結晶であり、ちょっとした技術的な問題で不自由なことになっているのが現状です。
ありがたいことに、オープンソースソフトウェアは、gettext tool suite を使って実際に非常によく翻訳されています。これらのツールは、プログラムから翻訳する文字列を抽出し、標準化された形式(POファイル、または翻訳カタログと呼ばれます)で翻訳する文字列を提示するために使用されます。翻訳者が実際にこのPOファイルを翻訳するのを助けるために、このツールのエコシステム全体が成立しました。そしてその結果を実行時にgettextが使用し、エンドユーザーに翻訳された文言を表示するのです。
しかし、ドキュメンテーションに関しては、まだ少し残念な状況です。最初の時点は文書の元のファイルを複製して翻訳を始めるだけなので、プログラムの翻訳よりも簡単そうに見えるかもしれません。しかし、元の文書が変更された場合、その変更点を把握しておくことは、すぐさま翻訳者にとって悪夢となります。手作業で行う場合、この作業は不快であり、誤りの温床となるのです。
古い翻訳は、全く翻訳がないことよりも悪いことがよくあります。エンドユーザは、プログラムの古い動作を説明した文書に騙される可能性があります。さらに、彼らは英語を話せないので、保守者と直接対話することができません。加えて、保守者は文書が翻訳されているすべての言語は知らないので、問題を修正することができません。このような困難はしばしば貧弱なツールによって引き起こされるもので、ボランティアの翻訳者のモチベーションを蝕み、問題をさらに悪化させる可能性があります。
po4aプロジェクトの目標は、文書を翻訳する人の仕事を楽にすることです。特に、文書の翻訳を保守可能なものにすることです。
アイディアとしてはgettextの手法を再利用し、この分野に適応させようというものです。gettextと同様、テキストは元の場所から抽出され、POの翻訳カタログとして翻訳者に提示されます。翻訳者はgettextの古典的なツールを活用して、取り組んでいる作業を監査し、チームとして協力・組織化することができます。それからpo4aは翻訳を文書構造に直接注入して、英語ファイルと同様に処理・配布できる翻訳済みソースファイルを生成します。翻訳されなかった段落は、翻訳後の文書に英語のまま残され、エンドユーザーが文書内の古い翻訳を目にすることがないようにします。
これにより、翻訳の保守におけるほとんどの荷の重い作業が自動化されます。更新が必要な段落を発見するのは非常に簡単であり、要素の順番が変わっただけでそれ以外の変更がなければ工程は完全に自動化されます。また何らかの検証を行うことで、文書が壊れてしまうような形式エラーの可能性を減らすことができます。
このアプローチの利点と欠点のより詳しい一覧は、このドキュメントの後ろの FAQ を参照してください。
現在、このアプローチで実装に成功しているのは、以下のテキスト整形フォーマットです:
Locale::Po4a::Man(3pm) モジュールは、BSD の man ページで使われている mdoc 形式にも対応しています(Linux でもかなり一般的になっています)。
詳しくはLocale::Po4a::AsciiDocをご覧ください。
詳細はLocale::Po4a::Podを見てください。
現在、DebianDoc と DocBook DTD のみに対応していますが、新しく対応するものを追加するのは、本当に簡単です。また、コマンドラインに必要な情報を与え、コードを変更せずに未知の SGML DTD を po4a で使用することもできます。詳細は Locale::Po4a::Sgml(3pm) を参照してください。
Locale::Po4a::LaTeX(3pm) モジュールは Python のドキュメントや書籍、プレゼンのスライドでの実績があります。
これは、静的サイトジェネレータ、README、その他のドキュメントシステムで使用される一般的な形式に対応しています。詳しくは Locale::Po4a::Text(3pm) をご覧ください。
現在、DocBook の DTD (詳細は Locale::Po4a::Docbook(3pm) を参照)とXHTMLがpo4aでは対応されています。
詳しくはLocale::Po4a::BibTexをご覧ください。
詳しくはLocale::Po4a:Docbookをご覧ください。
詳しくはLocale::Po4a:Guideをご覧ください。
より詳しくは Locale::Po4a::Wmlを見てください。
より詳しくは Locale::Po4a::Yaml を見てください。
より詳しくは Locale::Po4a::RubyDocを見てください。
詳しくはLocale::Po4a:Halibutをご覧ください。
詳しくはLocale::Po4a::Iniをご覧ください。
プロジェクトでこのツールを使う一番簡単な方法は、po4aプログラム用の構成ファイルを書き、このプログラムだけを使って作業することです。po4a(1)のドキュメントを参照してください。この節の残りでは理解を深めたい中級以上のpo4aの利用者向けに詳細情報を提供します。
本節の余りにも詳細な節を読む前に、po4a(1)を読んでpo4aでの作業工程の単純化された全体像を掴んでください。ほぼ全ての詳細が載っている、おどろおどろしい全貌を掴みたくなったらこちらに戻ってきてください。
以下の図解では、master.docは翻訳される文書の名前の例です。XX.docは言語XXで翻訳された同じ文書であり、doc.XX.poはXX言語の文書用の翻訳カタログです。文書の著者は主にmaster.doc(manページ、XML文書、AsciiDocファイルなど)に注力し、翻訳者は主にPOファイルに注力します。一般の利用者はXX.docファイルだけを目にします。
"[po4aがpoを更新]"といった角括弧の遷移はpo4aツールの実行を表しており、"{master.docの更新}"のような中括弧の遷移はプロジェクトのファイルの手作業での変更を表します。
master.doc
|
V
+<-----<----+<-----<-----<--------+------->-------->-------+
: | | :
{翻訳} | {master.docの更新} :
: | | :
XX.doc | V V
(省略可能) | master.doc ->-------->------>+
: | (新) |
V V | |
[po4a-gettextize] doc.XX.po -->+ | |
| (旧) | | |
| ^ V V |
| | [po4aがpoを更新] |
V | | V
translation.pot ^ V |
| | doc.XX.po |
| | (ファジー) |
{翻訳} | | |
| ^ V V
| | {手作業の編集} |
| | | |
V | V V
doc.XX.po --->---->+<---<-- doc.XX.po addendum master.doc
(初期状態) (最新) (省略可能) (最新)
: | | |
: V | |
+----->----->----->------> + | |
| | |
V V V
+------>-----+------<------+
|
V
[po4aが翻訳を更新]
|
V
XX.doc
(最新)
繰り返しますが、この図解は余りにも複雑です。単純化された全体像についてはpo4a(1)をご確認ください。
左の部分は、po4a-gettextize(1)を使って、既存の翻訳プロジェクトをpo4aのインフラへと変換する方法を示しています。このスクリプトは、元の文書と翻訳された対応する文書を受け取り、対応するPOファイルを構築しようとします。このような手動変換はかなり面倒ですが(詳しくは po4a-gettextize(1) のドキュメントを参照してください)、既存の翻訳を変換するために一度だけ必要になります。もし変換する翻訳がなければこのことは忘れてよく、スキーマの右の部分に集中することができます。
右上には原著作者が文書を更新する動作が図示されています。右中段では翻訳ファイルの自動的な動作が図示されています。新しい資料から抽出され、既存の翻訳と比較されます。変更されていない部分には以前の翻訳が使用され、部分的に変更された部分には、翻訳を更新する必要があることを示す "fuzzy" の印が以前の翻訳に付けられます。新しい部分や大きく変更された箇所は翻訳されないまま残されます。
次に、手動編集の箇所では、翻訳者がPOファイルを修正して、すべての元の文字列と段落に翻訳を提供する動作が表されていることがわかります。これは、GNOME Translation Editor や KDE の Lokalize や poedit などの特定のエディタ、あるいは weblate や pootle などのオンライン現地語化プラットフォームを使用して行うことができます。翻訳結果は、1言語につき1つのPOファイルの集まりです。詳細は gettext のドキュメントを参照してください。
図の下部は、po4a(1)が元の文書である master.docと翻訳者によって更新された翻訳カタログである doc.XX.poから翻訳された文書のソースを作成する様子を示しています。文書の構造は再利用され、元の内容は翻訳された対応部分に置き換えられます。もし補遺があれば、それを使って翻訳に追加のテキストを差し込めます。これは、最終的な文書に翻訳者の名前を追加するためによく使用されます。詳しくは以下をご覧ください。
コマンドで呼び出すと、po4aは翻訳ファイルと翻訳された文書ファイルの両方を自動的に更新します。
一から始める場合、po4aの構成ファイルを書きさえすれば準備が整います。欠けているファイルについては関係する雛形が作成され、貢献者が母語でプロジェクトの翻訳をできるようにします。速習のための入門や全ての詳細についてはpo4a(1)をあたってください。
既存の翻訳がある場合、例えば手作業で翻訳された文書ファイルがある場合、po4a-gettextizeを使ってpo4aの作業工程に内容を組み入れることができます。この作業は(このツールのmanページに記述されているように)少し面倒ですが、プロジェクトをpo4aの作業工程に変換してしまえば、全てが自動的に更新されるようになります。
準備が整ったら、po4aを呼び出すだけで翻訳のPOファイルと翻訳文書の両方が更新されます。po4aに"--no-translations"を渡して翻訳を更新しない(したがってPOファイルのみが更新される)ようにしたり、"--no-update"を渡してPOファイルを更新しない(したがって翻訳のみが更新される)ようにしたりできます。これはざっくり言うとそれぞれpo4a-updatepoとpo4a-translateスクリプトに対応しており、これらは現在非推奨です(後述のFAQの「なぜ個々のスクリプトが非推奨なのか」を参照)。
ファイルを手動で翻訳する管理をしていれば、翻訳文に新しいテキストを追加することは、長い目で見れば恐らくどんな管理方法よりも簡単でしょう :)。こうしたことは翻訳された文書に、元の文書のどの内容にも対応しない追加部分を入れたい場合に起こります。古典的な使用例としては、翻訳チームに謝辞を述べたり、翻訳特有の問題を報告する方法を示したりすることがあります。
po4a では addendum ファイルを指定しなければなりませんが、これは概念的に処理後に現地化された文書に適用されるパッチと見なすことができます。各補遺は別々のファイルとして与えられていなければなりませんが、その形式は従来のパッチとは全く異なっています。最初の行は header line で、補遺の挿入位置を定義し(残念ながら不可解な構文で……。後述)、ファイルの残りの部分は決められた位置にそのまま追加されます。
ヘッダ行は、文字列 PO4A-HEADER:で始まり、key=valueフィールドのリストをセミコロンで区切ったものが続く必要があります。
例えば以下のヘッダは翻訳の一番最後に置かなければならない補遺を宣言しています。
PO4A-HEADER: mode=eof
文書の途中に追加の内容を入れたい場合、話はより複雑になります。以下のヘッダは補遺を "About this document"という文字列を含むXMLのsectionの後に置かなければならないと宣言しています。
PO4A-HEADER: position=About this document; mode=after; endboundary=</section>
実際には、補遺を適用しようとするとき、po4aは"position"引数(これは正規表現でも構いません)に照合する最初の行を探します。po4aはここで翻訳されたの文書を考慮することを忘れないでください。この文書は英語ですが、補遺がフランス語に翻訳された文書に適用されることを意図しているなら、行はおそらく次のようなものにするべきでしょう。
PO4A-HEADER: position=À propos de ce document; mode=after; endboundary=</section>
"position"が対象の文書で見つかると、po4aは"position"以降で与えられた "endboundary"に照合する行を探します。その行のすぐ後ろに補遺が追加されます(なぜなら現在の節が終わる境界である endboundaryを与えたためです)。
ちょうど同じ効果が以下のヘッダでも付与でき、等価です。
PO4A-HEADER: position=About this document; mode=after; beginboundary=<section>
ここでは、po4a は翻訳の中の "About this document" に照合する行の後にある "<section>" に照合する最初の行を探し、beginboundary、つまり次の節の始まりを示す境界を与えたので、その行の 前に 補遺を追加するのです。ですから、このヘッダ行は、"About this document"を含む節の後に補遺を置き、"<section>"タグを含む行から節が始まるとpo4aに指示する必要があるとしています。これは前の例と同じです。なぜならここで本当にしたいことは、この補遺を "</section> > の後か "<section> > の前に追加することだからです。""
挿入 l<mode>を値 "before"に設定することもでき、これは似た意味を持ちます。 "mode=before"と "endboundary"を組み合わせると、照合した境界のちょうど 後ろに補遺を置き、最後の潜在的な境界線が "position"の前に来ます。"mode=before"と "beginboundary"を組み合わせると照合した境界のちょうど 前に 補遺が置かれ、最後の潜在的な境界線が "position"の前に来ます。
Mode | Boundary kind | Used boundary | Insertion point compared to the boundary ========|===============|========================|========================================= 'before'| 'endboundary' | last before 'position' | Right after the selected boundary 'before'|'beginboundary'| last before 'position' | Right before the selected boundary 'after' | 'endboundary' | first after 'position' | Right after the selected boundary 'after' |'beginboundary'| first after 'position' | Right before the selected boundary 'eof' | (none) | n/a | End of file
補遺についてのヒントとコツ
PO4A-HEADER: position=About this document; mode=after; beginboundary=<section>
PO4A-HEADER: position=About this document ; mode=after; beginboundary=<section>
補遺の例
.SH "AUTHORS"
mode=afterを設定して2工程の手法を選択します。それから position引数の正規表現でAUTHORSの後の行に検索を絞り込みます。次に、beginboundary引数の正規表現で、次の節の先頭(つまり^\.SH)に照合させます。つまり次のようになります。
PO4A-HEADER:mode=after;position=AUTHORS;beginboundary=\.SH
PO4A-HEADER:mode=after;position=Copyright Big Dude, 2004;beginboundary=^
PO4A-HEADER:mode=after;position=About this document;beginboundary=FakePo4aBoundary
もっと詳細な例
オリジナルドキュメント (POD フォーマット):
|=head1 NAME | |dummy - a dummy program | |=head1 AUTHOR | |me
そして、以下の補遺は確実に(フランス語で)翻訳者についての節をファイルの最後に追加されます(フランス語の "TRADUCTEUR" は "TRANSLATOR"、"moi" は "me" の意味です)。
|PO4A-HEADER:mode=after;position=AUTEUR;beginboundary=^=head | |=head1 TRADUCTEUR | |moi |
AUTHOR の前に追加内容を追加するには、以下のヘッダを使用してください:
PO4A-HEADER:mode=after;position=NOM;beginboundary=^=head1
これは、"NAME" 節(フランス語では "NOM"と翻訳される)の後にある beginboundary "/^=head1/" に照合した次の行が作者を宣言しているからうまくいくのです。そのため、両方の節の間に追加内容が挿入されます。なお他の節がNAMEとAUTHORの節に後で追加される場合、po4aは補遺を新しい節の前に置くため間違ったことになります。
これを防ぐためにmode=l<before>を使って同じことを達成できます:
PO4A-HEADER:mode=before;position=^=head1 AUTEUR
この章ではpo4aの内部の全体像を手短かに説明します。こちらをお読みいただければ、po4aを保守し改善していく手助けをするにあたってより自信がつくかもしれません。また、なぜ思ったように動作しないか、どのように問題を解決すればいいかを理解する助けになるかもしれません。
po4aプロジェクトの核心にはLocale::Po4a::TransTractor(3pm)クラスがあり、全てのpo4a構文解析器に共通する先祖となっています。この奇妙な名前は文書の翻訳と文字列の抽出を同時に行うところから付けられています。
もっと形式張っていうと、入力として翻訳する文書と翻訳が入っているPOファイルを取り、結果を別のPOファイル(入力文書から翻訳可能な文字列を抽出した結果)と翻訳済み文書(入力したファイルと同じ構造ですが、翻訳可能な文字列は入力POファイルの内容で置換されているファイル)の2つに分けて出力します。以下に図示します。
入力ドキュメント-\ /---> 出力ドキュメント
\ TransTractor:: / (翻訳済み)
+-->-- parse() --------+
/ \
入力 PO ---------/ \---> 出力 PO
(抽出済み)
この小さな骨子は、po4aアーキテクチャの中核の全てを表しています。両方の入力を与え、出力のPOを無視すると、po4a-translateになります。代えて出力文書を無視するとpo4a-updatepoになります。po4aは最初にTransTractorを使って最新の出力POTファイルを得て、msgmerge -Uを呼び出してディスク上の翻訳POファイルを更新します。それからこれらの更新されたPOファイルで2回目のTransTractorを構築し、出力文書を更新します。要は、po4aは必要な更新について1つの構成ファイルを使って一括で行う解決策を提供するわけです。
po4a-gettextizeも2つのTransTractorを使いますが、違ったやり方をします。1つ目のTransTractorを言語毎に構築し、それから元の文書のmsgidはmsgidとして、翻訳文書のmsgidはmsgstrとして使い、新しいPOファイルを構築します。po4a-gettextize(1)に記載されているように、このようにして照合された文字列が実際に照合できているかについては注意が必要です。
全てのpo4aの形式の構文解析器はTransTractorを土台に実装されています。Text、Markdown、AsciiDocといったとても単純なものがあります。これらはTransTractor::shiftline()を使って1行ずつ読み込み、段落の内容か何かを積み上げていきます。文字列が完全に構文解析できたら、構文解析器はTransTractor::translate()を使って(1)この文字列を出力POファイルに追加し(2)入力POファイルから翻訳を取得します。それから構文解析器はTransTractor::pushline()を使って出力ファイルに結果を押し込みます。
その他の構文解析器はもっと複雑ですが、それは入力文書を解析するために外部の構文解析器に依存しているためです。Xml、HTML、SGML、Podの構文解析器はSAX構文解析器を土台に構築されています。これらの解析器は「以下の内容の新規題名を見付けました」というようなイベントのコールバックを宣言し、TransTractor::translate()とTransTractor::pushline()を使って入力の内容に沿って出力文書と出力POTファイルを更新します。Yaml構文解析器はより単純ですが毛色が違います。YAML::Tiny構文解析器により生成されるデータ構造を直列化するのです。これがpo4aのYamlモジュールでは参照行の宣言ができないことの理由です。入力ファイルの各文字列の位置は構文解析器で保持されず、文字列の場所として与えられるのは「$filename:1」のみなのです。SAXに基づく構文解析は大域変数とその他の仕掛けを使ってファイル名と参照のための行番号を保存します。
ファイルの符号化方式とBOMの印により引き起こされる問題があります。単純な構文解析器では TransTractor::read() (入力文書の行を取得するために内部的に使用されます)で対処されるためこの問題を無視できます。しかし外部の構文解析器に依存するモジュールでは全てのファイルがPerlIOデコード層で適切に読み込まれるようにしなくてはなりません。最も簡単なのは、外部の構文解析器にファイルを自分で開いてファイルハンドルないし完全な文字列を与えることです。一例についてはPod::read()とPod::parse()を見てください。TransTractorにより読まれた内容は無視されますが、外部の構文解析器に新しいファイルハンドルが渡されています。重要な部分は "<:encoding($charset)" モードで、これがopen() perl関数に渡されます。
Locale::Po4a::Po(3pm)クラスはPOとPOTファイルの読み込みと活用を担います。基本的に、ファイルを読み、項目を追加し、 gettext() メソッドで翻訳を取得し、POをファイルに書き込めます。POTファイルにPOファイルを統合したりファイルを健勝したりするより発展的な機能についてはそれぞれmsgmergeとmsgfmtに説明を委ねます。
過去に全くオープンソースプロジェクトに貢献したことがなくても歓迎します。喜んで手助けしたりメンタリングします。po4aは今日も利用者により最高の保守がなされています。人手は足りていませんが、ドキュメントと自動的なテストを改善してプロジェクトへの貢献に自信が持てるようにすることで、プロジェクトへの貢献を迎え入れられるように努めています。詳細についてはCONTRIBUTING.mdファイルを参照してください。
以下はプロダクション環境でドキュメント用にpo4aを使っているプロジェクトのほんの一部の一覧です。もし一覧へのご自身のプロジェクトの追加をご希望でしたらEメール(またはマージリクエスト)をお寄せください。
このプロジェクトは多くのパッケージと別の言語に翻訳するインフラを提供しており、複数の主要なディストリビューション(Arch Linux、Debianとその派生、Fedora)への統合の準備が整っています。
個人的には<pouah|https://en.wiktionary.org/wiki/pouah>と発声しており、これは yuck の意味のフランス語の擬音語です :)。ヘンなユーモアのセンスかもしれません :)
po4a-updatepoとpo4a-translateはpo4aが代わることにより非推奨となりました。その理由はpo4aがこれらのスクリプトをそのまま置き換えられる一方、コードの重複がかなりあるためです。個別のスクリプトは150行のコードですがpo4aプログラムは1200行に及びます。故にこれらの個別のスクリプトは共通する内部の処理に加えて沢山のことをしています。コードの重複は両方のバージョンに於いて不具合に繋がっており、2つともを修正する必要が生じます。そのような重複の例はDebianの#1022216やGitHubの#442です。これは全く同じ修正ですが、かつはpo4aでかつはpo4a-updatepoにありました。
長い目で見たとき、個別のスクリプトを管理から外し、この1つのバージョンのみを保守したいと考えています。確かなことは個別のスクリプトは最早改善が行われないということで、po4aだけに新機能が加えられます。つまり、取り急ぎの非推奨というものはありません。少なくとも2030年まではできる限り個別のスクリプトを保持しておく計画でいます。もしプロジェクトでまだpo4a-updatepoとpo4a-translateを使っているのであれば、問題が起こるかもしれません。
コードの改修によりコードの重複がゼロに低減されれば、どこかの時点でこれらのスクリプトの非推奨を取り消す可能性もあります。もし思い付いたこと(欲を言えばパッチ)があればご協力を歓迎します。
いくつかあります。以下は恐らく不完全な一覧で、もっと多くのツールが現れてきています。
これは XML のみ、さらに特定の DTD のみを扱えます。リストが一つの大きな msgid になってしまうため、私はリストの扱いにかなり不満があります。リストが大きくなると、ひとかたまりの構造をつかみにくくなります。
以上に対する po4a の主な利点は、簡単に内容を追加できること (欠点かもしれませんが) と、gettext 化が簡単なことです。
- https://docs.kde.org/stable5/en/kdesdk/lokalize/project-view.html
- http://www.debian.org/intl/l10n/
しかし、すべて問題ないわけではありません。このアプローチには対処するべき欠点もあります。
私の夢の一つは Gtranslator や Lokalize に何らかの形で統合することです。文書ファイルを開くと文字列を自動的に抽出し、翻訳されたファイルとPOファイルをディスクに書き込みます。MS Word (TM) モジュール (少なくとも RTF) でこれができれば、プロの翻訳家もこれを使ってくれるかも知れません。
Denis Barbier <barbier,linuxfr.org> Martin Quinson (mquinson#debian.org)
倉澤 望 <nabetaro@debian.or.jp> Debian JP Documentation ML <debian-doc@debian.or.jp>
Hey! The above document had some coding errors, which are explained below:
| 2024-08-06 | perl v5.38.2 |