@starterapp/i18n-next
Next.js bindings for localization providers, middleware, navigation, and SEO helpers.
@starterapp/i18n-next bridges @starterapp/i18n-core with Next.js. It wraps dictionary registration, localized route handling, runtime providers, and edge negotiation helpers.
Pro Tip
Everything in this package is safe for Server Components and middleware. The provider is the only client boundary ("use client").
createAppI18n Factory
import { createAppI18n } from "@starterapp/i18n-next/app-factory";
import { marketingDictionaries, marketingRoutes } from "~/i18n";
const marketingI18n = createAppI18n({
dictionaries: marketingDictionaries,
routes: marketingRoutes,
baseNamespaces: ["common"],
loadSharedMessages: async () =>
(await import("../messages/shared/common.json")).default,
});
export const createMarketingLocalizedPage = marketingI18n.createLocalizedPage;
Factory options
dictionaries— locale → namespace loaders (lazily registered)routes— sharedLocalizedRouteRegistryused by middleware & server helperslocalizedRoutes— initial allow-list when you don’t provide a registrybaseNamespaces— namespaces loaded for every pageloadSharedMessages— merge global dictionaries (e.g., navigation)wrapWithProviders— optional callback to wrap the i18n provider with app-specific providers
Runtime API
| Method | Description |
|---|---|
resolve(options) | Returns { locale, messages } for the current request. |
createLocalizedPage({ namespaces, render }) | Wraps RSC exports with the provider and preloaded dictionaries. |
loadMessages(locale, namespaces) | Fetches merged dictionaries without rendering. |
isLocalizedRoute(path) / registerLocalizedRoute(path) | Manage the allow list used by middleware. |
Provider & Client Hooks
return (
<StarterAppI18nProvider locale={locale} messages={messages}>
{content}
</StarterAppI18nProvider>
);
When localization is disabled, the provider falls back to identity translations but still injects runtime context (locale + messages). Client hooks read from that context:
const t = useTranslations("localizedExample.client");
const format = useFormatter();
const locale = useLocale();
If dictionaries exist locally (single-locale mode), useTranslations builds a translator with use-intl/createTranslator so components still see localized strings without mounting next-intl.
Middleware Helpers
import {
resolveRequestLocale,
createI18nResponse,
applyI18nHeaders,
} from "@starterapp/i18n-next";
const resolution = resolveRequestLocale(request);
const response = createI18nResponse(request, resolution);
if (response) {
return applyI18nHeaders(response, resolution);
}
resolveRequestLocale— determines locale from pathname, cookie, Accept-LanguagecreateI18nResponse— issues redirect/rewrites + locale cookie when neededapplyI18nHeaders— attachesVaryandx-starterapp-locale
Combine these helpers with createLocalizedRouteRegistry to ensure only opted-in routes negotiate locales.
Navigation & SEO
const href = buildLocalizedPath("/pricing", "es");
const normalized = removeLocaleFromPath("/es/pricing");
const links = generateHreflangLinks({
baseUrl: "https://example.com",
pathname: "/pricing",
});
Helpers respect the default-locale opt-out: when only English is enabled, paths remain unprefixed and hreflang generation returns an empty array.
Testing Patterns
Reset registered dictionaries and locales between tests to avoid state leakage:
import { clearDictionaries } from "@starterapp/i18n-next/dictionary-map";
import { registerLocaleDefinition, resetEnabledLocales } from "@starterapp/i18n-core";
beforeEach(() => {
clearDictionaries();
resetEnabledLocales();
registerLocaleDefinition({
id: "es",
label: "Español",
dir: "ltr",
fallback: ["es", "en"],
enabled: true,
coverageThreshold: 0.95,
});
});
Integration tests in apps/marketing/__tests__/localized-example.integration.test.tsx demonstrate middleware + provider behavior for both English and Spanish.
Related Packages
@starterapp/i18n-core— locale registry, fallback chains, Accept-Language parsing@starterapp/i18n-next/navigation&@starterapp/i18n-next/seo— tree-shakable submodules for client/server usage