作者:
    Creation:2025-09-22Last update:2026-06-14

    Intlayer v8 新功能 - 有什么新变化?

    欢迎使用 Intlayer v8!此版本侧重于通过自动内容检测提升开发者体验、通过 schema 验证确保数据完整性,并为字典管理提供更细粒度的控制。

    youtube.com

    目录

    富内容演进:Markdown & HTML

    Intlayer v8 对富内容的处理方式进行了重大改进,引入了 HTML 节点(v7 中不存在),并统一了与 Markdown 节点(v7 中已存在但经过增强)的 API。

    统一的 .use() API

    我们为 Markdown 和 HTML 节点引入了 .use() 方法。此方法允许你自定义渲染过程中使用的 HTML 标签或组件。

    • 组件替换:你可以轻松地将 HTML 标签或自定义组件替换为你自己的框架组件(例如,将 <a> 替换为 NextLink,或将 <CustomCmp> 替换为 React 组件)。
    • 类型安全:所有提供组件的函数都是完全类型化 (fully typed)的,确保你接收到正确的 props。

    默认渲染行为

    在 v7 中,如果没有定义 provider,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>
    }}
    >
    {"# My Title"}
    </MarkdownRenderer>
    

    2. 使用 Hook:

    tsx
    import { useMarkdownRenderer } from "react-intlayer/markdown";
    
    const renderMarkdown = useMarkdownRenderer({
    components: {
      h1: ({ children }) => <h1 className="text-red-500">{children}</h1>
    }
    });
    
    return <div>{renderMarkdown("# My Title")}</div>;
    

    3. 使用实用函数:

    tsx
    import { renderMarkdown } from "react-intlayer/markdown";
    
    const html = renderMarkdown("# My Title", {
    forceBlock: true
    });
    

    1. 使用组件:

    vue
    <script setup>
    import { MarkdownRenderer } from "vue-intlayer/markdown";
    </script>
    
    <template>
    <MarkdownRenderer :forceBlock="true" content="# My Title" />
    </template>
    

    1. 使用组件:

    svelte
    <script>
    import { MarkdownRenderer } from "svelte-intlayer/markdown";
    </script>
    
    <MarkdownRenderer forceBlock={true} value="# My Title" />
    

    2. 使用 Hook:

    svelte
    <script>
    import { useMarkdownRenderer } from "svelte-intlayer/markdown";
    const render = useMarkdownRenderer();
    </script>
    
    {@html render("# My Title")}
    

    3. 使用实用函数:

    svelte
    <script>
    import { renderMarkdown } from "svelte-intlayer/markdown";
    </script>
    
    {@html renderMarkdown("# My Title")}
    

    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}>
    {"# My Title"}
    </MarkdownRenderer>
    

    2. 使用 Hook:

    tsx
    import { useMarkdownRenderer } from "solid-intlayer/markdown";
    
    const render = useMarkdownRenderer();
    
    return <div>{render("# My Title")}</div>;
    

    3. 使用实用函数:

    tsx
    import { renderMarkdown } from "solid-intlayer/markdown";
    
    return <div>{renderMarkdown("# My Title")}</div>;
    

    1. 使用组件:

    tsx
    import { MarkdownRenderer } from "preact-intlayer/markdown";
    
    <MarkdownRenderer forceBlock={true}>
    {"# My Title"}
    </MarkdownRenderer>
    

    2. 使用 Hook:

    tsx
    import { useMarkdownRenderer } from "preact-intlayer/markdown";
    
    const render = useMarkdownRenderer();
    
    return <div>{render("# My Title")}</div>;
    

    3. 使用实用函数:

    tsx
    import { renderMarkdown } from "preact-intlayer/markdown";
    
    return <div>{renderMarkdown("# My Title")}</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. 使用 Hook:

    tsx
    import { useHTMLRenderer } from "react-intlayer/html";
    
    const renderHTML = useHTMLRenderer({
    components: {
      strong: ({ children }) => <b className="font-bold">{children}</b>
    }
    });
    
    return <div>{renderHTML("<p>Hello <strong>World</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. 使用 Hook:

    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. 使用 Hook:

    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. 使用 Hook:

    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 文件扩展名声明内容。这非常适合长篇内容、博客、文档和隐私政策。

    这些文件可以从可视化编辑器完全编辑,为非技术团队成员提供了简化的工作流程。

    有关更多详细信息,请参阅 YAML 内容文件文档Markdown 内容文件文档

    自定义 URL 重写

    Intlayer v8 引入对 自定义 URL 重写 的支持,允许你定义与标准 /locale/path 结构不同的特定语言路径。此功能可显著提升本地 SEO,并为非英文用户提供更自然的使用体验。

    v8 的主要增强:

    • Framework Formatters: 新增 nextjsRewritesvelteKitRewritereactRouterRewritevueRouterRewritesolidRouterRewritetanstackRouterRewritenuxtRewriteviteRewrite,为每种路由器提供符合惯用语法的模式。
    • useRewriteURL Hook: 一个新的客户端 Hook,它在不触发路由导航的情况下静默地将地址栏更正为更友好的本地化 URL。
    • 自动 SEO 重定向: 内置代理现在会自动将用户从手动输入的规范路径(例如 /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;
    

    此功能通过 Intlayer 代理在 Next.jsVite 中开箱即用,并且可以轻松集成到其他路由器,例如 TanStack RouterReact RouterVue RouterSvelteKitSolid 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("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'), // Vue 元素
        })"
      />
    </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)

    基于 Unicode CLDR 项目定义的复数规则,引入了一个新的 plural 节点助手,改进了对复杂语言的支持。

    src/openings.content.ts
    import { plural, t, type Dictionary } from "intlayer";
    
    const openingsContent = {
      key: "total_openings",
      content: {
        totalOpenings: t({
          zh: plural({
            one: "{{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>
      );
    };
    

    更多信息,请参见 复数化文档

    内容 Schema 验证

    Intlayer v8 为字典引入了 schema 验证。你现在可以在配置中使用 Zod 定义可重用的验证 schema,并将其应用到你的内容文件中。这可以确保你的内容始终遵循预期的结构,并在构建时 (build time)捕获错误。

    1. 定义 Schemas

    intlayer.config.ts 中定义你的 schemas:

    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. 将 Schemas 应用到字典

    在字典定义中引用 schema 键:

    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;
    

    如果内容不符合模式(例如,title 太短),构建过程将抛出错误。

    TypeScript: IntlayerNode Primitive Accessors

    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 Example

    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 钩子

    现在在 React、Next.js 和 Vue 中可以使用新的 useIntl() 钩子。它提供了一个与语言环境绑定的 Intl 对象,能够自动使用当前语言来格式化数字、日期等,而无需手动传入 locale。

    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 & OXC-based LSP (v8.12.0)

    Intlayer v8.12.0 为开发者体验带来了重大改进:

    • Parcel Watcher:内容监听器已从 chokidar 迁移到 @parcel/watcher,利用原生 C++ 文件监听来提供更快的更新和显著降低的内存消耗。
    • 新的语言服务器协议 (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
    

    实验性构建优化:代码缩小和清理 (v8.7.0)

    Intlayer v8.7.0 在你的 intlayer.config.ts 中引入了新的实验性构建功能,旨在优化你的生产包:

    • 字典缩小化:缩小字典文件以减小大小。
    • 未使用密钥清理:扫描并从字典中移除未使用的翻译密钥,将活跃密钥重命名为短字符(例如 "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 属性。这样可以明确定义 dictionaries 应如何加载:静态、动态,或通过实时同步。

    模式

    • static(默认):dictionary 在构建时 (build time)被打包。性能最佳。
    • dynamic:dictionary 在运行时加载(例如,通过 JSON fetch 或 suspense)。
    • fetch:dictionary 在运行时从 CMS/服务器 获取并同步。

    迁移:

    v7 配置v8 配置
    live: trueimportMode: 'fetch'
    live: falseimportMode: 'static'(或 'dynamic'

    注意:在 Intlayer v8 中,importMode 属性已从 build 配置移动到 intlayer.config.ts 中的 dictionary 配置。这样可以为所有字典定义一个默认的导入模式,同时仍然可以在每个字典级别上覆盖该设置。

    全局配置示例:

    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 将内容源配置与内部 system 和输出路径分离。这使得 content 属性更简洁,并且明确哪些设置由用户管理,哪些由 Intlayer 系统管理。

    以下属性已从 content 移动到 intlayer.config.ts 中的新 system 属性:

    • 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"], // 仅在此扫描用于代码转换的代码
      },
    };
    

    框架:Svelte 改进

    在 Svelte 中的 Markdown 和 HTML 内容在被字符串化(stringified)时现在会自动解析为 HTML。这样与 Svelte 的 {@html} 语法一起使用更加方便,因为你现在可以直接传递内容节点。

    Angular 支持:Vite Bundler (v8.11.0)

    对于 Angular 用户,Intlayer 现在通过新的自定义 esbuild 插件原生支持 Vite bundler,加快了开发和构建时间。

    有关配置详情,请参阅 Angular 环境文档

    网站地图生成 (v8.6.0)

    Intlayer 现在根据您的语言环境和路由模式自动生成网站地图。这对于 TanStack Router 等路由架构中的 SEO 优化特别有用:

    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
    • contentDircodeDircontentDir 现在专用于内容文件。新增 codeDir 属性用于代码转换。如果未设置 codeDir,Intlayer 将回退到使用 contentDir 并记录一个警告。
    • 模式验证:要使用新的 schema 功能,请确保在项目中已安装 zod

    有用的链接