移行¶
プロジェクトですでにgettextを使っているなら、このライブラリを採用できるかを 決める問いは限られています。手元のカタログを無効にしてしまうのか、まだ変える つもりのないコードと共存できるのか、そして移行のどこまでを一度にやらなければ ならないのか。短い答えから並べます。
| 問い | 答え |
|---|---|
既存の.poと.moはそのまま使えるか? |
使えます。同じファイル、同じツールです。 |
| 旧来の呼び出しと新しい呼び出しは1つのファイルに同居できるか? | できます。1つのextractor mappingが両方を拾います。 |
| msgidは変わるか? | .format()からなら変わりません。%形式からなら変わります。 |
| プロジェクト全体を一度に移す必要があるか? | ありません。呼び出し箇所1つでも有効な変更です。 |
| Jinja、Djangoテンプレート、JavaScriptはどうなるか? | 手つかずのまま、同じカタログを使い続けます。 |
このページの残りは、それぞれの詳細です。
.format()から:msgidは変わらない¶
移行の費用がほとんどかからないのがこの場合です。str.formatのメッセージと
t-stringのメッセージは同じカタログkeyを導きます。どちらでもkeyは{name}を
残したままのテキストだからです。
# Before
_("Hello {name}").format(name=name)
# After — the msgid is still "Hello {name}"
tr(t"Hello {name}")
そのため既存の翻訳は結び付いたままです。次のカタログを出発点とします。
呼び出しを変更し、再抽出して、更新します。
$ pybabel extract -F babel.cfg -o locales/messages.pot .
extracting messages from app.py (encoding="utf-8")
writing PO template file to locales/messages.pot
$ pybabel update -i locales/messages.pot -d locales
updating catalog locales/ja/LC_MESSAGES/messages.po based on locales/messages.pot
戻ってくるエントリの違いはメタデータ2行だけで、他は何も変わりません。t-string メッセージであることを示すmarker commentと、ソースの行番号です。
fuzzyフラグは付かず、どの言語でも再翻訳は要りません。メッセージはすぐに
表示されます。
$ pybabel compile -d locales
compiling catalog locales/ja/LC_MESSAGES/messages.po to locales/ja/LC_MESSAGES/messages.mo
$ python app.py
こんにちは Ada
update --checkはカタログを古いと報告します
そのmarker commentと動いた行番号だけで、pybabel update --checkはカタログの
再生成が必要だと言います。翻訳だけでなくエントリ全体を比較するからです。
コード変更と同じcommitで本物のpybabel updateを実行し、カタログも一緒に
commitしてください。すでにCIゲートが求めている
のと同じ習慣です。
%形式から:msgidが変わり、翻訳はfuzzyになる¶
printf構文はメッセージの内側にあるため、これを置き換えるとカタログkeyが
書き換わります。回避する方法はなく、%(name)sを手放すことの正直な代償です。
pybabel updateは、新しいメッセージが削除されたメッセージの近縁だと認識し、
古い翻訳をfuzzy付きで引き継ぎます。
#. gettext-tstrings
#: app.py:4
#, fuzzy, python-brace-format, python-format
msgid "Hello {name}"
msgstr "こんにちは %(name)s"
この状態について知っておくことが3つあります。
- 実行時に壊れるものはありません。 fuzzyエントリはコンパイル済みの
.moから 除外されるため、人が組を確認するまでアプリケーションはソースメッセージを 表示します。書き換えられたメッセージが通るのと 同じ劣化の仕方です。 pybabel compileはその1件ずつを報告し、非ゼロで終了します。引き継がれた{name}ではない%(name)sが正しい波括弧プレースホルダーではないからです。 その一覧は誤報ではなく作業リストです。並んだエントリは本当に編集を必要と しています。- 古い
python-formatフラグも一緒に付いてきます。fuzzyフラグと一緒に 削除してください。さもないとmsgfmt --check-formatが、波括弧形式の メッセージにprintfの規則を当て続けます。
名前付きのprintfプレースホルダーなら編集は機械的です。%(name)sが{name}に
なるだけで、他は動きません。ですから大きなカタログでも、再翻訳ではなく、
スクリプトによる一括処理と翻訳者のレビューで済みます。位置指定の%sは機械的
ではありません。引き継ぐ名前がなく、その名前を選ぶことこそが今回の変更の
主眼だからです。
そのため実務上の順序は、%形式のメッセージを意図的に — モジュール単位、
リリース単位、言語単位で — 移すことです。すべてのカタログを一度に赤くする
一括作業は避けてください。
旧来の呼び出しと新しい呼び出しは同居する¶
t-stringを読むextractorは通常のgettext呼び出しも読むため、移行途中のファイルも mapping 1つで足ります。
from gettext_tstrings import tr
from myapp.i18n import _
name = "Ada"
print(_("Save changes"))
print(tr(t"Hello {name}"))
どちらのメッセージも同じtemplateへ入り、このライブラリの追加検査を有効にする marker commentが付くのはt-stringの方だけです。
#: app.py:5
msgid "Save changes"
msgstr ""
#. gettext-tstrings
#: app.py:6
#, python-brace-format
msgid "Hello {name}"
msgstr ""
認識するのは_()、4つの標準gettext名、tr() / ntr() alias、遅延用の
lazy_gettext() / lazy_pgettext()です。独自のhelperは
mappingへ名前を登録する
必要があります。
実行時にも、2つのスタイルは同じように独立しています。gettext.translation()が
翻訳オブジェクトを1つ返し、_もこのライブラリの入り口も、そこから読みます。
移らないもの¶
- テンプレート言語。 Jinja2の
{% trans %}、Djangoのtemplate tag、そして それぞれのBabel extractorは、そのまま動き続け、同じPOカタログへ流れ込み 続けます。t-stringはPythonの構文であり、対象はPythonソースです。 - カタログファイル。 形式の変更も、新しいファイルも、変換工程もありません。
- 翻訳プラットフォーム。
.poによるやり取りは同一で、t-stringメッセージが 持つpython-brace-formatフラグは.format()のメッセージが持つものと同じ フラグです。プレースホルダーQAはそのまま働きます。 - Python以外のコード。 同じプロジェクト内のJavaScriptやCのカタログは 影響を受けません。
移行のチェックリスト¶
pybabelを実行する場所へbabelextraを追加し、babel.cfgのpythonmappingをgettext_tstringsメソッドへ変更します。これでmapping 1つが両方の スタイルを拾い、通常の呼び出しには-kが引き続き効きます。- まず
.format()の呼び出し箇所を変換します。再抽出してpybabel updateを 実行し、カタログをコードと一緒にcommitします。fuzzyエントリは出ないはずです。 %形式の呼び出し箇所を、レビューできる大きさの単位で変換します。引き継がれた プレースホルダーを書き換え、fuzzyとpython-formatのフラグを外します。- 制約が拒否するものを直します。補間は単純な名前でなければならないので、
t"Hello {user.name}"はまずローカル変数にします。これは呼び出し箇所の編集 であり、カタログの編集ではありません。 - 一巡し終えたらextractor mappingの
strict = trueを有効にします。抽出できない メッセージがtemplateから消えるのではなく、 ビルドを落とすようになります。 - 実運用の実行時チェックを追加します。出荷する
言語ごとに1つのメッセージを、strictな
Translatorでレンダリングします。
ステップ2と3は普通のcommitです。この一覧に、一斉切り替えの日を要するものは ありません。