# i18n (Thai / English) LCBP3 frontend **must not** hardcode Thai or English UI strings in components. ## Rules 1. **All user-facing strings go through the i18n layer** (`next-intl` / `i18next` — check `frontend/package.json`). 2. **Keys use kebab-case**, namespaced by feature: - `correspondence.list.title` - `correspondence.form.submit` - `common.actions.cancel` 3. **Comments in code remain Thai** (business logic explanation); **only UI copy** goes through i18n. 4. **Error messages** from backend (via ADR-007 `userMessage`) are already localized server-side — render them directly, don't translate client-side. --- ## ❌ Wrong ```tsx export function CorrespondenceHeader() { return

รายการหนังสือติดต่อ

; // ❌ hardcoded Thai } toast.success('บันทึกสำเร็จ'); // ❌ hardcoded ``` --- ## ✅ Right ```tsx import { useTranslations } from 'next-intl'; export function CorrespondenceHeader() { const t = useTranslations('correspondence.list'); return

{t('title')}

; } toast.success(t('save.success')); ``` Translation files: ```json // messages/th.json { "correspondence": { "list": { "title": "รายการหนังสือติดต่อ" }, "save": { "success": "บันทึกสำเร็จ" } } } // messages/en.json { "correspondence": { "list": { "title": "Correspondence List" }, "save": { "success": "Saved successfully" } } } ``` --- ## Zod Error Messages Zod error messages shown in forms **do** stay in Thai inline (per `specs/05-Engineering-Guidelines/05-03-frontend-guidelines.md`), because they're schema-bound and rarely need translation. If dual-language support becomes required, wrap with an i18n-aware resolver: ```ts const schema = z.object({ projectUuid: z.string().uuid(t('validation.project.required')), }); ``` --- ## Reference - [i18n Guidelines](../../../specs/05-Engineering-Guidelines/05-08-i18n-guidelines.md) - [Frontend Guidelines](../../../specs/05-Engineering-Guidelines/05-03-frontend-guidelines.md)