> ## 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.

# 다국어 DB 컬럼

> name_ko, name_en 또는 locale map 형태의 다국어 컬럼 처리

데이터베이스에 다국어 값을 저장해야 할 때, `name`, `name_ko`, `name_en`처럼 locale별 컬럼을 두거나 `name: { ko: "...", en: "..." }` 형태의 locale map을 사용할 수 있습니다. Sonamu는 `localizedColumn()` 함수로 현재 locale에 맞는 값을 자동으로 선택합니다.

## localizedColumn 함수

```typescript theme={null}
import { localizedColumn } from "../i18n/sd.generated";

const tag = {
  id: 1,
  name: "default",
  name_ko: "태그",
  name_en: "Tag",
};

const nestedTag = {
  id: 2,
  name: {
    ko: "태그",
    en: "Tag",
  },
};

// Context locale이 "ko"일 때
localizedColumn(tag, "name"); // "태그"
localizedColumn(nestedTag, "name"); // "태그"

// Context locale이 "en"일 때
localizedColumn(tag, "name"); // "Tag"
localizedColumn(nestedTag, "name"); // "Tag"

// Context locale이 "fr"일 때 (지원하지 않는 locale)
localizedColumn(tag, "name"); // "태그" (default locale 사용)
```

## 우선순위

`localizedColumn()`은 다음 순서로 값을 찾습니다:

| 현재 locale | 우선순위                           |
| --------- | ------------------------------ |
| `ko`      | `name_ko` → `name` → `name_en` |
| `en`      | `name_en` → `name` → `name_ko` |

Suffix 컬럼과 locale map이 모두 있으면 현재 locale의 suffix 컬럼을 먼저 확인한 뒤 locale map 값을 확인합니다. 빈 문자열(`""`)이나 `null`은 건너뛰고 다음 우선순위를 확인합니다.

## Entity 정의

다국어 컬럼을 가진 Entity 예시:

```json theme={null}
{
  "id": "Tag",
  "table": "tags",
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "name", "type": "string", "length": 100, "desc": "기본 이름" },
    { "name": "name_ko", "type": "string", "length": 100, "nullable": true, "desc": "한국어 이름" },
    { "name": "name_en", "type": "string", "length": 100, "nullable": true, "desc": "영어 이름" }
  ]
}
```

## Model에서 사용

### Subset에서 가공

Subset의 enhancer에서 `localizedColumn`을 사용하여 클라이언트에 단일 `name` 필드만 전달할 수 있습니다:

```typescript theme={null}
import { localizedColumn } from "../i18n/sd.generated";

class TagModelClass extends BaseModelClass {
  async findMany<T extends TagSubsetKey>(subset: T, params: TagListParams) {
    // ...

    const enhancers = this.createEnhancers({
      A: (row) => ({
        ...row,
        // locale에 맞는 이름으로 대체
        displayName: localizedColumn(row, "name"),
      }),
    });

    return this.executeSubsetQuery({ subset, qb, params, enhancers });
  }
}
```

### API 응답에서 사용

```typescript theme={null}
@api({ httpMethod: "GET" })
async getTagOptions(): Promise<{ id: number; label: string }[]> {
  const tags = await this.findMany("A", { num: 100 });

  return tags.rows.map(tag => ({
    id: tag.id,
    label: localizedColumn(tag, "name") ?? tag.name,
  }));
}
```

## 프론트엔드에서 사용

프론트엔드에서도 동일한 함수를 사용할 수 있습니다 (web의 sd.generated.ts에서 export):

```tsx theme={null}
import { localizedColumn } from "@/i18n/sd.generated";

function TagBadge({ tag }: { tag: Tag }) {
  return <span className="badge">{localizedColumn(tag, "name")}</span>;
}
```

## 지원하는 값 형태

`localizedColumn()`은 두 가지 저장 형태를 지원합니다:

| 형태         | 예시                                  | 설명                           |
| ---------- | ----------------------------------- | ---------------------------- |
| Suffix 컬럼  | `name`, `name_ko`, `name_en`        | locale별 컬럼을 각각 둡니다.          |
| Locale map | `{ name: { ko: "태그", en: "Tag" } }` | 기본 컬럼 안에 locale별 값을 객체로 둡니다. |

선택된 값은 `string` 또는 `string[]`일 수 있습니다. `string[]`는 문자열로 변환하지 않고 배열 그대로 반환합니다. Suffix 컬럼이나 기본 컬럼에 `number`, `boolean`, `bigint` 같은 scalar 값이 들어오면 기존 호환성을 위해 문자열로 변환해 반환합니다.

## Suffix 컬럼 네이밍 패턴

| 기본 컬럼         | locale 컬럼                          | 설명 |
| ------------- | ---------------------------------- | -- |
| `name`        | `name_ko`, `name_en`               | 이름 |
| `title`       | `title_ko`, `title_en`             | 제목 |
| `description` | `description_ko`, `description_en` | 설명 |
| `content`     | `content_ko`, `content_en`         | 내용 |

<Note>
  Suffix 컬럼을 사용할 때는 `{기본컬럼}_{locale}` 형식을 사용합니다. Locale map을 사용할 때는 기본 컬럼 값에 locale별 객체를 저장할 수 있습니다.
</Note>

## 주의사항

* `localizedColumn`은 단순히 컬럼 값을 선택하는 함수입니다. 쿼리 최적화(필요한 컬럼만 SELECT)는 별도로 처리해야 합니다.
* 모든 locale 컬럼이 `null`이고 기본 컬럼도 빈 값이면 `undefined`를 반환합니다.
* Context에 locale이 설정되지 않으면 `defaultLocale`을 사용합니다.
* Context locale이 지원하지 않는 값이면 `defaultLocale` 기준으로 값을 찾습니다.

<CardGroup cols={2}>
  <Card title="i18n 설정" icon="gear" href="/ko/advanced-features/i18n/setup">
    i18n 초기 설정
  </Card>

  <Card title="SD 함수 사용법" icon="code" href="/ko/advanced-features/i18n/dictionary">
    딕셔너리 작성 및 사용
  </Card>
</CardGroup>
