AIエージェントを並列で動かすなら知っておきたいターミナル、cmuxの日本語化PRを出した

はじめに

Claude CodeやCodexを複数セッション同時に走らせていると、ターミナルが追いつかなくなります。通知が埋もれる、タブが見分けられない、どのペインでどのエージェントが動いているかわからない。

cmuxはこの問題を正面から解決するターミナルです。エージェントが入力待ちになるとペインにリングが光り、サイドバーでgitブランチやPRステータスまで一覧できます。Ghosttyベースなので、既存の設定ファイルをそのまま引き継げます。

使い始めて気に入ったものの、UIが英語のままでした。日本語化されていないなら自分でやろうと思い、Claude Codeと一緒にローカライズPRを出しました。624エントリの翻訳と14コミットのレビュー対応を経てマージされています。

feat: add Japanese localization with String Catalog by atani · Pull Request #819 · manaflow-ai/cmux
Summarycmux is an outstanding terminal app that has been rapidly gaining popularity among Japanese developers. Many user...

この記事では、cmuxの紹介、i18n PRの進め方、Claude Codeをどう使ったか、そしてレビュー対応で学んだことを書きます。

cmuxとは

cmuxは、AIコーディングエージェントとの並列作業を前提に設計されたmacOSネイティブのターミナルアプリです。Swift/AppKitで作られており、ターミナル描画にはGhosttyのlibghosttyを使っています。

GitHub - manaflow-ai/cmux: Ghostty-based macOS terminal with vertical tabs and notifications for AI coding agents
Ghostty-based macOS terminal with vertical tabs and notifications for AI coding agents - manaflow-ai/cmux

主な特徴は3つあります。

通知リング

エージェントが入力を待っているペインに青いリングが表示されます。サイドバーのタブも光るので、複数のペインを開いていても「どのエージェントが自分を呼んでいるか」がひと目でわかります。Cmd+Shift+Uで最新の未読に飛べます。

縦タブとサイドバー

サイドバーにはgitブランチ、PRステータス、作業ディレクトリ、リッスン中のポート、最新の通知テキストが表示されます。横タブでは収まりきらない情報が一覧できます。

アプリ内ブラウザ

ターミナルの横にブラウザペインを分割して配置できます。agent-browserから移植されたスクリプタブルなAPIを持ち、エージェントがアクセシビリティツリーの取得やクリック、フォーム入力、JavaScript実行を行えます。

Ghostty互換

既存の ~/.config/ghostty/config をそのまま読みます。テーマ、フォント、カラー設定を引き継げるので、Ghosttyユーザーは設定なしで使い始められます。

日本語化しようと思った理由

cmuxを使い始めて、メニューや設定画面が英語のままでも特に困りはしませんでした。ただ、コマンドパレットの項目やエラーメッセージが日本語で読めると、認知負荷が下がるのは確かです。

READMEには既に多言語版(中国語、韓国語など)がありましたが、アプリのUI自体のローカライズはされていませんでした。SwiftのString Catalog(.xcstrings)がXcode 15から使えるようになり、ローカライズの基盤が整っていたこともあって、「自分で出してみよう」と考えました。

i18n PRで意識したこと

PRの技術的な詳細へ入る前に、i18nのOSS貢献で意識したポイントをまとめます。これから同じようなPRを出したい方の参考になれば幸いです。

プロジェクトの既存パターンに合わせる

cmuxには既にREADMEの多言語版がありました。アプリ内UIのローカライズは未実装でしたが、国際化への関心はあると判断できました。プロジェクトのi18n方針やコーディングスタイルに合わせることで、レビュー負荷を下げられます。

翻訳しないものを明確にする

デバッグログ、内部キー、アクセシビリティ識別子は翻訳対象外としました。PRの説明に「Not translated」セクションを設け、意図的に除外したものを明記しています。レビュアーが「これは漏れか、意図的か」と迷わずに済みます。

mainの変更をキャッチアップする

大きなPRはレビュー期間が長くなります。その間もmainの開発は進み、コンフリクトが発生します。今回は3回mainをマージし、新規UIのローカライズも追加しました。PRを出したら終わりではなく、マージされるまで面倒を見る姿勢が大事です。

PRの作成からマージまで

コードを読む

まず、ソースコード全体からUIに表示されるハードコードの文字列を洗い出しました。18のSwiftソースファイルに約620のハードコード文字列が散らばっていました。

対象は以下のカテゴリに分かれます。

カテゴリ ファイル数 エントリ数
設定画面 1 約180
コマンドパレット・サイドバー 1 約200
メニュー・ダイアログ 1 約70
アップデートUI 4 約80
タブ管理 2 約50
ブラウザ 3 約35
その他 5 約45

翻訳方針を決める

翻訳を始める前に、以下のルールを決めました。

  • Apple公式の用語ガイドラインに従う — 「キャンセル」「適用」など、macOSの標準的な表現を使う
  • 翻訳しないもの#if DEBUGブロック、ログメッセージ、UserDefaultsキー、アクセシビリティ識別子
  • キー命名規則{section}.{subsection}.{descriptor}の形式(例: settings.app.themecommand.newWorkspace.title

ほぼClaude Codeとの対話だけで進めた

この作業は、ほとんどClaude Codeとの対話だけで完了しています。自分がやったのは方針を決めることと、翻訳の品質をチェックすることだけです。

  • 「18ファイルからハードコード文字列を洗い出して」
  • String(localized:defaultValue:)に置き換えて」
  • 「624エントリ分の.xcstringsを生成して」
  • 「レビューでこの指摘が来たから直して」

こうした指示を出すと、Claude Codeがコードを読み、修正し、コミットまで行います。624エントリ分のJSONを手書きするのは非現実的ですし、18ファイルにまたがる一括置き換えも手作業では厳しい。Claude CodeがなければこのPRは出していなかったでしょう。

PRを出す

最初のコミットでString Catalogの基盤と日本語翻訳を追加し、2つ目のコミットでハードコード文字列の置き換えを行いました。

// Before
alert.messageText = "Connection isn't secure"

// After
alert.messageText = String(
    localized: "browser.error.insecure.title",
    defaultValue: "Connection isnu{2019}t secure"
)

String(localized:defaultValue:) APIを使うことで、翻訳カタログにエントリがなくてもdefaultValueの英語テキストがフォールバックとして表示されます。

レビュー対応

PRを出した後、自動レビューツール(cubic)から10件の指摘がありました。主なものは以下です。

  • 同じキーに異なるdefaultValuebrowser.error.insecure.titleで普通のアポストロフィ(')と活字用のアポストロフィ(u{2019})が混在していた
  • ローカライズ漏れ — エラーメッセージのタイトルは翻訳したが本文が英語のまま残っていた箇所
  • StaticString制約String(localized:)のキーはStaticStringである必要があり、変数を渡せない

これらの指摘もClaude Codeとの対話で修正しました。指摘内容を伝えて「直して」と言うだけです。

コンフリクトとの戦い

一番大変だったのはレビュー対応そのものではなく、コンフリクトの解消でした。

cmuxは開発ペースが速いプロジェクトです。PRのレビューを待っている間にも、他のPRが次々とmainにマージされていきます。UIに文字列を追加する変更が入るたび、自分のPRとコンフリクトが発生します。i18nのPRは全ファイルのUI文字列に触れているため、ほぼどんな変更とも衝突します。

結果として、mainのマージを3回行いました。そのたびにコンフリクトを解消し、新しく追加されたUI(検索バー、通知設定、コピーモード切替)のローカライズも追加しています。

:::details 14コミットの全ログを見る。

437f486 Add i18n infrastructure with String Catalog and Japanese translations
94fa290 Replace hardcoded UI strings with String(localized:defaultValue:)
a2b9624 Fix localization gaps from review feedback
4224ce6 Fix String(localized:defaultValue:) keys to use StaticString
3563cb7 Localize remaining UI strings across all source files
814512d Address review feedback: fix missing localizations and terminology
05fe052 fix: address remaining PR 819 review feedback
018d5e6 fix: use a single localized key for close-other-tabs
49e27cd fix: avoid inflection markup in close-other-tabs message
44e69e0 Merge origin/main into feature/i18n-japanese
b80bb27 Merge origin/main: add toggleTerminalCopyMode localization
960da38 Merge main into feature/i18n-japanese to resolve conflicts
4ecad69 Address review feedback: localize tooltip, fix subtitle concat, unify keys
3fc58d0 Merge origin/main: resolve conflicts with find bar refactor and debug logs

14コミットのうち、レビュー対応が8件、mainのマージが3件です。コンフリクト解消もClaude Codeに任せましたが、新規UIの翻訳追加は自分で確認する必要がありました。PRを出したら終わりではなく、マージされるまで面倒を見続ける覚悟が要ります。

技術的なポイント

String Catalog (.xcstrings)

Xcode 15で導入されたString Catalogは、JSON形式のローカライズファイルです。.strings.stringsdictを統合した形式で、複数形やデバイスバリエーションも1ファイルで管理できます。

ビルド時に従来の.strings形式にコンパイルされるため、ランタイムの互換性はmacOS 13以降で保たれます。

{
  "sourceLanguage": "en",
  "version": "1.0",
  "strings": {
    "settings.app.theme": {
      "localizations": {
        "en": {
          "stringUnit": {
            "state": "translated",
            "value": "Theme"
          }
        },
        "ja": {
          "stringUnit": {
            "state": "translated",
            "value": "テーマ"
          }
        }
      }
    }
  }
}

defaultValueによるフォールバック

String(localized:defaultValue:)defaultValueには英語テキストを設定しています。翻訳カタログにエントリがない言語でも、英語UIが表示されます。

String(
    localized: "command.newWorkspace.title",
    defaultValue: "New Workspace"
)

この設計により、日本語以外の言語でもアプリは問題なく動作します。新しい言語を追加したい場合は.xcstringsにエントリを足すだけで済みます。

キー命名規則

624エントリのキーを一貫した命名規則で管理するために、{section}.{subsection}.{descriptor}の3階層ルールを採用しました。

settings.app.theme        — 設定画面 > アプリ > テーマ
command.newWorkspace.title — コマンド > 新規ワークスペース > タイトル
browser.error.insecure.title — ブラウザ > エラー > 安全でない接続 > タイトル
alert.renameWorkspace.message — アラート > ワークスペース名変更 > メッセージ

ファイル内でgrepするときに、セクション名で絞り込めるのが利点です。

おわりに

cmuxは、AIエージェントを並列で動かす開発者にとって実用的なターミナルです。Ghostty互換で既存の設定を引き継げるため、移行コストも低く抑えられます。

cmux — The terminal built for multitasking
Native macOS terminal for AI coding agents. Works with Claude Code, Codex, OpenCode, Gemini CLI, Kiro, Aider, and any CL...

日本語UIは既に使えます。macOSのシステム言語が日本語なら、インストール後すぐに日本語で表示されます。

i18n系のPRは、コードの機能を変更しないため、OSSへの最初の貢献として取り組みやすいテーマです。624エントリの翻訳と聞くと大変そうですが、Claude Codeに実装を任せれば、人間は方針決定とレビュー対応に集中できます。「使いたいツールが日本語対応していない」と思ったら、自分でPRを出してみるのも良い選択肢です。

コメント

タイトルとURLをコピーしました