Skip to content
C.W.K.
Stream
Lesson 08 of 09 · published

Internationalization

~20 min · i18n, next-intl, locale

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Two routing strategies

StrategyURL pattern
Sub-path/[locale]/page/en/about, /ko/about
Domain-per-localeen.example.com, ko.example.com

Sub-path is simpler and SEO-friendly out of the box. Domain-per-locale is for separate marketing teams or geo-isolated infrastructure.

next-intl is the well-paved path

It handles message catalogs, locale negotiation, formatting (dates/numbers), and works in both Server and Client Components.

Make locale part of the route and load only the selected message catalog on the server. Define supported locales, a default, negotiation precedence, and a missing-key policy. Format dates, numbers, plural forms, and time zones with locale-aware APIs rather than concatenated strings. Do not collapse language and region into one value. A Korean-language user may still need another currency or date convention, so test precedence across the URL, cookie, and saved profile. Internationalization is not a dictionary lookup added at the end. Text expansion, reading direction, search indexing, canonical URLs, emails, validation messages, and user-selected locale persistence all affect the product boundary. Navigate directly to every supported locale, switch locale on a nested route, refresh, and inspect generated metadata. Test a missing key, long translation, plural count, time-zone boundary, and unsupported locale without silently falling back to mixed-language UI.

Code

Locale layout under [locale]·tsx
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
Translations in components·tsx
import { useTranslations } from 'next-intl';

export default function HomePage() {
  const t = useTranslations('Home');
  return (
    <>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </>
  );
}

External links

Exercise

Add en and ko locales to a small page using next-intl. Confirm the URL switches and a third-party translation key resolves correctly in both locales.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.