0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JSXに[[日本語]]と書くだけでi18nする——50ページ・約4万語・五言語を自作した話

0
Last updated at Posted at 2026-09-12

Next.js で多言語化(i18n)を本気でやると、最初に来るのは「枠」ではなく キー切り地獄 です。
t('home.title') を切って、辞書に移して、翻訳を回して、戻して、用語がズレたらまた直す。

弊社音楽系プロダクト Imaju の DOC は、蓋を開けると 約4万語超 × 五か国語 × 50ページ超
正攻法は正しい。でもこの量でレイアウト調整まで五言語ぶん回すのは、キツいというより非現実的でした。

そこで自作したのが OSS の bracket-i18n です。
JSX に日本語正本を [[…]] で書いて scan すると ID 化され、fill で他言語の下書きが入ります。出荷合否は人(lock)。

たたき台まで(実測イメージ): 約11時間(Gemini 無料枠当時)。最終は人の精査です。


Quick Start

npm install -D bracket-i18n
npx bracket-i18n init

initi18n-config.jssrc/i18n/、npm scripts(i18n:*)をプロジェクト側に用意します。
まずは生成された config を、出荷する locale に合わせて編集してください。

init は保存の設定まで用意します。
autosave 変換には別途 VS Code(や Cursor)用の拡張機能「Run on Save」 が必要です。必要無ければ手動で scan します。

書く:

export default function Pricing({ locale }: { locale: string }) {
  return <h1>[[成長に合わせて選べる料金プラン]]</h1>;
}

変換:

npx bracket-i18n scan

(または npm run i18n:scan。エディタの Run on Save でも可)

変換後のイメージ:

import { Languages } from '@/i18n';

export default function Pricing({ locale }: { locale: string }) {
  return <h1>{Languages('eb573cd25ff_', locale) /* 成長に合わせて選べる料金プラン */}</h1>;
}

他言語の下書き(任意・API キー必要):

# プロジェクトルートの .env.development / .env.local / .env などへ
# GEMINI_API_KEY=...

npx bracket-i18n fill

fill下書きです。通したものだけ lock: true
大量に [[ ]] を書き直している最中は i18n:pause → 確定後に i18n:resume → まとめて scan が安全です(保存のたびに途中状態で落ちやすい)。

生成される呼び出しは識別子 locale をそのまま使います。スコープに locale があること。

学習コストこれだけ。プロダクトに全集中出来ます。

詳細は README(英語 / 日本語)へ。


背景:どうしたもんか

Imaju を手掛けて数年。海外展開を視野に、初めから日本語以外にも広げたいと思っていました。
DOC だけで約4万語以上、五か国語。音楽用語も多い。

翻訳精度もさることながら、翻訳依頼のコストも「それはもう」という感じでした。

薄々、詰むことは少し前から見えていました。
キーを切って、辞書に移して、翻訳を回して、戻して、用語がズレたらまた直す。
正攻法は知っている。正しい。でもこの量は回せない。

そんな困りごとから降りてきた話です。


そうだ、作ってしまえ

思った。そうだ、作ってしまえ。

原型は、いまの bracket-i18n につながるローカル自動スクリプトでした。
JSX に日本語を [[…]] で書いて保存すると、

  • [[日本語]] を拾う
  • ID を振る
  • Languages(id, locale) に差し替える
  • 辞書へエントリを足す

まで一気にやる。

翻訳が AI で不安なら、data は空でいい。CSV に落として翻訳さんにお願いできる。

<h1>[[成長に合わせて選べる料金プラン]]</h1>

{/* 複数形 */}
<p>[[--n Steve has {count} dogs.]]</p>

{/* 性別 */}
<p>[[--g He posted.]]</p>

でも壁はあった

もちろん問題もありました。

  • 多次元配列・被り — グリッドの [[4,4]] が i18n の [[…]] と誤検知される類
  • 勝手に {} に置き換え問題
  • 複数形・性別・アラビア語まわりなど、動的な単語変換

なかなか壁は高い。でもこれは関数と正規表現の問題で、致命傷ではない、と気づきました。

長文問題

長い文はコンポーネントも嵩みます。囲んで変換すると本文が ID に置き換わるので、「どこに何が書いてあるか」がわからなくならないよう、先頭あたりをコメント化します。

{Languages('……_', locale) /* 正攻法は知っている */}

コンポーネントは縮小しつつ、中身も追える。


Cursor と AI 下書き

ここで Cursor の存在を知りました。流れはこうです。

  1. 正本言語を config で設定(日本人なら ja
  2. 必要ならニュアンス(トーン、製品用語、音楽 Domains で崩したくない語)も config へ
  3. たたき台が欲しければ API KEY(私は Gemini。たたき台としてのコスパが理由。固定信仰ではない)
  4. 翻訳対象言語を設定(私は五か国語)
  5. 本文がまだ固まる前は pause
  6. コンポーネントで本文を書く → [[ ]]scan / fill

実数イメージ:

  • 40000語のたたき台まで、およそ 11時間
  • たたき台側(Gemini)は当時 無料枠
  • 最終チェックはたたき台のあと が見て lock(必要なら別モデルで数回見る、も含む)

Languages(...) になった行を消せばデータも追従しやすい。だから 何度でもやり直せる


比較:正攻法・Next・われら

他人の否定はしません。得意分野が違う。
比較するのは「正しさ」ではなく、DOC 五か国語という仕事の実コストです。

項目 正攻法の i18n Next.js の i18n われら(bracket-i18n)
書き方 t('home.title') などキー中心 ルーティング+辞書/メッセージ JSX に [[日本語]]
強み 枯れてる・チーム共通言語 ロケール URL・Next との一体感 執筆速度・AI 下書き・やり直しの軽さ
弱みになりやすいところ キー設計と往復が先に来る 「枠」はくれるが翻訳作業自体は別問題 自前ツールの学習コスト
向く場面 既存規約・大規模チーム App/Pages の locale 設計 日本語正本・大量 DOC・高速展開

正攻法

キーを切り、辞書に載せ、t(id) で読む。長年の正解。
ただし作業としては「文言 → キー名 → 辞書 → 画面 → 翻訳レーン」になりやすい。正しさはある。でも正直かなりしんどい。

Next.js の i18n

locale 付き URL、ミドルウェア、メッセージ読み込み。
「どの言語のページか」を Next の世界観で扱えるのは大きい。

ただ誤解しやすい点があります。

Next の i18n = 翻訳が終わる

実際は 配信の枠 であって、4万語・50ページ超の DOC を五言語にしてくれるものではありません。
枠は借りて、中身のパイプラインは別に要る。

bracket-i18n

発想を逆にしました。

  1. まず正本言語で書く
  2. 固まったら [[…]] で囲む
  3. scan で ID 化(Languages(id, locale)
  4. fill で他言語の下書き(不安なら空のまま CSV)
  5. 人が見て、通したものだけ lock

キー設計を、執筆の前に置かない。
正本は設定言語。翻訳は後工程。


AI 翻訳が不安なら、コスト構造で見る

不安、わかります。では 構造だけで見ます(単価は案件ごとに違うので実額は出しません)。

項目 ゼロから翻訳者に五か国語・約50ページ AI たたき台あり → 人がチェック・精査・修正
主な作業 全文の新規翻訳 × 言語数 下書き確認・用語修正・lock
待ち時間 長い 短い(下書きは時間単位で見える)
お金の重心 語数 × 言語数 × 単価がそのまま乗る 人の精査時間が本体
やり直し 高い 行を消して再挑戦しやすい

従来:

語数 × 言語数 ×(単価 or 人日)

AI 下書きがあると:

語数 × 言語数 ×(下書きの待ち)+ 人間の精読と lock

「翻訳がタダ」ではない。
最初の下書きが極端に安くなるだけです。


メリットはコストだけじゃない。data 構造にも

五か国語が 1文脈(1エントリ)にまとまっているわかりやすさがあります。

{
  // イメージ(実際のスキーマはプロジェクト/パッケージ設定に従う)
  id: 'ec2339bf326_',
  ja: '成長に合わせて選べる料金プラン',
  en: 'Pricing plans that grow with you',
  es: '',
  fr: '',
  it: '',
  lock: true,
}
  • この文の正本はどれか
  • どの言語が空か
  • lock したか

が一箇所で追えます。CSV 往復とも相性がいい。

専門用語が多い領域は、やはりこうが無難です。

  1. 空(または最小)の訳で出す
  2. CSV で配布し、担当者が記入
  3. 戻して lock
  4. lock したものは以後、勝手に潰れない

fill は下書き。出荷の合格線は人側。


おわりに

世界へ配信したい、必要だというニーズが一人でもいたら——主観ですけど、正直便利だと思います。あとは、導入できる環境かどうか、ですね。

creathree の理念 「ない物は作る」 でお役に立てるならと、npm へ出した次第です。

どうしたもんか → 40000語超 × 五言語 × 50ページ超 → そうだ、作ってしまえ。

それが bracket-i18n です。

同じ痛みを持っている人に届いたら嬉しいです。Issue や感想、歓迎です。

creathree が発信する記事は、このような模索ストーリーを中心に不定期で出します。
今後ともどうぞよろしくお願いいたします。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?