GKE で iOS アプリ用の .xcstrings を自動翻訳

はじめに

iOS アプリの多言語対応を進めていると .xcstrings ファイルの翻訳が地味に面倒になります。エントリを手で書き、英語に翻訳し、状態を translated に変える、この繰り返しです。

Google Cloud Translation API は月 50 万文字まで無料で使えます。これを使って .xcstrings を受け取り、未翻訳のエントリだけを抽出して英語に変換する API サーバーを作れば自動化できます。ちょうど GKE の学習も兼ねてやってみたい気持ちがあったので、ローカルで Docker 化してからデプロイするところまでやってみました。

技術スタック

API サーバーは Python 3 + FastAPI、翻訳エンジンは Google Cloud Translation API(日本語→英語)を使います。コンテナは Docker で管理し、オーケストレーションは GKE Standard Zonal クラスタ(e2-small)です。

実装

.xcstrings パーサー

.xcstrings は JSON 形式のファイルで、各エントリに state フィールドがあります。newneeds_review 状態のエントリが翻訳対象で、翻訳後に状態を translated に更新します。既に translated のエントリはスキップさせるため、差分だけを処理できるようにしました。

Cloud Translation API クライアント

翻訳のクライアントは Workload Identity に対応させました。ローカル実行時はサービスアカウントキーを使い、GKE 上では Workload Identity でサービスアカウントを自動的に引き受けます。

POST /translate エンドポイント

.xcstrings ファイルをアップロードすると翻訳済みファイルをレスポンスで返す、シンプルな構成です。ローカルでは docker-compose up で起動して curl で動作確認できます。

curl -X POST http://localhost:8000/translate \
  -F "file=@Localizable.xcstrings"

GKE デプロイ

クラスタのコスト

GKE Standard Zonal クラスタには月 $74.40 のクラスタ管理費がかかりますが、Zonal クラスタには同額の無料クレジットが付いているため管理費は相殺されます。実費はノードのコンピューティング費用のみです。

ノードのマシンタイプは e2-small(RAM 2GB)を選びました。最初は e2-micro(RAM 1GB)を試しましたが、GKE のシステム Pod だけで RAM をほぼ消費してしまいアプリ Pod がスケジューリングできませんでした。e2-small であれば約 $12/月(約 1,800 円)で、使用後にクラスタを削除すれば約 $0.017/時間で済みます。

GKE 構成

GKE 構成図

クライアントからのリクエストは LoadBalancer Service 経由で FastAPI Pod に届きます。Pod は Workload Identity を使って Cloud Translation API を呼び出します。イメージは Artifact Registry から pull します。

動作確認

デプロイ後、外部 IP に向けて curl を実行して翻訳結果が返ることを確認しました。

curl -X POST http://{EXTERNAL_IP}/translate \
  -F "file=@Localizable.xcstrings"

レスポンスの例です。

{
  "sourceLanguage": "ja",
  "strings": {
    "ログイン": {
      "localizations": {
        "ja": {
          "stringUnit": {
            "state": "translated",
            "value": "ログイン"
          }
        }
      }
    },
    "パスワードを忘れた場合": {
      "localizations": {
        "ja": {
          "stringUnit": {
            "state": "translated",
            "value": "パスワードを忘れた場合"
          }
        },
        "en": {
          "stringUnit": {
            "state": "translated",
            "value": "Forgot your password?"
          }
        }
      }
    },
    "設定": {
      "comment": "ナビゲーションバーのタイトル",
      "localizations": {
        "ja": {
          "stringUnit": {
            "state": "translated",
            "value": "設定"
          }
        },
        "en": {
          "stringUnit": {
            "state": "translated",
            "value": "setting"
          }
        }
      }
    },
    "アカウント設定": {
      "localizations": {
        "ja": {
          "stringUnit": {
            "state": "translated",
            "value": "アカウント設定"
          }
        },
        "en": {
          "stringUnit": {
            "state": "translated",
            "value": "Account Settings"
          }
        }
      }
    }
  },
  "version": "1.0"
}

まとめ

正直なところ、今の時代は AI(Claude や Gemini)にファイルを渡せばコマンドひとつで翻訳できます。このサービス自体が AI の普及により不要になることは自覚しています。Kubernetes の学習目的として取り組んだので、GKE のコスト構造や Workload Identity の仕組みをひと通り手を動かして確認できてよかったです。

参考リンク