Автор:
    Создание:2025-09-22Последнее обновление:2026-06-14

    Новый Intlayer v8. Что нового?

    Добро пожаловать в Intlayer v8! В этом релизе сделан акцент на улучшении опыта разработчика с автоматическим обнаружением контента, обеспечении целостности данных с помощью валидации схем и предоставлении большего контроля над управлением словарями.

    youtube.com

    Table of contents

    Эволюция насыщенного контента: Markdown и HTML

    Intlayer v8 вносит значительные улучшения в способы обработки насыщенного контента, представляя HTML-узлы (которых не было в v7) и унифицируя API с Markdown-узлами (которые были в v7, но были улучшены).

    Унифицированный API .use()

    Мы ввели метод .use() как для Markdown, так и для HTML-узлов. Этот метод позволяет настраивать HTML-теги или компоненты, используемые при рендеринге.

    • Замена компонентов: Вы можете легко заменить HTML-теги или пользовательские компоненты компонентами вашего фреймворка (например, заменить <a> на NextLink или <CustomCmp> на React-компонент).
    • Типизация: Все функции для предоставления компонентов полностью типизированы, что гарантирует получение правильных пропсов.

    Поведение рендеринга по умолчанию

    В v7, если провайдер не был определен, узлы Markdown рендерились как необработанные строки, что часто требовало использования внешних библиотек для их парсинга.

    В v8 Intlayer включает собственный внутренний парсер Markdown. По умолчанию узлы Markdown теперь рендерится напрямую в HTML без необходимости использования каких-либо внешних библиотек.

    Новые утилиты Renderer и Provider

    Мы представили новые автономные функции рендеринга и компоненты, чтобы дать вам больше контроля вне стандартного потока useIntlayer.

    • Markdown: MarkdownRenderer, useMarkdownRenderer, renderMarkdown. (Примечание: MarkdownProvider существовал в v7, но теперь интегрируется с этими новыми инструментами).
    • HTML: HTMLRenderer, useHTMLRenderer, renderHTML, HTMLProvider.

    Примеры: Инструменты рендеринга Markdown

    1. Использование компонента:

    tsx
    import { MarkdownRenderer } from "react-intlayer/markdown";
    
    <MarkdownRenderer
    forceBlock={true}
    components={{
      h1: ({ children }) => <h1 className="text-2xl">{children}</h1>
    }}
    >
    {"# Мой заголовок"}
    </MarkdownRenderer>
    

    2. Использование хука:

    tsx
    import { useMarkdownRenderer } from "react-intlayer/markdown";
    
    const renderMarkdown = useMarkdownRenderer({
    components: {
      h1: ({ children }) => <h1 className="text-red-500">{children}</h1>
    }
    });
    
    return <div>{renderMarkdown("# Мой заголовок")}</div>;
    

    3. Использование утилитарной функции:

    tsx
    import { renderMarkdown } from "react-intlayer/markdown";
    
    const html = renderMarkdown("# Мой заголовок", {
    forceBlock: true
    });
    

    1. Использование компонента:

    vue
    <script setup>
    import { MarkdownRenderer } from "vue-intlayer/markdown";
    </script>
    
    <template>
    <MarkdownRenderer :forceBlock="true" content="# Мой заголовок" />
    </template>
    

    1. Использование компонента:

    svelte
    <script>
    import { MarkdownRenderer } from "svelte-intlayer/markdown";
    </script>
    
    <MarkdownRenderer forceBlock={true} value="# Мой заголовок" />
    

    2. Использование хука:

    svelte
    <script>
    import { useMarkdownRenderer } from "svelte-intlayer/markdown";
    const render = useMarkdownRenderer();
    </script>
    
    {@html render("# Мой заголовок")}
    

    3. Использование утилитарной функции:

    svelte
    <script>
    import { renderMarkdown } from "svelte-intlayer/markdown";
    </script>
    
    {@html renderMarkdown("# Мой заголовок")}
    

    1. Использование сервиса:

    typescript
    import { Component } from "@angular/core";
    import { IntlayerMarkdownService } from "angular-intlayer";
    
    @Component({ ... })
    export class MyComponent {
    constructor(private markdownService: IntlayerMarkdownService) {}
    
    render(markdown: string) {
      return this.markdownService.renderMarkdown(markdown);
    }
    }
    

    1. Использование компонента:

    tsx
    import { MarkdownRenderer } from "solid-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true}>
    {"# Мой заголовок"}
    </MarkdownRenderer>
    

    2. Использование хука:

    tsx
    import { useMarkdownRenderer } from "solid-intlayer/markdown";
    
    const render = useMarkdownRenderer();
    
    return <div>{render("# Мой заголовок")}</div>;
    

    3. Использование утилитарной функции:

    tsx
    import { renderMarkdown } from "solid-intlayer/markdown";
    
    return <div>{renderMarkdown("# Мой заголовок")}</div>;
    

    1. Использование компонента:

    tsx
    import { MarkdownRenderer } from "preact-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true}>
    {"# Мой заголовок"}
    </MarkdownRenderer>
    

    2. Использование хука:

    tsx
    import { useMarkdownRenderer } from "preact-intlayer/markdown";
    
    const render = useMarkdownRenderer();
    
    return <div>{render("# Мой заголовок")}</div>;
    

    3. Использование утилитарной функции:

    tsx
    import { renderMarkdown } from "preact-intlayer/markdown";
    
    return <div>{renderMarkdown("# Мой заголовок")}</div>;
    

    Примеры: Инструменты рендеринга HTML

    1. Использование компонента:

    tsx
    import { HTMLRenderer } from "react-intlayer/html";
    
    <HTMLRenderer
    components={{
      p: ({ children }) => <p className="mb-4">{children}</p>
    }}
    >
    {"<p>Привет, мир</p>"}
    </HTMLRenderer>
    

    2. Использование хука:

    tsx
    import { useHTMLRenderer } from "react-intlayer/html";
    
    const renderHTML = useHTMLRenderer({
    components: {
      strong: ({ children }) => <b className="font-bold">{children}</b>
    }
    });
    
    return <div>{renderHTML("<p>Привет <strong>мир</strong></p>")}</div>;
    

    3. Использование утилитарной функции:

    tsx
    import { renderHTML } from "react-intlayer/html";
    
    const html = renderHTML("<p>Привет, мир</p>");
    

    1. Использование компонента:

    vue
    <script setup>
    import { HTMLRenderer } from "vue-intlayer/html";
    </script>
    
    <template>
    <HTMLRenderer content="<p>Привет, мир</p>" />
    </template>
    

    1. Использование компонента:

    svelte
    <script>
    import { HTMLRenderer } from "svelte-intlayer/html";
    </script>
    
    <HTMLRenderer value="<p>Привет, мир</p>" />
    

    2. Использование хука:

    svelte
    <script>
    import { useHTMLRenderer } from "svelte-intlayer/html";
    const render = useHTMLRenderer();
    </script>
    
    {@html render("<p>Привет, мир</p>")}
    

    3. Использование утилитарной функции:

    svelte
    <script>
    import { renderHTML } from "svelte-intlayer/html";
    </script>
    
    {@html renderHTML("<p>Привет, мир</p>")}
    

    1. Прямое использование:

    В Angular вы можете использовать стандартную привязку [innerHTML].

    html
    <div [innerHTML]="'<p>Привет, мир</p>'"></div>
    

    1. Использование компонента:

    tsx
    import { HTMLRenderer } from "solid-intlayer/html";
    
    <HTMLRenderer>
    {"<p>Привет, мир</p>"}
    </HTMLRenderer>
    

    2. Использование хука:

    tsx
    import { useHTMLRenderer } from "solid-intlayer/html";
    
    const render = useHTMLRenderer();
    
    return <div>{render("<p>Привет, мир</p>")}</div>;
    

    3. Использование утилитарной функции:

    tsx
    import { renderHTML } from "solid-intlayer/html";
    
    return <div>{renderHTML("<p>Привет, мир</p>")}</div>;
    

    1. Использование компонента:

    tsx
    import { HTMLRenderer } from "preact-intlayer/html";
    
    <HTMLRenderer>
    {"<p>Привет, мир</p>"}
    </HTMLRenderer>
    

    2. Использование хука:

    tsx
    import { useHTMLRenderer } from "preact-intlayer/html";
    
    const render = useHTMLRenderer();
    
    return <div>{render("<p>Привет, мир</p>")}</div>;
    

    3. Использование утилитарной функции:

    tsx
    import { renderHTML } from "preact-intlayer/html";
    
    return <div>{renderHTML("<p>Привет, мир</p>")}</div>;
    

    Для получения более подробной информации см. Документацию по HTML-контенту и Документацию по Markdown.

    Файлы контента YAML и Markdown (v8.10.0)

    Теперь вы можете объявлять контент напрямую, используя расширения файлов .content.md и .content.yaml. Это идеально подходит для длинного контента, блогов, документации и политик конфиденциальности.

    Эти файлы полностью редактируются в Visual Editor, обеспечивая оптимизированный рабочий процесс для нетехнических членов команды.

    Для получения более подробной информации см. Документацию по файлам контента YAML и Документацию по файлам контента Markdown.

    Пользовательские правила перезаписи URL

    Intlayer v8 добавляет поддержку пользовательских правил перезаписи URL, позволяя задавать локализованные пути, отличающиеся от стандартной структуры /locale/path. Эта мощная функция помогает улучшить локальную SEO и обеспечить более естественный пользовательский опыт для неанглоязычных пользователей.

    Основные улучшения в v8:

    • Форматтеры для фреймворков: новые nextjsRewrite, svelteKitRewrite, reactRouterRewrite, vueRouterRewrite, solidRouterRewrite, tanstackRouterRewrite, nuxtRewrite и viteRewrite, обеспечивающие идиоматичный синтаксис паттернов для каждого роутера.
    • useRewriteURL Hook: Новый Hook на стороне клиента, который тихо исправляет адресную строку на «красивый» локализованный URL без инициирования переходов маршрутизатора.
    • Automatic SEO Redirects: Встроенные прокси теперь автоматически перенаправляют пользователей с вручную введённых канонических путей (например, /fr/about) на их более «красивые» локализованные версии (например, /fr/a-propos).

    Пример конфигурации:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    import { nextjsRewrite } from "intlayer/routing";
    
    const config: IntlayerConfig = {
    internationalization: {
      locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
      defaultLocale: Locales.ENGLISH,
    },
    routing: {
      mode: "prefix-no-default",
      rewrite: nextjsRewrite({
        "/[locale]/about": {
          fr: "/[locale]/a-propos",
          es: "/[locale]/acerca-de",
        },
        "/[locale]/products/[id]": {
          fr: "/[locale]/produits/[id]",
          es: "/[locale]/productos/[id]",
        },
      }),
    },
    };
    
    export default config;
    
    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    import { reactRouterRewrite } from "intlayer/routing";
    
    const config: IntlayerConfig = {
    internationalization: {
      locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
      defaultLocale: Locales.ENGLISH,
    },
    routing: {
      mode: "prefix-all",
      rewrite: reactRouterRewrite({
        "/:locale/about": {
          fr: "/:locale/a-propos",
          es: "/:locale/acerca-de",
        },
        "/:locale/products/:id": {
          fr: "/:locale/produits/:id",
          es: "/:locale/productos/:id",
        },
      }),
    },
    };
    
    export default config;
    
    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    import { viteRewrite } from "intlayer/routing";
    
    const config: IntlayerConfig = {
    internationalization: {
      locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
      defaultLocale: Locales.ENGLISH,
    },
    routing: {
      mode: "prefix-all",
      rewrite: viteRewrite({
        "/:locale/about": {
          fr: "/:locale/a-propos",
          es: "/:locale/acerca-de",
        },
        "/:locale/products/:id": {
          fr: "/:locale/produits/:id",
          es: "/:locale/productos/:id",
        },
      }),
    },
    };
    
    export default config;
    
    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    import { nuxtRewrite } from "intlayer/routing";
    
    const config: IntlayerConfig = {
    internationalization: {
      locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
      defaultLocale: Locales.ENGLISH,
    },
    routing: {
      mode: "prefix-all",
      rewrite: nuxtRewrite({
        "/[locale]/about": {
          fr: "/[locale]/a-propos",
          es: "/[locale]/acerca-de",
        },
        "/[locale]/products/[id]": {
          fr: "/[locale]/produits/[id]",
          es: "/[locale]/productos/[id]",
        },
      }),
    },
    };
    
    export default config;
    
    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    import { svelteKitRewrite } from "intlayer/routing";
    
    const config: IntlayerConfig = {
    internationalization: {
      locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
      defaultLocale: Locales.ENGLISH,
    },
    routing: {
      mode: "prefix-all",
      rewrite: svelteKitRewrite({
        "/[locale]/about": {
          fr: "/[locale]/a-propos",
          es: "/[locale]/acerca-de",
        },
        "/[locale]/products/[id]": {
          fr: "/[locale]/produits/[id]",
          es: "/[locale]/productos/[id]",
        },
      }),
    },
    };
    
    export default config;
    

    Эта функция поддерживается из коробки в Next.js и Vite через прокси Intlayer и может быть легко интегрирована с другими роутерами, такими как TanStack Router, React Router, Vue Router, SvelteKit и Solid Router.

    Для получения дополнительной информации и руководств по интеграции смотрите Документацию по пользовательским правилам переписывания URL.

    Расширенные значения вставок

    В v8 значения вставок теперь могут принимать React-элементы (или Vue-узлы) в дополнение к строкам и числам. Это позволяет внедрять богатые интерактивные компоненты непосредственно в ваши шаблоны вставок.

    Intlayer теперь надёжно обрабатывает вложенные узлы React и Preact внутри вставок, гарантируя сохранение и корректный рендеринг сложных UI-структур.

    Пример:

    src/example.content.ts
    import { insert } from "intlayer";
    
    export default {
      key: "my-key",
      content: {
        myInsertion: insert("Привет, {{name}}"),
      },
    };
    
    tsx
    import { useIntlayer } from "next-intlayer";
    
    const { myInsertion } = useIntlayer("my-key");
    
    return (
    <div>
      {myInsertion({
        name: 2, // число
        // или
        name: "John", // строка
        // или
        name: <span>John</span>, // React-элемент
      })}
    </div>
    );
    
    tsx
    import { useIntlayer } from "react-intlayer";
    
    const { myInsertion } = useIntlayer("my-key");
    
    return (
    <div>
      {myInsertion({
        name: 2, // число
        // или
        name: "John", // строка
        // или
        name: <span>John</span>, // элемент React
      })}
    </div>
    );
    
    vue
    <script setup>
    import { h } from "vue";
    import { useIntlayer } from "vue-intlayer";
    
    const { myInsertion } = useIntlayer("my-key");
    </script>
    
    <template>
    <div>
      <component
        :is="myInsertion({
          name: 2,
          // или
          name: 'John',
          // или
          name: h('span', 'John'),
        })"
      />
    </div>
    </template>
    
    tsx
    import { useIntlayer } from "preact-intlayer";
    
    const { myInsertion } = useIntlayer("my-key");
    
    return (
    <div>
      {myInsertion({
        name: 2, // число
        // или
        name: "John", // строка
        // или
        name: <span>John</span>, // элемент Preact
      })}
    </div>
    );
    
    tsx
    import { useIntlayer } from "solid-intlayer";
    
    const { myInsertion } = useIntlayer("my-key");
    
    return (
    <div>
      {myInsertion({
        name: 2, // число
        // или
        name: "John", // строка
        // или
        name: <span>John</span>, // элемент Solid
      })}
    </div>
    );
    
    svelte
    <script>
    import { useIntlayer } from "svelte-intlayer";
    
    const { myInsertion } = useIntlayer("my-key");
    </script>
    
    <div>
    {myInsertion({
      name: 2, // число
      // или
      name: "John", // строка
    })}
    </div>
    
    typescript
    import { Component } from "@angular/core";
    import { useIntlayer } from "angular-intlayer";
    
    @Component({
    selector: "app-insertion-example",
    template: `
      <div>
        {{ content().myInsertion({
          name: 'John'
        }) }}
      </div>
    `,
    })
    export class InsertionExampleComponent {
    content = useIntlayer("my-key");
    }
    

    Unicode CLDR Plural Rules (v8.8.0)

    Был добавлен новый хелпер узла plural, основанный на правилах множественного числа, определенных проектом Unicode CLDR, что улучшает поддержку сложных языков.

    src/openings.content.ts
    import { plural, t, type Dictionary } from "intlayer";
    
    const openingsContent = {
      key: "total_openings",
      content: {
        totalOpenings: t({
          ru: plural({
            one: "{{count}} вакансия",
            few: "{{count}} вакансии",
            many: "{{count}} вакансий",
            other: "{{count}} вакансии",
          }),
          en: plural({
            one: "{{count}} opening",
            other: "{{count}} openings",
          }),
          fr: plural({
            one: "{{count}} offre",
            other: "{{count}} offres",
          }),
        }),
      },
    } satisfies Dictionary;
    
    export default openingsContent;
    

    Затем вы можете динамически передавать количество в компонент вашего фреймворка:

    tsx
    import type { FC } from "react";
    import { useIntlayer } from "react-intlayer";
    
    const OpeningsComponent: FC<{ count: number }> = ({ count }) => {
      const { totalOpenings } = useIntlayer("total_openings");
    
      return (
        <div>
          {/* На английском:                                  */}
          {/*  count=0  → "0 openings"   (other)           */}
          {/*  count=1  → "1 opening"    (one)             */}
          {/*  count=2  → "2 openings"   (other)           */}
          {/*  count=21 → "21 openings"  (other)           */}
          <p>{totalOpenings(count)}</p>
        </div>
      );
    };
    

    Для получения дополнительной информации см. Документацию по плюрализации.

    Валидация схемы содержимого

    В Intlayer v8 добавлена валидация схем для словарей. Теперь вы можете определить переиспользуемые схемы валидации в вашей конфигурации с помощью Zod и применять их к файлам контента. Это гарантирует, что ваш контент всегда соответствует ожидаемой структуре и позволяет обнаруживать ошибки на этапе сборки.

    1. Определите схемы

    Определите свои схемы в intlayer.config.ts:

    intlayer.config.ts
    import { z } from "zod";
    
    export default {
      schemas: {
        "seo-metadata": z.object({
          title: z.string().min(50).max(60),
          description: z.string().min(150).max(160),
        }),
      },
    };
    

    2. Примените схемы к словарям

    Укажите ключ схемы в определении словаря:

    src/example.content.ts
    import { type Dictionary } from "intlayer";
    
    const aboutPageMetaContent = {
      key: "about-page-meta",
      schema: "seo-metadata", // <--
      content: {
        title: "О нашей компании, узнайте больше о нас",
        description: "Узнайте о миссии, ценностях и команде нашей компании.",
      },
    } satisfies Dictionary;
    
    export default aboutPageMetaContent;
    

    Если содержимое не соответствует схеме (например, заголовок слишком короткий), процесс сборки выдаст ошибку.

    TypeScript: Примитивные аксессоры IntlayerNode

    Тип IntlayerNode был обновлен, чтобы позволить вызывать примитивные методы JavaScript напрямую на узлах контента. Это делает возможным взаимодействие с вашим локализованным контентом напрямую, как если бы он был стандартным примитивом строки, числа, булева значения или массива.

    Поддерживается в React, Preact, Solid, Svelte, Vue и Angular.

    Пример со строкой

    typescript
    content.placeholder; // Возвращает IntlayerNode<string>
    content.placeholder.value; // Возвращает string
    content.placeholder.toString(); // Возвращает string
    content.placeholder.toLowerCase(); // Возвращает string
    String(content.placeholder); // Возвращает string
    content.placeholder.toUpperCase(); // Возвращает строку в верхнем регистре
    content.placeholder.replace("a", "b"); // Возвращает измененную строку
    

    Пример с массивом

    typescript
    content.myArrayOfString             // Возвращает IntlayerNode<Array<string>>
    content.myArrayOfString.find(...)   // Возвращает элемент
    content.myArrayOfString.join(', ')  // Возвращает объединенную строку
    

    Улучшенное автоматическое определение содержимого

    В v8 Intlayer автоматически определяет синтаксис Markdown, HTML-теги и вставки переменных в строках содержимого. Это означает, что вы часто можете опускать вспомогательные функции, такие как md(), html() или insert().

    Это поведение включено по умолчанию. Теперь вы можете тонко настраивать это обнаружение либо глобально в вашем intlayer.config.ts, либо для конкретного словаря.

    Тонкая настройка

    Вы можете включать или отключать определенные типы преобразований:

    intlayer.config.ts
    export default {
      dictionary: {
        // contentAutoTransformation: false (по умолчанию)
        contentAutoTransformation: {
          markdown: true,
          html: true,
          insertion: false, // Отключить автоматическое обнаружение вставок
        },
      },
    };
    

    Поведение v7 (Ручная обёртка):

    src/example.content.ts
    import { md, insert } from "intlayer";
    
    export default {
      key: "my-key",
      content: {
        myMarkdown: md("## Hello World"),
        myInsertion: insert("Привет, {{name}}"),
      },
    };
    

    Поведение v8 (Автоматическое определение):

    src/example.content.ts
    export default {
      key: "my-key",
      contentAutoTransformation: true, // Может также быть задано в определении словаря или глобально в intlayer.config.ts
      content: {
        myMarkdown: "## Hello World", // Автоматически определяется как Markdown
        myHTML: "<p>Hello World</p>", // Автоматически определяется как HTML
        myInsertion: "Привет, {{name}}", // Автоматически определяется как Вставка
      },
    };
    

    Подлежащий JSON-результат остается прежним, сохраняя богатую информацию о типах, необходимую для рендеринга:

    json
    {
      "key": "my-key",
      "content": {
        "myMarkdown": {
          "nodeType": "markdown",
          "markdown": "## Hello World"
        },
        "myHTML": {
          "nodeType": "html",
          "html": "<p>Hello World</p>"
        },
        "myInsertion": {
          "nodeType": "insertion",
          "insertion": "Hi {{name}}"
        }
      }
    }
    

    Локализация: новый хук useIntl

    В React, Next.js и Vue теперь доступен новый хук useIntl(). Он предоставляет объект Intl, привязанный к локали, который автоматически использует текущий язык для форматирования чисел, дат и прочего, без необходимости вручную передавать локаль.

    tsx
    import { useIntl } from "next-intlayer";
    
    const intl = useIntl();
    
    const formattedPrice = new intl.NumberFormat({
    style: "currency",
    currency: "USD",
    }).format(123.45);
    
    tsx
    import { useIntl } from "react-intlayer";
    
    const intl = useIntl();
    
    const formattedPrice = new intl.NumberFormat({
    style: "currency",
    currency: "USD",
    }).format(123.45);
    
    vue
    <script setup>
    import { useIntl } from "vue-intlayer";
    
    const intl = useIntl();
    
    const formattedPrice = new intl.NumberFormat({
    style: "currency",
    currency: "USD",
    }).format(123.45);
    </script>
    
    tsx
    import { useIntl } from "preact-intlayer";
    
    const intl = useIntl();
    
    const formattedPrice = new intl.NumberFormat({
    style: "currency",
    currency: "USD",
    }).format(123.45);
    
    tsx
    import { useIntl } from "solid-intlayer";
    
    const intl = useIntl();
    
    const formattedPrice = new intl.NumberFormat({
    style: "currency",
    currency: "USD",
    }).format(123.45);
    
    svelte
    <script>
    import { useIntl } from "svelte-intlayer";
    
    const intl = useIntl();
    
    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    </script>
    
    typescript
    import { Component, computed } from "@angular/core";
    import { useIntl } from "angular-intlayer";
    
    @Component({
    selector: "app-intl-example",
    template: `<div>{{ formattedPrice() }}</div>`,
    })
    export class IntlExampleComponent {
    intl = useIntl();
    
    formattedPrice = computed(() =>
      new (this.intl().NumberFormat)({
        style: "currency",
        currency: "USD",
      }).format(123.45)
    );
    }
    

    Инструменты: улучшения расширения VSCode

    Расширение Intlayer для VSCode получает крупные обновления в версии v8 для упрощения рабочего процесса интернационализации:

    • Время запуска: Повышение производительности при открытии проекта.
    • Кэширование: Улучшенный слой кэширования для почти мгновенной валидации и автодополнения.
    • Обнаружение неиспользуемых и дублированных ключей: Новые функции для автоматического обнаружения неиспользуемых ключей и дублированных ключей в ваших словарях, что помогает поддерживать контент чистым и эффективным.

    Инструменты: C++ Watcher и LSP на базе OXC (v8.12.0)

    Intlayer v8.12.0 приносит важные улучшения в опыт разработчика:

    • Parcel Watcher: Наблюдатель за контентом был перенесен с chokidar на @parcel/watcher, используя нативное отслеживание файлов на C++ для обеспечения более быстрых обновлений и значительно меньшего потребления памяти.
    • Новый Language Server Protocol (LSP): Теперь доступен совершенно новый LSP. Созданный на основе парсинга OXC, он помогает вашей IDE и AI-агентам беспрепятственно связывать вызовы useIntlayer('my-key') с соответствующими файлами .content и наоборот.

    Для получения более подробной информации см. Документацию по LSP.

    Оптимизации компилятора

    Intlayer v8 включает новый уровень кеширования для компилятора Markdown и HTML. Это гарантирует, что идентичные строки контента с одинаковой конфигурацией парсятся только один раз, что значительно снижает накладные расходы при повторных рендерах или при использовании одного и того же контента в нескольких местах.

    babel.config.js
    const {
    intlayerExtractBabelPlugin,
    intlayerOptimizeBabelPlugin,
    getExtractPluginOptions,
    getOptimizePluginOptions,
    } = require('@intlayer/babel');
    
    module.exports = {
    presets: ['next/babel'],
    plugins: [
      // Извлечение контента из компонентов в словари
      [intlayerExtractBabelPlugin, getExtractPluginOptions()],
      // Оптимизация импортов: замена useIntlayer на прямые импорты словарей
      [intlayerOptimizeBabelPlugin, getOptimizePluginOptions()],
    ],
    };
    
    vite.config.js
    import { defineConfig } from 'vite';
    import { intlayer, intlayerCompiler } from "vite-intlayer";
    
    export default defineConfig({
    plugins: [intlayer(), intlayerCompiler()],
    });
    

    Для Vue / Svelte необходимо установить соответствующий пакет компилятора:

    bash
    # Для Vue
    npm install @intlayer/vue-compiler
    
    bash
    # Для Svelte
    npm install @intlayer/svelte-compiler
    

    Экспериментальные оптимизации сборки: Minify и Purge (v8.7.0)

    Intlayer v8.7.0 представляет новые экспериментальные функции сборки в вашем intlayer.config.ts, разработанные для оптимизации вашего продакшн-бандла:

    • Минификация словарей: Минифицирует файлы словарей для уменьшения их размера.
    • Очистка неиспользуемых ключей (Purging): Сканирует и удаляет неиспользуемые ключи перевода из словарей, переименовывая активные ключи в короткие символы (например, "products" -> "a", "pricing" -> "b"). Это уменьшает размер бандла до 5%.
    • Отключение проверок TypeScript: Ускоряет сборку за счет отключения проверки типов TypeScript на этапе компиляции.
    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH],
        defaultLocale: Locales.ENGLISH,
      },
      dictionary: {
        importMode: "dynamic",
      },
      build: {
        /**
         * Минификация словарей для уменьшения размера бандла.
         */
        minify: true,
    
        /**
         * Очистка неиспользуемых ключей в словарях.
         */
        purge: true,
    
        /**
         * Указывает, должна ли сборка проверять типы TypeScript.
         */
        checkTypes: false,
      },
    };
    
    export default config;
    

    Гибкость: Унифицированный режим импорта

    Булево свойство live устарело в пользу более универсального свойства importMode. Это позволяет явно задавать, как словари должны загружаться: статически, динамически или посредством синхронизации в реальном времени.

    Режимы

    • static (по умолчанию): словарь включается в сборку на этапе сборки. Лучший вариант для производительности.
    • dynamic: словарь загружается во время выполнения (например, через JSON-запрос или с использованием suspense).
    • fetch: словарь запрашивается с CMS/сервера во время выполнения и синхронизируется.

    Миграция:

    v7 Конфигурацияv8 Конфигурация
    live: trueimportMode: 'fetch'
    live: falseimportMode: 'static' (или 'dynamic')

    Примечание: В Intlayer v8 свойство importMode было перемещено из конфигурации build в конфигурацию dictionary в intlayer.config.ts. Это позволяет определить режим импорта по умолчанию для всех словарей, при этом сохраняя возможность переопределять его для каждого словаря отдельно.

    Пример глобальной конфигурации:

    intlayer.config.ts
    export default {
      dictionary: {
        importMode: "dynamic", // Глобальный режим по умолчанию
      },
      // ...
    };
    

    Пример словаря:

    src/example.content.ts
    export default {
        key: 'my-key',
        importMode: "fetch", // Переопределяет глобальную конфигурацию
        content: { ... }
    }
    

    Управление местоположением словаря

    В v8 введено свойство location, которое позволяет явно управлять тем, где находятся словари и как они синхронизируются. Это особенно полезно для гибридных рабочих процессов, включающих как локальные файлы, так и контент удаленной CMS.

    Варианты

    • local: Словарь существует только локально. Он не будет отправлен в удаленную CMS.
    • remote: Словарь управляется удаленно. После отправки в CMS он будет отделён от локальной копии. Удалённый словарь будет подтягиваться из CMS.
    • local_and_remote: Словарь существует в обоих местах. Локальные изменения отправляются, а удалённые, подтягиваются (синхронизируются).

    Пример:

    src/example.content.ts
    export default {
        key: 'my-key',
        location: "local", // Оставить этот словарь только локальным
        content: { ... }
    }
    

    Разделение системной конфигурации

    Intlayer v8 разделяет конфигурацию источников контента и внутренние системные и выходные пути. Это разгружает свойство content и ясно показывает, какие настройки предназначены для управления пользователем, а какие управляются системой Intlayer.

    Следующие свойства были перемещены из content в новое свойство system в intlayer.config.ts:

    • dictionariesDir
    • moduleAugmentationDir
    • unmergedDictionariesDir
    • typesDir
    • mainDir
    • configDir
    • cacheDir
    • outputFilesPatternWithPath

    Поведение v7:

    intlayer.config.ts
    export default {
      content: {
        contentDir: ["src"],
        dictionariesDir: ".intlayer/dictionary", // Смешано с конфигурацией исходников
      },
    };
    

    Поведение v8:

    intlayer.config.ts
    export default {
      content: {
        contentDir: ["src"],
      },
      system: {
        dictionariesDir: ".intlayer/dictionary", // Чётко отделено
      },
    };
    

    Разделение директорий контента и кода

    Intlayer v8 разделяет конфигурацию файлов определения контента и конфигурацию трансформации кода. Это позволяет более точно настраивать отслеживание и сканирование, повышая производительность сборки.

    Ранее contentDir использовался и для отслеживания файлов .content.*, и для сканирования кода на наличие вызовов useIntlayer. Теперь:

    • contentDir: предназначен исключительно для файлов объявлений контента.
    • codeDir: предназначен исключительно для кода вашего приложения, который требует трансформации (например, pruning, удаление неиспользуемого кода, оптимизация).

    Миграция:

    Если ранее вы устанавливали contentDir, Intlayer v8 будет использовать его также в качестве значения по умолчанию для codeDir, но при этом выдаст предупреждение. Вам следует явно указать codeDir в конфигурации.

    Поведение в v7:

    intlayer.config.ts
    export default {
      content: {
        contentDir: ["src", "@packages/design-system"], // Используется и для контента, и для кода
      },
    };
    

    Поведение в v8:

    intlayer.config.ts
    export default {
      content: {
        contentDir: ["src/content", "@packages/design-system"], // Здесь только наблюдение за файлами src/content/*.content.* и @packages/design-system/dist/*.content.*
        codeDir: ["src", "@packages/design-system"], // Здесь только сканирование для трансформации кода и файлы @packages/design-system/src/*.content.*
      },
    };
    

    Фреймворк: улучшения для Svelte

    Markdown и HTML-контент в Svelte теперь автоматически парсятся в HTML при преобразовании в строку. Это значительно упрощает использование синтаксиса Svelte {@html}, поскольку теперь вы можете просто передавать узел контента напрямую.

    Поддержка Angular: Сборщик Vite (v8.11.0)

    Для пользователей Angular Intlayer теперь нативно поддерживает сборщик Vite через новый кастомный плагин esbuild, ускоряя время разработки и сборки.

    Подробности о конфигурации см. в Документации по окружению Angular.

    Генерация карты сайта (Sitemap) (v8.6.0)

    Intlayer теперь автоматически генерирует карты сайта на основе ваших локалей и режима маршрутизации. Это особенно полезно для SEO-оптимизации в архитектурах роутеров, таких как TanStack Router:

    src/routes/sitemap.xml.ts
    import { createFileRoute } from "@tanstack/react-router";
    import { generateSitemap } from "intlayer";
    
    const SITE_URL = (
      import.meta.env.VITE_SITE_URL ?? "http://localhost:3000"
    ).replace(/\/$/, "");
    
    export const Route = createFileRoute("/sitemap.xml")({
      server: {
        handlers: {
          GET: async () => {
            const sitemap = generateSitemap(
              [
                { path: "/", changefreq: "daily", priority: 1.0 },
                { path: "/about", changefreq: "monthly", priority: 0.8 },
              ],
              { siteUrl: SITE_URL }
            );
    
            return new Response(sitemap, {
              headers: { "Content-Type": "application/xml" },
            });
          },
        },
      },
    });
    

    Примечания по миграции из v7

    Изменения в конфигурации

    • live property: Свойство live в словарях удалено. Используйте importMode: 'fetch' вместо него.
    • importMode: Свойство build.importMode в конфигурации устарело. Используйте dictionary.importMode вместо него.
    • contentDir и codeDir: contentDir теперь специально для файлов контента. Добавлено новое свойство codeDir для трансформации кода. Если codeDir не установлен, Intlayer будет использовать contentDir и выведет предупреждение.
    • Валидация схемы: Для использования новой функции schema убедитесь, что в вашем проекте установлен zod.

    Полезные ссылки