ICU MessageFormat

Category
OpenSource
Status
Published
Tags
국제화
ICU
Description
Published
Slug

ICU MessageFormat이란

ICU MessageFormat은 Unicode Consortium에서 개발한 국제화(i18n) 표준
다양한 언어의 복잡한 문법 규칙을 처리할 수 있는 메시지 포맷 시스템
 

왜 필요한가

일반적인 문자열 치환으로는 해결할 수 없는 언어별 문법 차이 처리
// 단순한 방식 (문제가 많음) function getMessage(count) { if (count === 1) { return count + " file found"; } else { return count + " files found"; } }
이 방식의 문제점
  • 영어 중심적 사고
  • 러시아어, 폴란드어 등은 복수형 규칙이 3-4가지
  • 아랍어는 6가지 복수형 규칙
  • 중국어, 일본어는 복수형 구분이 없음
 

ICU MessageFormat 문법 구조

기본 문법

// 변수 삽입 "Hello {name}" // 복수형 처리 "{count, plural, =0 {no items} =1 {one item} other {# items}}" // 선택형 처리 "{gender, select, male {He} female {She} other {They}} went to the store" // 중첩 구조 "{count, plural, =0 {no files} =1 {one file} other {{count, number} files}}"

Lighthouse에서 사용하는 패턴

const UIStrings = { // 내가 추가한 커스텀 프로퍼티 메시지 unsupportedCustomCSSProperty: `{propertyCount, plural, =1 {Custom CSS properties cannot be animated on the compositor: {properties}} other {Custom CSS properties cannot be animated on the compositor: {properties}} }`, // 기존 일반 프로퍼티 메시지 unsupportedCSSProperty: `{propertyCount, plural, =1 {Unsupported CSS Property: {properties}} other {Unsupported CSS Properties: {properties}} }`, };

복수형 처리 세부 분석

영어 복수형 규칙

// 영어의 단순한 복수형 {count, plural, =0 {no files} // 정확히 0개 =1 {one file} // 정확히 1개 other {# files} // 그 외 모든 수 (2, 3, 4, ...) }

Lighthouse i18n 시스템 구현

메시지 생성 과정

// core/lib/i18n/i18n.js function createIcuMessageFn(filename, UIStrings) { return function str_(id, values = {}) { const message = UIStrings[id]; // ICU MessageFormat 파싱 및 실행 const formatter = new IntlMessageFormat(message, locale); const formattedString = formatter.format(values); return { i18nId: `${filename} | ${id}`, values: values, formattedDefault: formattedString }; }; }

다국어 파일 생성 과정

// build/build-locale-files.js function buildLocaleFiles() { const enUSStrings = collectAllUIStrings(); // 각 audit에서 UIStrings 수집 const auditStrings = { 'core/audits/non-composited-animations.js | unsupportedCSSProperty': { message: '{propertyCount, plural, =1 {...} other {...}}', description: 'Error message for unsupported CSS properties' } }; // 번역가들을 위한 XLIFF 파일 생성 generateXLIFFFiles(auditStrings); }

런타임 다국어 처리

// 사용자 브라우저 언어 감지 function getLocale() { const browserLocale = navigator.language || navigator.userLanguage; const supportedLocales = ['en-US', 'ko', 'ja', 'de', ...]; // 지원하는 가장 가까운 언어 찾기 return findBestMatch(browserLocale, supportedLocales); } // 실제 메시지 포맷팅 function formatMessage(messageObj, locale) { const {i18nId, values, formattedDefault} = messageObj; if (locale === 'en-US') { return formattedDefault; // 기본 영어 } // 번역된 메시지 로드 const translatedMessage = loadTranslation(i18nId, locale); const formatter = new IntlMessageFormat(translatedMessage, locale); return formatter.format(values); }

구현한 메시지 다국어 동작

실행 시 동작 과정

// 1. str_ 함수 호출 str_(UIStrings.unsupportedCustomCSSProperty, { propertyCount: 2, properties: '--swing-y, --rotation' }); // 2. ICU MessageFormat 파싱 const message = `{propertyCount, plural, =1 {Custom CSS properties cannot be animated on the compositor: {properties}} other {Custom CSS properties cannot be animated on the compositor: {properties}} }`; // 3. 값 대입 및 복수형 선택 // propertyCount = 2 이므로 'other' 선택 // properties = '--swing-y, --rotation' 대입 // 4. 최종 결과 "Custom CSS properties cannot be animated on the compositor: --swing-y, --rotation"

한국어 번역 시 동작

// shared/localization/locales/ko.json (예상) { "core/audits/non-composited-animations.js | unsupportedCustomCSSProperty": { "message": "{propertyCount, plural, other {컴포지터에서 애니메이션할 수 없는 커스텀 CSS 속성: {properties}}}" } }
한국어는 복수형 구분이 없으므로 'other'만 사용합니다.

 

ICU vs 다른 i18n 방식 비교

Template 방식 (React i18next 등)

// 단순한 템플릿 방식 t('message', {count: 5}) // "You have {{count}} messages" → "You have 5 messages"
한계: 복수형 처리가 각 언어마다 하드코딩되어야 함

ICU MessageFormat 방식

// ICU 방식 "{count, plural, =0 {no messages} =1 {one message} other {# messages}}"
장점
  • 언어별 복수형 규칙이 런타임에 자동 적용
  • CLDR(Common Locale Data Repository) 기반 정확한 언어학적 규칙