Skip to main content

i18n Architecture - Server & Client Locale Detection

Overview

Locale management is implemented consistently across both server and client environments. Both layers share and cache locale data through localeCache.ts, ensuring a single, authoritative source of truth for all locale operations.

Locale Detection Priority

Server-Side (getServerLocale())

  1. Cookie - User's explicit locale choice
  2. Accept-Language header - Browser preferences
  3. Default locale - From application config

Client-Side (getClientLocale())

  1. Cookie - User's explicit locale choice
  2. navigator.languages - Browser language preferences
  3. Default locale - From application config

Both implementations use the same matching algorithm and cache locale data via localeCache.ts.

Core Modules

1. Locale Cache (localeCache.ts)

Module-level cache used by both server and client to avoid repeated API calls within each environment. The cache is NOT shared between server and client. Each environment maintains its own cache. This means the first server request and first client request will each fetch locale data independently, but subsequent calls within the same environment will use the cached values.

import {
getDefaultLocale,
getAvailableLocaleCodes,
resetLocaleCache,
} from '@/utils/localeCache';

const defaultLocale = await getDefaultLocale(); // e.g., 'en-US'
const availableCodes = await getAvailableLocaleCodes(); // e.g., ['en-US', 'en-PH', 'es-ES']

2. Locale Matching (matchLocale.ts)

Determines the best match between user-preferred locales and supported locales.

import { matchLocale } from '@/utils/matchLocale';

// Exact match
matchLocale(['en-US'], ['en-US', 'en-PH', 'es-ES']); // → 'en-US'

// Language-only match (fallback)
matchLocale(['en'], ['en-US', 'en-PH', 'es-ES']); // → 'en-US' (first match)

// Multiple preferences
matchLocale(['de', 'en'], ['en-US', 'es-ES']); // → 'en-US' (tries 'de' first, falls back to 'en')

3. Server Locale Detection (getServerLocale.ts)

import { getServerLocale } from '@/utils/getServerLocale';

// In Server Components
export async function MyServerComponent() {
const locale = await getServerLocale();
// Use locale...
}

// In API routes
export async function GET(request: Request) {
const locale = await getServerLocale();
// Use locale...
}

4. Client Locale Detection (getClientLocale() / setClientLocale())

Manages locale detection and persistence on the client side.

import { getClientLocale, setClientLocale } from '@/utils/userLocale';

const locale = await getClientLocale();

// Set user preference (saves to cookie)
setClientLocale('es-ES');

For React components, use the useCurrentLocale() hook as the preferred method:

'use client';
import useCurrentLocale from '@/hooks/useCurrentLocale';

export function MyClientComponent() {
const locale = useCurrentLocale();

return <div>Current locale: {locale}</div>;
}

Alternative: If you're already using I18nContext in your component for other purposes (e.g., to set locale), you can access currentLocale directly instead:

'use client';
import { useContext } from 'react';
import { I18nContext } from '@/context/i18nContext';

export function MyClientComponent() {
const i18nContext = useContext(I18nContext);
const { currentLocale: locale } = i18nContext;

return <div>Current locale: {locale}</div>;
}