유니코드 확장을 사용하여 로케일을 커스터마이징하는 방법
로케일 식별자에 달력 시스템, 숫자 형식 및 시간 표시 환경설정 추가하기
소개
en-US와 같은 로케일 식별자는 JavaScript에게 형식 지정에 사용할 언어와 지역을 알려줍니다. 그러나 어떤 달력 시스템을 사용할지, 어떤 숫자 형식을 표시할지, 또는 시간을 12시간 형식으로 표시할지 24시간 형식으로 표시할지는 지정하지 않습니다. 이러한 형식 지정 환경설정은 위치뿐만 아니라 사용자 선택에 따라 다릅니다.
유니코드 확장은 이 문제를 해결합니다. 로케일 식별자에 형식 지정 환경설정을 직접 추가할 수 있습니다. 각 포맷터에 대해 별도의 구성 옵션을 사용하는 대신, 로케일 문자열 자체에 환경설정을 한 번에 인코딩합니다.
이 가이드는 유니코드 확장이 어떻게 작동하는지, 어떤 확장 유형이 있는지, 그리고 국제화 코드에서 언제 사용해야 하는지 설명합니다.
유니코드 확장이란
유니코드 확장은 형식 지정 환경설정을 지정하기 위해 로케일 식별자에 추가하는 추가 태그입니다. 이는 로케일 식별자를 정의하는 것과 동일한 사양인 BCP 47에 정의된 표준 형식을 따릅니다.
확장은 -u-로 시작하고 그 뒤에 키-값 쌍이 옵니다. u는 유니코드를 나타냅니다. 각 키는 두 글자이며, 값은 키 유형에 따라 다릅니다.
const locale = "en-US-u-ca-gregory-hc-h12";
이 로케일 식별자는 그레고리안 달력과 12시간 시간 표시가 있는 미국 영어를 지정합니다.
로케일 문자열에 확장을 추가하는 방법
확장은 언어, 스크립트 및 지역 구성 요소 뒤에 로케일 식별자의 끝에 나타납니다. -u- 마커는 핵심 식별자와 확장을 구분합니다.
기본 구조는 다음 패턴을 따릅니다:
language-region-u-key-value-key-value
각 키-값 쌍은 하나의 형식 지정 환경설정을 지정합니다. 단일 로케일 문자열에 여러 키-값 쌍을 포함할 수 있습니다.
const japanese = new Intl.Locale("ja-JP-u-ca-japanese-nu-jpan");
console.log(japanese.calendar); // "japanese"
console.log(japanese.numberingSystem); // "jpan"
키-값 쌍의 순서는 중요하지 않습니다. "en-u-ca-gregory-nu-latn"과 "en-u-nu-latn-ca-gregory" 모두 유효하고 동등합니다.
캘린더 확장
'ca' 키는 날짜 형식화에 사용할 캘린더 시스템을 지정합니다. 다양한 문화권에서는 서로 다른 캘린더 시스템을 사용하며, 일부 사용자는 종교적 또는 문화적 이유로 그레고리안 캘린더가 아닌 다른 캘린더를 선호합니다.
일반적인 캘린더 값은 다음과 같습니다:
gregory: 그레고리안 캘린더buddhist: 불교 캘린더islamic: 이슬람 캘린더hebrew: 히브리 캘린더chinese: 중국 캘린더japanese: 일본 황실 캘린더
const islamicLocale = new Intl.Locale("ar-SA-u-ca-islamic");
const date = new Date("2025-03-15");
const formatter = new Intl.DateTimeFormat(islamicLocale, {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.format(date));
// 출력: "٢٠ رمضان ١٤٤٦ هـ"
이는 이슬람 캘린더에 따라 날짜를 형식화합니다. 동일한 그레고리안 날짜가 이슬람 캘린더 시스템에서는 다른 연도, 월, 일로 표시됩니다.
불교 캘린더는 태국에서 일반적으로 사용됩니다. 이는 부처의 탄생인 기원전 543년부터 연도를 계산하므로, 불교 연도는 그레고리안 연도보다 543년 앞서 있습니다.
const buddhistLocale = new Intl.Locale("th-TH-u-ca-buddhist");
const formatter = new Intl.DateTimeFormat(buddhistLocale, {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(formatter.format(new Date("2025-03-15")));
// 출력: "15 มีนาคม 2568"
그레고리안 캘린더의 2025년은 불교 캘린더에서는 2568년입니다.
숫자 체계 확장
'nu' 키는 숫자를 표시하는 데 사용할 숫자 체계를 지정합니다. 대부분의 로케일은 서양식 아라비아 숫자(0-9)를 사용하지만, 많은 지역에서는 자체적인 전통 숫자 체계를 가지고 있습니다.
일반적인 숫자 체계 값은 다음과 같습니다:
latn: 서양식 아라비아 숫자(0-9)arab: 아랍-인도 숫자hanidec: 중국 십진 숫자deva: 데바나가리 숫자thai: 태국 숫자
const arabicLocale = new Intl.Locale("ar-EG-u-nu-arab");
const number = 123456;
const formatter = new Intl.NumberFormat(arabicLocale);
console.log(formatter.format(number));
// 출력: "١٢٣٬٤٥٦"
아랍-인도 숫자는 서양 숫자와 모양이 다르지만 동일한 값을 나타냅니다. 숫자 123456은 ١٢٣٬٤٥٦으로 표시됩니다.
태국 숫자의 예시:
const thaiLocale = new Intl.Locale("th-TH-u-nu-thai");
const formatter = new Intl.NumberFormat(thaiLocale);
console.log(formatter.format(123456));
// 출력: "๑๒๓,๔๕๖"
많은 아랍 로케일은 아랍-인도 숫자와 라틴 숫자를 모두 지원합니다. 사용자는 개인 선호도나 상황에 따라 원하는 시스템을 선택할 수 있습니다.
시간 주기 확장
'hc' 키는 시간을 표시하는 방법을 지정합니다. 일부 지역에서는 AM과 PM 표시가 있는 12시간제를 선호하고, 다른 지역에서는 24시간제를 선호합니다. 시간 주기는 자정이 어떻게 표시되는지도 결정합니다.
네 가지 시간 주기 값이 제공됩니다:
h12는 자정이 12:00 AM인 1-12시간을 사용합니다h11은 자정이 0:00 AM인 0-11시간을 사용합니다h23은 자정이 0:00인 0-23시간을 사용합니다h24는 자정이 24:00인 1-24시간을 사용합니다
h12와 h11 값은 12시간제를 나타내고, h23과 h24는 24시간제를 나타냅니다. 차이점은 시간 범위가 0에서 시작하는지 1에서 시작하는지에 있습니다.
const us12Hour = new Intl.Locale("en-US-u-hc-h12");
const japan11Hour = new Intl.Locale("ja-JP-u-hc-h11");
const europe23Hour = new Intl.Locale("en-GB-u-hc-h23");
const date = new Date("2025-03-15T00:30:00");
console.log(new Intl.DateTimeFormat(us12Hour, { hour: "numeric", minute: "numeric" }).format(date));
// 출력: "12:30 AM"
console.log(new Intl.DateTimeFormat(japan11Hour, { hour: "numeric", minute: "numeric" }).format(date));
// 출력: "0:30 AM"
console.log(new Intl.DateTimeFormat(europe23Hour, { hour: "numeric", minute: "numeric" }).format(date));
// 출력: "00:30"
h12 형식은 자정을 12:30 AM으로 표시하고, h11은 0:30 AM으로 표시합니다. h23 형식은 AM이나 PM 없이 00:30으로 표시합니다.
대부분의 애플리케이션은 h12 또는 h23을 사용합니다. h11 형식은 주로 일본에서 사용되며, h24는 실제로 거의 사용되지 않습니다.
정렬 확장
'co' 키는 문자열 정렬을 위한 정렬 규칙을 지정합니다. 정렬은 텍스트를 정렬할 때 문자의 순서를 결정합니다. 다양한 언어와 지역에는 서로 다른 정렬 규칙이 있습니다.
일반적인 정렬 값은 다음과 같습니다:
standard: 표준 유니코드 정렬phonebk: 전화번호부 순서(독일어)pinyin: 병음 순서(중국어)stroke: 획수 순서(중국어)
독일어 전화번호부 정렬은 표준 정렬과 달리 움라우트를 다르게 처리합니다. 전화번호부 순서는 정렬 목적으로 ä를 ae로, ö를 oe로, ü를 ue로 확장합니다.
const names = ["Müller", "Meyer", "Möller", "Mueller"];
const standard = new Intl.Collator("de-DE");
const phonebook = new Intl.Collator("de-DE-u-co-phonebk");
console.log(names.sort((a, b) => standard.compare(a, b)));
// 출력: ["Meyer", "Möller", "Mueller", "Müller"]
console.log(names.sort((a, b) => phonebook.compare(a, b)));
// 출력: ["Meyer", "Möller", "Mueller", "Müller"]
중국어 정렬은 여러 정렬 시스템을 제공합니다. 병음 정렬은 발음에 따라 정렬하고, 획수 정렬은 각 문자를 쓰는 데 사용되는 붓 획수에 따라 정렬합니다.
const pinyinCollator = new Intl.Collator("zh-CN-u-co-pinyin");
const strokeCollator = new Intl.Collator("zh-CN-u-co-stroke");
정렬 확장은 Intl.Collator API와 정렬기를 사용할 때 Array.prototype.sort() 같은 메서드에만 영향을 미칩니다.
대소문자 우선 확장
'kf' 키는 정렬 시 대문자 또는 소문자가 먼저 정렬되는지 결정합니다. 이 선호도는 언어와 사용 사례에 따라 다릅니다.
세 가지 값을 사용할 수 있습니다:
upper: 대문자를 소문자보다 먼저 정렬lower: 소문자를 대문자보다 먼저 정렬false: 로케일의 기본 대소문자 순서 사용
const words = ["apple", "Apple", "APPLE", "banana"];
const upperFirst = new Intl.Collator("en-US-u-kf-upper");
const lowerFirst = new Intl.Collator("en-US-u-kf-lower");
console.log(words.sort((a, b) => upperFirst.compare(a, b)));
// 출력: ["APPLE", "Apple", "apple", "banana"]
console.log(words.sort((a, b) => lowerFirst.compare(a, b)));
// 출력: ["apple", "Apple", "APPLE", "banana"]
대소문자 우선 순서는 단어가 대소문자를 제외하고 동일할 때 정렬에 영향을 미칩니다. 기본 문자를 비교한 후 이차적 정렬 순서를 결정합니다.
숫자 정렬 확장
'kn' 키는 숫자 정렬을 활성화하여 숫자 시퀀스를 사전식 순서가 아닌 숫자 값에 따라 정렬합니다. 숫자 정렬이 없으면 문자 순서에서 "1"이 "2"보다 먼저 오기 때문에 "10"이 "2"보다 먼저 정렬됩니다.
숫자 정렬은 두 가지 값을 허용합니다:
true: 숫자 정렬 활성화false: 숫자 정렬 비활성화(기본값)
const items = ["item1", "item10", "item2", "item20"];
const standard = new Intl.Collator("en-US");
const numeric = new Intl.Collator("en-US-u-kn-true");
console.log(items.sort((a, b) => standard.compare(a, b)));
// 출력: ["item1", "item10", "item2", "item20"]
console.log(items.sort((a, b) => numeric.compare(a, b)));
// 출력: ["item1", "item2", "item10", "item20"]
숫자 정렬이 활성화되면 2가 10보다 작기 때문에 "item2"가 "item10"보다 먼저 올바르게 정렬됩니다. 이는 숫자가 포함된 문자열에 대해 예상되는 정렬 순서를 생성합니다.
숫자 정렬은 파일 이름, 버전 번호, 주소 및 숫자가 포함된 모든 텍스트를 정렬할 때 유용합니다.
확장 문자열 대신 옵션 객체 사용하기
로케일 문자열에 확장을 인코딩하는 대신, Intl.Locale 생성자에 옵션으로 전달할 수 있습니다. 이 접근 방식은 기본 로케일과 서식 지정 환경설정을 분리합니다.
const locale = new Intl.Locale("ja-JP", {
calendar: "japanese",
numberingSystem: "jpan",
hourCycle: "h11"
});
console.log(locale.toString());
// 출력: "ja-JP-u-ca-japanese-hc-h11-nu-jpan"
생성자는 옵션을 자동으로 확장 태그로 변환합니다. 두 접근 방식 모두 동일한 로케일 객체를 생성합니다.
옵션 객체 접근 방식은 여러 이점을 제공합니다. 두 글자 코드 대신 전체 속성 이름을 사용하여 코드 가독성을 높입니다. 또한 구성 데이터에서 로케일을 동적으로 구성하기 쉽게 합니다.
const userPreferences = {
language: "ar",
region: "SA",
calendar: "islamic",
numberingSystem: "arab"
};
const locale = new Intl.Locale(`${userPreferences.language}-${userPreferences.region}`, {
calendar: userPreferences.calendar,
numberingSystem: userPreferences.numberingSystem
});
포맷터 생성자에 직접 옵션을 전달할 수도 있습니다:
const formatter = new Intl.DateTimeFormat("th-TH", {
calendar: "buddhist",
numberingSystem: "thai",
year: "numeric",
month: "long",
day: "numeric"
});
이는 로케일별 서식 지정 옵션과 표현 옵션을 단일 생성자 호출로 결합합니다.
확장과 포맷터 옵션을 사용하는 시기
확장과 포맷터 옵션은 서로 다른 목적을 가집니다. 각 접근 방식을 언제 사용할지 이해하면 더 깔끔하고 유지보수하기 쉬운 코드를 작성하는 데 도움이 됩니다.
서식 지정 환경설정이 사용자의 로케일에 내재되어 있을 때 로케일 문자열에 확장을 사용하세요. 태국 사용자가 항상 불교 달력과 태국 숫자를 보고 싶어한다면, 이러한 환경설정을 로케일 식별자에 인코딩하세요.
const userLocale = "th-TH-u-ca-buddhist-nu-thai";
이렇게 하면 환경설정을 반복하지 않고 로케일을 모든 포맷터에 전달할 수 있습니다:
const dateFormatter = new Intl.DateTimeFormat(userLocale);
const numberFormatter = new Intl.NumberFormat(userLocale);
두 포맷터 모두 자동으로 불교 달력과 태국 숫자를 사용합니다.
서식 지정 환경설정이 하나의 사용 사례에 특정될 때 포맷터 옵션을 사용하세요. 애플리케이션의 한 부분에서는 이슬람 달력을 표시하고 다른 곳에서는 그레고리안 달력을 표시하려면, 특정 포맷터에 달력 옵션을 전달하세요.
const islamicFormatter = new Intl.DateTimeFormat("ar-SA", {
calendar: "islamic"
});
const gregorianFormatter = new Intl.DateTimeFormat("ar-SA", {
calendar: "gregory"
});
동일한 로케일 식별자가 달력 옵션에 따라 다른 서식을 생성합니다.
로케일 문자열의 확장은 기본값으로 작동합니다. 포맷터 옵션은 지정된 경우 이러한 기본값을 재정의합니다. 이를 통해 사용자 환경설정을 기준으로 사용하면서 특정 포맷터를 사용자 지정할 수 있습니다.
const locale = "en-US-u-hc-h23";
const formatter12Hour = new Intl.DateTimeFormat(locale, {
hourCycle: "h12"
});
사용자는 24시간 형식을 선호하지만, 이 특정 포맷터는 해당 환경설정을 재정의하여 12시간 형식을 표시합니다.
로케일에서 확장 값 읽기
Intl.Locale 객체는 확장 값을 속성으로 노출합니다. 이러한 속성을 읽어 로케일의 서식 지정 기본 설정을 검사하거나 검증할 수 있습니다.
const locale = new Intl.Locale("ar-SA-u-ca-islamic-nu-arab-hc-h12");
console.log(locale.calendar); // "islamic"
console.log(locale.numberingSystem); // "arab"
console.log(locale.hourCycle); // "h12"
이러한 속성은 확장이 존재하는 경우 확장 값을 반환하고, 확장이 지정되지 않은 경우 undefined를 반환합니다.
이러한 속성을 사용하여 구성 인터페이스를 구축하거나 사용자 기본 설정을 검증할 수 있습니다:
function describeLocalePreferences(localeString) {
const locale = new Intl.Locale(localeString);
return {
language: locale.language,
region: locale.region,
calendar: locale.calendar || "default",
numberingSystem: locale.numberingSystem || "default",
hourCycle: locale.hourCycle || "default"
};
}
console.log(describeLocalePreferences("th-TH-u-ca-buddhist-nu-thai"));
// 출력: { language: "th", region: "TH", calendar: "buddhist", numberingSystem: "thai", hourCycle: "default" }
collation, caseFirst 및 numeric 속성은 co, kf 및 kn 확장 키에 해당합니다:
const locale = new Intl.Locale("de-DE-u-co-phonebk-kf-upper-kn-true");
console.log(locale.collation); // "phonebk"
console.log(locale.caseFirst); // "upper"
console.log(locale.numeric); // true
numeric 속성은 문자열이 아닌 불리언을 반환합니다. 값 true는 숫자 정렬이 활성화되었음을 나타냅니다.
여러 확장 결합하기
단일 로케일 식별자에서 여러 확장을 결합할 수 있습니다. 이를 통해 모든 서식 지정 기본 설정을 한 번에 지정할 수 있습니다.
const locale = new Intl.Locale("ar-SA-u-ca-islamic-nu-arab-hc-h12-co-standard");
const dateFormatter = new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
const date = new Date("2025-03-15T14:30:00");
console.log(dateFormatter.format(date));
// 출력은 이슬람력, 아랍-인도 숫자 및 12시간제를 사용합니다
각 확장 키는 로케일 문자열에 한 번만 나타날 수 있습니다. 동일한 키를 여러 번 지정하면 마지막 값이 우선합니다.
const locale = new Intl.Locale("en-US-u-hc-h23-hc-h12");
console.log(locale.hourCycle); // "h12"
프로그래밍 방식으로 로케일을 구성할 때는 모호함을 방지하기 위해 각 확장 키가 한 번만 나타나도록 해야 합니다.
실용적인 사용 사례
유니코드 확장은 국제화된 애플리케이션에서 실제 문제를 해결합니다. 일반적인 사용 사례를 이해하면 확장을 효과적으로 적용하는 데 도움이 됩니다.
사용자 환경설정 저장
여러 구성 필드 대신 단일 로케일 문자열에 사용자 서식 환경설정을 저장합니다:
function saveUserPreferences(userId, localeString) {
const locale = new Intl.Locale(localeString);
return {
userId,
language: locale.language,
region: locale.region,
localeString: locale.toString(),
preferences: {
calendar: locale.calendar,
numberingSystem: locale.numberingSystem,
hourCycle: locale.hourCycle
}
};
}
const preferences = saveUserPreferences(123, "ar-SA-u-ca-islamic-nu-arab-hc-h12");
이 접근 방식은 서식 환경설정을 단일 문자열로 저장하면서도 개별 구성 요소에 대한 구조화된 접근을 제공합니다.
로케일 선택기 구축
확장이 포함된 로케일 문자열을 구성하여 사용자가 UI를 통해 서식 환경설정을 선택할 수 있게 합니다:
function buildLocaleFromUserInput(language, region, preferences) {
const options = {};
if (preferences.calendar) {
options.calendar = preferences.calendar;
}
if (preferences.numberingSystem) {
options.numberingSystem = preferences.numberingSystem;
}
if (preferences.hourCycle) {
options.hourCycle = preferences.hourCycle;
}
const locale = new Intl.Locale(`${language}-${region}`, options);
return locale.toString();
}
const userLocale = buildLocaleFromUserInput("th", "TH", {
calendar: "buddhist",
numberingSystem: "thai",
hourCycle: "h23"
});
console.log(userLocale);
// 출력: "th-TH-u-ca-buddhist-hc-h23-nu-thai"
종교적 달력 존중
종교 공동체를 위한 애플리케이션은 해당 달력 시스템을 지원해야 합니다:
function createReligiousCalendarFormatter(religion, baseLocale) {
const calendars = {
jewish: "hebrew",
muslim: "islamic",
buddhist: "buddhist"
};
const calendar = calendars[religion];
if (!calendar) {
return new Intl.DateTimeFormat(baseLocale);
}
const locale = new Intl.Locale(baseLocale, { calendar });
return new Intl.DateTimeFormat(locale, {
year: "numeric",
month: "long",
day: "numeric"
});
}
const jewishFormatter = createReligiousCalendarFormatter("jewish", "en-US");
console.log(jewishFormatter.format(new Date("2025-03-15")));
// 출력: "15 Adar II 5785"
사용자 정의 규칙으로 정렬
로케일별 정렬을 구현하기 위해 콜레이션 확장을 사용합니다:
function sortNames(names, locale, collationType) {
const localeWithCollation = new Intl.Locale(locale, {
collation: collationType
});
const collator = new Intl.Collator(localeWithCollation);
return names.sort((a, b) => collator.compare(a, b));
}
const germanNames = ["Müller", "Meyer", "Möller", "Mueller"];
const sorted = sortNames(germanNames, "de-DE", "phonebk");
console.log(sorted);
전통적인 숫자 표시하기
문화적으로 적절한 표시를 위해 전통적인 숫자 체계로 숫자를 표시합니다:
function formatTraditionalNumber(number, locale, numberingSystem) {
const localeWithNumbering = new Intl.Locale(locale, {
numberingSystem
});
return new Intl.NumberFormat(localeWithNumbering).format(number);
}
console.log(formatTraditionalNumber(123456, "ar-EG", "arab"));
// 출력: "١٢٣٬٤٥٦"
console.log(formatTraditionalNumber(123456, "th-TH", "thai"));
// 출력: "๑๒๓,๔๕๖"
브라우저 지원
유니코드 확장은 모든 최신 브라우저에서 작동합니다. Chrome, Firefox, Safari 및 Edge는 로케일 식별자의 확장 구문과 Intl.Locale 객체의 해당 속성을 지원합니다.
특정 확장 값의 가용성은 브라우저 구현에 따라 다릅니다. 모든 브라우저는 달력의 gregory, 숫자 체계의 latn, 시간 주기의 h12 또는 h23와 같은 일반적인 값을 지원합니다. 전통적인 중국 달력이나 소수 언어 숫자 체계와 같은 덜 일반적인 값은 모든 브라우저에서 작동하지 않을 수 있습니다.
덜 일반적인 확장 값을 사용할 때는 대상 브라우저에서 로케일 식별자를 테스트하세요. 브라우저가 확장 값을 인식했는지 확인하려면 Intl.Locale 속성을 사용하세요:
const locale = new Intl.Locale("zh-CN-u-ca-chinese");
console.log(locale.calendar);
// 브라우저가 중국 달력을 지원하는 경우: "chinese"
// 브라우저가 지원하지 않는 경우: undefined
Node.js는 버전 12부터 유니코드 확장을 지원하며, 버전 18 이상에서는 모든 속성에 대한 완전한 지원을 제공합니다.
요약
유니코드 확장을 사용하면 로케일 식별자에 서식 지정 기본 설정을 추가할 수 있습니다. 각 포맷터를 개별적으로 구성하는 대신, 로케일 문자열에 기본 설정을 한 번에 인코딩할 수 있습니다.
주요 개념:
- 확장은
-u-로 시작하고 그 뒤에 키-값 쌍이 옵니다 ca키는 달력 시스템을 지정합니다nu키는 숫자 체계를 지정합니다hc키는 시간 주기 형식을 지정합니다co키는 정렬 규칙을 지정합니다kf키는 대소문자 우선 순서를 지정합니다kn키는 숫자 정렬을 활성화합니다- 확장 문자열 또는 옵션 객체를 사용할 수 있습니다
- 확장은 포맷터 옵션이 재정의할 수 있는 기본값으로 작동합니다
Intl.Locale객체는 확장을 속성으로 노출합니다
사용자 기본 설정 저장, 문화적 달력 존중, 전통적인 숫자 표시 및 로케일별 정렬 구현을 위해 유니코드 확장을 사용하세요. 이는 JavaScript 국제화 코드에서 서식 지정 동작을 사용자 정의하는 표준 방법을 제공합니다.