完全なメッセージを訳す、
Pythonのt-stringで。¶
gettext-tstringsは、Python 3.14以降のt-stringを標準のgettextカタログとBabel
のツール群へつなぎます。値と書式指定はアプリケーションのコードに残り、翻訳者は
完全なメッセージと{name}という単純なプレースホルダーを扱います。
import gettext
from gettext_tstrings import Translator
_ = Translator(gettext.translation("messages", localedir="locales"))
name = "Ada"
print(_(t"Hello {name}")) # with a Japanese catalog: こんにちは Ada
カタログに入るのはHello {name}です。翻訳では{name}を移動させたり繰り返したり
できます。プレースホルダーを削除したり、名前を変えたり、書式を付け替えたりすると、
カタログの検証がそのエラーを報告します。不正なエントリがそれでも本番へ届いた
場合、ライブラリは警告をログへ記録し、クラッシュせずにソースメッセージを
レンダリングします。
alpha版 · Python 3.14以降 · 標準のPO/MOカタログ · サードパーティの実行時依存なし
このサイト自身が、ここに書かれていることを実践しています。ナビゲーション、
ラベル、複数形に対応したビルドレポートを含むどの言語版も、POカタログから
gettext-tstrings自身で描画しています。
あなたに向いているか¶
いま向いているのは、アプリケーションがPython 3.14以降で動いていて、すでに gettextとBabelを使っている(あるいはそのPO/MOワークフローを採り入れたい)、 そしてレンダリング前に検査される名前付きプレースホルダーをt-stringの構文で 書きたい場合です。
まだ向いていないのは、Python 3.13以前が必要な場合、安定したPython APIが 必要な場合 — これはalpha版で、固まっているのは仕様の部分です — あるいは翻訳対象のテキストのほとんどが、Pythonソースではなくテンプレート言語の 中にある場合です。
すでにカタログがありますか。そのまま動き続けます。
_("Hello {name}").format(name=name)とtr(t"Hello {name}")は同じmsgidを
生成するので、既存の翻訳は乗り換えても失われません — 移行の全体像は
移行で説明します。
カタログが言えること¶
翻訳は、自分が翻訳するメッセージの構造を変えられない。約束はこれがすべてで、
このサイトの残りはすべてそこから導かれます。翻訳では{name}の順序を変えたり
繰り返したりでき、その周りの語はすべて書き換えられます。ただし、プレースホルダーを
省略すること、新しいものをでっち上げること、そこからあなたのオブジェクトへ手を
伸ばすこと、独自の書式指定を付けることはできません。
ライブラリはそれを入口で — カタログのコンパイル時に — 検査し、レンダリング時に もう一度検査します。これが、レビューで見つかる誤りと、ユーザーに見つかる誤りとの 違いです。
gettextが初めての方へ — ワークフロー全体を4文で
gettextは、Pythonに限らず広く使われている、ソフトウェアを翻訳するための
標準的な仕組みです。コードが翻訳対象のメッセージをマークし、抽出器がそれらを
templateファイル(.pot)へ集めます。翻訳者 — 多くの場合programmerでは
ありません — が言語ごとに1つのカタログファイル(.po)を埋め、それが
バイナリの.moへコンパイルされ、アプリケーションが実行時に読み込みます。
翻訳関数の慣習的な名前は_なので、_(t"Hello {name}")は「このメッセージを
翻訳する」と読めます。チュートリアルでは、マーク、抽出、
翻訳、コンパイル、実行という一連の流れを5分ほどで一巡します。
解決する課題¶
f-stringはライブラリが受け取る時点ですでに補間済みです。f"Hello {name}"は
"Hello Ada"になっており、値の前後の断片を翻訳することは、ほとんどの言語で
文法を壊します。t-string(PEP 750)は、静的なテキスト、評価済みの値、元の式、
変換指定、フォーマット指定を分離したまま保持します。これはメッセージカタログが
必要とする分離そのものです。%(name)s、.format()、$文字列と比べて
何が変わるのかを確認できます。
ただし、gettextにもBabelにも、t-stringをどのようにメッセージへ変換するかの 規定はありません。このライブラリはその規約を定め、 バージョン付きの仕様として文書化し、実装を検証する 適合性テストスイートも提供します。
設計上の規則¶
- 文の断片ではなく、常に完全なメッセージを翻訳します。
{name}のような単純な変数名だけを受け付けます。!rや:.2fはアプリケーション側で管理し、カタログには渡しません。- 翻訳が既知のプレースホルダーを並べ替えたり繰り返したりすることは認めつつ、 属性へ手を伸ばしたり書式を追加したりすることは防ぎます。
- 一般的なPOT、PO、MOファイルと、それらに対応する既存ツールを再利用します。
そして、意図的に手を出さないことの一覧も対になっています。数値、通貨、日付の ローカライズはしません — 先に整形してください、 Babelで。HTML、shell、terminalといった出力先に応じたレンダリング結果のescape もしません。そして翻訳が正しいかどうかは判定できません。判定できるのは プレースホルダーが無傷かどうかだけです。
インストール¶
Python 3.14以降が必要です。レンダリングには依存パッケージがありません。
標準ライブラリの gettext だけを使用します。
抽出とカタログ検証はBabel経由で行います。pybabel を実行する環境にextraを
インストールしてください。通常は本番イメージではなく、開発環境やCIです。
次に読むページ¶
まずはここから — gettextの経験は前提としません:
- チュートリアル — 空のディレクトリから動く日本語翻訳まで 5ステップ、すべてのコマンドを出力付きで示します。
- t-stringを選ぶ理由 — 同じメッセージを4通りで記述し、
%(name)s、.format()、$文字列がそれぞれカタログに何を渡すかを比較します。
使う — 実務のリファレンス:
理解する — 歴史から実装まで:
リファレンス — 契約:
開発状況¶
| パッケージ版数 | 0.1.0a8 |
| APIの安定性 | alpha — Python APIは今後変更される可能性があります |
| 仕様 | v1、適合性テスト付き |
| Python | 3.14以降。3.14、3.14t(free-threaded)、3.15でテスト済み |
| Babel | 2.18以降。pybabelを動かす場所でのみ必要 |
| 実行時依存 | なし — 標準ライブラリのgettextのみ |
| カタログ形式 | 通常のPOT、PO、MO |
| 変更履歴 | CHANGELOG |
現在はalpha版です。契約は意図的に小さく保たれ、 仕様が安定部分です。Python APIは今後変更される可能性があります。 安定版までには、より多くの言語のfixture、継続的な性能計測、gettextとBabelの 実利用者によるAPIレビュー、対応するすべてのPython/Babelリリースでの互換性検証が 必要です。
IssueとPull Requestを歓迎します。 alpha版の今こそ、インターフェースを議論する価値があります。
コミュニティに参加する¶
- 範囲が明確な good first issue から取り組めます。
- 使い方の質問は Q&A Discussionsへ。
- 本番のgettextワークフローやAPIの提案は Ideas Discussionsへ。
- Pull Requestを作る前に コントリビューションガイド をお読みください。