抽出¶
抽出は、ソースコード中のマークされたすべてのメッセージを、翻訳者向けの
.pot templateへ集める工程です。チュートリアルのループの
ステップ3にあたります。このページはその工程のリファレンスです。設定、独自の
関数名、CI向けのstrictモード、そしてカタログをその後守り続ける検証を
説明します。
抽出にはbabel extraが必要です。
ワークフロー¶
babel.cfgを作成します。
その後は通常のBabelコマンドを使います。
pybabel extract -F babel.cfg -c "Translators:" -o locales/messages.pot .
pybabel init -i locales/messages.pot -d locales -l ja
pybabel compile -d locales
initを実行するのは言語ごとに一度きりです。それ以降は、pybabel updateが
新しいtemplateを既存のカタログへ折り込みます。この繰り返しのサイクルと、
そのfuzzyエントリがリリースにとって何を意味するかは、
実運用で一巡します。
gettext_tstrings extractorは通常の_()、gettext()、ngettext()呼び出しも
処理するため、混在したcodebaseを1つのmappingで抽出できます。_()、4つの標準
gettext名、tr() / ntr() alias、遅延用のlazy_gettext() /
lazy_pgettext()を認識します。
-cで翻訳者向けコメントを有効にする
通常のgettext呼び出しと同じく、pybabel extractで翻訳者向けコメントを
収集するには-c "Translators:"を渡す必要があります。渡さなくても抽出
自体は動きますが、コメントがカタログへ届かなくなります。カタログ上の
コメントは、ワークフロー全体で
最も安上がりな品質向上の手段です。
独自の関数名を登録する¶
iniファイルでは1つの文字列、TOML mappingではlistを指定します。文字列内の名前は 空白またはcommaで区切れます。4通りの表記すべてに対応します。
optionはtr_functions、ntr_functions、gettext_functions、
ngettext_functions、pgettext_functions、npgettext_functionsです。
-kはt-stringへ届きません
mytr(t"…")のような独自helperは、上記optionのいずれかへ名前を登録する必要が
あります。Babelの--keyword機構はt-string literalを読めないため、
pybabel extract -k mytrは何も見つけず、警告も出しません。メッセージがPOTに
入らないだけです。併せて抽出される通常のgettext呼び出しには、引き続き-kを
使用できます。
対応するのは標準の引数順だけです。通常はmessageが先、pgettextではcontextの
次にmessage、npgettextではcontext、単数形、複数形の順です。
ローカルでは寛容に、CIでは厳密に¶
既定では、1つの不正なファイルで抽出全体が停止することはありません。
- extractorが拒否するt-string(属性アクセス、式、不正な引数)は警告して skipします。
- parseできないファイルも同様にskipします。
astでは受理されてもtokenizeだけが拒否するファイルもskipします。そうしないと Babel自身のpassが停止するためです。
これは編集中には便利ですが、そうでないときには危険です。skipされたメッセージは
単にPOTに現れないため、翻訳されることもなく、それを知らせるものもありません。
人の目が抽出を見ていない場所では、mapping optionでstrict = trueを指定して
ください。
これで上記の警告はすべてhard failureになります。こちらを本番向けの設定、既定を ローカル向けの設定と考えてください。
既存toolchainによるカタログ検証¶
Babelは抽出した各メッセージへ標準flagを付けます。この1行だけで、すでに使っている ツールのプレースホルダー検証が有効になります。
こんにちは {nombre}と翻訳すると、追加設定なしで誤りが検出されます。
$ msgfmt --check-format -o /dev/null locales/ja/LC_MESSAGES/messages.po
locales/ja/LC_MESSAGES/messages.po:25: a format specification for argument
'name' doesn't exist in 'msgstr'
msgfmt: found 1 fatal error
Weblateでは同じ検証がPython brace formatとして文書化されており、 商用platformにも同じflagに基づくplaceholder QAがあります。各platformの挙動は それぞれのものです。ここで検証しているのは以下の2ツールです。
さらに、このパッケージはBabel checkerを登録します。そのため
pybabel compileはgettext-tstrings marker commentを持つすべてのメッセージへ
仕様の規則を適用します。
$ pybabel compile -d locales -l ja
error: locales/ja/LC_MESSAGES/messages.po:24: translation does not match the
source placeholders: {name} is missing; {nombre} is not in the source message
1 errors encountered.
複数形メッセージでは対象のformも示します。Babelが報告する行番号はmsgidの行であり、
ロシア語のblockにはその下に3つのmsgstrがあるためです。
error: locales/ru/LC_MESSAGES/messages.po:31: msgstr[1]: translation does not
match the source placeholders: {n} is missing
pybabel compileはそれでも.moを書き出します
上記エラーは報告され、終了statusは1ですが、不正なカタログもコンパイル
されます。出荷を止められるのはその終了statusだけです。それを働かせるビルド
ステップはCIで防ぐことで示します。
2つの検証は重複していません。このパッケージのcheckerの方が厳密な場面が 少なくとも2つあります。
- msgidの波括弧がescapeされたものだけ(
Config {{raw}} only)ならpython-brace-formatflagが付かないため、外部ツールは一切検証しません。 - 複数形はformごとに検証します。上記ファイルを
msgfmt --check-formatで読むと status0になります。兄弟formが保持するプレースホルダーを1つのformだけが 省略してもmsgfmtでは受理され、こちらのcheckerでは拒否されます。
msgfmtが検証するのは、Python brace formatとしてparseできるプレースホルダー名だけ
です。ASCII名を使えば、toolchain内のすべてのツールがメッセージを検証できます。
ライブラリ自体はstr.isidentifier()を満たすすべての名前を受理します。
Templateとその他のツール¶
t-stringはPython構文なので、このライブラリの対象はPython sourceです。template言語は
それぞれのi18n機能(Jinja2の{% trans %}、Djangoのtemplate tag)とBabel extractor
を引き続き使います。すべて同じPOカタログへ入るため、混在したcodebaseでも1つの
翻訳ワークフローを維持できます。
現在のpygettextはt-stringをparseできないため、抽出にはBabelを使います。他の
extractorや将来のpygettextが同じ規約を実装できるよう、
仕様として文書化しています。