Skip to main content

sonamu.config.ts Configuration

Enable the i18n option in sonamu.config.ts:
import { defineConfig } from "sonamu";

export default defineConfig({
  // ... other settings

  i18n: {
    defaultLocale: "ko",           // Default language
    supportedLocales: ["ko", "en"], // List of supported languages
  },
});
OptionTypeDescription
defaultLocalestringDefault language code (fallback)
supportedLocalesstring[]All supported language codes

Directory Structure

When i18n is enabled, the api/src/i18n/ directory is required:

Creating Dictionary Files

defaultLocale Dictionary (ko.ts)

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

Other Locale Dictionary (en.ts)

import { plural } from "sonamu/dict";
import { defineLocale } from "./sd.generated";

export default defineLocale({
  // Common UI
  "common.save": "Save",
  "common.cancel": "Cancel",
  "common.delete": "Delete",
  "common.results": (count: number) =>
    plural(count, { one: `${count} result`, other: `${count} results` }),

  // Error messages
  "error.notFound": "Not found",
  "error.unauthorized": "Authentication required",

  // Validation messages
  "validation.required": (field: string) => `${field} is required`,
  "validation.email": "Invalid email format",

  // Simple keys (namespace pattern recommended)
  notFound: (name: string, id: number) => `${name} ID ${id} not found`,
});
defineLocale is exported from sd.generated.ts and provides type checking based on the keys from defaultLocale.

Setting Locale in Context

To set the locale per request, configure the Context in middleware:
// api/src/middlewares/locale.ts
import { 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;
}

Verifying Initialization

After completing the setup, run pnpm sync to verify that sd.generated.ts is generated:
cd api
pnpm sync
The generated sd.generated.ts includes:
  • Labels extracted from Entity (entityLabels)
  • SD() function
  • localizedColumn() function
  • defineLocale() function