import { defineConfig } from "sonamu";export default defineConfig({ // ... other settings i18n: { defaultLocale: "ko", // Default language supportedLocales: ["ko", "en"], // List of supported languages },});
import { createFormat, josa } from "sonamu/dict";const format = createFormat("ko");export default { // Common UI "common.save": "저장", "common.cancel": "취소", "common.delete": "삭제", "common.results": (count: number) => `${count}개 결과`, // Error messages "error.notFound": "찾을 수 없습니다", "error.unauthorized": "인증이 필요합니다", // Validation messages (function type) "validation.required": (field: string) => `${josa(field, "은는")} 필수입니다`, "validation.email": "올바른 이메일 형식이 아닙니다", // Simple keys are also allowed (but namespace pattern is recommended) notFound: (name: string, id: number) => `존재하지 않는 ${name} ID ${id}`, test: (date: Date) => format.date(date),} as const;
Recommended key naming pattern: Using namespace patterns like "domain.item" helps organize
keys systematically and makes IDE autocomplete more convenient. - "common.*" - Common UI -
"error.*" - Error messages - "validation.*" - Validation messages - "entity.*" - Entity
related
To set the locale per request, configure the Context in middleware:
// api/src/middlewares/locale.tsimport { Sonamu } from "sonamu";export async function localeMiddleware(request: FastifyRequest) { // Extract locale from Accept-Language header or query parameter const locale = request.headers["accept-language"]?.split(",")[0]?.split("-")[0] || request.query.locale || "ko"; // Set locale in Context const ctx = Sonamu.getContext(); ctx.locale = locale;}
When the client calls setLocale(), the axios interceptor automatically sends the
Accept-Language header, and the server’s locale middleware reads it so that SD() calls return
translations in the correct language. Client-server locale synchronization works without any
additional configuration.