はじめに
cmuxはGhosttyベースのmacOSターミナルアプリで、AIコーディングエージェント向けの通知機能や縦タブを備えています。日常的にcmuxを使う中で3つのPRを出しました。日本語ローカライゼーション(PR #819)、omnibarのハング修正(PR #928)、CJKフォントが意図しない装飾フォントで表示される問題の修正(PR #1017)です。使いながら次々と改善していった記録です。
日本語UIの翻訳: PR #819
cmuxのUIは英語のみでした。メニュー、設定画面、コマンドパレット、通知——すべてハードコードされた英語文字列です。
Xcode 15以降で使えるString Catalog(.xcstrings)を採用しました。620以上のUI文字列を String(localized:defaultValue:) APIで置き換えています。
設計方針
- キーの命名規則は
{section}.{subsection}.{descriptor}(例:settings.app.theme) defaultValueで英語文字列を維持し、翻訳がなくても壊れない構成にしました#if DEBUGブロック、ログメッセージ、UserDefaultsキーは翻訳対象外としました
規模
| カテゴリ | エントリ数 |
|---|---|
| 設定画面 | 約180 |
| コマンドパレット・サイドバー | 約200 |
| メニュー・ダイアログ | 約70 |
| アップデートUI | 約80 |
| タブ管理 | 約50 |
| ブラウザ | 約35 |
| その他 | 約45 |
| 合計 | 624 |
Apple公式の日本語用語ガイドラインに沿って翻訳しました。たとえば「Preferences」は「設定」、「Quit」は「終了」とします。macOSアプリとして自然な日本語表現を選びました。
このPRは#819としてマージされました。
omnibarのハング修正: PR #928
ローカライゼーション作業中に、omnibar(URLバー風の入力欄)をクリックするとCPU使用率が100%に張り付いてフリーズする問題に遭遇しました。
原因
mouseDown ハンドラが super.mouseDown(with:) を呼び出すと、NSTextView の内部マウストラッキングループに入ります。特定の条件下で NSTextLayoutManager.enumerateTextLayoutFragments が無限の再描画サイクルに陥り、kill -9 でしか復旧できなくなります。
既存の回避策として3秒後に合成 mouseUp イベントを投げるコードがありました。しかしトラッキングループ内部の nextEvent(matching:) がイベントキューから取得しないケースがあり、効果がありませんでした。
修正内容
フィールドエディタがアクティブな場合(2回目以降のクリック)は super.mouseDown をバイパスします。characterIndexForInsertion(at:) でカーソル位置を直接設定する方式に変更しました。
- シングルクリック: クリック位置にカーソルを配置
- Shift+クリック: 選択範囲を拡張
- ダブルクリック: 単語選択(フィールドエディタに委譲)
ドラッグ選択は使えなくなりますが、単一行の入力欄ではダブルクリックやShift+クリックで十分です。
このPRは#928としてマージされました。
CJKフォントの表示問題: PR #1017
ローカライゼーションとハング修正を終えて日本語環境で使い続けていると、今度はターミナル内のひらがな・カタカナが装飾的な筆文字フォントで表示される問題が発生しました。

Before: フォールバックで AB_appare が選ばれ、筆文字になっている

After: font-codepoint-map により Hiragino Sans が適用されている
原因
Ghosttyのフォントフォールバックには2つのパスがあります。CJK統合漢字(U+4E00-9FFF)は CTFontCreateForString が処理し、Hiragino Sans W3 が正しく選ばれます。一方、ひらがな・カタカナ・記号などは CTFontCollection とスコアリングで選ばれます。
スコアリングの優先順位は以下のとおりです。
- そのコードポイントを持っているか
- モノスペースか
- スタイルがRegularか
- グリフ数
筆者の環境では、Adobe Creative Cloud経由で同期されたABあっぱれ(AB_appare)が原因でした。このデコレーティブフォントがモノスペース属性を持っていたため、スコアリングで最優先されていました。インストール済みフォントによって選ばれるフォールバックは変わります。どの環境でも共通するのは「意図しないフォントが選ばれる」という問題です。
修正: font-codepoint-mapの自動注入
Locale.preferredLanguages の先頭言語に基づいて、CJKフォントを自動的にマッピングする仕組みを実装しました。
| 言語 | フォント |
|---|---|
| ja | Hiragino Sans |
| ko | Apple SD Gothic Neo |
| zh-Hant / zh-TW / zh-HK | PingFang TC |
| zh | PingFang SC |
対象のUnicodeレンジは以下のとおりです。
共通(全CJK言語)
- U+3000-U+303F: CJK記号・句読点
- U+3400-U+4DBF: CJK統合漢字拡張A
- U+4E00-U+9FFF: CJK統合漢字
- U+F900-U+FAFF: CJK互換漢字
- U+FF00-U+FFEF: 全角記号・英数字
言語固有
- U+3040-U+309F: ひらがな(ja のみ)
- U+30A0-U+30FF: カタカナ(ja のみ)
- U+AC00-U+D7AF: ハングル音節(ko のみ)
- U+1100-U+11FF: ハングル字母(ko のみ)
ユーザーが既に font-codepoint-map を設定している場合は自動注入をスキップします。ユーザーの設定を上書きしない設計です。
設定ファイルの再帰的スキャン
Ghosttyの設定は config-file ディレクティブで別ファイルをインクルードできます。自動注入の判定では、インクルード先を再帰的にスキャンして font-codepoint-map の有無を確認します。
実装したスキャンロジックは以下のとおり。
- 相対パスの解決
- オプショナルインクルード(末尾
?)のサポート - インクルードの循環参照の検出
- Application Supportディレクトリのスキャン
このPRは#1017として現在レビュー中です。
dotfiles側の対応
PR #1017がマージされるまでの間、dotfilesでGhosttyの設定を手動で管理しています。
font-family = Menlo
font-family-bold = Menlo
font-family-italic = Menlo
font-family-bold-italic = Menlo
font-codepoint-map = U+3000-U+9FFF=Hiragino Sans W4
font-codepoint-map = U+F900-U+FAFF=Hiragino Sans W4
font-codepoint-map = U+FF00-U+FFEF=Hiragino Sans W4
Menloに落ち着くまでに、PT Mono → JetBrains Mono → Menlo と試行錯誤しました。JetBrains MonoはCJKグリフを含まず、cmux/Ghosttyでのレンダリングに問題がありました。MenloはmacOS組み込みフォントで、Core Textとの相性が安定しています。
この設定はchezmoiで管理し、chezmoi apply で ~/.config/ghostty/config に配置されます。
OSSコントリビュートの進め方
cmuxへの3つのPRを通じて学んだことを共有します。
使いながら直す: ローカライゼーション作業中にomnibarのハングに遭遇し、そのままPRにしました。実際に使っているからこそ見つかるバグがあります。
ワークアラウンドを本体に還元する: dotfilesで font-codepoint-map を手動設定していたワークアラウンドを、本体側で自動化するPRにしました。自分が困っていることは他のユーザーも困っています。
テストを書く: PR #1017では、言語マッピングのユニットテスト9件と設定ファイルスキャンのテストを追加しました。レビュアーの負担を減らし、マージされやすくなります。
まとめ
cmuxはGhosttyベースの新しいターミナルアプリとして急速に成長しています。日本語環境で使うには、ローカライゼーションとCJKフォントの対応が不可欠です。
日本語ローカライゼーション(PR #819)はマージ済み、CJKフォントフォールバック(PR #1017)はレビュー中です。使っているツールに問題を見つけたら、修正してPRを出す——OSSはそうやって良くなっていきます。


コメント