Internationalization

Multi-language support architecture using next-intl in your SaaS application.

This SaaS Starter kit uses next-intl for internationalization, providing a robust system for multi-language support with server-side rendering, namespace-based organization, and automatic locale detection.

Architecture Overview

The i18n system is split between a shared package and app-specific configuration:

packages/i18n/ # Shared i18n package
├── src/
│ ├── routing.ts # Locale configuration
│ ├── navigation.ts # Locale-aware navigation utilities
│ └── client-provider.tsx # Client-side provider
apps/web/i18n/ # App-specific configuration
├── request.ts # Namespace definitions and loading
└── messages/
└── en/ # English translations
├── common.json
├── auth.json
├── account.json
├── organizations.json
├── billing.json
├── marketing.json
├── settings.json
├── goodbye.json
└── errors.json

Configuration Files

Locale Configuration

The locale configuration is defined in packages/i18n/src/routing.ts:

import { defineRouting } from 'next-intl/routing';
const defaultLanguage = process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? 'en';
export const languages: string[] = [
defaultLanguage,
// Add other languages here:
// 'es', // Spanish
// 'fr', // French
];
export const routing = defineRouting({
locales: languages,
defaultLocale: defaultLanguage,
localePrefix: 'as-needed',
localeDetection: true,
});
export type Locale = (typeof routing.locales)[number];

Key settings:

  • locales: Array of supported language codes
  • defaultLocale: The default language (from NEXT_PUBLIC_DEFAULT_LOCALE env variable)
  • localePrefix: 'as-needed': Default locale has no URL prefix, other locales do
  • localeDetection: true: Automatically detect locale from browser headers/cookies

URL Structure

With localePrefix: 'as-needed':

URLLocale
/aboutEnglish (default)
/es/aboutSpanish
/fr/aboutFrench

Namespace Configuration

Namespaces are defined in apps/web/i18n/request.ts:

const namespaces = [
'common',
'auth',
'account',
'organizations',
'billing',
'marketing',
'settings',
'goodbye',
'errors',
] as const;

Each namespace corresponds to a JSON file in apps/web/i18n/messages/{locale}/.

Package Exports

The @kit/i18n package provides several entry points:

@kit/i18n/routing

Locale configuration and type definitions:

import { routing, languages, type Locale } from '@kit/i18n/routing';

@kit/i18n/navigation

Locale-aware navigation utilities:

import { Link, redirect, useRouter, usePathname } from '@kit/i18n/navigation';

These work like their Next.js counterparts but automatically handle locale prefixes.

@kit/i18n/server

Server-side utilities for React Server Components:

import {
getTranslations,
getLocale,
getMessages,
getNow,
getTimeZone
} from '@kit/i18n/server';

@kit/i18n/provider

Client-side provider for client components:

import { I18nClientProvider } from '@kit/i18n/provider';

Supported Namespaces

NamespacePurpose
commonShared UI labels, routes, roles, OTP, cookie banner
authSign up, sign in, password reset, MFA
accountProfile updates, email/password management
organizationsTeam management, invitations, member actions
billingSubscriptions, plans, billing portal
marketingLanding page, blog, FAQ, contact
settingsPersonal and organization settings
goodbyeAccount deletion confirmation
errorsError messages and codes

Next: Using Translations →