著者:
    作成:2025-09-22最終更新:2026-06-14

    Intlayer v8 の新機能

    Intlayer v8 へようこそ!このリリースでは、自動コンテンツ検出による開発者体験の向上、スキーマ検証によるデータ整合性の確保、辞書管理に対するより細かい制御の提供に注力しています。

    youtube.com

    目次

    リッチコンテンツの進化: Markdown & HTML

    Intlayer v8 は、リッチコンテンツの処理方法に大きな改善をもたらしました。HTML ノード(v7 には存在しませんでした)の導入と、Markdown ノード(v7 から存在していましたが、強化されました)との API 統合が行われました。

    統合された .use() API

    Markdown と HTML ノードの両方に .use() メソッドが導入されました。このメソッドを使用すると、レンダリング時に使用される HTML タグやコンポーネントをカスタマイズできます。

    • コンポーネントの置換: HTML タグやカスタムコンポーネントを、自身のフレームワークコンポーネント(例: <a>NextLink に、<CustomCmp> を React コンポーネントに)で簡単に置き換えられます。
    • 型安全性: コンポーネントを提供するためのすべての関数は完全に型付けされており、正しいプロップを受け取ることが保証されます。

    デフォルトのレンダリング動作

    v7 では、プロバイダーが定義されていない場合、Markdown ノードは生の文字列としてレンダリングされ、解析のために外部ライブラリが必要になることがよくありました。

    v8 では、Intlayer に独自の内部 Markdown パーサーが含まれています。 デフォルトで、Markdown ノードは外部ライブラリを必要とせずに直接 HTML としてレンダリングされるようになりました。

    新しいレンダラーとプロバイダーのユーティリティ

    標準の useIntlayer フロー以外でも制御を強化できるように、新しいスタンドアロンのレンダラー関数とコンポーネントが導入されました。

    • Markdown: MarkdownRendereruseMarkdownRendererrenderMarkdown。(注: MarkdownProvider は v7 から存在していましたが、これらの新しいツールと統合されました)。
    • HTML: HTMLRendereruseHTMLRendererrenderHTMLHTMLProvider

    例: 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. フックの使用:

    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. フックの使用:

    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. フックの使用:

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

    3. ユーティリティ関数の使用:

    typescript
    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. フックの使用:

    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. フックの使用:

    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. フックの使用:

    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 では、標準 of [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 の主な強化点:

    • Framework Formatters: nextjsRewritesvelteKitRewritereactRouterRewritevueRouterRewritesolidRouterRewritetanstackRouterRewritenuxtRewrite、および viteRewrite を新たに追加し、各ルーターに対して慣用的なパターン構文を提供します。
    • useRewriteURL フック: クライアントサイドの新しいフックで、ルーターのナビゲーションを発生させることなく、アドレスバーをより見栄えの良いローカライズされた 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'),
        })"
      />
    </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({
          ja: plural({
            other: "{{count}} 個の空き",
          }),
          en: plural({
            one: "{{count}} opening",
            other: "{{count}} openings",
          }),
          fr: plural({
            one: "{{count}} offre",
            other: "{{count}} offres",
          }),
        }),
      },
    } satisfies Dictionary;
    
    export default openingsContent;
    

    その後、フレームワークコンポーネント内で動的にカウントを渡すことができます:

    tsx
    import type { FC } from "react";
    import { useIntlayer } from "react-intlayer";
    
    const OpeningsComponent: FC<{ count: number }> = ({ count }) => {
      const { totalOpenings } = useIntlayer("total_openings");
    
      return (
        <div>
          {/* 英語の場合:                                  */}
          {/*  count=0  → "0 openings"   (other)           */}
          {/*  count=1  → "1 opening"    (one)             */}
          {/*  count=2  → "2 openings"   (other)           */}
          {/*  count=21 → "21 openings"  (other)           */}
          <p>{totalOpenings(count)}</p>
        </div>
      );
    };
    

    詳細については、複数形化のドキュメント を参照してください。

    コンテンツスキーマ検証

    Intlayer v8 では、辞書に対するスキーマ検証が導入されました。Zod を使って設定内に再利用可能なバリデーションスキーマを定義し、それをコンテンツファイルに適用できます。これにより、コンテンツが常に期待される構造に準拠していることが保証され、ビルド時にエラーを検出できます。

    1. スキーマの定義

    intlayer.config.ts にスキーマを定義します:

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

    2. 辞書へのスキーマの適用

    辞書定義でスキーマキーを参照します:

    src/example.content.ts
    import { type Dictionary } from "intlayer";
    
    const aboutPageMetaContent = {
      key: "about-page-meta",
      schema: "seo-metadata", // <--
      content: {
        title: "About Our Company - Learn More About Us",
        description: "Discover our company's mission, values, and team.",
      },
    } satisfies Dictionary;
    
    export default aboutPageMetaContent;
    

    コンテンツがスキーマと一致しない場合(例: title が短すぎる)、ビルドプロセスでエラーが発生します。

    TypeScript: IntlayerNode プリミティブアクセサー

    IntlayerNode 型が更新され、コンテンツノード上で直接 JavaScript のプリミティブメソッドを呼び出せるようになりました。これにより、ローカライズされたコンテンツを、標準の文字列、数値、真偽値、または配列のプリミティブであるかのように直接操作できます。

    React、Preact、Solid、Svelte、Vue、および Angular でサポートされています。

    文字列の例

    typescript
    content.placeholder; // IntlayerNode<string> を返す
    content.placeholder.value; // string を返す
    content.placeholder.toString(); // string を返す
    content.placeholder.toLowerCase(); // string を返す
    String(content.placeholder); // string を返す
    content.placeholder.toUpperCase(); // 大文字に変換された string を返す
    content.placeholder.replace("a", "b"); // 変更された string を返す
    

    配列の例

    typescript
    content.myArrayOfString             // IntlayerNode<Array<string>> を返す
    content.myArrayOfString.find(...)   // 要素を返す
    content.myArrayOfString.join(', ')  // 結合された string を返す
    

    強化された自動コンテンツ検出

    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 オブジェクトを提供し、数値や日付などのフォーマットに現在の言語を自動的に使用するため、ロケールを手動で渡す必要がありません。

    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++ ウォッチャー & OXC ベースの LSP (v8.12.0)

    Intlayer v8.12.0 は、開発者体験に大きな改善をもたらします:

    • Parcel Watcher: コンテンツウォッチャーが chokidar から @parcel/watcher に移行されました。ネイティブの C++ ファイル監視を活用することで、より高速なアップデートと大幅なメモリ消費の削減を実現します。
    • 新しい Language Server Protocol (LSP): まったく新しい LSP が利用可能になりました。OXC ベースのパースで構築されており、IDE や AI エージェントが useIntlayer('my-key') の呼び出しと対応する .content ファイルをシームレスにリンク(およびその逆)できるように支援します。

    詳細については、LSP のドキュメント を参照してください。

    コンパイラの最適化

    Intlayer v8 には Markdown と HTML コンパイラ向けの新しいキャッシング層が導入されました。これにより、同じ設定で同一のコンテンツ文字列は一度だけ解析されるため、再レンダリング時や複数箇所で同じコンテンツを使用する場合のオーバーヘッドが大幅に削減されます。

    babel.config.js
    const {
    intlayerExtractBabelPlugin,
    intlayerOptimizeBabelPlugin,
    getExtractPluginOptions,
    getOptimizePluginOptions,
    } = require('@intlayer/babel');
    
    module.exports = {
    presets: ['next/babel'],
    plugins: [
      // コンポーネントから辞書へコンテンツを抽出する
      [intlayerExtractBabelPlugin, getExtractPluginOptions()],
      // useIntlayer を直接辞書インポートに置き換えることでインポートを最適化
      [intlayerOptimizeBabelPlugin, getOptimizePluginOptions()],
    ],
    };
    
    vite.config.js
    import { defineConfig } from 'vite';
    import { intlayer, intlayerCompiler } from "vite-intlayer";
    
    export default defineConfig({
    plugins: [intlayer(), intlayerCompiler()],
    });
    

    Vue / Svelte の場合は、適切なコンパイラパッケージをインストールする必要があります:

    bash
    # Vue の場合
    npm install @intlayer/vue-compiler
    
    bash
    # Svelte の場合
    npm install @intlayer/svelte-compiler
    

    実験的なビルド最適化: Minify & Purge (v8.7.0)

    Intlayer v8.7.0 では、本番環境のバンドルを最適化するために設計された、intlayer.config.ts 内の新しい実験的なビルド機能が導入されました:

    • 辞書のミニファイ (Minification): 辞書ファイルをミニファイしてサイズを削減します。
    • 未使用キーのパージ (Purging): 辞書から未使用の翻訳キーをスキャンして削除し、アクティブなキーを短い文字にリネームします(例: "products" -> "a", "pricing" -> "b")。これにより、バンドルサイズが最大 5% 削減されます。
    • TypeScript チェックの無効化: コンパイルステップ中の TypeScript 型チェックを無効にすることで、ビルドを高速化します。
    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      internationalization: {
        locales: [Locales.ENGLISH, Locales.FRENCH],
        defaultLocale: Locales.ENGLISH,
      },
      dictionary: {
        importMode: "dynamic",
      },
      build: {
        /**
         * バンドルサイズを削減するために辞書をミニファイします。
         */
        minify: true,
    
        /**
         * 辞書内の未使用キーをパージします。
         */
        purge: true,
    
        /**
         * ビルド時に TypeScript の型チェックを行うかどうかを示します。
         */
        checkTypes: false,
      },
    };
    
    export default config;
    

    柔軟性: 統一されたインポートモード

    live ブールプロパティは、より包括的な importMode プロパティに置き換えられ、非推奨になりました。これにより、辞書をどのように読み込むか(静的、動的、またはライブ同期)を明示的に定義できるようになります。

    モード

    • static(デフォルト): 辞書はビルド時にバンドルされます。パフォーマンスに最適です。
    • dynamic: 辞書はランタイムで読み込まれます(例: JSON フェッチや suspense 経由)。
    • fetch: 辞書はランタイムで CMS/サーバー から取得され、同期されます。

    移行:

    v7 設定v8 設定
    live: trueimportMode: 'fetch'
    live: falseimportMode: 'static'(または 'dynamic')

    注: Intlayer v8 では、importMode プロパティは intlayer.config.tsbuild 設定から dictionary 設定へ移動しました。これにより、すべての辞書に対するデフォルトのインポートモードを定義しつつ、各辞書ごとに上書きできるようになります。

    グローバル設定 of 例:

    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 から 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 は、コンテンツ定義ファイルの設定とコード変換の設定を分離します。これにより、監視とスキャンの対象をより正確に指定でき、ビルドパフォーマンスが向上します。

    以前は、.content.* ファイルの監視と useIntlayer 呼び出しのためのコードスキャンの両方に contentDir が使われていました。現在は以下のように分かれています:

    • 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 バンドラー (v8.11.0)

    Angular ユーザー向けに、Intlayer は新しいカスタム esbuild プラグインを介して Vite バンドラーをネイティブにサポートするようになり、開発およびビルド時間が短縮されました。

    設定の詳細については、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 を使用してください。
    • contentDircodeDir: contentDir はコンテンツファイル専用になりました。コード変換用に新しく codeDir プロパティが追加されました。codeDir が設定されていない場合、Intlayer は contentDir にフォールバックし、警告をログに出力します。
    • スキーマ検証: 新しい schema 機能を使用するには、プロジェクトに zod がインストールされていることを確認してください。

    参考リンク