GEMINI LABEN
CHAT — 8月26日から Google Chat が Ask Gemini のハブになります。検索・下書き・会話の追いつき・タスクとイベントの管理を Workspace の文脈のまま扱えます。残り2日ですANDROID — 9月4日から Android の Google アシスタントを Gemini が置き換えます。残り11日です。アシスタント前提の音声ショートカットは、いま挙動を確かめておきたいところですROBOTICS — gemini-robotics-er-1.6-preview は8月31日で停止します。残り7日です。後継の ER 2 系は空間推論・多段のツール連携・複数ロボットの協調に対応しますPRICE — Gemini 3.7 Flash の導入価格は100万トークンあたり入力0.75ドル・出力3.75ドルで、12月31日までです。2027年1月1日から入力1.50ドル・出力7.50ドルへ倍になりますFREE — Google AI Studio には1日あたりのリクエスト上限つきの無料 API ティアがあり、クレジットカードなしで試せます。3.7 Flash の挙動を確かめるだけなら、まずここで足りますSCALE — Gemini アプリは8月11日に月間10億ユーザーへ到達しました。深い推論は 3.1 Pro、速度と単価が要る本番処理は Flash 系という住み分けが定着しつつありますCHAT — 8月26日から Google Chat が Ask Gemini のハブになります。検索・下書き・会話の追いつき・タスクとイベントの管理を Workspace の文脈のまま扱えます。残り2日ですANDROID — 9月4日から Android の Google アシスタントを Gemini が置き換えます。残り11日です。アシスタント前提の音声ショートカットは、いま挙動を確かめておきたいところですROBOTICS — gemini-robotics-er-1.6-preview は8月31日で停止します。残り7日です。後継の ER 2 系は空間推論・多段のツール連携・複数ロボットの協調に対応しますPRICE — Gemini 3.7 Flash の導入価格は100万トークンあたり入力0.75ドル・出力3.75ドルで、12月31日までです。2027年1月1日から入力1.50ドル・出力7.50ドルへ倍になりますFREE — Google AI Studio には1日あたりのリクエスト上限つきの無料 API ティアがあり、クレジットカードなしで試せます。3.7 Flash の挙動を確かめるだけなら、まずここで足りますSCALE — Gemini アプリは8月11日に月間10億ユーザーへ到達しました。深い推論は 3.1 Pro、速度と単価が要る本番処理は Flash 系という住み分けが定着しつつあります
記事一覧/開発ツール
開発ツール/2026-08-24初級

Gemini の訳文で %1$s が消えても、レビューでは気づけません。文字列リソースへ戻す前の検査

Gemini に訳させたアプリの文字列から書式指定子が抜けたり全角になったりする事象を、実運用の中で拾い続けています。壊れ方を4種類に整理し、生成時に守らせる書き方と、受け取った直後に弾く短い検査を紹介します。

Gemini API218ローカライズ3個人開発107構造化出力15品質チェック

訳文そのものは自然でした。意味も通っていました。それなのに、保存完了のメッセージから枚数の数字だけが消えていました。

私が個人で運用している壁紙アプリは、通知文やダイアログの文字列を複数の言語で持っています。その多言語化に Gemini を使うようになってから、翻訳の品質そのものより先に手を焼いたのが、この「書式指定子が静かにいなくなる」現象でした。

人間が訳文を読み返すとき、目は意味のほうへ行きます。%1$s%@ は意味を持たない記号ですから、読み飛ばされます。そして実機で初めて、数字の入るはずの場所が空白になっていることに気づきます。

機械的に弾けるものは、機械に弾かせたほうが確実でした。ここでは、その検査を短いコードで用意するところまでを書きます。

訳文レビューでプレースホルダだけがすり抜ける理由

書式指定子は、翻訳という作業の中で特殊な立ち位置にあります。文の一部でありながら、訳してはいけない部分です。

モデルから見ると、%1$s は前後の文脈から切り離された短い記号列でしかありません。訳文を自然な語順に組み替える過程で、この記号が巻き込まれます。文としての完成度を上げようとするほど、記号は邪魔者になります。

そして人間のレビューでは、この欠落は見つかりません。訳文を読んで「意味が通るか」を確かめる作業と、「記号が同じ数だけ残っているか」を確かめる作業は、使う注意の種類が違います。前者に集中しているとき、後者は働いていません。

私の場合、レビューを丁寧にやろうとするほど検出率が下がりました。文章として読み込むほど、記号が背景に沈むためです。

壊れ方は4種類に収まりました

しばらく記録を取ったところ、壊れ方の種類はそれほど多くありませんでした。

種類起きること実行時の見え方
欠落訳文から指定子が消える値が入らない。言語によっては例外で停止する
全角化%@%@ になる置換されず、記号がそのまま画面に出る
増殖同じ指定子が2つ以上に増える引数の数と合わず、未定義の値を読みにいく
並び替え語順に合わせて指定子の順番が入れ替わる位置指定なしの場合、値が別の場所に入る

厄介なのは4番目です。並び替え自体は間違いではありません。%1$s のように位置を明示した指定子であれば、訳文の中で順番が変わっても正しく解決されます。

問題は、原文が %s%s のように位置指定なしで書かれている場合です。このとき順番が入れ替わると、値が入れ替わったまま、例外も出さずに表示されます。数字と名前が入れ替わった文が、そのまま利用者に届きます。

つまり、原文側を位置指定つきに揃えておくことが、翻訳を頼む前の下ごしらえになります。ここを済ませておかないと、後段の検査でも「並び替えを許してよいのか」を判断できません。

生成の時点で、記号を訳させない

検査の前に、そもそも壊れにくい頼み方があります。指定子を翻訳対象から外してしまう方法です。

一つは、送る前に指定子を目印へ置き換え、戻ってきてから復元するやり方です。%1$s[[P1]] のような目立つ文字列にしておくと、モデルはそれを訳語ではなく記号として扱いやすくなります。

もう一つは、構造化出力で受け取ることです。原文と訳文を対にした JSON で返させると、どの原文に対する訳文なのかが確定します。対応関係が曖昧なまま長い文字列を受け取るより、後段の検査がずっと書きやすくなります。構造化出力そのものでつまずいた場合は、Gemini APIのJSON出力・構造化出力が正しく返らない原因と対処法 に症状別の切り分けをまとめてあります。

なお、出力の揺れを抑える目的で temperature を指定していた方は、そろそろ別の方法を考える時期です。サンプリング系のパラメータは非推奨になりました。揺れを潰す役目は、モデルの設定ではなく、こちらの検査側へ移していくことになります。この記事の検査を用意しておくことは、その移行の準備にもなります。

受け取った直後に弾く、短い検査

実際に使っている検査は、原文と訳文からそれぞれ指定子を拾い出し、個数を比べるだけのものです。

import re
import unicodedata
from collections import Counter
 
PLACEHOLDER = re.compile(r"%(\d+\$)?[@sdfxu]|%%")
 
def placeholders(text: str) -> Counter:
    # 全角の記号を半角へ寄せてから数える
    normalized = unicodedata.normalize("NFKC", text)
    return Counter(m.group(0) for m in PLACEHOLDER.finditer(normalized))
 
def has_fullwidth(text: str) -> bool:
    # 正規化した場合としない場合で結果が変わるなら、全角が混ざっている
    raw = Counter(m.group(0) for m in PLACEHOLDER.finditer(text))
    return placeholders(text) != raw
 
def verify(source: str, target: str) -> list[str]:
    src, dst = placeholders(source), placeholders(target)
    problems = []
    if src - dst:
        problems.append(f"欠落: {dict(src - dst)}")
    if dst - src:
        problems.append(f"増殖: {dict(dst - src)}")
    if has_fullwidth(target):
        problems.append("全角の書式指定子が含まれています")
    if src and not all("$" in p for p in src):
        # 位置指定なしの指定子が原文にある場合、並び替えを検出できない
        problems.append("原文に位置指定なしの指定子があります(並び替えを検証できません)")
    return problems

手元で確かめたところ、この検査はいくつかの点で素直ではありませんでした。

まず、unicodedata.normalize("NFKC", text) を通すと %@%@ になります。つまり、正規化してから個数を比べる限り、全角化は差分として現れません。全角の検出には、正規化前後の結果を比べるという別の一手が必要でした。個数の比較だけを信じていると、この壊れ方は素通りします。

次に、並び替えの扱いです。Counter は多重集合ですから、順番は見ていません。位置指定つきの指定子であればそれで正しく、位置指定なしであれば検証できません。この検査に「原文の指定子が位置指定つきかどうか」を確かめる行が入っているのは、そのためです。検査で守れる範囲を、検査自身に申告させています。

%% は二重にした百分率の記号で、置換の対象ではありません。正規表現の最後にこれを入れておかないと、% を拾い損ねて誤検出につながります。

どこに置くか、そして落ちたときにどうするか

私はこの検査を、翻訳を受け取った直後に置いています。文字列リソースのファイルへ書き出す前です。

書き出してからビルド時に検証する構成も考えましたが、やめました。壊れた訳文がいったんファイルに入ってしまうと、どの言語のどの行が生成物なのかが曖昧になります。境界は、外から来たものが自分のリポジトリへ入る手前に置くのが分かりやすいと感じています。

検査に引っかかった行は、その言語だけ保留にします。全体をやり直すのではなく、該当する原文だけを再度投げます。多くの場合、二度目は通ります。三度続けて通らない場合は、原文の側に無理があると考えて、文そのものを短く書き直します。

再生成を自動で繰り返す仕組みは、あえて入れていません。同じ原文が何度も引っかかるという事実自体が、原文を直す合図になるためです。自動で押し流してしまうと、その合図が見えなくなります。

まとめ

まず、自分のアプリの原文を開いて、位置指定なしの %s%@ が残っていないかを確かめてみてください。そこを位置指定つきに揃えるところからで、翻訳の並び替えは安全になります。

翻訳の品質そのものをどう見るか、スコアで測る仕組みまで踏み込みたい場合は、Gemini API でアプリのローカリゼーションを自動QAする で構造化出力を使った評価パイプラインを扱っています。この記事の検査が「壊れていないこと」を守るものだとすれば、あちらは「良い訳であること」を測るものです。

ロケールごとの実際の使い分けについては、Gemini 2.5 Flash で App Store のキーワード欄を多言語最適化した1ヶ月の所感 にも運用の記録を残しています。

小さな検査ですが、実機で気づく前に止められるようになったことの安心感は、思っていたよりも大きなものでした。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-08-20
Gemini に書かせたレビュー返信を、送信前に止めるための最小の検査
ストアのレビュー返信は下書きより送信後が怖い作業です。守れない約束・連絡先の混入・文体崩れを送信前に機械で止める Python の検査を、実際に走らせた結果と誤検出の修正まで含めて置いておきます。
開発ツール2026-08-22
段階公開を止める判断だけは、Gemini に渡さないことにしました
段階公開の初日に見えるクラッシュ率には、良し悪しを判定できるだけの母数がありません。止める・進める・まだ言えないの3状態に分け、Gemini にはレビュー本文の帰属判定だけを任せた配線を残します。
開発ツール2026-08-17
配布済みアプリに残るモデル名と、停止日をまたいだ切り戻しの設計
サーバー側の移行が終わっても、ユーザーの端末で動いている旧バージョンのアプリは古いモデル名を握ったままです。停止日をまたぐと切り戻しが復旧手段でなくなる構造と、モデル解決をサーバーに寄せる設計をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →