StarterApp Docs
Packages

@starterapp/i18n-core

Locale configuration, fallback chains, and Accept-Language negotiation helpers.

@starterapp/i18n-core owns StarterApp’s locale metadata. It exposes pure utilities for registering locales, managing fallback chains, and interpreting Accept-Language headers. Import it from server-only contexts (middleware, Server Components, scripts, tests).

Heads up

This package is server-only. Importing it in the browser throws an error.

Locale Registry

export const DEFAULT_LOCALE = "en";

export function registerLocaleDefinition(definition: LocaleDefinition): void;
export function getLocaleDefinition(localeId: string): LocaleDefinition | null;
export function getEnabledLocaleIds(): string[];
export function enableLocale(localeId: string): void;
export function resetEnabledLocales(): void;
export function isI18nEnabled(): boolean;

Pro Tip

Only English ships in the base config. Apps register additional locales with registerLocaleDefinition() and provide their own dictionaries.

Fallback & Negotiation

export function getFallbackChain(localeId: string): string[];
export function parseAcceptLanguage(header?: string): AcceptLanguagePreference[];
export function selectLocaleFromAcceptLanguage(
  preferences: AcceptLanguagePreference[],
  permittedLocales: string[]
): string | null;

These utilities power the middleware helpers in @starterapp/i18n-next, keeping Accept-Language parsing and fallback ordering consistent.

Usage Examples

Registering a Locale

import { registerLocaleDefinition } from "@starterapp/i18n-core";

registerLocaleDefinition({
  id: "es",
  label: "Español",
  dir: "ltr",
  fallback: ["es", "en"],
  enabled: true,
  coverageThreshold: 0.95,
});

Reading Enabled Locales in Middleware

import { DEFAULT_LOCALE, isI18nEnabled } from "@starterapp/i18n-core";

const activeLocale = isI18nEnabled()
  ? request.headers.get("x-starterapp-locale") ?? DEFAULT_LOCALE
  : DEFAULT_LOCALE;

Testing Helpers

Use resetEnabledLocales() and registerLocaleDefinition() in Vitest to keep tests isolated:

import {
  enableLocale,
  registerLocaleDefinition,
  resetEnabledLocales,
} from "@starterapp/i18n-core";

beforeEach(() => {
  resetEnabledLocales();
  registerLocaleDefinition({
    id: "es",
    label: "Español",
    dir: "ltr",
    fallback: ["es", "en"],
    enabled: true,
    coverageThreshold: 0.95,
  });
  enableLocale("es");
});

The negotiation unit tests in the package demonstrate both single-locale and multi-locale flows.