유니코드 확장을 사용하여 로케일을 사용자 지정하는 방법
로케일 식별자에 달력 시스템, 숫자 형식 및 시간 표시 기본 설정 추가
소개
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));
// Output: "٢٠ رمضان ١٤٤٦ هـ"
이는 이슬람력에 따라 날짜를 형식화합니다. 동일한 그레고리력 날짜가 이슬람력 체계에서는 다른 연도, 월, 일로 표시됩니다.
불교력은 태국에서 일반적으로 사용됩니다. 기원전 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")));
// Output: "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));
// Output: "١٢٣٬٤٥٦"
아라비아-인도 숫자는 서양 숫자와 다르게 보이지만 동일한 값을 나타냅니다. 숫자 123456은 ١٢٣٬٤٥٦으로 표시됩니다.
태국 숫자는 또 다른 예를 제공합니다:
const thaiLocale = new Intl.Locale("th-TH-u-nu-thai");
const formatter = new Intl.NumberFormat(thaiLocale);
console.log(formatter.format(123456));
// Output: "๑๒๓,๔๕๖"
많은 아랍어 로케일은 아라비아-인도 숫자와 라틴 숫자를 모두 지원합니다. 사용자는 개인 선호도나 상황에 따라 원하는 체계를 선택할 수 있습니다.
시간 주기 확장
hc 키는 시간 표시 방식을 지정합니다. 일부 지역에서는 AM 및 PM 표시가 있는 12시간 형식을 선호하는 반면, 다른 지역에서는 24시간 형식을 선호합니다. 시간 주기는 자정이 표시되는 방식도 결정합니다.
네 가지 시간 주기 값을 사용할 수 있습니다:
h12: 1-12시를 사용하며 자정은 12:00 AMh11: 0-11시를 사용하며 자정은 0:00 AMh23: 0-23시를 사용하며 자정은 0:00h24: 1-24시를 사용하며 자정은 24:00
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));
// Output: "12:30 AM"
console.log(new Intl.DateTimeFormat(japan11Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "0:30 AM"
console.log(new Intl.DateTimeFormat(europe23Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "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)));
// Output: ["Meyer", "Möller", "Mueller", "Müller"]
console.log(names.sort((a, b) => phonebook.compare(a, b)));
// Output: ["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와 collator와 함께 사용되는 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)));
// Output: ["APPLE", "Apple", "apple", "banana"]
console.log(words.sort((a, b) => lowerFirst.compare(a, b)));
// Output: ["apple", "Apple", "APPLE", "banana"]
대소문자 우선 순서는 단어가 대소문자를 제외하고 동일할 때 조합에 영향을 미칩니다. 기본 문자를 비교한 후 보조 정렬 순서를 결정합니다.
숫자 조합 확장
kn 키는 숫자 대조를 활성화하여 숫자 시퀀스를 사전순이 아닌 숫자 값으로 정렬합니다. 숫자 대조가 없으면 "10"이 "2"보다 먼저 정렬되는데, 이는 문자 순서에서 "1"이 "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)));
// Output: ["item1", "item10", "item2", "item20"]
console.log(items.sort((a, b) => numeric.compare(a, b)));
// Output: ["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());
// Output: "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"));
// Output: { 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));
// Output uses Islamic calendar, Arabic-Indic numerals, and 12-hour time
각 확장 키는 로케일 문자열에서 한 번만 나타날 수 있습니다. 동일한 키를 여러 번 지정하면 마지막 값이 우선합니다.
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);
// Output: "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")));
// Output: "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"));
// Output: "١٢٣٬٤٥٦"
console.log(formatTraditionalNumber(123456, "th-TH", "thai"));
// Output: "๑๒๓,๔๕๖"
브라우저 지원
유니코드 확장은 모든 최신 브라우저에서 작동합니다. 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);
// If browser supports Chinese calendar: "chinese"
// If browser does not support it: undefined
Node.js는 버전 12부터 유니코드 확장을 지원하며, 버전 18 이상에서 모든 속성을 완전히 지원합니다.
요약
유니코드 확장을 사용하면 로케일 식별자에 형식 기본 설정을 추가할 수 있습니다. 각 포매터를 개별적으로 구성하는 대신 로케일 문자열에 기본 설정을 한 번만 인코딩하면 됩니다.
주요 개념:
- 확장은
-u-로 시작하고 키-값 쌍이 뒤따릅니다 ca키는 달력 체계를 지정합니다nu키는 숫자 체계를 지정합니다hc키는 시간 주기 형식을 지정합니다co키는 정렬 규칙을 지정합니다kf키는 대소문자 우선 순서를 지정합니다kn키는 숫자 정렬을 활성화합니다- 확장 문자열 또는 옵션 객체를 사용할 수 있습니다
- 확장은 포매터 옵션이 재정의할 수 있는 기본값으로 작동합니다
Intl.Locale객체는 확장을 속성으로 노출합니다
유니코드 확장을 사용하여 사용자 기본 설정을 저장하고, 문화적 달력을 존중하며, 전통 숫자를 표시하고, 로케일별 정렬을 구현할 수 있습니다. 이는 JavaScript 국제화 코드에서 형식 동작을 사용자 정의하는 표준 방법을 제공합니다.