---
createdAt: 2025-09-22
updatedAt: 2026-01-26
title: Intlayer v8 — Có gì mới?
description: Khám phá những điểm mới trong Intlayer v8. Cải tiến chính về trải nghiệm nhà phát triển, xác thực theo schema và quản lý từ điển.
keywords:
  - Intlayer
  - CMS
  - Trải nghiệm nhà phát triển
  - Tính năng
  - React
  - Next.js
  - JavaScript
  - TypeScript
youtubeVideo: https://www.youtube.com/watch?v=ia6JmVf-kkU
slugs:
  - doc
  - releases
  - v8
---

# Intlayer v8 — Có gì mới?

Chào mừng đến với Intlayer v8! Bản phát hành này tập trung vào nâng cao trải nghiệm nhà phát triển với phát hiện nội dung tự động, đảm bảo tính toàn vẹn dữ liệu bằng xác thực theo schema và cung cấp nhiều khả năng kiểm soát hơn đối với quản lý từ điển.

<iframe
  src="https://www.youtube.com/embed/ia6JmVf-kkU"
  className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
  title="Demo Video - New Intlayer v8 - What's new?"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
  loading="lazy"
/>

## Mục lục

<TOC levels={[2]} maxDepth={1} />

---

## Sự tiến hóa của nội dung phong phú: Markdown & HTML

Intlayer v8 mang đến những cải tiến lớn về cách xử lý nội dung phong phú, giới thiệu **nút HTML** (không tồn tại trong v7) và hợp nhất API với **nút Markdown** (đã tồn tại trong v7 nhưng được nâng cao).

### API `.use()` hợp nhất

Chúng tôi đã giới thiệu phương thức `.use()` cho cả nút Markdown và HTML. Phương thức này cho phép bạn tùy chỉnh các thẻ HTML hoặc các component được sử dụng trong quá trình render.

- **Thay thế component**: Bạn có thể dễ dàng thay thế các thẻ HTML hoặc các component tùy chỉnh bằng các component framework của riêng bạn (ví dụ: thay thế `<a>` bằng `NextLink` hoặc `<CustomCmp>` bằng một component React).
- **An toàn kiểu dữ liệu (Type Safety)**: Tất cả các hàm cung cấp component đều được định kiểu đầy đủ, đảm bảo bạn nhận được các props chính xác.

### Hành vi render mặc định

Trong v7, nếu không có provider nào được xác định, các nút Markdown sẽ được render dưới dạng chuỗi thô, thường yêu cầu các thư viện bên ngoài để phân tích cú pháp.

**Trong v8, Intlayer bao gồm trình phân tích cú pháp Markdown nội bộ của riêng mình.** Theo mặc định, các nút Markdown hiện được render trực tiếp dưới dạng HTML mà không cần bất kỳ thư viện bên ngoài nào.

### Các tiện ích Renderer & Provider mới

Chúng tôi đã giới thiệu các hàm renderer và component độc lập mới để cung cấp cho bạn nhiều quyền kiểm soát hơn bên ngoài luồng `useIntlayer` tiêu chuẩn.

- **Markdown**: `MarkdownRenderer`, `useMarkdownRenderer`, `renderMarkdown`. (Lưu ý: `MarkdownProvider` đã tồn tại trong v7 nhưng hiện tích hợp với các công cụ mới này).
- **HTML**: `HTMLRenderer`, `useHTMLRenderer`, `renderHTML`, `HTMLProvider`.

#### Ví dụ: Công cụ render Markdown

<Tabs group='framework'>
  <Tab label="React / Next.js" value="react">

    **1. Sử dụng Thành phần (Component):**

    ```tsx
    import { MarkdownRenderer } from "react-intlayer";

    <MarkdownRenderer
      forceBlock={true}
      components={{
        h1: ({ children }) => <h1 className="text-2xl">{children}</h1>
      }}
    >
      {"# My Title"}
    </MarkdownRenderer>
    ```

    **2. Sử dụng Hook:**

    ```tsx
    import { useMarkdownRenderer } from "react-intlayer";

    const renderMarkdown = useMarkdownRenderer({
      components: {
        h1: ({ children }) => <h1 className="text-red-500">{children}</h1>
      }
    });

    return <div>{renderMarkdown("# My Title")}</div>;
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```tsx
    import { renderMarkdown } from "react-intlayer";

    const html = renderMarkdown("# My Title", {
      forceBlock: true
    });
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    **1. Sử dụng Thành phần (Component):**

    ```vue
    <script setup>
    import { MarkdownRenderer } from "vue-intlayer";
    </script>

    <template>
      <MarkdownRenderer :forceBlock="true" content="# My Title" />
    </template>
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    **1. Sử dụng Thành phần (Component):**

    ```svelte
    <script>
      import { MarkdownRenderer } from "svelte-intlayer";
    </script>

    <MarkdownRenderer forceBlock={true} value="# My Title" />
    ```

    **2. Sử dụng Hook:**

    ```svelte
    <script>
      import { useMarkdownRenderer } from "svelte-intlayer";
      const render = useMarkdownRenderer();
    </script>

    {@html render("# My Title")}
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```svelte
    <script>
      import { renderMarkdown } from "svelte-intlayer";
    </script>

    {@html renderMarkdown("# My Title")}
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    **1. Sử dụng Dịch vụ (Service):**

    ```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);
      }
    }
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    **1. Sử dụng Thành phần (Component):**

    ```tsx
    import { MarkdownRenderer } from "solid-intlayer";

    <MarkdownRenderer forceBlock={true}>
      {"# My Title"}
    </MarkdownRenderer>
    ```

    **2. Sử dụng Hook:**

    ```tsx
    import { useMarkdownRenderer } from "solid-intlayer";

    const render = useMarkdownRenderer();

    return <div>{render("# My Title")}</div>;
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```tsx
    import { renderMarkdown } from "solid-intlayer";

    return <div>{renderMarkdown("# My Title")}</div>;
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    **1. Sử dụng Thành phần (Component):**

    ```tsx
    import { MarkdownRenderer } from "preact-intlayer";

    <MarkdownRenderer forceBlock={true}>
      {"# My Title"}
    </MarkdownRenderer>
    ```

    **2. Sử dụng Hook:**

    ```tsx
    import { useMarkdownRenderer } from "preact-intlayer";

    const render = useMarkdownRenderer();

    return <div>{render("# My Title")}</div>;
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```tsx
    import { renderMarkdown } from "preact-intlayer";

    return <div>{renderMarkdown("# My Title")}</div>;
    ```

  </Tab>
</Tabs>

#### Ví dụ: Công cụ render HTML

<Tabs group='framework'>
  <Tab label="React / Next.js" value="react">

    **1. Sử dụng Thành phần (Component):**

    ```tsx
    import { HTMLRenderer } from "react-intlayer";

    <HTMLRenderer
      components={{
        p: ({ children }) => <p className="mb-4">{children}</p>
      }}
    >
      {"<p>Hello World</p>"}
    </HTMLRenderer>
    ```

    **2. Sử dụng Hook:**

    ```tsx
    import { useHTMLRenderer } from "react-intlayer";

    const renderHTML = useHTMLRenderer({
      components: {
        strong: ({ children }) => <b className="font-bold">{children}</b>
      }
    });

    return <div>{renderHTML("<p>Hello <strong>World</strong></p>")}</div>;
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```tsx
    import { renderHTML } from "react-intlayer";

    const html = renderHTML("<p>Hello World</p>");
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    **1. Sử dụng Thành phần (Component):**

    ```vue
    <script setup>
    import { HTMLRenderer } from "vue-intlayer";
    </script>

    <template>
      <HTMLRenderer content="<p>Hello World</p>" />
    </template>
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    **1. Sử dụng Thành phần (Component):**

    ```svelte
    <script>
      import { HTMLRenderer } from "svelte-intlayer";
    </script>

    <HTMLRenderer value="<p>Hello World</p>" />
    ```

    **2. Sử dụng Hook:**

    ```svelte
    <script>
      import { useHTMLRenderer } from "svelte-intlayer";
      const render = useHTMLRenderer();
    </script>

    {@html render("<p>Hello World</p>")}
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```svelte
    <script>
      import { renderHTML } from "svelte-intlayer";
    </script>

    {@html renderHTML("<p>Hello World</p>")}
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    **1. Sử dụng Trực tiếp:**

    Trong Angular, bạn có thể sử dụng liên kết `[innerHTML]` tiêu chuẩn.

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

  </Tab>
  <Tab label="Solid" value="solid">

    **1. Sử dụng Thành phần (Component):**

    ```tsx
    import { HTMLRenderer } from "solid-intlayer";

    <HTMLRenderer>
      {"<p>Hello World</p>"}
    </HTMLRenderer>
    ```

    **2. Sử dụng Hook:**

    ```tsx
    import { useHTMLRenderer } from "solid-intlayer";

    const render = useHTMLRenderer();

    return <div>{render("<p>Hello World</p>")}</div>;
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```tsx
    import { renderHTML } from "solid-intlayer";

    return <div>{renderHTML("<p>Hello World</p>")}</div>;
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    **1. Sử dụng Thành phần (Component):**

    ```tsx
    import { HTMLRenderer } from "preact-intlayer";

    <HTMLRenderer>
      {"<p>Hello World</p>"}
    </HTMLRenderer>
    ```

    **2. Sử dụng Hook:**

    ```tsx
    import { useHTMLRenderer } from "preact-intlayer";

    const render = useHTMLRenderer();

    return <div>{render("<p>Hello World</p>")}</div>;
    ```

    **3. Sử dụng Hàm tiện ích:**

    ```tsx
    import { renderHTML } from "preact-intlayer";

    return <div>{renderHTML("<p>Hello World</p>")}</div>;
    ```

  </Tab>
</Tabs>

Để biết thêm chi tiết, xem [Tài liệu Nội dung HTML](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/dictionary/html.md) và [Tài liệu Markdown](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/dictionary/markdown.md).

---

## Viết lại URL tùy chỉnh

Intlayer v8 giới thiệu hỗ trợ cho **Viết lại URL tùy chỉnh**, cho phép bạn định nghĩa các đường dẫn theo ngôn ngữ (locale) khác với cấu trúc chuẩn `/locale/path`. Đây là một tính năng mạnh mẽ để cải thiện SEO địa phương và mang lại trải nghiệm người dùng tự nhiên hơn cho những người không nói tiếng Anh.

**Các cải tiến chính trong v8:**

- **Bộ định dạng cho framework**: Thêm `nextjsRewrite`, `svelteKitRewrite`, `reactRouterRewrite`, `vueRouterRewrite`, `solidRouterRewrite`, `tanstackRouterRewrite`, `nuxtRewrite`, và `viteRewrite` để cung cấp cú pháp mẫu đặc trưng cho từng router.
- **Hook `useRewriteURL`**: một hook phía client mới tự động chỉnh sửa thanh địa chỉ thành URL địa phương "đẹp" mà không kích hoạt điều hướng của router.
- **Chuyển hướng SEO tự động**: Các proxy tích hợp bây giờ tự động chuyển hướng người dùng từ các đường dẫn chuẩn được gõ thủ công (ví dụ: `/fr/about`) sang các phiên bản địa phương "đẹp" hơn của chúng (ví dụ: `/fr/a-propos`).

**Cấu hình ví dụ:**

<Tabs group='routers'>
  <Tab label="Next.js" value="nextjs">

    ```typescript fileName="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;
    ```

  </Tab>
    <Tab label="React Router" value="reactrouter">

    ```typescript fileName="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;
    ```

  </Tab>
  <Tab label="Vite (Vue, Solid, Svelte)" value="vite">

    ```typescript fileName="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;
    ```

  </Tab>
  <Tab label="Nuxt" value="nuxt">

    ```typescript fileName="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;
    ```

  </Tab>
  <Tab label="SvelteKit" value="sveltekit">

    ```typescript fileName="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;
    ```

  </Tab>
</Tabs>

Tính năng này được hỗ trợ sẵn (out-of-the-box) trong **Next.js** và **Vite** thông qua các proxies của Intlayer, và có thể dễ dàng tích hợp vào các router khác như **TanStack Router**, **React Router**, **Vue Router**, **SvelteKit**, và **Solid Router**.

Để biết thêm thông tin và hướng dẫn tích hợp, xem [Tài liệu Viết lại URL tùy chỉnh](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/custom_url_rewrites.md).

---

### Giá trị insertion được cải tiến

Trong v8, các giá trị insertion bây giờ có thể **chấp nhận React elements (hoặc Vue nodes)** bên cạnh chuỗi và số. Điều này cho phép bạn chèn các thành phần phong phú và tương tác trực tiếp vào các mẫu insertion của mình.

Intlayer hiện xử lý mạnh mẽ các node React và Preact lồng nhau trong các insertions, đảm bảo các cấu trúc UI phức tạp được giữ nguyên và kết xuất đúng.

**Ví dụ:**

```typescript fileName="src/example.content.ts"
import { insert } from "intlayer";

export default {
  key: "my-key",
  content: {
    myInsertion: insert("Hi {{name}}"),
  },
};
```

<Tabs group='framework'>
  <Tab label="Next.js" value="nextjs">

    ```tsx
    import { useIntlayer } from "next-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // số
          // hoặc
          name: "John", // chuỗi
          // hoặc
          name: <span>John</span>, // phần tử React
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="React" value="react">

    ```tsx
    import { useIntlayer } from "react-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // số
          // hoặc
          name: "John", // chuỗi
          // hoặc
          name: <span>John</span>, // phần tử React
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    ```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,
            // hoặc
            name: 'John',
            // hoặc
            name: h('span', 'John'),
          })"
        />
      </div>
    </template>
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    ```tsx
    import { useIntlayer } from "preact-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // số
          // hoặc
          name: "John", // chuỗi
          // hoặc
          name: <span>John</span>, // phần tử Preact
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    ```tsx
    import { useIntlayer } from "solid-intlayer";

    const { myInsertion } = useIntlayer("my-key");

    return (
      <div>
        {myInsertion({
          name: 2, // số
          // hoặc
          name: "John", // chuỗi
          // hoặc
          name: <span>John</span>, // phần tử Solid
        })}
      </div>
    );
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    ```svelte
    <script>
      import { useIntlayer } from "svelte-intlayer";

      const { myInsertion } = useIntlayer("my-key");
    </script>

    <div>
      {myInsertion({
        name: 2, // số
        // hoặc
        name: "John", // chuỗi
      })}
    </div>
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    ```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");
    }
    ```

  </Tab>
</Tabs>

## Xác thực Schema Nội dung

Intlayer v8 giới thiệu tính năng xác thực schema cho các dictionaries. Bây giờ bạn có thể định nghĩa các schema xác thực có thể tái sử dụng trong cấu hình của mình bằng Zod và áp dụng chúng cho các file nội dung. Điều này đảm bảo nội dung của bạn luôn tuân theo cấu trúc mong đợi, giúp phát hiện lỗi trong quá trình build.

### 1. Định nghĩa Schema

Định nghĩa các schema trong `intlayer.config.ts`:

```typescript fileName="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. Áp dụng Schema cho các Dictionaries

Tham chiếu khóa schema trong định nghĩa dictionary của bạn:

```typescript fileName="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<"seo-metadata">;

export default aboutPageMetaContent;
```

Nếu nội dung không khớp với schema (ví dụ: title quá ngắn), quá trình build sẽ báo lỗi.

---

### Phát hiện nội dung tự động nâng cao

Trong v8, Intlayer tự động nhận diện thông minh cú pháp Markdown, thẻ HTML và các chèn biến trong các chuỗi nội dung của bạn. Điều này có nghĩa là bạn thường có thể bỏ qua các hàm trợ giúp như `md()`, `html()` hoặc `insert()`.

Hành vi này được bật theo mặc định. Bạn có thể tinh chỉnh việc phát hiện này toàn cục trong `intlayer.config.ts` hoặc cho từng dictionary.

#### Kiểm soát chi tiết

Bạn có thể bật hoặc tắt các loại chuyển đổi cụ thể:

```typescript fileName="intlayer.config.ts"
export default {
  dictionary: {
    // contentAutoTransformation: false (mặc định)
    contentAutoTransformation: {
      markdown: true,
      html: true,
      insertion: false, // Vô hiệu hóa phát hiện tự động cho insertion
    },
  },
};
```

**Hành vi v7 (Bao bọc thủ công):**

```typescript fileName="src/example.content.ts"
import { md, insert } from "intlayer";

export default {
  key: "my-key",
  content: {
    myMarkdown: md("## Hello World"),
    myInsertion: insert("Hi {{name}}"),
  },
};
```

**Hành vi v8 (Phát hiện tự động):**

```typescript fileName="src/example.content.ts"
export default {
  key: "my-key",
  contentAutoTransformation: true, // Có thể cũng được đặt bởi định nghĩa dictionary hoặc toàn cục trong intlayer.config.ts
  content: {
    myMarkdown: "## Xin chào Thế giới", // Tự động được phát hiện là Markdown
    myHTML: "<p>Xin chào Thế giới</p>", // Tự động được phát hiện là HTML
    myInsertion: "Chào {{name}}", // Tự động được phát hiện là Insertion
  },
};
```

The underlying JSON result remains the same, preserving the rich type information needed for rendering:

```json
{
  "key": "my-key",
  "content": {
    "myMarkdown": {
      "nodeType": "markdown",
      "markdown": "## Xin chào Thế giới"
    },
    "myHTML": {
      "nodeType": "html",
      "html": "<p>Xin chào Thế giới</p>"
    },
    "myInsertion": {
      "nodeType": "insertion",
      "insertion": "Hi {{name}}"
    }
  }
}
```

---

## Địa phương hóa: hook `useIntl` mới

Một hook `useIntl()` mới hiện có sẵn trong React, Next.js và Vue. Nó cung cấp một đối tượng `Intl` liên kết với locale, tự động sử dụng ngôn ngữ hiện tại để định dạng số, ngày tháng và nhiều thứ khác, mà không cần phải truyền thủ công locale.

<Tabs group='framework'>
  <Tab label="Next.js" value="nextjs">

    ```tsx
    import { useIntl } from "next-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="React" value="react">

    ```tsx
    import { useIntl } from "react-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="Vue" value="vue">

    ```vue
    <script setup>
    import { useIntl } from "vue-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    </script>
    ```

  </Tab>
  <Tab label="Preact" value="preact">

    ```tsx
    import { useIntl } from "preact-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="Solid" value="solid">

    ```tsx
    import { useIntl } from "solid-intlayer";

    const intl = useIntl();

    const formattedPrice = new intl.NumberFormat({
      style: "currency",
      currency: "USD",
    }).format(123.45);
    ```

  </Tab>
  <Tab label="Svelte" value="svelte">

    ```svelte
    <script>
      import { useIntl } from "svelte-intlayer";

      const intl = useIntl();

      const formattedPrice = new intl.NumberFormat({
        style: "currency",
        currency: "USD",
      }).format(123.45);
    </script>
    ```

  </Tab>
  <Tab label="Angular" value="angular">

    ```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)
      );
    }
    ```

  </Tab>
</Tabs>

---

## Công cụ: Cải tiến Extension VSCode

Phần mở rộng Intlayer cho VSCode nhận được các cập nhật lớn trong v8 nhằm hợp lý hóa quy trình làm việc về quốc tế hóa của bạn:

- **Thời gian khởi động**: Cải thiện hiệu suất khi mở một dự án.
- **Bộ nhớ đệm**: Tầng caching được nâng cấp để xác thực và gợi ý tự động gần như tức thì.
- **Phát hiện Khóa Không Sử dụng & Khóa Trùng Lặp**: Các tính năng mới để tự động phát hiện **khóa không sử dụng** và **khóa trùng lặp** trong các từ điển của bạn, giúp giữ nội dung gọn gàng và hiệu quả.

---

## Tối ưu trình biên dịch

Intlayer v8 bao gồm một lớp cache mới cho trình biên dịch Markdown và HTML. Điều này đảm bảo rằng các chuỗi nội dung giống hệt với cùng cấu hình chỉ được phân tích một lần, giảm đáng kể chi phí khi tái kết xuất hoặc khi sử dụng cùng nội dung ở nhiều nơi.

<Tabs group='bundler'>
  <Tab label="Next.js" value="nextjs">
  
    ```typescript fileName="babel.config.js"
      const {
      intlayerExtractBabelPlugin,
      intlayerOptimizeBabelPlugin,
      getExtractPluginOptions,
      getOptimizePluginOptions,
    } = require('@intlayer/babel');

    module.exports = {
      presets: ['next/babel'],
      plugins: [
        // Trích xuất nội dung từ components vào dictionaries
        [intlayerExtractBabelPlugin, getExtractPluginOptions()],
        // Tối ưu các imports bằng cách thay thế useIntlayer bằng import trực tiếp các dictionary
        [intlayerOptimizeBabelPlugin, getOptimizePluginOptions()],
      ],
    };
    ```

  </Tab>
  <Tab label="Vite" value="vite">
   
    ```typescript fileName="vite.config.js"
    import { defineConfig } from 'vite';
    import {intlayer, intlayerCompiler} from 'vite-intlayer';

    export default defineConfig({
      plugins: [intlayer(), intlayerCompiler()],
    });
    ```

> Đối với Vue / Svelte, bạn sẽ cần cài gói compiler tương ứng:
>
> ```bash
> # Cho Vue
> npm install @intlayer/vue-compiler
> ```
>
> ```bash
> # Cho Svelte
> npm install @intlayer/svelte-compiler
> ```

  </Tab>
</Tabs>

---

## Tính linh hoạt: Chế độ import hợp nhất

Thuộc tính boolean `live` đã bị đánh dấu là deprecated và được thay bằng thuộc tính `importMode` toàn diện hơn. Điều này cho phép xác định rõ ràng cách các dictionary nên được tải: theo kiểu tĩnh, động, hoặc qua đồng bộ trực tiếp.

### Các chế độ

- **`static`** (Mặc định): Dictionary được đóng gói tại thời điểm build. Tối ưu cho hiệu năng.
- **`dynamic`**: Dictionary được tải vào thời điểm runtime (ví dụ: thông qua fetch JSON hoặc suspense).
- **`fetch`**: Dictionary được lấy từ CMS/Server vào thời điểm runtime và được đồng bộ.

**Di chuyển:**

| Cấu hình v7   | Cấu hình v8                             |
| :------------ | :-------------------------------------- |
| `live: true`  | `importMode: 'fetch'`                   |
| `live: false` | `importMode: 'static'` (hoặc 'dynamic') |

Lưu ý: Trong Intlayer v8, thuộc tính `importMode` đã được chuyển từ cấu hình `build` sang cấu hình `dictionary` trong `intlayer.config.ts`. Điều này cho phép bạn định nghĩa chế độ import mặc định cho tất cả các dictionary trong khi vẫn có thể ghi đè cho từng dictionary riêng lẻ.

**Ví dụ Cấu hình Toàn cục:**

```typescript fileName="intlayer.config.ts"
export default {
  dictionary: {
    importMode: "dynamic", // Mặc định toàn cục
  },
  // ...
};
```

**Ví dụ Dictionary:**

```typescript fileName="src/example.content.ts"
export default {
    key: 'my-key',
    importMode: "fetch", // Ghi đè cấu hình toàn cục
    content: { ... }
}
```

---

## Kiểm soát vị trí Dictionary

v8 giới thiệu thuộc tính `location` để quản lý rõ ràng nơi lưu trữ các dictionary và cách chúng đồng bộ hóa. Điều này đặc biệt hữu ích cho các luồng công việc hybrid liên quan đến cả file cục bộ và nội dung CMS từ xa.

### Tùy chọn

- **`local`**: Từ điển chỉ tồn tại cục bộ. Nó sẽ không được đẩy lên CMS từ xa.
- **`remote`**: Từ điển được quản lý từ xa. Khi đã được đẩy lên CMS, nó sẽ tách rời khỏi bản cục bộ. Từ điển từ xa sẽ được kéo từ CMS.
- **`local_and_remote`**: Từ điển tồn tại ở cả hai nơi. Thay đổi cục bộ được đẩy lên, và thay đổi từ xa được kéo về (đồng bộ hóa).

**Ví dụ:**

```typescript fileName="src/example.content.ts"
export default {
    key: 'my-key',
    location: "local", // Giữ từ điển này chỉ ở cục bộ
    content: { ... }
}
```

---

## Phân tách Cấu hình Hệ thống

Intlayer v8 tách cấu hình các nguồn nội dung khỏi các đường dẫn hệ thống nội bộ và đường dẫn đầu ra. Điều này làm gọn thuộc tính `content` và làm rõ những thiết lập nào dành cho quản lý bởi người dùng so với những thiết lập được quản lý bởi hệ thống Intlayer.

Các thuộc tính sau đã được chuyển từ `content` sang thuộc tính `system` mới trong `intlayer.config.ts`:

- `dictionariesDir`
- `moduleAugmentationDir`
- `unmergedDictionariesDir`
- `typesDir`
- `mainDir`
- `configDir`
- `cacheDir`
- `outputFilesPatternWithPath`

**Hành vi v7:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src"],
    dictionariesDir: ".intlayer/dictionary", // Bị trộn với cấu hình nguồn
  },
};
```

**Hành vi v8:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src"],
  },
  system: {
    dictionariesDir: ".intlayer/dictionary", // Được tách riêng rõ ràng
  },
};
```

---

## Tách thư mục nội dung và mã nguồn

Intlayer v8 tách cấu hình cho các tệp định nghĩa nội dung khỏi cấu hình cho chuyển đổi mã. Điều này cho phép việc theo dõi và quét chính xác hơn, cải thiện hiệu năng build.

Trước đây, `contentDir` được sử dụng cho cả việc theo dõi các tệp `.content.*` và quét mã để tìm các lời gọi `useIntlayer`. Hiện nay:

- **`contentDir`**: Dành riêng cho các tệp khai báo nội dung của bạn.
- **`codeDir`**: Dành riêng cho mã ứng dụng của bạn cần chuyển đổi (ví dụ: pruning, optimization).

**Di chuyển:**

Nếu trước đây bạn đã thiết lập `contentDir`, Intlayer v8 sẽ sử dụng nó làm mặc định cho `codeDir` nhưng sẽ ghi một cảnh báo. Bạn nên định nghĩa rõ `codeDir` trong cấu hình của mình.

**Hành vi v7:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src", "@packages/design-system"], // Được sử dụng cho cả content và code
  },
};
```

**Hành vi v8:**

```typescript fileName="intlayer.config.ts"
export default {
  content: {
    contentDir: ["src/content", "@packages/design-system"], // Chỉ theo dõi các file src/content/*.content.* ở đây và các file @packages/design-system/dist/*.content.*
    codeDir: ["src", "@packages/design-system"], // Chỉ quét để biến đổi mã ở đây
  },
};
```

---

## Framework: Cải tiến cho Svelte

Nội dung Markdown và HTML trong Svelte bây giờ tự động được phân tích cú pháp thành HTML khi được chuyển thành chuỗi. Điều này khiến việc sử dụng với cú pháp `{@html}` của Svelte dễ dàng hơn nhiều, vì bạn giờ có thể truyền trực tiếp node nội dung.

---

## Ghi chú nâng cấp từ v7

### Thay đổi cấu hình

- **`live` property**: Thuộc tính `live` trong các dictionary đã bị xóa. Hãy sử dụng `importMode: 'fetch'` thay thế.
- **importMode**: Thuộc tính `build.importMode` trong cấu hình đã bị đánh dấu là không còn được khuyến khích. Hãy sử dụng `dictionary.importMode` thay thế.
- **`contentDir` và `codeDir`**: `contentDir` hiện được dùng riêng cho các tệp nội dung. Một thuộc tính mới `codeDir` đã được thêm để chuyển đổi mã. Nếu `codeDir` không được thiết lập, Intlayer sẽ fallback về `contentDir` và ghi log một cảnh báo.
- **Xác thực Schema**: Để sử dụng tính năng `schema` mới, đảm bảo bạn đã cài `zod` trong dự án của mình.

---

## Liên kết hữu ích

- [Configuration Reference](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/configuration.md)
- [Content File Documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/dictionary/content_file.md)
- [HTML Content Documentation](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/dictionary/html.md)
- [Tài liệu Nội dung Markdown](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/dictionary/markdown.md)
- [Tài liệu Viết lại URL Tùy chỉnh](https://github.com/aymericzip/intlayer/blob/main/docs/docs/vi/custom_url_rewrites.md)
