Skip to main content
Sonamu provides helper functions to assist with writing multilingual messages.

josa - Korean Particle Handling

The josa() function selects the appropriate Korean particle based on whether the preceding word ends with a consonant (batchim):
import { josa } from "sonamu/dict";

josa("사람", "은는")  // "사람은"
josa("나", "은는")    // "나는"

josa("물", "이가")    // "물이"
josa("바다", "이가")  // "바다가"

josa("밥", "을를")    // "밥을"
josa("커피", "을를")  // "커피를"

josa("빵", "과와")    // "빵과"
josa("우유", "과와")  // "우유와"

josa("집", "으로")    // "집으로"
josa("학교", "으로")  // "학교로"

Supported Particles

ParticleWith final consonantWithout final consonant
은는
이가
을를
과와
으로으로

Using in Dictionary

// ko.ts
import { josa } from "sonamu/dict";

export default {
  "validation.required": (field: string) => `${josa(field, "은는")} 필수입니다`,
  "validation.duplicate": (field: string) => `이미 사용중인 ${josa(field, "이가")} 있습니다`,
  "error.notFound": (name: string) => `${josa(name, "을를")} 찾을 수 없습니다`,
} as const;

// Usage
SD("validation.required")("이메일")  // "이메일은 필수입니다"
SD("validation.required")("이름")    // "이름은 필수입니다"

plural - Pluralization Handling

The plural() function returns different text based on the number:
import { plural } from "sonamu/dict";

plural(0, { zero: "none", one: "1 item", other: "multiple items" })  // "none"
plural(1, { zero: "none", one: "1 item", other: "multiple items" })  // "1 item"
plural(5, { zero: "none", one: "1 item", other: "multiple items" })  // "multiple items"

// Using a function for other
plural(5, { other: (n) => `${n} items` })  // "5 items"

// Falls back to other if zero or one is not defined
plural(0, { other: "default" })  // "default"
plural(1, { other: "default" })  // "default"

Options

OptionConditionDescription
zeron === 0Text when count is 0
onen === 1Text when count is 1
otherotherwiseDefault text (required)

Using in Dictionary

// ko.ts
import { plural } from "sonamu/dict";

export default {
  "items.count": (count: number) => plural(count, {
    zero: "항목이 없습니다",
    one: "1개 항목",
    other: (n) => `${n}개 항목`,
  }),

  "cart.items": (count: number) => plural(count, {
    zero: "장바구니가 비어있습니다",
    other: (n) => `장바구니에 ${n}개 상품`,
  }),
} as const;

createFormat - Number/Date Formatting

The createFormat() function creates number and date formatters for a specific locale:
import { createFormat } from "sonamu/dict";

const format = createFormat("ko");

format.number(1234567)  // "1,234,567"
format.date(new Date("2024-01-15"))  // "2024. 1. 15."

const enFormat = createFormat("en");
enFormat.number(1234567)  // "1,234,567"
enFormat.date(new Date("2024-01-15"))  // "1/15/2024"

Using in Dictionary

// ko.ts
import { createFormat } from "sonamu/dict";

const format = createFormat("ko");

export default {
  "price.display": (price: number) => `${format.number(price)}원`,
  "date.created": (date: Date) => `${format.date(date)} 등록`,
  "validation.range": (field: string, min: number, max: number) =>
    `${field}은(는) ${format.number(min)}~${format.number(max)} 사이여야 합니다`,
} as const;

Combining Multiple Helpers

Complex messages can be written by combining multiple helpers:
// ko.ts
import { createFormat, josa, plural } from "sonamu/dict";

const format = createFormat("ko");

export default {
  // josa + plural
  "search.results": (keyword: string, count: number) =>
    `'${keyword}'${josa(keyword, "으로")} 검색한 결과 ` +
    plural(count, {
      zero: "없음",
      other: (n) => `${format.number(n)}건`,
    }),

  // format + josa
  "order.total": (itemCount: number, total: number) =>
    `${itemCount}개 상품, 총 ${format.number(total)}원`,

  // Complex message
  "dashboard.summary": (userName: string, taskCount: number, date: Date) =>
    `${userName}님, ${format.date(date)} 기준 ` +
    plural(taskCount, {
      zero: "처리할 작업이 없습니다",
      one: "1개의 작업이 대기 중입니다",
      other: (n) => `${n}개의 작업이 대기 중입니다`,
    }),
} as const;