Codex、Claude Code、Cursorにワンプロンプトで翻訳させたアプリは、リリース前に7点を確認してください。長い文字列が収まるか、複数形が各言語に合っているか、変数が無事か、用語が統一されているか、トーンがネイティブにとって自然か、すべてのキーが全ロケールに存在するか、そして次の機能がそもそも翻訳されるか、です。globalize.nowはAI搭載のローカライズ基盤です。ワンショット翻訳の後に起きる問題の多くは機械的なもので、これは朗報です。機械的な問題は検査できるからです。

問題になるのは、たいてい初回の翻訳そのものではありません。最近のエージェントは、読める品質の翻訳を素早く出力してくれます。ワンプロンプトに含まれないのは、出力を検証するステップです。ここで紹介するチェックが、まさにそのステップにあたります。

翻訳後もテキストは収まっていますか?

収まらないことが多く、最初に壊れるのはボタンです。W3Cの翻訳時のテキストサイズに関するガイダンスでは、元のメッセージが短いほど、拡張率が大きくなりやすいとされています。そのため、2語のボタンラベルは段落より比率として大きく伸びます。また、ドイツ語のような言語では複合名詞が折り返されないことがあり、文字体系によってはラテン文字より横方向・縦方向に広いスペースが必要になる点も指摘されています。

JSONファイルの文字列を翻訳するエージェントは画面を見ていないため、「Save changes」が翻訳後にボタンいっぱいに広がることを知りようがありません。出荷する中で最も文字列が長くなるロケールでアプリを動かし、主要なフローを一通り操作して、テキストの見切れ、ボタン内での折り返し、テーブルヘッダーの切り詰めを探してください。右から左へ書く言語(RTL)に対応している場合は、そのうち1言語でも同じ確認を行いましょう。はみ出しチェックだけでは分からない文字方向については、Lovableアプリのアラビア語・RTL対応ガイドで解説しています。

恒久的な対処は、翻訳を短くすることではなくレイアウトの修正です。コンテナが伸縮できるようにし、テキストに固定幅を使わず、収めるための省略もやめましょう。これはコードの話なので、エージェントに任せてください。

複数形は対象言語に合っていますか?

これは早めに確認してください。エラーにならず、静かに壊れるためです。Unicode CLDRは、zero、one、two、few、many、otherの6つの複数形カテゴリを定義しています。英語で使うのはoneとotherだけですが、ロシア語はone、few、many、otherを使い、ウェールズ語は6つすべてを使います。i18nextのドキュメントでも、アラビア語に6つすべての複数形キーが示されています。

英語の2つのキーをそのままロシア語やアラビア語のファイルにコピーするエージェントは、パースもレンダリングもできるカタログを生成します。しかし、ファイルに存在しないカテゴリが必要な数値では、すべて誤った形が表示されます。エラーは出ません。i18nextでは、変数名は厳密にcountでなければならず、欠けている場合はプレーンなキーへのフォールバックもない、とドキュメントに明記されています。コンポーネント内でcount === 1 ? 'item' : 'items'と書くエージェントは、翻訳が始まる前から同じバグを抱えています。構文面については、ICU MessageFormatとは何か、AI生成アプリでつまずく理由で解説しています。

チェックは機械的に行えます。ロケールごとに、複数形キーがその言語に必要なカテゴリと一致しているかを比較してください。

補間変数は無事ですか?

無事とは限りません。これはスクリプトで確実に判定できます。{{name}}や{count}のような補間変数は、すべての翻訳に一字一句そのまま含まれていなければなりません。長いファイルを翻訳するエージェントは、変数を言い換えたり、文が自然になるからと削除したり、タグの中身を翻訳したりすることがあります。その結果、画面に空白や波括弧そのままの文字列が表示されたり、クラッシュしたりします。

翻訳後の各文字列に含まれる変数の集合を、ソース文字列のものと比較してください。次の短いNodeスクリプトはその比較に加えて、英語には存在するのに他のロケールで欠けているキーも報告します。

import fs from "node:fs";

const [src, ...targets] = process.argv.slice(2);
const read = (f) => JSON.parse(fs.readFileSync(f, "utf8"));
const vars = (s) =>
  [...String(s).matchAll(/\{\{?\s*[\w.]+\s*\}?\}/g)].map((m) => m[0]).sort().join(",");

const base = read(src);
let problems = 0;
for (const file of targets) {
  const loc = read(file);
  for (const [key, text] of Object.entries(base)) {
    if (!(key in loc)) { console.log(file, "missing", key); problems++; continue; }
    if (typeof text === "string" && vars(text) !== vars(loc[key])) {
      console.log(file, "variable mismatch", key); problems++;
    }
  }
}
process.exit(problems ? 1 : 0);

CIでnode check-locales.mjs locales/en.json locales/de.json locales/ru.jsonとして実行してください。このスクリプトは単一階層のキーファイルを前提としているため、カタログがネストしている場合はループを調整してください。

用語は統一されていますか?

1回のレスポンスの中では統一されますが、複数回にまたがるとぶれていきます。大規模アプリの翻訳をファイル・セッション・バッチに分割して行うと、「ワークスペース」「プロジェクト」、そしてプロダクト名が毎回同じ訳になる保証はどこにもありません。結果として、ボタンとヘルプページで言っていることが食い違うことになります。

用語は一度書き出しておきましょう。プロダクト名、機能名、翻訳してはいけない語を用語集にまとめ、翻訳ステップがそれを必ず読むようにします。globalize.nowは、コードベースの変換時に共有用語集を生成するため、翻訳が始まる前から用語がデータとして存在します。ツールを使わない場合でも、エージェントへの指示から参照される短い用語集ファイルは最低限用意してください。指示の置き場所については、AGENTS.mdのルールについての記事で紹介しています。

ネイティブスピーカーにとって自然に聞こえますか?

これは、スクリプトではできない唯一のチェックです。ワンショット翻訳が機械翻訳っぽく読める原因は、文体の丁寧さ、フォーマルさ、大文字・小文字の扱いにあります。呼びかけや敬語の丁寧さのレベルが合っていない、命令形のボタンが命令口調に聞こえる、大文字の見出しが怒鳴っているように読まれる言語でそのまま大文字にしている、といった具合です。

すべての文字列をレビューする必要はありません。提供する言語ファミリーごとに1言語を選び、ネイティブスピーカーに、初回起動時の画面、チェックアウトやサインアップのフロー、エラーメッセージを読んでもらいましょう。トーンのずれでユーザーを失いやすいのは、これらの画面です。指摘された修正は用語集に記録しておけば、次回の実行はその内容を踏まえた状態から始められます。

すべてのキーが存在し、すべての文字列がカタログに入っていますか?

画面上では同じに見える、2種類の欠落があります。1つは、キーが英語には存在するのに特定のロケールで欠けているケースで、前述のスクリプトで検出できます。もう1つは、文字列がコンポーネント内にハードコードされていてカタログに一切届いておらず、そもそも翻訳しようがないケースです。エージェントは後者を、表示テキストよりもattribute、トースト、エラーメッセージに残しがちです。このパターンと、それを検出するlintルールについては、エージェントがハードコード文字列を追加し続ける理由で解説しています。

アプリを英語以外のロケールに切り替えて、一通り操作してみてください。英語が残っていれば、それはキーの欠落か、そもそも仕組みに取り込まれなかった文字列のどちらかで、対処法は異なります。

次の機能も翻訳されますか?

翻訳が、コードをリリースする流れの一部になっている場合に限ります。そして、これはワンプロンプトでは通過できないチェックです。プロンプトが翻訳したのは、その日の午後時点のアプリです。エージェントが次に作る機能は、同じファイルに英語のまま入り、ロケールは機能が増えるたびに遅れていきます。この遅れが積み重なる仕組みは、翻訳ファイルが同期ずれを起こす理由で説明しています。

ここが、ワンショット方式とパイプラインの分かれ目です。ローカライズが開発ループの上流に移るでは、このポジションの考え方を解説しています。実践面では、変換は一度だけ行い、その後の変更は変更を加えた場所で翻訳する、ということです。globalize.nowでは、アプリでリポジトリを接続するとコードベースが一度変換され、以降はプッシュジョブが新しいカタログ単位を翻訳し、結果をレビュー・マージする変更として届けます。ランタイムライブラリとカタログ形式は、引き続きあなたのものです。すでに人間の翻訳者がいるチームは、そのレビューステップをそのまま残せます。ただしそれが、英語のみのコードとリリースの間に立つ唯一の関門ではなくなります。

自前のスクリプトで運用したい場合の最低ラインは、上記のスクリプトをCIで実行することと、新しい文字列は必ずカタログを通すというルールです。AI生成アプリをローカライズする方法で全体の流れを、Claude Codeガイドでこのエージェント固有の設定を解説しています。

どこから始めればよいのでしょうか?

まず今日、現在のロケールファイルに対してスクリプトを実行してみてください。1分ほどで、構造が健全かどうか分かります。次に、最も文字列が長くなるロケールでアプリを開き、初回起動のフローを一通り操作してみましょう。AIで作ったアプリをすでにリリースしていて、変換・翻訳・その後の更新を手作業で保守するのではなく任せたい場合は、vibe coders向けページで概要を、開発者向けリファレンスでエージェントのセットアップを確認できます。料金は別ページにまとめています。

globalize.nowは、ハードコードされたアプリ内テキストを翻訳可能なロケールファイルに変換し、リリースのたびに自動で最新の状態を保ちます。

globalize.nowを無料で試す