Author:
    Creation:2026-06-14Last update:2026-06-29

    New Intlayer v9 - What's new?

    Welcome to Intlayer v9! This major release marks a huge milestone in simplifying the migration path to Intlayer with Compat Adapter Packages for major i18n libraries (react-i18next, next-intl, vue-i18n, etc.) and adds support for rich content structures: Collections and Variants.

    Table of contents


    Compat Adapter Packages

    Migrating to Intlayer from popular i18n libraries is now easier than ever. We have created five compat packages that expose the exact same public APIs as standard i18n libraries but delegate all translation work to Intlayer at runtime.

    Expose Same Public API with Strict Typing

    By replacing imports, you get all the benefits of Intlayer (including compile-time type safety against your actual dictionaries) with minimal code changes:

    • @intlayer/i18next
    • @intlayer/react-i18next
    • @intlayer/next-intl
    • @intlayer/react-intl
    • @intlayer/next-i18next
    • @intlayer/vue-i18n
    • @intlayer/lingui

    For example, simply change:

    ts
    import { useTranslation } from "react-i18next";

    to:

    ts
    import { useTranslation } from "@intlayer/react-i18next";

    Your keys will now be strictly typed against your Intlayer dictionaries, offering full dot-path auto-completion in your IDE!

    Bundler Alias Plugins (Vite, Next.js, Turbopack)

    To allow migrating without rewriting all your import statements manually, each compat adapter package includes a custom bundler plugin (Vite or Next.js) under the /plugin subpath.

    These plugins automatically rewrite existing imports (e.g. react-i18next or next-intl) to their @intlayer/* equivalents at build time.

    Next.js (Webpack / Turbopack) Example

    Instead of withIntlayer, wrap your Next.js configuration with the compat plugin:

    next.config.ts
    import { createNextI18nPlugin } from "@intlayer/next-i18next/plugin";import type { NextConfig } from "next";const withIntlayer = createNextI18nPlugin();const nextConfig: NextConfig = {};export default withIntlayer(nextConfig);

    Vite (React, Vue, Solid, Svelte) Example

    vite.config.ts
    import vueI18nVitePlugin from "@intlayer/vue-i18n/plugin";export default defineConfig({  plugins: [vueI18nVitePlugin()],});

    Mutualized Runtime Resolver

    All compat adapters now route translation resolution through a single, highly optimized runtime parser: @intlayer/core/messageFormat.

    • Interpolate Message: Resolves standard {{var}} (whitespace & dot-paths), ICU formatted args ({v, number, percent} etc.), and bare {var} templates.
    • Message Node Resolver: Resolves nested nodes: insert(), plural() (CLDR plural rules), enu() (enumeration), gender(), HTML tags, arrays, and callable function nodes.
    • Tokenized Tag Parser: Supports inline XML/HTML tags and numbered tags (e.g., <1>children</1>) to power rich-text rendering out of the box.

    Feature Spec: Collections & Variants

    Intlayer v9 expands beyond static key-value objects, allowing dictionaries to declare dynamic layout structures that are fully typed end-to-end.

    1. Collections

    Define a CMS-managed list of ordered items (e.g. FAQs, products, or blog lists). Each item lives in its own .content.ts file with the same key and an item number:

    faq.1.content.ts
    import { t, type Dictionary } from "intlayer";export default {  key: "faq",  item: 1,  content: {    question: t({ en: "What is Intlayer?", fr: "Qu'est-ce qu'Intlayer ?" }),    answer: t({ en: "An i18n toolkit.", fr: "Une boîte à outils i18n." }),  },} satisfies Dictionary;
    faq.2.content.ts
    import { t, type Dictionary } from "intlayer";export default {  key: "faq",  item: 2,  content: {    question: t({ en: "Is it free?", fr: "Est-ce gratuit ?" }),    answer: t({ en: "Yes, open-source.", fr: "Oui, open-source." }),  },} satisfies Dictionary;

    Usage:

    ts
    // Fetch all items as an arrayconst allFaqs = useIntlayer("faq"); // -> { question: string, answer: string }[]// Fetch a single item by indexconst faq = useIntlayer("faq", { item: 2 }); // -> { question: string, answer: string }

    2. Variants

    Deliver A/B tests, seasonal headers, feature flags, or custom landing pages:

    String variants (e.g. A/B testing)

    hero.content.ts
    import { t, type Dictionary } from "intlayer";export default {  key: "hero-banner",  variant: "default",  content: {    control: t({ en: "Welcome", fr: "Bienvenue" }),    black_friday: t({ en: "Shop now", fr: "Acheter maintenant" }),  },} satisfies Dictionary;

    Usage:

    ts
    const banner = useIntlayer("hero-banner", { variant: "black_friday" });

    Object variants (e.g. Dynamic Records)

    product.content.ts
    import { t, type Dictionary } from "intlayer";export default {  key: "product-copy",  variant: {    id: "prod_123",    category: "books",  },  content: {    title: t({ en: "Clean Code", fr: "Code Propre" }),  },} satisfies Dictionary;

    Usage:

    ts
    // Fetches only the requested record dynamically (requires Suspense)const product = useIntlayer("product-copy", {  variant: { id: "prod_123", category: "books" },});

    Vite Plugin: Bundled Compiler & Proxy

    The intlayer() Vite plugin now bundles the compiler and the locale-routing proxy directly, so most projects only need a single plugin in vite.config.ts:

    vite.config.ts
    import { defineConfig } from "vite";import { intlayer } from "vite-intlayer";export default defineConfig({  plugins: [intlayer()],});
    • Compiler: Activates automatically when compiler.enabled is set to true and a compiler.output path is configured. You no longer need to register intlayerCompiler() separately.
    • Proxy: Activates automatically based on the new routing.enableProxy option (true by default). It wires the locale detection / redirect / rewrite middleware in development, preview, and production SSR. You no longer need to register intlayerProxy() separately.

    routing.enableProxy option

    A new routing.enableProxy option controls whether the locale-routing proxy is plugged in. It defaults to true. Disable it when you want to handle locale routing yourself:

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  routing: {    enableProxy: false, // Default: true  },};export default config;

    The standalone intlayerCompiler() and intlayerProxy() plugins remain exported for advanced setups. Registering them alongside intlayer() is safe — each plugin deduplicates itself and runs only once.


    Compiler disabled by default

    Starting with Intlayer v9, the compiler is disabled by default (compiler.enabled now defaults to false). To opt in to build-time extraction of your .content.ts files, set compiler.enabled: true in your configuration:

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  compiler: {    enabled: true, // Default: false — required to enable the compiler since v9    output: ({ fileName }) => `./${fileName}.content.ts`,  },};export default config;

    When the compiler is disabled, Intlayer skips the build-time extraction step and relies on your already-declared dictionaries. Enable it only when you want the bundler plugin (@intlayer/swc, @intlayer/babel, or the intlayer() Vite plugin) to extract content automatically.


    React Native: single-package imports

    In a React Native or Expo app, you no longer need to juggle both react-intlayer and react-native-intlayer. The react-native-intlayer package now re-exports the full react-intlayer API (hooks, utilities, and the /format, /html, and /markdown subpaths), and its IntlayerProvider automatically applies the React Native polyfills.

    Import everything from the single react-native-intlayer package:

    tsx
    import {  IntlayerProvider,  useIntlayer,  useLocale,} from "react-native-intlayer";
    bash
    npm install intlayer react-native-intlayer
    Importing from react-intlayer keeps working, but react-native-intlayer is now the recommended single entry point for React Native — its provider ships the polyfills the web-oriented react-intlayer provider does not.

    CMS SDK: use Intlayer as a headless content database

    Intlayer v9 ships a clean, auto-authenticating SDK in @intlayer/api to interact with the CMS programmatically — fetch projects, fetch dictionaries, and push or update them from your own server, scripts, or CI. Authentication (OAuth2 client_credentials) is handled and refreshed for you.

    The SDK is split into two separate imports so your bundle includes only the domains you actually use:

    1. createIntlayerCMS — a lightweight authenticator holding the credentials and the managed token (no domain client bundled).
    2. dictionaryEndpoint, projectEndpoint, … — per-domain endpoint binders, each imported from its own subpath.
    cms.ts
    import { createIntlayerCMS } from "@intlayer/api";import { dictionaryEndpoint } from "@intlayer/api/dictionary";const cms = createIntlayerCMS();// Readconst { data: dictionaries } = await dictionaryEndpoint(cms).getDictionaries();// Write — use the CMS like a databaseawait dictionaryEndpoint(cms).pushDictionaries([myDictionary]);
    Security: the CMS credentials grant write access to your content. Only ever create the authenticator on the server side — never ship clientId / clientSecret to the browser.

    Self-Hosting

    Intlayer v9 ships first-class support for running your own Intlayer instance with a single command. No Intlayer Cloud account required.

    sh
    curl -fsSL https://intlayer.org/install.sh | sh

    The installer downloads a docker-compose.yml and a .env, auto-generates the required secrets, and runs docker compose up -d. Everything — the dashboard, the API, the database, object storage, and transactional email — runs locally in containers.


    Migration notes from v8

    If you are upgrading from v8, note that the v9 does not include breaking changes. But here are the key changes:

    • Compiler disabled by default: compiler.enabled now defaults to false. If you rely on build-time extraction of your .content.ts files, set compiler.enabled: true in your intlayer.config.ts.
    • Vite plugin: The compiler and locale-routing proxy are now bundled into intlayer(). If you previously registered intlayerCompiler() or intlayerProxy() manually, you can remove them — they are deduplicated automatically, so keeping them is harmless. Use routing.enableProxy: false to opt out of the proxy.
    • Locales & Dialects: If using external i18n dependencies, add their respective compat adapter plugins in your configuration or bundler setup to automatically rewrite imports.
    • Custom Selectors: When calling useIntlayer, the second parameter is now reserved for an option object containing { locale, item, variant }. If you previously passed a locale string directly, you can still do so, but using the options object is recommended for advanced selections.
    • React Native: react-native-intlayer now re-exports the entire react-intlayer API. In a React Native app, import everything from react-native-intlayer and drop the direct react-intlayer dependency. Existing react-intlayer imports continue to work.