로케일 식별자를 표준 형식으로 정규화하는 방법
로케일 식별자를 올바른 대소문자 및 구성 요소 순서로 정규 형식으로 변환
소개
로케일 식별자는 동일한 언어와 지역을 나타내면서도 여러 가지 방식으로 작성될 수 있습니다. 사용자는 EN-us, en-US, 또는 en-us로 작성할 수 있으며, 이 세 가지 모두 미국 영어를 나타냅니다. 로케일 식별자를 저장, 비교 또는 표시할 때 이러한 변형은 일관성 문제를 야기합니다.
정규화는 로케일 식별자를 표준 정규 형식으로 변환합니다. 이 프로세스는 구성 요소의 대소문자를 조정하고, 확장 키워드를 알파벳순으로 정렬하며, 애플리케이션 전체에서 신뢰할 수 있는 일관된 표현을 생성합니다.
JavaScript는 로케일 식별자를 자동으로 정규화하는 내장 메서드를 제공합니다. 이 가이드는 정규화의 의미, 코드에서 적용하는 방법, 그리고 정규화된 식별자가 국제화 로직을 개선하는 시점을 설명합니다.
로케일 식별자에 대한 정규화의 의미
정규화는 BCP 47 표준 및 유니코드 사양에 따라 로케일 식별자를 정규 형식으로 변환합니다. 정규 형식은 대소문자, 순서 및 구조에 대한 특정 규칙을 가지고 있습니다.
정규화된 로케일 식별자는 다음 규칙을 따릅니다:
- 언어 코드는 소문자
- 스크립트 코드는 첫 글자가 대문자인 타이틀 케이스
- 지역 코드는 대문자
- 변형 코드는 소문자
- 확장 키워드는 알파벳순으로 정렬
- 확장 속성은 알파벳순으로 정렬
이러한 규칙은 각 로케일에 대해 단일 표준 표현을 생성합니다. 사용자가 로케일 식별자를 어떻게 작성하든 정규화된 형식은 항상 동일합니다.
정규화 규칙 이해하기
로케일 식별자의 각 구성 요소는 정규 형식에서 특정 대소문자 규칙을 가지고 있습니다.
언어 대소문자 표기
언어 코드는 항상 소문자를 사용합니다:
en (correct)
EN (incorrect, but normalizes to en)
eN (incorrect, but normalizes to en)
이는 2자리 및 3자리 언어 코드 모두에 적용됩니다.
스크립트 대소문자 표기
스크립트 코드는 첫 글자는 대문자로, 나머지 세 글자는 소문자로 표기하는 타이틀 케이스를 사용합니다:
Hans (correct)
hans (incorrect, but normalizes to Hans)
HANS (incorrect, but normalizes to Hans)
일반적인 스크립트 코드에는 라틴 문자를 나타내는 Latn, 키릴 문자를 나타내는 Cyrl, 간체 한자를 나타내는 Hans, 번체 한자를 나타내는 Hant가 있습니다.
지역 대소문자 표기
지역 코드는 항상 대문자를 사용합니다:
US (correct)
us (incorrect, but normalizes to US)
Us (incorrect, but normalizes to US)
이는 대부분의 로케일 식별자에서 사용되는 2자리 국가 코드에 적용됩니다.
확장 순서
유니코드 확장 태그는 형식 기본 설정을 지정하는 키워드를 포함합니다. 정규 형식에서는 이러한 키워드가 키를 기준으로 알파벳 순서로 표시됩니다:
en-US-u-ca-gregory-nu-latn (correct)
en-US-u-nu-latn-ca-gregory (incorrect, but normalizes to first form)
달력 키 ca는 알파벳순으로 숫자 체계 키 nu보다 앞에 오므로, 정규화된 형식에서 ca-gregory가 먼저 나타납니다.
Intl.getCanonicalLocales를 사용한 정규화
Intl.getCanonicalLocales() 메서드는 로케일 식별자를 정규화하고 표준 형식으로 반환합니다. 이는 JavaScript에서 정규화를 위한 주요 메서드입니다.
const normalized = Intl.getCanonicalLocales("EN-us");
console.log(normalized);
// ["en-US"]
이 메서드는 모든 대소문자 표기의 로케일 식별자를 받아 적절한 대소문자 표기의 정규 형식을 반환합니다.
언어 코드 정규화
이 메서드는 언어 코드를 소문자로 변환합니다:
const result = Intl.getCanonicalLocales("FR-fr");
console.log(result);
// ["fr-FR"]
언어 코드 FR는 출력에서 fr로 변환됩니다.
스크립트 코드 정규화
이 메서드는 스크립트 코드를 타이틀 케이스로 변환합니다:
const result = Intl.getCanonicalLocales("zh-HANS-cn");
console.log(result);
// ["zh-Hans-CN"]
스크립트 코드 HANS는 Hans로, 지역 코드 cn는 CN로 변환됩니다.
지역 코드 정규화
이 메서드는 지역 코드를 대문자로 변환합니다:
const result = Intl.getCanonicalLocales("en-gb");
console.log(result);
// ["en-GB"]
지역 코드 gb는 출력에서 GB로 변환됩니다.
확장 키워드 정규화
이 메서드는 확장 키워드를 알파벳순으로 정렬합니다:
const result = Intl.getCanonicalLocales("en-US-u-nu-latn-hc-h12-ca-gregory");
console.log(result);
// ["en-US-u-ca-gregory-hc-h12-nu-latn"]
키워드는 nu-latn-hc-h12-ca-gregory에서 ca-gregory-hc-h12-nu-latn로 재정렬됩니다. 이는 알파벳순으로 ca가 hc보다 앞에 오고, hc가 nu보다 앞에 오기 때문입니다.
여러 로케일 식별자 정규화
Intl.getCanonicalLocales() 메서드는 로케일 식별자 배열을 받아 모두 정규화합니다:
const locales = ["EN-us", "fr-FR", "ZH-hans-cn"];
const normalized = Intl.getCanonicalLocales(locales);
console.log(normalized);
// ["en-US", "fr-FR", "zh-Hans-CN"]
배열의 각 로케일은 정규 형식으로 변환됩니다.
중복 제거
이 메서드는 정규화 후 중복된 로케일 식별자를 제거합니다. 여러 입력 값이 동일한 정규 형식으로 정규화되면 결과에는 하나의 복사본만 포함됩니다:
const locales = ["en-US", "EN-us", "en-us"];
const normalized = Intl.getCanonicalLocales(locales);
console.log(normalized);
// ["en-US"]
세 입력 모두 동일한 로케일을 나타내므로 출력에는 단일 정규화된 식별자가 포함됩니다.
이 중복 제거는 사용자 입력을 처리하거나 여러 소스의 로케일 목록을 병합할 때 유용합니다.
잘못된 식별자 처리
배열의 로케일 식별자가 유효하지 않은 경우, 메서드는 RangeError를 발생시킵니다:
try {
Intl.getCanonicalLocales(["en-US", "invalid", "fr-FR"]);
} catch (error) {
console.error(error.message);
// "invalid is not a structurally valid language tag"
}
사용자가 제공한 목록을 정규화할 때는 각 로케일을 개별적으로 검증하거나 오류를 포착하여 어떤 특정 식별자가 잘못되었는지 식별하세요.
정규화를 위한 Intl.Locale 사용
Intl.Locale 생성자는 로케일 객체를 생성할 때 로케일 식별자를 정규화합니다. toString() 메서드를 통해 정규화된 형식에 액세스할 수 있습니다.
const locale = new Intl.Locale("EN-us");
console.log(locale.toString());
// "en-US"
생성자는 유효한 대소문자를 모두 허용하며 정규화된 로케일 객체를 생성합니다.
정규화된 구성 요소 액세스
로케일 객체의 각 속성은 해당 구성 요소의 정규화된 형식을 반환합니다.
const locale = new Intl.Locale("ZH-hans-CN");
console.log(locale.language);
// "zh"
console.log(locale.script);
// "Hans"
console.log(locale.region);
// "CN"
console.log(locale.baseName);
// "zh-Hans-CN"
language, script, region 속성은 모두 정규 형식에 올바른 대소문자를 사용합니다.
옵션을 사용한 정규화
옵션과 함께 로케일 객체를 생성하면 생성자는 기본 식별자와 옵션을 모두 정규화합니다.
const locale = new Intl.Locale("EN-us", {
calendar: "gregory",
numberingSystem: "latn",
hourCycle: "h12"
});
console.log(locale.toString());
// "en-US-u-ca-gregory-hc-h12-nu-latn"
옵션 객체가 특정 순서를 지정하지 않더라도 확장 키워드는 출력에서 알파벳 순서로 나타납니다.
정규화가 중요한 이유
정규화는 애플리케이션 전체에서 일관성을 제공합니다. 로케일 식별자를 저장, 표시 또는 비교할 때 정규 형식을 사용하면 미묘한 버그를 방지하고 안정성을 향상시킵니다.
일관된 저장
데이터베이스, 구성 파일 또는 로컬 스토리지에 로케일 식별자를 저장할 때 정규화된 형식은 중복을 방지합니다.
const userPreferences = new Set();
function saveUserLocale(identifier) {
const normalized = Intl.getCanonicalLocales(identifier)[0];
userPreferences.add(normalized);
}
saveUserLocale("en-US");
saveUserLocale("EN-us");
saveUserLocale("en-us");
console.log(userPreferences);
// Set { "en-US" }
정규화가 없으면 세트에는 동일한 로케일에 대한 세 개의 항목이 포함됩니다. 정규화를 사용하면 올바르게 하나만 포함됩니다.
안정적인 비교
로케일 식별자를 비교하려면 정규화가 필요합니다. 대소문자만 다른 두 식별자는 동일한 로케일을 나타냅니다.
function isSameLocale(locale1, locale2) {
const normalized1 = Intl.getCanonicalLocales(locale1)[0];
const normalized2 = Intl.getCanonicalLocales(locale2)[0];
return normalized1 === normalized2;
}
console.log(isSameLocale("en-US", "EN-us"));
// true
console.log(isSameLocale("en-US", "en-GB"));
// false
정규화되지 않은 식별자의 직접 문자열 비교는 잘못된 결과를 생성합니다.
일관된 표시
사용자에게 로케일 식별자를 표시하거나 디버깅 출력에서 표시할 때 정규화된 형식은 일관된 형식을 제공합니다.
function displayLocale(identifier) {
try {
const normalized = Intl.getCanonicalLocales(identifier)[0];
return `Current locale: ${normalized}`;
} catch (error) {
return "Invalid locale identifier";
}
}
console.log(displayLocale("EN-us"));
// "Current locale: en-US"
console.log(displayLocale("zh-HANS-cn"));
// "Current locale: zh-Hans-CN"
사용자는 입력 형식에 관계없이 올바르게 형식화된 로케일 식별자를 볼 수 있습니다.
실용적인 활용 사례
정규화는 실제 애플리케이션에서 로케일 식별자를 다룰 때 발생하는 일반적인 문제를 해결합니다.
사용자 입력 정규화
사용자가 폼이나 설정에서 로케일 식별자를 입력할 때, 저장하기 전에 입력값을 정규화하세요:
function processLocaleInput(input) {
try {
const normalized = Intl.getCanonicalLocales(input)[0];
return {
success: true,
locale: normalized
};
} catch (error) {
return {
success: false,
error: "Please enter a valid locale identifier"
};
}
}
const result = processLocaleInput("fr-ca");
console.log(result);
// { success: true, locale: "fr-CA" }
이를 통해 데이터베이스나 구성 파일에서 일관된 형식을 보장할 수 있습니다.
로케일 조회 테이블 구축
번역이나 로케일별 데이터를 위한 조회 테이블을 생성할 때, 정규화된 키를 사용하세요:
const translations = new Map();
function addTranslation(locale, key, value) {
const normalized = Intl.getCanonicalLocales(locale)[0];
if (!translations.has(normalized)) {
translations.set(normalized, {});
}
translations.get(normalized)[key] = value;
}
addTranslation("en-us", "hello", "Hello");
addTranslation("EN-US", "goodbye", "Goodbye");
console.log(translations.get("en-US"));
// { hello: "Hello", goodbye: "Goodbye" }
addTranslation에 대한 두 호출 모두 동일한 정규화된 키를 사용하므로 번역이 동일한 객체에 저장됩니다.
로케일 목록 병합
여러 소스의 로케일 식별자를 결합할 때, 정규화하고 중복을 제거하세요:
function mergeLocales(...sources) {
const allLocales = sources.flat();
const normalized = Intl.getCanonicalLocales(allLocales);
return normalized;
}
const userLocales = ["en-us", "fr-FR"];
const appLocales = ["EN-US", "de-de"];
const systemLocales = ["en-US", "es-mx"];
const merged = mergeLocales(userLocales, appLocales, systemLocales);
console.log(merged);
// ["en-US", "fr-FR", "de-DE", "es-MX"]
이 메서드는 모든 소스에서 중복을 제거하고 대소문자를 정규화합니다.
로케일 선택 인터페이스 생성
드롭다운 메뉴나 선택 인터페이스를 구축할 때, 표시를 위해 로케일 식별자를 정규화하세요:
function buildLocaleOptions(locales) {
const normalized = Intl.getCanonicalLocales(locales);
return normalized.map(locale => {
const localeObj = new Intl.Locale(locale);
const displayNames = new Intl.DisplayNames([locale], {
type: "language"
});
return {
value: locale,
label: displayNames.of(localeObj.language)
};
});
}
const options = buildLocaleOptions(["EN-us", "fr-FR", "DE-de"]);
console.log(options);
// [
// { value: "en-US", label: "English" },
// { value: "fr-FR", label: "French" },
// { value: "de-DE", label: "German" }
// ]
정규화된 값은 폼 제출을 위한 일관된 식별자를 제공합니다.
구성 파일 검증
구성 파일에서 로케일 식별자를 로드할 때, 초기화 중에 정규화하세요:
function loadLocaleConfig(config) {
const validatedConfig = {
defaultLocale: null,
supportedLocales: []
};
try {
validatedConfig.defaultLocale = Intl.getCanonicalLocales(
config.defaultLocale
)[0];
} catch (error) {
console.error("Invalid default locale:", config.defaultLocale);
validatedConfig.defaultLocale = "en-US";
}
config.supportedLocales.forEach(locale => {
try {
const normalized = Intl.getCanonicalLocales(locale)[0];
validatedConfig.supportedLocales.push(normalized);
} catch (error) {
console.warn("Skipping invalid locale:", locale);
}
});
return validatedConfig;
}
const config = {
defaultLocale: "en-us",
supportedLocales: ["EN-us", "fr-FR", "invalid", "de-DE"]
};
const validated = loadLocaleConfig(config);
console.log(validated);
// {
// defaultLocale: "en-US",
// supportedLocales: ["en-US", "fr-FR", "de-DE"]
// }
이를 통해 구성 오류를 조기에 발견하고 애플리케이션이 유효한 정규화된 식별자를 사용하도록 보장할 수 있습니다.
정규화와 로케일 매칭
정규화는 로케일 매칭 알고리즘에 중요합니다. 사용자 선호도에 가장 적합한 로케일을 찾을 때, 정규화된 형식을 비교하세요:
function findBestMatch(userPreference, availableLocales) {
const normalizedPreference = Intl.getCanonicalLocales(userPreference)[0];
const normalizedAvailable = Intl.getCanonicalLocales(availableLocales);
if (normalizedAvailable.includes(normalizedPreference)) {
return normalizedPreference;
}
const preferenceLocale = new Intl.Locale(normalizedPreference);
const languageMatch = normalizedAvailable.find(available => {
const availableLocale = new Intl.Locale(available);
return availableLocale.language === preferenceLocale.language;
});
if (languageMatch) {
return languageMatch;
}
return normalizedAvailable[0];
}
const available = ["en-us", "fr-FR", "DE-de"];
console.log(findBestMatch("EN-GB", available));
// "en-US"
정규화는 입력 대소문자와 관계없이 매칭 로직이 올바르게 작동하도록 보장합니다.
정규화는 의미를 변경하지 않습니다
정규화는 로케일 식별자의 표현에만 영향을 미칩니다. 식별자가 나타내는 언어, 문자 체계 또는 지역을 변경하지 않습니다.
const locale1 = new Intl.Locale("en-us");
const locale2 = new Intl.Locale("EN-US");
console.log(locale1.language === locale2.language);
// true
console.log(locale1.region === locale2.region);
// true
console.log(locale1.toString() === locale2.toString());
// true
두 식별자 모두 미국 영어를 나타냅니다. 정규화는 단순히 동일한 방식으로 작성되도록 보장합니다.
이는 구성 요소를 추가하거나 제거하고 식별자의 특정성을 변경할 수 있는 maximize() 및 minimize()와 같은 작업과는 다릅니다.
브라우저 지원
Intl.getCanonicalLocales() 메서드는 모든 최신 브라우저에서 작동합니다. Chrome, Firefox, Safari, Edge가 완전히 지원합니다.
Node.js는 버전 9부터 Intl.getCanonicalLocales()를 지원하며, 버전 10 이상에서 완전히 지원합니다.
Intl.Locale 생성자와 정규화 동작은 Intl.Locale API를 지원하는 모든 브라우저에서 작동합니다. 여기에는 Chrome, Firefox, Safari, Edge의 최신 버전이 포함됩니다.
요약
정규화는 표준 대소문자 규칙을 적용하고 확장 키워드를 정렬하여 로케일 식별자를 정규 형식으로 변환합니다. 이를 통해 안정적으로 저장, 비교 및 표시할 수 있는 일관된 표현을 생성합니다.
주요 개념:
- 정규 형식은 언어에 소문자, 스크립트에 타이틀 케이스, 지역에 대문자를 사용합니다
- 확장 키워드는 정규 형식에서 알파벳순으로 정렬됩니다
Intl.getCanonicalLocales()메서드는 식별자를 정규화하고 중복을 제거합니다Intl.Locale생성자도 정규화된 출력을 생성합니다- 정규화는 로케일 식별자의 의미를 변경하지 않습니다
- 저장, 비교, 표시를 위해 정규화된 식별자를 사용하세요
정규화는 로케일 식별자를 다루는 모든 애플리케이션의 기본 작업입니다. 일관되지 않은 대소문자로 인한 버그를 방지하고 국제화 로직이 로케일 식별자를 안정적으로 처리하도록 보장합니다.