Skip to content

Metanaka Nuxt i18n Rule

Nuxt app 内でユーザーに見える文言、validation message key、locale file を扱うときに使う、このリポジトリ固有の i18n ルール。

source と output

feature 固有の翻訳 source は、その feature の近くに置く。 feature directory が未作成の場合も、source は新しい feature slice の i18n/ 配下に作る。

text
app/features/<feature>/i18n/ja.json
app/features/<feature>/i18n/en.json

shared または app-wide な message source は、その文言を所有する shared concept の近くに置く。

text
app/shared/<concept>/i18n/ja.json
app/shared/<concept>/i18n/en.json

Nuxt が読む locale file だけを i18n/ 配下に置き、生成物として扱う。

text
i18n/locales/ja.json
i18n/locales/en.json

i18n/locales/*.json を直接の編集場所にしない。feature source または shared concept source を編集してから merge する。

ディレクトリルール

feature ディレクトリは基本的にフラットだが、i18n だけは例外として i18n/ ディレクトリを切る。

text
app/features/host/
  model.ts
  use-room-create.ts
  use-room-create.test.ts
  i18n/
    ja.json
    en.json

host/ja.jsonhost/en.json のように i18n file を feature root にフラット配置しない。

shared 配下も概念単位で slice 化する。共有文言は app/shared/i18n のような横断ディレクトリに集めず、所有する shared concept の i18n/ に置く。

text
app/shared/server-error/
  model.ts
  resolve-server-error-keys.ts
  i18n/
    ja.json
    en.json

key 命名

feature が所有する message は feature namespace 配下に置く。shared concept が所有する message は shared concept namespace 配下に置く。所有者の namespace に置くことで、他 feature や他 shared concept の key との競合を防ぐ。

json
{
  "host": {
    "error": {
      "wolfCountInvalid": "ウルフ人数は整数で選択してください。",
      "wolfCountMin": "ウルフ人数は1人以上にしてください。"
    }
  }
}

基本形は次の通り。

text
<owner>.<category>.<shortCamelCaseName>

短い action や label で category を切るほどではない場合は、次の形も許可する。

text
<owner>.<leaf>

例:

  • host.error.wolfCountInvalid
  • host.error.wolfCountMin
  • host.error.voteSecondsInvalid
  • host.create
  • room.notFound.title
  • serverError.error.SE10001

owner namespace で十分に文脈が分かる場合、過剰に深い key は避ける。

text
error.front.roomCreate.voteSeconds.invalid

代わりに次を優先する。

text
host.error.voteSecondsInvalid

命名規則

  • leaf key は camelCase にする。例: wolfCountInvalidwolf_count_invalid は使わない。
  • category object は errorformsummarynotFound のように短くする。
  • feature が所有する message は feature name を top-level namespace にする。
  • shared concept が所有する message は concept name を camelCase にした top-level namespace にする。例: server-error concept は serverError
  • commonapp などの曖昧な shared namespace は原則として使わず、明確な所有 concept に寄せる。アプリのブランドや最上位 shell のように本当に app-wide なものだけ app を使う。
  • ja.jsonen.json の key set は一致させる。
  • 後段の表示境界で t(key) する値は、code 上では i18n key として持つ。
  • Firestore、共有 game state、round history、validation schema に翻訳済み display string を保存しない。

所有権と再定義禁止

他 feature または他 shared concept が所有している言葉は、利用側の i18n で再定義しない。文言の責務は、その概念を所有する feature または shared concept が持つ。

例えば feature/user/i18nuser.displayName を定義している場合、feature/host/i18n では host.displayName を再定義しない。host 側の UI で表示名ラベルが必要なら、t("user.displayName") のように user feature が所有する key を参照する。

再定義してよいのは、利用側 feature 固有の意味や文脈を持つ場合だけにする。

text
user.displayName

は user が所有する一般的な表示名。

text
host.hostDisplayNameHelp

は host のルーム作成文脈に固有の補助文。

validation と Zod

このリポジトリでは Zod v4 を前提にする。Zod schema の message は、UI で表示される validation error の i18n key として使ってよい。

ts
wolfCount: z.number("host.error.wolfCountInvalid")
  .int("host.error.wolfCountInvalid")
  .min(1, "host.error.wolfCountMin")

composable や form state では、Zod issue の message をそのまま入れる。

ts
error.value[field] = issue.message

schema が具体的な key を持てる場合、composable 側で field name から i18n key を組み立てない。

Zod 用の i18n key は定数化せず、schema の message に直接文字列で書く。

merge workflow

source locale file を編集したら、Nuxt project root で merge する。

bash
pnpm run i18n:merge

pnpm run i18n:merge が存在しない場合は、この skill に同梱している merge script を Nuxt project root の scripts/merge-i18n.mjs に配置し、package.json に実行コマンドを追加する。

text
.codex/skills/metanaka-nuxt-i18n-rule/scripts/merge-i18n.mjs
json
{
  "scripts": {
    "i18n:merge": "node scripts/merge-i18n.mjs"
  }
}

同梱 script は次を満たす。

  • app/features/*/i18n/{ja,en}.json を読む。
  • app/shared/*/i18n/{ja,en}.json を読む。
  • i18n/locales/{ja,en}.json を書く。
  • source directory に ja.json だけ、または en.json だけがある場合は失敗する。
  • 同じ i18n source directory 内で jaen の key set が違う場合は失敗する。

typecheck、build、dev の前に pretcprebuildpredev などの package script で merge が走る構成なら、それに従う。

検証

i18n file を編集したら実行する。

bash
pnpm run i18n:merge

この command は Nuxt project root で実行する。 review や編集禁止の評価で merge を実行できない場合は、実行すべき command と作業 directory を成果物や review comment に明記する。

i18n key を consume する code に触った場合は、狭い unit test と typecheck も実行する。