글로벌 서비스를 만들려면 i18n은 선택이 아닌 필수입니다.

개념 정의

i18n(Internationalization) 은 애플리케이션이 여러 언어와 지역을 지원할 수 있도록 설계하는 것입니다. SvelteKit에서는 라우팅 기반 로케일 감지와 번역 파일 관리가 핵심입니다.

기본 구조 — 수동 구현

JAVASCRIPT
// 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;
}

라우팅 기반 로케일

PLAINTEXT
src/routes/
├── [[lang]]/             # 선택적 매개변수
│   ├── +layout.js
│   ├── +page.svelte      # / 또는 /ko 또는 /en
│   ├── about/
│   │   └── +page.svelte  # /about 또는 /ko/about
JAVASCRIPT
// 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 };
}
SVELTE
<!-- 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에서 로케일 감지

JAVASCRIPT
// 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),
  });
}

날짜/숫자 포맷팅

JAVASCRIPT
// 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 같은 전용 라이브러리를 도입하는 것이 효율적입니다.

댓글 로딩 중...