[EXT-016][실무] 사용자에게 맞는 표시 언어 고르기
페이지 정보

본문
[이번 수업]
여러 언어를 지원하려면 표시할 문구 묶음을 골라야 합니다. 이번에는 언어 태그(language tag, 언어와 지역 등을 나타내는 표준 표기)를 확인하고 저장된 선택과 실행 환경의 선호 언어를 순서대로 적용합니다.
[선수지식]
JavaScript 객체·함수와 Node.js 실행 방법을 알아야 합니다. 지역별 숫자 표시는 EXT-015에서 연습할 수 있습니다.
[학습목표]
1. BCP 47 언어 태그를 설명한다.
2. 사용자 선택, 환경 선호, 기본값의 우선순위를 구현한다.
3. 선택 결과를 허용 목록과 HTML lang 속성에 연결한다.
[핵심개념]
ko, en-US, zh-Hant-TW는 BCP 47 언어 태그입니다. 하이픈으로 나뉜 하위 태그가 언어, 문자 체계, 지역 등을 구체화합니다. en_US처럼 밑줄을 쓴 값을 임의로 만들지 마세요. Intl.Locale은 태그를 검사하고 language 같은 요소를 읽습니다. 잘못된 태그에는 RangeError가 날 수 있어 외부 입력은 예외 처리해야 합니다.
선택 정책은 표준이 대신 정하지 않습니다. 이 수업에서는 사용자가 저장한 선택, 브라우저 선호 목록, 서비스 기본값 순으로 적용합니다. HTTP Accept-Language에는 여러 언어 범위와 q 가중치가 들어갈 수 있으므로 첫 쉼표 앞만 잘라 쓰지 마세요. 서버에서는 검증된 파서를 쓰고, 지원 태그 하나를 고를 때는 RFC 4647의 lookup 방식을 적용할 수 있습니다.
[따라하기]
language_picker.js 파일에 다음 코드를 저장합니다. 지역별 번역이 따로 없는 서비스의 예입니다.
```javascript
const messages = {
ko: { hello: "안녕하세요" },
en: { hello: "Hello" },
ja: { hello: "こんにちは" },
};
function baseLanguage(tag) {
if (!tag) return null;
try {
return new Intl.Locale(tag).language;
} catch {
return null;
}
}
function chooseLanguage(saved, requested, fallback = "ko") {
for (const tag of [saved, ...requested]) {
const language = baseLanguage(tag);
if (language && Object.hasOwn(messages, language)) return language;
}
return fallback;
}
const browserChoice = chooseLanguage(null, ["ja-JP", "en-US"]);
const savedChoice = chooseLanguage("ko-KR", ["ja-JP"]);
console.log(browserChoice);
console.log(savedChoice);
console.log(messages[savedChoice].hello);
```
Windows PowerShell·명령 프롬프트, macOS 터미널, Linux 셸에서 같은 명령을 실행합니다.
```text
node language_picker.js
```
예상 결과입니다.
```text
ja
ko
안녕하세요
```
웹에서는 결과를 실제 문서 언어와 맞춰 `<html lang="ko">`처럼 설정하세요. 보조기술과 검색 도구가 콘텐츠 언어를 판단할 때 사용합니다. 지역별 문구가 따로 있다면 기본 언어만 꺼내지 말고, 전체 태그를 포함한 RFC 4647 lookup이나 검증된 국제화 라이브러리를 사용하세요.
[흔한 실수]
첫째, IP 주소나 시간대로 언어를 확정합니다. 위치와 읽는 언어는 같지 않습니다. 둘째, 번역이 없는 태그를 선택해 빈 화면을 만듭니다. 지원 목록과 기본값이 필요합니다. 셋째, 문구는 한국어인데 lang을 en으로 둡니다. 넷째, 외부 태그를 검증 없이 파일 이름에 붙입니다.
[보안 주의]
언어 태그는 인증·권한·국적을 뜻하지 않습니다. 외부 태그를 파일 경로로 조합하지 말고 코드의 허용 목록에서 번역을 찾으세요. Accept-Language는 사용자 선호를 포함하므로 필요 이상으로 보관하지 않습니다. 번역 문자열을 HTML로 출력할 때도 이스케이프가 필요합니다. 실습은 본인 소유의 로컬 파일에서만 진행합니다.
[직접 해볼 과제]
messages에서 ja를 제거하고 요청 목록이 ["ja-JP", "en-GB"]일 때 en이 선택되는지 확인하세요. 잘못된 태그 bad_tag를 saved에 넣어도 중단되지 않는지 확인하고, 기본값이 선택되는 사례도 만드세요.
[확인문제]
1. 저장된 사용자 선택을 환경 선호보다 먼저 적용하는 이유는 무엇인가요?
2. Accept-Language를 첫 쉼표 기준으로만 자르면 왜 잘못될 수 있나요?
3. 외부 언어 태그를 번역 파일 경로에 바로 붙이면 어떤 위험이 있나요?
[다음 학습]
다음에는 언어마다 달라지는 단수·복수 규칙을 Intl.PluralRules로 처리합니다.
[공식 참고 자료]
IETF BCP 47 언어 태그(RFC 5646)
https://www.rfc-editor.org/rfc/rfc5646.html
IETF 언어 태그 매칭(RFC 4647)
https://www.rfc-editor.org/rfc/rfc4647.html
HTTP Accept-Language(RFC 9110)
https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.4
ECMA-402 Locale 객체
https://tc39.es/ecma402/#locale-objects
WHATWG HTML lang 속성
https://html.spec.whatwg.org/multipage/dom.html#the-lang-and-xml:lang-attributes
여러 언어를 지원하려면 표시할 문구 묶음을 골라야 합니다. 이번에는 언어 태그(language tag, 언어와 지역 등을 나타내는 표준 표기)를 확인하고 저장된 선택과 실행 환경의 선호 언어를 순서대로 적용합니다.
[선수지식]
JavaScript 객체·함수와 Node.js 실행 방법을 알아야 합니다. 지역별 숫자 표시는 EXT-015에서 연습할 수 있습니다.
[학습목표]
1. BCP 47 언어 태그를 설명한다.
2. 사용자 선택, 환경 선호, 기본값의 우선순위를 구현한다.
3. 선택 결과를 허용 목록과 HTML lang 속성에 연결한다.
[핵심개념]
ko, en-US, zh-Hant-TW는 BCP 47 언어 태그입니다. 하이픈으로 나뉜 하위 태그가 언어, 문자 체계, 지역 등을 구체화합니다. en_US처럼 밑줄을 쓴 값을 임의로 만들지 마세요. Intl.Locale은 태그를 검사하고 language 같은 요소를 읽습니다. 잘못된 태그에는 RangeError가 날 수 있어 외부 입력은 예외 처리해야 합니다.
선택 정책은 표준이 대신 정하지 않습니다. 이 수업에서는 사용자가 저장한 선택, 브라우저 선호 목록, 서비스 기본값 순으로 적용합니다. HTTP Accept-Language에는 여러 언어 범위와 q 가중치가 들어갈 수 있으므로 첫 쉼표 앞만 잘라 쓰지 마세요. 서버에서는 검증된 파서를 쓰고, 지원 태그 하나를 고를 때는 RFC 4647의 lookup 방식을 적용할 수 있습니다.
[따라하기]
language_picker.js 파일에 다음 코드를 저장합니다. 지역별 번역이 따로 없는 서비스의 예입니다.
```javascript
const messages = {
ko: { hello: "안녕하세요" },
en: { hello: "Hello" },
ja: { hello: "こんにちは" },
};
function baseLanguage(tag) {
if (!tag) return null;
try {
return new Intl.Locale(tag).language;
} catch {
return null;
}
}
function chooseLanguage(saved, requested, fallback = "ko") {
for (const tag of [saved, ...requested]) {
const language = baseLanguage(tag);
if (language && Object.hasOwn(messages, language)) return language;
}
return fallback;
}
const browserChoice = chooseLanguage(null, ["ja-JP", "en-US"]);
const savedChoice = chooseLanguage("ko-KR", ["ja-JP"]);
console.log(browserChoice);
console.log(savedChoice);
console.log(messages[savedChoice].hello);
```
Windows PowerShell·명령 프롬프트, macOS 터미널, Linux 셸에서 같은 명령을 실행합니다.
```text
node language_picker.js
```
예상 결과입니다.
```text
ja
ko
안녕하세요
```
웹에서는 결과를 실제 문서 언어와 맞춰 `<html lang="ko">`처럼 설정하세요. 보조기술과 검색 도구가 콘텐츠 언어를 판단할 때 사용합니다. 지역별 문구가 따로 있다면 기본 언어만 꺼내지 말고, 전체 태그를 포함한 RFC 4647 lookup이나 검증된 국제화 라이브러리를 사용하세요.
[흔한 실수]
첫째, IP 주소나 시간대로 언어를 확정합니다. 위치와 읽는 언어는 같지 않습니다. 둘째, 번역이 없는 태그를 선택해 빈 화면을 만듭니다. 지원 목록과 기본값이 필요합니다. 셋째, 문구는 한국어인데 lang을 en으로 둡니다. 넷째, 외부 태그를 검증 없이 파일 이름에 붙입니다.
[보안 주의]
언어 태그는 인증·권한·국적을 뜻하지 않습니다. 외부 태그를 파일 경로로 조합하지 말고 코드의 허용 목록에서 번역을 찾으세요. Accept-Language는 사용자 선호를 포함하므로 필요 이상으로 보관하지 않습니다. 번역 문자열을 HTML로 출력할 때도 이스케이프가 필요합니다. 실습은 본인 소유의 로컬 파일에서만 진행합니다.
[직접 해볼 과제]
messages에서 ja를 제거하고 요청 목록이 ["ja-JP", "en-GB"]일 때 en이 선택되는지 확인하세요. 잘못된 태그 bad_tag를 saved에 넣어도 중단되지 않는지 확인하고, 기본값이 선택되는 사례도 만드세요.
[확인문제]
1. 저장된 사용자 선택을 환경 선호보다 먼저 적용하는 이유는 무엇인가요?
2. Accept-Language를 첫 쉼표 기준으로만 자르면 왜 잘못될 수 있나요?
3. 외부 언어 태그를 번역 파일 경로에 바로 붙이면 어떤 위험이 있나요?
[다음 학습]
다음에는 언어마다 달라지는 단수·복수 규칙을 Intl.PluralRules로 처리합니다.
[공식 참고 자료]
IETF BCP 47 언어 태그(RFC 5646)
https://www.rfc-editor.org/rfc/rfc5646.html
IETF 언어 태그 매칭(RFC 4647)
https://www.rfc-editor.org/rfc/rfc4647.html
HTTP Accept-Language(RFC 9110)
https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.4
ECMA-402 Locale 객체
https://tc39.es/ecma402/#locale-objects
WHATWG HTML lang 속성
https://html.spec.whatwg.org/multipage/dom.html#the-lang-and-xml:lang-attributes
- 이전글[EXT-017][실무] 수량에 맞는 문구 고르기 26.09.08
- 다음글[EXT-015][실무] 언어와 지역에 맞게 숫자 표시하기 26.09.08
댓글목록
등록된 댓글이 없습니다.
