Автор:
    Дата створення:2025-09-22Останнє оновлення:2026-06-14

    Новий Intlayer v8, що нового?

    Ласкаво просимо до Intlayer v8! У цьому випуску основна увага приділена покращенню досвіду розробника за допомогою автоматичного виявлення контенту, забезпеченню цілісності даних через валідацію схем і наданню більшого контролю над управлінням словниками.

    youtube.com

    Зміст

    Еволюція розширеного вмісту: 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. Використання утиліти:

    typescript
    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>Hello World</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>Hello World</p>");
    

    1. Використання компонента:

    vue
    <script setup>
    import { HTMLRenderer } from "vue-intlayer/html";
    </script>
    
    <template>
    <HTMLRenderer content="<p>Hello World</p>" />
    </template>
    

    1. Використання компонента:

    svelte
    <script>
    import { HTMLRenderer } from "svelte-intlayer/html";
    </script>
    
    <HTMLRenderer value="<p>Hello World</p>" />
    

    2. Використання хука:

    svelte
    <script>
    import { useHTMLRenderer } from "svelte-intlayer/html";
    const render = useHTMLRenderer();
    </script>
    
    {@html render("<p>Hello World</p>")}
    

    3. Використання утиліти:

    svelte
    <script>
    import { renderHTML } from "svelte-intlayer/html";
    </script>
    
    {@html renderHTML("<p>Hello World</p>")}
    

    1. Пряме використання:

    В Angular ви можете використовувати стандартне зв'язування [innerHTML].

    html
    <div [innerHTML]="'<p>Hello World</p>'"></div>
    

    1. Використання компонента:

    tsx
    import { HTMLRenderer } from "solid-intlayer/html";
    
    <HTMLRenderer>
    {"<p>Hello World</p>"}
    </HTMLRenderer>
    

    2. Використання хука:

    tsx
    import { useHTMLRenderer } from "solid-intlayer/html";
    
    const render = useHTMLRenderer();
    
    return <div>{render("<p>Hello World</p>")}</div>;
    

    3. Використання утиліти:

    tsx
    import { renderHTML } from "solid-intlayer/html";
    
    return <div>{renderHTML("<p>Hello World</p>")}</div>;
    

    1. Використання компонента:

    tsx
    import { HTMLRenderer } from "preact-intlayer/html";
    
    <HTMLRenderer>
    {"<p>Hello World</p>"}
    </HTMLRenderer>
    

    2. Використання хука:

    tsx
    import { useHTMLRenderer } from "preact-intlayer/html";
    
    const render = useHTMLRenderer();
    
    return <div>{render("<p>Hello World</p>")}</div>;
    

    3. Використання утиліти:

    tsx
    import { renderHTML } from "preact-intlayer/html";
    
    return <div>{renderHTML("<p>Hello World</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: Новий клієнтський хук, який безшумно виправляє адресний рядок до «гарної» локалізованої 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.

    Для отримання додаткової інформації та посібників з інтеграції див. документацію Custom URL Rewrites.

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

    У версії v8 значення вставок тепер можуть приймати React-елементи (або Vue-вузли) на додаток до рядків і чисел. Це дозволяє вставляти багаті, інтерактивні компоненти безпосередньо у ваші шаблони вставок.

    Intlayer тепер надійно обробляє вкладені вузли React і Preact всередині вставок, гарантуючи, що складні UI-структури зберігаються та відображаються правильно.

    Приклад:

    src/example.content.ts
    import { insert } from "intlayer";
    
    export default {
      key: "my-key",
      content: {
        myInsertion: insert("Hi {{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 (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({
          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: "About Our Company - Learn More About Us",
        description: "Discover our company's mission, values, and team.",
      },
    } satisfies Dictionary;
    
    export default aboutPageMetaContent;
    

    Якщо контент не відповідає схемі (наприклад, заголовок занадто короткий), процес збірки видасть помилку.

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

    Тип IntlayerNode було оновлено, щоб дозволити виклик примітивних методів JavaScript безпосередньо на вузлах контенту. Це дає змогу взаємодіяти з вашим локалізованим контентом так, ніби це стандартний примітив рядка, числа, булевого значення або масиву.

    Підтримується в React, Preact, Solid, Svelte, Vue та Angular.

    Приклад з рядком (String)

    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"); // Повертає змінений рядок
    

    Приклад з масивом (Array)

    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("Hi {{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: "Hi {{name}}", // Автоматично визначається як Insertion
      },
    };
    

    Підлягаючий 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

    Новий хук useIntl() тепер доступний у React, Next.js та Vue. Він надає прив'язаний до локалі об'єкт 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 для оптимізації вашого робочого процесу інтернаціоналізації:

    • Starting Time: Покращення продуктивності під час відкриття проєкту.
    • Caching: Покращений шар кешування для майже миттєвої валідації та автозаповнення.
    • Unused Keys & Duplicated Keys Detection: Нові функції для автоматичного виявлення невикористовуваних ключів та повторюваних ключів у ваших словниках, що допомагає підтримувати ваш контент чистим та ефективним.

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

    Intlayer v8.12.0 приносить значні покращення у досвід розробника:

    • Parcel Watcher: Спостерігач за контентом (content 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, розроблені для оптимізації вашого production-бандлу:

    • Мініфікація словників (Dictionary Minification): Мініфікує файли словників для зменшення їхнього розміру.
    • Очищення невикористовуваних ключів (Unused Key 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. Це дозволяє явно визначати, як словники мають завантажуватися: статично, динамічно або через live-синхронізацію.

    Режими

    • 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: Спеціально для коду вашого застосунку, який потребує трансформації (наприклад, обрізка, оптимізація).

    Міграція:

    Якщо у вас раніше було встановлено 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 Bundler (v8.11.0)

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

    Детальніше про конфігурацію див. у Документації середовища Angular.

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

    Intlayer тепер автоматично генерує карти сайтів (sitemaps) на основі ваших локалей та режиму маршрутизації. Це особливо корисно для 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: Властивість live у словниках видалена. Використовуйте importMode: 'fetch' замість неї.
    • importMode: Властивість build.importMode у конфігурації застаріла. Використовуйте dictionary.importMode замість неї.
    • contentDir та codeDir: contentDir тепер призначений саме для файлів контенту. Додано нову властивість codeDir для трансформації коду. Якщо codeDir не встановлено, Intlayer повернеться до contentDir та виведе попередження.
    • Валідація схеми: Щоб використовувати нову функцію schema, переконайтеся, що у вашому проєкті встановлено zod.

    Корисні посилання