i18n — SvelteKit에서 다국어 지원 구현하기
글로벌 서비스를 만들려면 i18n은 선택이 아닌 필수입니다.
개념 정의
i18n(Internationalization) 은 애플리케이션이 여러 언어와 지역을 지원할 수 있도록 설계하는 것입니다. SvelteKit에서는 라우팅 기반 로케일 감지와 번역 파일 관리가 핵심입니다.
기본 구조 — 수동 구현
// src/lib/i18n/translations.js
export const translations = {
ko: {
'nav.home': '홈',
'nav.about': '소개',
'greeting': '안녕하세요, {name}님!',
'items.count': '{count}개의 항목',
},
en: {
'nav.home': 'Home',
'nav.about': 'About',
'greeting': 'Hello, {name}!',
'items.count': '{count} items',
},
};
export function t(locale, key, params = {}) {
let text = translations[locale]?.[key] ?? key;
Object.entries(params).forEach(([k, v]) => {
text = text.replace(`{${k}}`, v);
});
return text;
}
라우팅 기반 로케일
src/routes/
├── [[lang]]/ # 선택적 매개변수
│ ├── +layout.js
│ ├── +page.svelte # / 또는 /ko 또는 /en
│ ├── about/
│ │ └── +page.svelte # /about 또는 /ko/about
// src/routes/[[lang]]/+layout.js
const supportedLocales = ['ko', 'en'];
const defaultLocale = 'ko';
export async function load({ params }) {
const lang = supportedLocales.includes(params.lang)
? params.lang
: defaultLocale;
return { lang };
}
<!-- src/routes/[[lang]]/+layout.svelte -->
<script>
import { setContext } from 'svelte';
import { t } from '$lib/i18n/translations';
let { data, children } = $props();
const lang = data.lang;
// 컨텍스트로 번역 함수 제공
setContext('i18n', {
t: (key, params) => t(lang, key, params),
lang,
});
</script>
<nav>
<a href="/{lang}">{t(lang, 'nav.home')}</a>
<a href="/{lang}/about">{t(lang, 'nav.about')}</a>
<div>
<a href="/ko">한국어</a> | <a href="/en">English</a>
</div>
</nav>
{@render children()}
hooks에서 로케일 감지
// src/hooks.server.js
export async function handle({ event, resolve }) {
// Accept-Language 헤더에서 감지
const acceptLanguage = event.request.headers.get('accept-language');
const preferredLang = acceptLanguage?.split(',')[0]?.split('-')[0];
event.locals.lang = ['ko', 'en'].includes(preferredLang) ? preferredLang : 'ko';
return resolve(event, {
transformPageChunk: ({ html }) =>
html.replace('%lang%', event.locals.lang),
});
}
날짜/숫자 포맷팅
// src/lib/i18n/formatters.js
export function formatDate(date, locale) {
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date);
}
export function formatNumber(number, locale) {
return new Intl.NumberFormat(locale).format(number);
}
export function formatCurrency(amount, locale, currency) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency || (locale === 'ko' ? 'KRW' : 'USD'),
}).format(amount);
}
면접 포인트
- "i18n에서 가장 어려운 부분은?": 복수형 처리(1 item vs 2 items), RTL(아랍어) 지원, 동적 컨텐츠의 번역 관리입니다. 한국어는 복수형이 없지만 영어는 있어서, 언어별 문법 규칙을 고려해야 합니다.
- "SEO와 i18n?": 각 언어별 URL이 있어야 검색 엔진이 올바른 언어 페이지를 인덱싱합니다.
hreflang태그와 sitemap에 언어별 URL을 포함해야 합니다.
정리
SvelteKit의 유연한 라우팅과 Context API를 조합하면 별도 라이브러리 없이도 기본적인 i18n을 구현할 수 있습니다. 규모가 커지면 sveltekit-i18n이나 paraglide-js 같은 전용 라이브러리를 도입하는 것이 효율적입니다.
댓글 로딩 중...