Scribe

翻訳

Scribeの翻訳機能の仕組み:更新状態のハッシュ管理、Geminiとの連携、出力の検証、コスト計算、レビューツールについて解説します。

View as Markdown

Scribeは、LLM(Gemini)を活用して英語のソースを他のすべてのロケールに翻訳し、その結果をSQLiteに保存します。ロケールファイルを直接編集する必要はありません。英語のコンテンツを編集し、scribe translateを再実行して、ストアをコミットするだけです。

翻訳の対象

各ドキュメントにおいて、翻訳エンジンに渡されるのは以下の2つのみです。

  1. 配列やオブジェクト内にネストされたフィールドも含め、field.translatable()が指定されたFrontmatterのフィールド。
  2. MDXの本文(body)。

それ以外の要素(field.structural()、リレーション、組み込みフィールドなど)は、ロード時に英語のドキュメントからそのままコピーされます。slugStrategy: "localized"を使用している場合、翻訳エンジンはロケールごとのURLスラッグも生成します(非ラテン文字のロケールの場合は翻字され、ASCIIのケバブケースになります)。

まだ翻訳が完了していないロケールがあっても、実行時に行き止まりになることはありません。resolve()は、ロケールフォールバックチェーンに従って利用可能な最も近い翻訳を提供します(例:pt-BRptにフォールバックし、その後デフォルトロケールにフォールバックします)。そのため、言語を段階的に追加していくことが可能です。

更新状態の追跡

保存された各翻訳には、その元となった英語の翻訳対象コンテンツのSHA-256ハッシュが記録されています。scribe translateがページを再翻訳するのは、以下の条件のいずれかに当てはまる場合のみです。

  • そのロケールの翻訳が存在しない場合(Missing
  • 翻訳後に英語の翻訳対象コンテンツが変更された場合(Stale

構造的なフィールドや_redirects.jsonのエントリを編集しても、翻訳が古い(Stale)とマークされることはありません。これらのフィールドは翻訳対象外だからです。

出力の検証

翻訳は、以下の2つのチェックをクリアした場合にのみ保存されます。

  1. 返されたMDXの本文が正しくパースできること。モデルが誤って生成することがあるエスケープ処理の不具合や、JSX属性のクォーテーションを正規化した上で検証します。
  2. 返されたFrontmatterが、プロジェクト全体のZodスキーマに照らし合わせて再検証をパスすること。

検証に失敗したアイテムは、処理の最後に自動的に1回だけ再試行されます。この際、モデルが自己修正できるように、検証エラーの内容がフィードバックされます。バッチ処理の場合、追加のバッチジョブとして1回再試行されますが、バッチレートは適用されたままです。再度失敗したものは、新しいエラー内容とともに最終サマリーに一覧表示され、コマンドは非ゼロで終了します。不正なモデルの出力がストアに保存されることはなく、本番環境に影響を与えることもありません。

コマンドの実行

export GEMINI_API_KEY=...        # .envや.env.localからも読み込まれます

scribe status                     # タイプやロケールごとのカバー率を確認
scribe translate                  # ターミナルでの対話型ピッカー
scribe translate --locale fr de   # 特定のロケールを指定
scribe translate --preset active  # 設定内のlocalePresetsグループを指定
scribe translate --type blog      # 単一のコンテンツタイプ(カンマ区切りで複数指定可:--type blog,glossary)
scribe translate --slug my-post   # 単一のドキュメントを指定
scribe translate --dry-run        # ワークリストと見積もり(トークン数、コスト)のみを表示し、書き込みは行わない
scribe translate --force          # ハッシュが一致していても強制的に再翻訳
scribe translate --strategy missing-only   # 更新分はスキップし、未翻訳の穴埋めのみを実施
scribe translate --batch          # バッチモードを強制(デフォルト)
scribe translate --direct         # ページごとにAPIを直接呼び出し、即座に結果を取得
scribe translate --resume         # 保留中のバッチジョブを再開し、新しいジョブは送信しない
scribe translate --concurrency 5  # 並列リクエスト数(directモードのみ)
scribe translate --model gemini-3.1-pro

ターミナルで実行すると、scribe translateはライブプログレスUIを表示します。バッチジョブのステータス、アイテムごとの結果、消費トークン数、USDベースの推定コストをリアルタイムで確認できます。Thinking(思考)トークンも使用量に含まれるため、見積もりはGoogleの実際の請求額と一致します。行単位のシンプルなログ出力が必要な場合(CI環境など)は、--no-progressを渡すか、非対話モードで実行してください。

--dry-runを指定すると、何も書き込まずにワークリストを出力します。実際のプロンプトと英語のペイロードサイズから計算された、アイテムごとの推定トークン数とUSDコスト、およびその合計が表示されます。--batch環境下ではバッチ割引が適用された見積もりが表示されるため、実際の実行コストを正確に予測できます。

確認後、.scribe/store.sqliteをコミットしてください。

バッチモードと再開機能

デフォルトでは、ワークリスト全体がGemini Batch API経由で処理されます。これにより、直接呼び出しの場合と比較してトークンコストが50%削減されます。Scribeはすべてのアクティビティを事前に計画し(モデルごとに1ジョブ、大規模なワークリストの場合は分割されます)、すべてのジョブを一度に送信します。その後、ジョブのステータスをまとめてポーリングし、完了した瞬間に結果を保存します。バッチジョブは通常数分で完了しますが、Googleが保証しているのは24時間以内の完了であるため、コマンドはページごとに結果をストリーミングするのではなく、完了を待機する設計になっています。

ポーリングが開始される前に、すべてのジョブは英語ソースのスナップショットとともにSQLiteストアに記録されます。そのため、実行中に中断しても安全です。

  • ポーリング中に中断(Ctrl+C)しても、データは失われません。
  • scribe translate --resumeを実行すると、保留中のジョブを確認して完了したものをインポートします。新規のジョブは送信しません。
  • 通常通りscribe translateを実行した場合も、まずは保留中のジョブの再開が優先され、進行中のアイテムが二重に送信されることはありません。
  • バッチの処理中に英語のページを編集しても問題ありません。結果は翻訳元のスナップショットに対して保存され、次回の実行時に古い(Stale)として検出されます。

ページごとの結果を即座に取得したい場合(通常料金が適用されます)は、--directを使用してください。これによりページごとのAPI呼び出しに戻り、--concurrencyによる並列処理が可能になります。ターミナルの対話型ピッカーでも同じ選択が可能です。一時的なAPIエラー(429、5xx、ネットワークの切断など)は、両方のモードで指数的バックオフを用いて自動的に再試行されます。

翻訳の方向づけ

組み込みのプロンプトは、直訳ではなくトランスクリエーション(意訳)を前提に設計されています。モデルは現地のマーケットに合わせたネイティブのコピーライターとして振る舞います。慣用句や言葉遊びは逐語訳するのではなくその意図を再現し、現地のタイポグラフィの規則(引用符、アポストロフィ、句読点のスペースなど)を適用します。あなたが指定するcontextrulespromptは、この基盤の上に追加されます。

プロジェクト全体のデフォルト設定と、タイプごとのオーバーライドの例です。

export default defineConfig({
  translate: {
    context: "MyBrand is a B2B SaaS. Never translate the brand name MyBrand.",
    rules: ["Keep numbers and statistics accurate."],
  },
  types: [
    defineContentType({
      id: "blog",
      // ...
      translate: {
        rules: [
          "Preserve all MDX/JSX component tags, props, and URLs exactly.",
          "Translate link anchor text; never change href paths.",
        ],
      },
    }),
  ],
});
オプションスコープ説明
contextプロジェクト + タイプすべてのリクエストの先頭に追加される、ブランドやドメインのコンテキスト(プロジェクトとタイプのコンテキストは結合されます)。
rulesプロジェクト + タイプデフォルトのルールに追加される追加ルール。
promptプロジェクトまたはタイプ追加のローカリゼーション指示。ターゲットロケールのディレクティブの前に挿入されます(ロケール名とコードは常に自動的に追加されます)。
defaultModel / modelプロジェクト / タイプGeminiのモデルID。デフォルトは gemini-3.1-pro(環境変数PROSE_GEMINI_MODEL--modelフラグでの上書きも可能)。

常に適用される組み込みルールには次のようなものがあります。ターゲット市場で広く認知されている現地名がない限りブランド名を翻訳しないこと。エスケープされた\nではなく実際の改行を使用してMDXの本文を返すこと。値にダブルクォーテーションが含まれる場合、JSX属性のクォーテーションを修正すること。

翻訳のレビュー

scribe history blog my-post fr    # 1ページにおける英語ソースのスナップショットのタイムライン
scribe studio                     # :3600で起動する読み取り専用のWeb UI

すべての翻訳は、その元となった英語ソースのスナップショット(同じSQLiteファイルに保存されています)にリンクされています。これにより、ページがいつ、どの内容から、どのモデルで翻訳されたかを監査できます。scribe historyはそのタイムラインを出力します。studioでは、ロケールごとのカバー率、現在の未翻訳・更新待ちのワークリスト、スナップショットと保存された翻訳を並べたドキュメントごとの詳細を確認できます。

ロケールプリセット

段階的な展開を行う場合は、設定ファイルでロケールのグループに名前を付け、--presetで対象を指定できます。

localePresets: {
  active: ["fr", "es", "ja"],
  ultraLight: ["fr"],
}
scribe translate --preset ultraLight
翻訳 · Scribe