Skip to content

Tutorial 5 Localization ja

James Morris edited this page Jul 29, 2026 · 1 revision

チュートリアル5 · 多言語対応(i18n)

ゴール: LockedIn CLIが33言語を話すしくみを理解し、さらに別の言語をエージェントに 追加させる練習をすること。多言語対応はエージェント向けの題材として非常に優秀です。 任せられる程度に機械的でありながら、テストゲート、レイアウト規則、文法レビューという 本物の制約があるため、レビューする力が鍛えられます。

← 前へ: チュートリアル4 プロンプトとレビュー · ホーム に戻る


ここでいう「多言語対応」とは

スペイン語、ヒンディー語、日本語、簡体字中国語、その他の同梱言語でCLIを起動すると、 表示のすべてが切り替わります。スプラッシュ、ヘルプの表、各コマンドの出力、 チャットセッション、法的な小さな注意書きまで。ジョークだけではなく、目に見える面すべてです。

lockedin --lang ja post
LOCKEDIN_LANG=hi lockedin
lockedin --lang zh aura

言語は起動時に自動判定され、優先順位は次のとおりです。

  1. --lang フラグ(--lang ja--lang=fr-l ja
  2. 環境変数 LOCKEDIN_LANG
  3. ロケール(LC_ALL / LC_MESSAGES / LANG、続いてOS・ランタイムのロケール)
  4. フォールバックとしての英語

normalizeLang() は通常、ロケールの先頭サブタグを見ます。de-DEde になりますが、 tlh が偶然 tl になることはありません。filtgl はタガログ語(tl)へ、 ノルウェー語 nbnnno へ、旧インドネシア語コード inid へ、 旧ヘブライ語コード iwhe へ対応づけています。2つの地域コードは先頭サブタグに 畳まず、そのまま保持します。pt-BRpt_BR は正規の地域コードを選び、汎用の pt は 後方互換のブラジル・ポルトガル語バンドルのままです(どちらもブラジル・ポルトガル語で、同じ pools/UI を共有します)。 en-SGen_SG はシングリッシュ(汎用の en は英語の まま)です。香港繁体字中国語も同種の例外で、 zh-HKzh_HK.UTF-8zh-Hant-HKzh-HK、汎用の zh と中国本土向けタグは 簡体字中国語(zh)になります。

その場で切り替える — /language パネル。 CLIはもともと別の言語で起動できました (--langLOCKEDIN_LANG)。今度はセッションの途中で切り替えられます。/language (別名 /lang/languages)と入力すると、33言語がコード順に、それぞれの文字で一覧表示され、 /language el で残りのセッションの言語が切り替わります。肝は脱出口です。切り替え後はパネルが 新しい言語で描き直され、続けて直前まで使っていた言語で、戻るための正確な方法——いまなら /language en、次回起動時なら lockedin --lang en——を表示します。うっかり 日本語ಕನ್ನಡ に迷い込んでも取り残されません。(2回切り替えると、LOCKEDIN_LANG が指す言語も 併せて案内されます。)--langLOCKEDIN_LANG はこれまでどおりです。/a11y と同じく、 これは真面目な実用機能で、風刺ではありません。

中心となる考え方 — 言語バンドル

翻訳対象のテキストはすべて、言語ごとのバンドルに入っています。形はこうです。

{ meta: { lang: 'ja', name: '日本語', dir: 'ltr' },
  pools: { HOOKS: [ /* 約25個 */ ], LESSONS: [ /* ... */ ], /* ... */ },
  ui:    { buzzwordDensity: 'バズワード密度:', /* ラベルや見出し */ } }
  • pools は第2章で見たコンテンツの配列(ジョーク本体)です。
  • ui は画面の枠組みにあたる文字列(ラベル、見出し、短いテンプレート)です。

英語は src/lockedin.js 内にある基準バンドルで、他の32個のモジュールは src/content/*.js にあります。 ar, bn, bo, de, el, en-SG, es, eu, fa, fi, fr, he, hi, id, is, it, ja, kn, ms, nl, no, pl, pt, pt-BR, ru, sv, tl, tr, uk, ur, zh, zh-HK です (pt-BRpt の pools/UI を再利用しますが、個別に登録されます)。 各バンドルは BUNDLES に登録され、SUPPORTED_LANGS はそのキーから作られます。 renderHelp()--lang コード一覧もこの生成リストを表示するため、ui 側で手書きの一覧を 持つ必要はありません。

setLang('fr');       // 現在の言語をフランス語バンドルに向ける
// L = 現在のpools、U = 現在のui
pick(L.HOOKS)        // フランス語のフック
U.buzzwordDensity    // "Densité de jargon : "

すべてのレンダラーが LU を読む(文字列を直接埋め込まない)ので、 setLang を呼ぶだけで体験全体が切り替わります。仕掛けはこれだけです。

安全網 — キーの一致

言語の追加を安全にしてくれる不変条件が、これです。

どのバンドルも、英語とまったく同じ pools / ui のキーを持つこと。

これは33個すべてのバンドルについてテストで強制されています。英語に新しいUI文字列を足したのに ウクライナ語で訳し忘れたら、npm test が赤になり、どのキーが足りないかを教えてくれます。 中途半端に翻訳された言語をこっそり出荷することは、そもそもできません。

難所 — ターミナル上の幅

文字体系によって、ターミナルのレイアウトは別々の壊れ方をします。

  • 日本語、簡体字中国語、香港繁体字中国語には East Asian Wide / Fullwidth の文字があります。vw() はそれらを 2カラムとして数え、wrap() は空白のない長いトークンを強制的に折り返すので、CJKの文字列も カードやボックスからはみ出しません。
  • ヒンディー語とカンナダ語には、マートラーやヴィラーマのような非間隔・囲み結合記号 (Mn / Me)があります。vw() はこれらを0カラムとして数えるため、表示幅が水増しされません。
  • box() は各行をパディングする前に折り返します。長い翻訳バナーが枠線を突き破ることはありません。
  • 各バンドルは sentenceEndlistSep(たとえば . / , / )を持ち、 ジェネレータが組み立てる文が自然に読めるようにしています。

新しい言語を追加するときも、カードのヘッダー文字列(cardSubtitlecardMetacardFooter)は 表示幅60カラム以下に収める必要があります。アラビア語・ペルシャ語・ヘブライ語・ウルドゥー語は meta.dir: 'rtl' を設定します。一部のターミナルが制御文字を枠付きラベルとして表示するため、 既定の出力にはbidi制御文字を含めません。対応を確認済みのターミナルでは LOCKEDIN_BIDI=on により、折り返し後の釣り合ったisolateを明示的に有効化できます。 その場合もANSI、ASCIIコマンド、コピー時の論理順序を維持し、アクセシブル出力では常に 制御文字を除去します。未指定時はRTL/LTR混在順序が単純になる場合がありますが、 対応状況をプローブや推測で決めることはありません。

もう一つの難所 — ユーザー入力を挟む文法

一部のテンプレートは {cap} のようなプレースホルダーで、ユーザーが入力した生の句を差し込みます。 ここはスロット単位で直訳してはいけません。差し込まれるのは整った名詞とは限らず、ユーザーが書いた 節そのものかもしれないからです。

実際の注意例として、日本語テンプレートで {cap} の直後に を置くと、{cap} が完全な節だった 場合に不自然になります。解決策は「もっと頑張って訳す」ことではなく、名詞化を入れる、置き場所を変える など、どんな入力でも文として成立する形にテンプレートを組み替えることです。

✅ エージェントと試してみよう — 言語を追加する

本章の演習は、今でも同じやり方で成立します。自分で(あるいはエージェントに頼んで)正しさを確認できる 言語を選び、最後まで通してみましょう。まずは仕様を書きます。

デンマーク語(da)を追加する。 src/content/da.js{ meta, pools, ui } バンドルとして作成し、英語と同じキーを持たせ、全項目を翻訳する (コンテンツプールは各約25件、UI文字列はすべて)。src/lockedin.jsBUNDLESda を登録する。--lang dada-* ロケールで選択できること。カードのヘッダー文字列は 幅の上限を守ること。npm test は緑のままとし、既存のローカライズ済みテストに倣って デンマーク語の不変条件テストと検出テストを追加すること。

あとは第3〜4章のループを回します。

  1. まず計画。 「コードを書く前に、変更するファイルと、英語とのキーの一致をどう保つかを 教えてください。」
  2. まずテスト。 「失敗するテストを追加してください。da の検出、da のキーの一致、そして デンマーク語の reflect / connect の不変条件です。バンドルはまだ作らないでください。」
  3. 実装。 「では既存バンドルをキー単位で翻訳して src/content/da.js を作り、登録して、 テストを通してください。乱数は pick/shuffle のみです。」
  4. ゲートとレビュー。 npm test を回し、lockedin --lang da post を実行し、そして差分を読むこと。 全キーが翻訳されていますか? 罫線はまだ揃っていますか? {cap} を含むテンプレートは、 生のユーザー節を入れても自然ですか?

言語まるごとは重すぎる、という場合の小さめの練習問題もどうぞ。

  • 「33個の言語バンドルすべてに TAGLINE を1つずつ追加し、件数を揃えたままにしてください。」
  • 「カンナダ語の cardFooter が表示幅60カラム以下か確認し、結合記号をどう測ったかも説明してください。」
  • ja.js から ui のキーを1つ削除したときに落ちるテストを見せてください。」

次に進むなら

  • src/content/es.js を眺めてみてください。新しいバンドルの雛形として今でも素直な例です。
  • docs/HANDOFF.md の「Adding a language」を読み返してみましょう。
  • コマンドリファレンス で多言語のジョークもどうぞ。

これでチュートリアルは全章終了です。あなたはもう、AIエージェントに指示して、テストゲートの内側で 機能を作り、さらにそれを多言語化できます。33言語、そしてまだ増やせます。同意する人は👇

📘 LockedIn CLI wiki

Tutorial

Reference


Satire · Sátira · 風刺. Not affiliated with LinkedIn. GPL-3.0-or-later.

Clone this wiki locally