お急ぎですか?翻訳のプレースホルダーをブラウザでそのままチェックできます。無料・登録不要で、何もアップロードされません。
% はどこから来るのか
iOS アプリでは、変数を含むローカライズ文字列は書式文字列になります。SwiftUI では暗黙的にそうなります。
Text("\(count) items left") // key: "%lld items left"
Text("Welcome back, \(name)!") // key: "Welcome back, %@!"
String(localized: "Total: \(price) €") // price is a Double: "Total: %lf €"
Int は %lld、String は %@、Double は %lf になります。古い API では書式を自分で書きます:String(format: NSLocalizedString("items_left", comment: ""), count)。この場合、.strings ファイルには %d や %ld が入ります。
いずれにしても、翻訳者が受け取るテキストには途中に % 記号が入っていて、そこで問題が起きます。
翻訳が壊れる 3 つのパターン
指定子を落とす。"Invitez %@ à modifier cette liste" のはずが "Invitez à modifier cette liste" になっているケースです。クラッシュはしません。引数が無視され、相手の名前が画面から消えるだけです。最もよくあるミスで、最も気づきにくいものです。
型を変える。%lld を %@ に置き換える、またはその逆です。書式は引数を間違った型として読みます。よくてもおかしな数値、悪ければクラッシュです。%@ はオブジェクトを想定しているのに、整数が渡されるからです。
黙って順序を変える。語順の異なる言語では、"%@ shared %@" が 2 人の名前が入れ替わった文になります。構文は正しくても、意味が違います。日本語は語順が英語と大きく異なるので、特に注意が必要です。
位置指定で順序を入れ替える
順序を変えるには、引数に番号を付けます。%1$@ は 1 番目の引数、%2$@ は 2 番目の引数で、文中のどこにあってもかまいません。
/* source */ "%@ shared %@ with you" = "%1$@ shared %2$@ with you";
/* French */ "%@ shared %@ with you" = "%1$@ vous a partagé %2$@";
/* other order */ "%@ shared %@ with you" = "%2$@ : partagé par %1$@";
1 つの文字列の中では、すべてに番号を付けるか、どれにも付けないかのどちらかにします。%1$@ と %@ の混在は未定義動作です。
フランス語の罠:「20 % de remise」
フランス語版を出すなら、次の点に注意してください。フランス語の組版ではパーセント記号の前にスペースを入れます:「20 % de remise」(20% オフ)。書式文字列ではこれが問題になります。% d(パーセント、スペース、d)が有効な指定子だからです。スペースは printf のフラグ(「正の数の前にスペースを入れる」)で、d は整数の変換です。次の文は
"Profitez de %lld % de remise"
引数 1 つに対して %lld と % d の 2 つの指定子を持つことになります。2 つ目は存在しない値を読みます。正しくは記号を二重にします。
"Profitez de %lld %% de remise"
影響するのは書式を通る文字列、つまり変数を 1 つ以上含む文字列だけです。引数のない固定の「20 % de remise」は書いたとおりに表示されます。ただ、翻訳は後から変わります。誰かがその文に変数を追加した時点で、単独の % はバグになります。
複数形
「1 item / 3 items」のために、キーを 2 つ用意してコードで if を書くのはやめましょう。String Catalog では、%lld items left キーに複数形バリエーション(one、other、言語によっては few や many)を持たせ、それぞれに %lld を入れます。日本語は other だけです。1 つの文に独立して変わる数値が 2 つある場合、Xcode は置換(%#@items@)を使います。こちらはまれなケースで、Xcode で編集します。
リリース前のチェック
基本のチェックは機械的です。各翻訳の指定子のリスト(型と位置)が、ソースと一致していなければなりません。LocalePilot の QA が .strings と .xcstrings ファイルで行うのがこれです。%@、%1$@、%d、%ld、%lld、%.2f、%u、%i、%s を認識し、%% はテキストとして扱い、指定子の欠落や余分を書き出し前に検出します。%e や %x のような珍しい変換は、「20%en」をエラーと誤認しないよう、あえて無視しています。上の「% d」のケースは人がレビューで確認してください。フランス語訳で、スペースが後に続く % を検索してください。