본문으로 건너뛰기

UI i18n 키 추가 컨벤션 (#1286)

ui/src/locales/{en,ko}.json 에 신규 i18n 키 추가 절차 + 회귀 가드.

컨벤션

1. 키 형식 — 도트 표기법

mlPlugins.headers.enabled
drift.actions.delete
modelGrants.dialog.create
  • 카멜케이스 컴포넌트명 (예: mlPlugins, modelGrants)
  • 의미적 grouping (headers, actions, dialog, status)
  • leaf 키 = 의미 명확 (예: enabled, disabled, view)

2. en.json + ko.json 동시 등록 필수

vitest setup (ui/src/test/setup.ts) 이 lng: "en" 강제 — en.json 누락 시 키 자체가 fallback 으로 렌더링 → screen.getByText("Enabled") 같은 단언 실패.

# 신규 키 추가 시 검증
grep -c '"mlPlugins"' ui/src/locales/en.json # > 0 확인
grep -c '"mlPlugins"' ui/src/locales/ko.json # > 0 확인

3. test 와 매칭 우선

  • 컴포넌트 test 가 getByText("Enabled") 처럼 literal 단언하면 en.json 의 값과 정확히 매칭 필수
  • humanize 만으로는 부족 — test 기대 문자열 확인 후 매핑

#1286 회귀 방지

본 회귀의 근본 원인: ML Plugin/Drift/ModelGrant UI 머지 시 i18n 키 추가 누락 → vitest 78 files 실패.

회귀 가드 (앞으로 추가 권장)

ui/src/test/i18n-coverage.test.ts (신규 follow-up):

import { expect, test } from "vitest";
import en from "@/locales/en.json";
import { glob } from "fast-glob";

test("all t() keys in admin components exist in en.json", async () => {
const files = await glob("src/components/admin/**/*.tsx");
for (const file of files) {
const content = readFileSync(file, "utf-8");
const keys = [...content.matchAll(/t\("([a-zA-Z][\w.]+)"/g)].map(m => m[1]);
for (const key of keys) {
const exists = key.split(".").reduce((o, k) => o?.[k], en as any);
expect(exists, `Missing key ${key} in en.json (from ${file})`).toBeDefined();
}
}
});

트러블슈팅

증상원인해결
vitest Unable to find element with the text: Enableden.json 에 키 없음 → fallback = 키 string누락 키 추가
mlPlugins.status.enabled 문자열이 UI 에 렌더링됨en.json mlPlugins.status.enabled 누락컴포넌트 + setup.ts 확인 후 키 추가
ko.json 에만 있고 en.json 누락비대칭 등록둘 다 추가 (test 는 en만)

관련

  • PR #1286
  • vitest setup: ui/src/test/setup.ts
  • i18n config: ui/src/lib/i18n.ts
  • follow-up: i18n coverage test 추가 (별도 PR)