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

localizedColumn 함수

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

Entity 정의

다국어 컬럼을 가진 Entity 예시:
{
  "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 필드만 전달할 수 있습니다:
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 응답에서 사용

@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):
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_enlocale별 컬럼을 각각 둡니다.
Locale map{ name: { ko: "태그", en: "Tag" } }기본 컬럼 안에 locale별 값을 객체로 둡니다.
선택된 값은 string 또는 string[]일 수 있습니다. string[]는 문자열로 변환하지 않고 배열 그대로 반환합니다. Suffix 컬럼이나 기본 컬럼에 number, boolean, bigint 같은 scalar 값이 들어오면 기존 호환성을 위해 문자열로 변환해 반환합니다.

Suffix 컬럼 네이밍 패턴

기본 컬럼locale 컬럼설명
namename_ko, name_en이름
titletitle_ko, title_en제목
descriptiondescription_ko, description_en설명
contentcontent_ko, content_en내용
Suffix 컬럼을 사용할 때는 {기본컬럼}_{locale} 형식을 사용합니다. Locale map을 사용할 때는 기본 컬럼 값에 locale별 객체를 저장할 수 있습니다.

주의사항

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

i18n 설정

i18n 초기 설정

SD 함수 사용법

딕셔너리 작성 및 사용