多言語対応のUI文言(i18n文字列)を、翻訳担当者と開発者の間でどう共有するか悩んでいるチームに向けた内容です。Google Sheets(表計算ツール)をAPI代わりに使う構成が話題になっていますが、外部サービスとして本番に組み込む前に確認すべき非機能要件を整理します。
小規模なWebサイトの多言語対応では、開発者はリポジトリ内のJSONで型安全に管理したい一方、翻訳担当者は使い慣れた表計算ソフトで直接編集したいという要望が対立しがちです。この対立を解消する方法として、Google SheetsをそのままDBとして扱い、Google Apps Script(GAS、Googleが提供するサーバーレススクリプト実行環境)でJSON APIとして公開し、Next.jsなどのビルド時にJSONを取得する構成が提案されています。1,000キー未満の小規模プロジェクトを対象とした割り切った設計です。
何が起きるか:どこで壊れるか
この構成が壊れる典型的なタイミングは3つあります。1つ目はGAS側のWebアプリのデプロイ設定変更時です。Apps ScriptのWebアプリは「新しいデプロイ」を作るたびにURLが変わる場合があり、環境変数に埋め込んだエンドポイントURLが古いまま残ると404やHTMLエラーページがJSONとして返ってきます。
2つ目はスプレッドシートの列名変更や行削除です。buildLocaleData関数はヘッダー行の文字列を小文字化してロケールのインデックスを作る実装になっており、翻訳担当者が列名を「EN」から「English」に変えただけで、対応するロケールのキーが丸ごと消えます。
3つ目はGoogle側の認証・クォータ(利用上限)変更です。Apps ScriptのWebアプリは匿名アクセス許可の設定次第で、Googleのポリシー変更や組織のセキュリティ設定変更によって突然401や403を返すようになる可能性があります。
なぜ起きるか:原因を分解する
根本の原因は、翻訳データの信頼できる情報源(Single Source of Truth)が、バージョン管理の外側にあることです。JSONファイルであればGitの差分・レビュー・ロールバックがそのまま効きますが、スプレッドシートの変更履歴はGit管理外にあるため、CIパイプラインからは見えません。
さらにApps ScriptのWebアプリは、本質的には個人アカウントやGoogle Workspaceの権限に紐づく実行環境です。SREやインフラチームが管理するAPIゲートウェイのようなSLA(サービス品質保証)は存在せず、可用性の責任範囲があいまいになりがちです。
もう1つの原因は、フォールバック処理が「空セルは既定ロケールで埋める」という1点だけに依存していることです。行自体が削除された場合や、キー列にタイポが入った場合は、このフォールバックの対象外になり、静かにキーが欠落します。単体テストで「空セルの既定ロケールへのフォールバック」「キーなし行のスキップ」「未知ロケールのエラー応答」の3ケースを検証する例が示されていますが、これは裏を返せば「シート構造そのものが変わるケース」はテスト範囲に入っていないということです。
自分のプロジェクトが該当するか確認する
次の観点で、今のi18n構成やこれから採用しようとしている構成を点検してみてください。
- 翻訳キーの総数が1,000件を超えていないか。locales/en.jsonなどのファイルサイズやキー数をjq 'keys | length' locales/en.jsonのようなコマンドで確認できます
- 翻訳データ取得を実行時(クライアントやSSRのリクエストごと)に行っていないか。ビルドログやネットワークタブで、外部APIへの呼び出しがリクエスト単位で発生していないか確認します
- GASのWebアプリのデプロイ設定で「アクセスできるユーザー」が「全員」になっているか。スクリプトエディタの「デプロイを管理」画面から確認できます
- スプレッドシートの変更履歴(ファイル > 変更履歴を表示)に、意図しない列名変更や行削除が残っていないか
- CI/CDのビルドログに、翻訳データ取得ステップの失敗時にビルド全体が止まる設定になっているか、それとも古いキャッシュにフォールバックする設定になっているか
該当する項目が多いほど、ビルド時の外部依存が可用性リスクとして顕在化しやすい状態です。
対策の手順
1. 取得タイミングをビルド時に固定する。ランタイムでシートを直接叩く構成は避け、scripts/sync-locales.tsのようなビルド前スクリプトで一度だけ取得し、public/locales配下に静的ファイルとしてコミットします。これによりGAS側の障害が本番サイトの表示に影響しなくなります
2. 取得失敗時のフォールバックをCIに組み込む。同期スクリプトのHTTPステータスが200以外、またはJSONパースに失敗した場合は、直前にコミットされているファイルをそのまま使い、ビルドを止めない分岐を書きます
curl -sf "$LOCALE_SOURCE_URL?locale=en" -o /tmp/en.json || echo "sync failed, keep existing locales/en.json"3. スキーマの変更検知を仕組み化する。同期スクリプトの実行後、取得したキー数が前回コミットのキー数から大きく減っていないかを比較するチェックを入れます。例えば「取得件数が前回の80%未満なら失敗扱い」のような閾値をCIに設定すると、列名変更による欠落を早期に検知できます
4. GASのデプロイURLを固定運用する。「デプロイを管理」から既存デプロイを編集して新バージョンを反映する運用にし、新規デプロイでURLを都度発行しないようにします。あわせてURLは環境変数として1箇所で管理し、直書きを避けます
5. スプレッドシートを翻訳DBとして採用するかどうかは、キー数の見通しと翻訳担当者の人数で判断します。数十人規模の翻訳者が関わる、あるいはスクリーンショット付きレビューが必要になった時点で、専用の翻訳管理ツールへの移行を検討する目安になります
まとめ
Google Sheetsを翻訳DBにする構成は、小規模な多言語サイトの調整コストを下げる現実的な選択肢です。
ただし信頼できる情報源がバージョン管理の外にある以上、スキーマ変更・デプロイURL変更・認証ポリシー変更という3つの落とし穴は常に残ります。
次の一歩として、既存の同期スクリプトに取得件数のしきい値チェックを追加し、GASのデプロイURLが環境変数1箇所に集約されているかを確認してみてください。
キー数や翻訳担当者の規模が増えてきたタイミングで、専用ツールへの移行判断も忘れずに検討することが望ましい状態です。