> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# i18n Setup

> Enabling internationalization support in a Sonamu project

export const FileItem = ({name, description, children}) => {
  const isLeaf = !children;
  const ext = getFileExtension(name);
  const extStyle = ext ? getExtensionStyle(ext) : null;
  return <div style={{
    marginLeft: "30px"
  }}>
      <div style={{
    display: "flex",
    alignItems: "center",
    gap: "4px"
  }}>
        <span style={{
    position: "relative",
    display: "inline-block"
  }}>
          <span className="file-icon">{isLeaf && !name.endsWith("/") ? "📄" : "📁"}</span>
          {ext && <span style={{
    position: "absolute",
    bottom: "2px",
    right: "0px",
    fontSize: "5px",
    fontWeight: "bold",
    padding: "1px",
    borderRadius: "2px",
    lineHeight: "1",
    minWidth: "8px",
    textAlign: "center",
    ...extStyle
  }}>
              {ext}
            </span>}
        </span>
        <code>{name}</code>
        {description && <span className="description"> - {description}</span>}
      </div>
      {children}
    </div>;
};

export const FileTree = ({children}) => {
  return <div className="file-tree" style={{
    margin: "20px",
    marginLeft: "-30px"
  }}>
      {children}
    </div>;
};

## sonamu.config.ts Configuration

Enable the i18n option in `sonamu.config.ts`:

```typescript theme={null}
import { defineConfig } from "sonamu";

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

  i18n: {
    defaultLocale: "ko", // Default language
    supportedLocales: ["ko", "en"], // List of supported languages
  },
});
```

| Option             | Type       | Description                      |
| ------------------ | ---------- | -------------------------------- |
| `defaultLocale`    | `string`   | Default language code (fallback) |
| `supportedLocales` | `string[]` | All supported language codes     |

## Directory Structure

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

<FileTree>
  <FileItem name="api/">
    <FileItem name="src/">
      <FileItem name="i18n/">
        <FileItem name="ko.ts" description="Korean dictionary (defaultLocale)" />

        <FileItem name="en.ts" description="English dictionary" />

        <FileItem name="sd.generated.ts" description="Auto-generated file" />
      </FileItem>
    </FileItem>
  </FileItem>
</FileTree>

## Creating Dictionary Files

### defaultLocale Dictionary (ko.ts)

```typescript theme={null}
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;
```

<Tip>
  **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
</Tip>

### Other Locale Dictionary (en.ts)

```typescript theme={null}
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`,
});
```

<Note>
  `defineLocale` is exported from `sd.generated.ts` and provides type checking based on the keys
  from defaultLocale.
</Note>

## Locale Configuration

### Frontend (Client)

`sd.generated.ts` includes `setLocale` and `getCurrentLocale` functions for client-side locale management:

```typescript theme={null}
import { setLocale, getCurrentLocale, SUPPORTED_LOCALES } from "@/i18n/sd.generated";

// Change locale
setLocale("en");

// Check current locale
const current = getCurrentLocale(); // "en"
```

The axios interceptor in `sonamu.shared.ts` automatically adds the `Accept-Language` header to all API requests:

```typescript theme={null}
// sonamu.shared.ts (auto-generated — no manual setup needed)
axios.interceptors.request.use((config) => {
  config.headers["Accept-Language"] = getCurrentLocale();
  return config;
});
```

After calling `setLocale("en")`, all subsequent API requests will include the `Accept-Language: en` header.

### Backend (Server)

To set the locale per request, configure the Context in middleware:

```typescript theme={null}
// 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;
}
```

<Note>
  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.
</Note>

## Verifying Initialization

After completing the setup, run `pnpm sync` to verify that `sd.generated.ts` is generated:

```bash theme={null}
cd api
pnpm sync
```

The generated `sd.generated.ts` includes:

* Labels extracted from Entity (`entityLabels`)
* `SD()` function
* `localizedColumn()` function
* `defineLocale()` function

<CardGroup cols={2}>
  <Card title="Using the SD Function" icon="code" href="/en/advanced-features/i18n/dictionary">
    Writing and using dictionaries
  </Card>

  <Card title="Entity Labels" icon="tag" href="/en/advanced-features/i18n/entity-labels">
    Auto-extracted labels
  </Card>
</CardGroup>
