@intlayer/docs 9.1.0 → 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2026-06-12
3
- updatedAt: 2026-06-26
3
+ updatedAt: 2026-08-04
4
4
  title: Varianti
5
5
  description: Usa il campo di metadati variant nei file di contenuto Intlayer per dichiarare alternative di contenuto con nome o strutturate — test A/B, banner stagionali, testo con feature flag, record di CMS, contenuti specifici per utente — e passare dall'una all'altra a runtime senza modifiche al codice.
6
6
  keywords:
@@ -22,7 +22,13 @@ history:
22
22
  changes: "Rilascio della funzionalità delle varianti"
23
23
  - version: 9.1.0
24
24
  date: 2026-06-26
25
- changes: "`variant` ora accetta una stringa o un oggetto — i precedenti `meta` / record dinamici si dichiarano come varianti oggetto"
25
+ changes: "`variant` ora accetta una stringa o un oggetto — i precedenti `meta` / record dinamici vengono dichiarati come varianti oggetto"
26
+ - version: 9.1.1
27
+ date: 2026-07-31
28
+ changes: "Una variante dichiara solo le chiavi che sovrascrive; le varianti non dichiarate ricadono sulla voce predefinita"
29
+ - version: 9.1.2
30
+ date: 2026-08-04
31
+ changes: "I provider accettano una prop `variant` ambientale; i selettori accettano una catena di preferenza ordinata"
26
32
  author: aymericzip
27
33
  ---
28
34
 
@@ -77,6 +83,37 @@ const dictionary = {
77
83
  export default dictionary;
78
84
  ```
79
85
 
86
+ ### Varianti parziali
87
+
88
+ Una variante dichiara **solo le chiavi che sovrascrive**; il resto viene ereditato dalla voce predefinita.
89
+
90
+ ```ts fileName="hero-banner.summer.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
91
+ import { t, type Dictionary } from "intlayer";
92
+
93
+ const dictionary = {
94
+ key: "hero-banner",
95
+ variant: "summer",
96
+ content: {
97
+ headline: t({
98
+ en: "Build faster all summer",
99
+ fr: "Développez plus vite tout l'été",
100
+ }),
101
+ },
102
+ } satisfies Dictionary;
103
+
104
+ export default dictionary;
105
+ ```
106
+
107
+ ```tsx
108
+ useIntlayer("hero-banner", { variant: "summer" });
109
+ // → { headline: "Développez plus vite tout l'été", cta: "Commencer" } — `cta` ereditato
110
+
111
+ useIntlayer("hero-banner", { variant: "never-declared" });
112
+ // → la voce predefinita
113
+ ```
114
+
115
+ Quindi aggiungi un file variante solo dove la formulazione differisce effettivamente. Una chiave si risolve in `null` solo quando dichiara varianti ma nessuna voce predefinita.
116
+
80
117
  ### Consumare varianti con nome
81
118
 
82
119
  #### Variante predefinita
@@ -464,6 +501,176 @@ const content = useIntlayer("product", {
464
501
  const content = useIntlayer("product", { variant: { id: "prod_abc" } });
465
502
  ```
466
503
 
504
+ ## Variante ambientale
505
+
506
+ Alcune dimensioni di variante sono fisse per un'intera sessione: il tenant, il tipo di istituto, il livello di piano. Vengono risolte una sola volta e nessun componente dovrebbe doverle passare a mano.
507
+
508
+ > Non incapsulare `useIntlayer` in un hook personalizzato per iniettarle. L'ottimizzazione in fase di build riscrive solo una chiamata letterale `useIntlayer("key")` importata dal pacchetto del framework, quindi nulla dietro un wrapper finisce nel bundle.
509
+
510
+ Dichiara invece la variante una sola volta sul provider, esattamente come `locale`:
511
+
512
+ <Tabs group="framework">
513
+ <Tab label="React" value="react">
514
+ ```tsx fileName="App.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
515
+ import { IntlayerProvider } from "react-intlayer";
516
+
517
+ export const App = ({ locale, schoolType }) => (
518
+ <IntlayerProvider locale={locale} variant={schoolType}>
519
+ <Hero />
520
+ </IntlayerProvider>
521
+ );
522
+ ```
523
+
524
+ </Tab>
525
+ <Tab label="Next.js" value="nextjs">
526
+ ```tsx fileName="layout.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
527
+ import { IntlayerServerProvider } from "next-intlayer/server";
528
+ import { IntlayerClientProvider } from "next-intlayer";
529
+
530
+ export default async function Layout({ children, params }) {
531
+ const { locale } = await params;
532
+ const schoolType = await getSchoolType();
533
+
534
+ return (
535
+ <IntlayerServerProvider locale={locale} variant={schoolType}>
536
+ <IntlayerClientProvider locale={locale} variant={schoolType}>
537
+ {children}
538
+ </IntlayerClientProvider>
539
+ </IntlayerServerProvider>
540
+ );
541
+ }
542
+ ```
543
+
544
+ </Tab>
545
+ <Tab label="Vue" value="vue">
546
+ ```ts fileName="main.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
547
+ import { createApp } from "vue";
548
+ import { installIntlayer } from "vue-intlayer";
549
+ import App from "./App.vue";
550
+
551
+ const app = createApp(App);
552
+
553
+ installIntlayer(app, { locale: "en", variant: schoolType });
554
+
555
+ app.mount("#app");
556
+ ```
557
+
558
+ </Tab>
559
+ <Tab label="Svelte" value="svelte">
560
+ ```svelte fileName="+layout.svelte" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
561
+ <script lang="ts">
562
+ import { setupIntlayer } from "svelte-intlayer";
563
+
564
+ export let schoolType: string;
565
+
566
+ setupIntlayer("en", schoolType);
567
+ </script>
568
+
569
+ <slot />
570
+ ```
571
+
572
+ </Tab>
573
+ <Tab label="Preact" value="preact">
574
+ ```tsx fileName="App.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
575
+ import { IntlayerProvider } from "preact-intlayer";
576
+
577
+ export const App = ({ locale, schoolType }) => (
578
+ <IntlayerProvider locale={locale} variant={schoolType}>
579
+ <Hero />
580
+ </IntlayerProvider>
581
+ );
582
+ ```
583
+
584
+ </Tab>
585
+ <Tab label="Solid" value="solid">
586
+ ```tsx fileName="App.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
587
+ import { IntlayerProvider } from "solid-intlayer";
588
+
589
+ export const App = (props) => (
590
+ <IntlayerProvider locale={props.locale} variant={props.schoolType}>
591
+ <Hero />
592
+ </IntlayerProvider>
593
+ );
594
+ ```
595
+
596
+ </Tab>
597
+ <Tab label="Angular" value="angular">
598
+ ```typescript fileName="app.config.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
599
+ import { ApplicationConfig } from "@angular/core";
600
+ import { provideIntlayer } from "angular-intlayer";
601
+
602
+ export const appConfig: ApplicationConfig = {
603
+ providers: [provideIntlayer("en", true, schoolType)],
604
+ };
605
+ ```
606
+
607
+ </Tab>
608
+ <Tab label="Vanilla JS" value="vanilla">
609
+ ```javascript fileName="main.js"
610
+ import { installIntlayer } from "vanilla-intlayer";
611
+
612
+ installIntlayer({ locale: "en", variant: schoolType });
613
+ ```
614
+
615
+ </Tab>
616
+ </Tabs>
617
+
618
+ Ogni lettura di dizionario sotto il provider si risolve ora con quella variante, e un selettore nel punto di chiamata vince sempre:
619
+
620
+ ```tsx
621
+ useIntlayer("hero-banner");
622
+ // → la variante del provider
623
+
624
+ useIntlayer("hero-banner", { variant: "summer" });
625
+ // → "summer" — sostituisce la variante del provider, non la estende
626
+ ```
627
+
628
+ ### Forme
629
+
630
+ La prop `variant` accetta tre forme:
631
+
632
+ | Forma | Significato |
633
+ | --------------------------------------------------------- | --------------------------------------- |
634
+ | `variant="school1"` | una variante denominata per ogni chiave |
635
+ | `variant={["school1", "default"]}` | una catena di preferenza ordinata |
636
+ | `variant={{ "hero-banner": "school1", default: "base" }}` | una variante per chiave di dizionario |
637
+
638
+ #### Catena di preferenza
639
+
640
+ Una catena viene percorsa da sinistra a destra tra le voci dichiarate da ciascuna chiave e vince la prima dichiarata. Quando nessuna è dichiarata, si usa la voce predefinita implicita, esattamente come per un valore singolo.
641
+
642
+ ```tsx
643
+ <IntlayerProvider variant={["school1", "school2"]} />
644
+ // `hero-banner` non dichiara una voce `school1` ma dichiara `school2` → "school2"
645
+ // una chiave che non dichiara nessuna delle due → la voce predefinita
646
+ ```
647
+
648
+ Quindi `["black_friday", "summer"]` si legge come «black friday se questa chiave ne ha una, altrimenti summer, altrimenti predefinita». Le catene sono accettate anche nel punto di chiamata:
649
+
650
+ ```tsx
651
+ useIntlayer("hero-banner", { variant: ["black_friday", "summer"] });
652
+ ```
653
+
654
+ > Nota che questa è l'immagine speculare dell'array accettato dal **campo** `variant` di un file di contenuto: lì un array _dichiara_ una voce per elemento, qui le _consuma_ in ordine di priorità.
655
+
656
+ #### Mappa per chiave
657
+
658
+ Indirizza ogni chiave di dizionario separatamente. La voce riservata `default` copre tutte le chiavi non elencate:
659
+
660
+ ```tsx
661
+ <IntlayerProvider
662
+ variant={{
663
+ "hero-banner": "school1",
664
+ product: ["school1", "default"],
665
+ default: "base",
666
+ }}
667
+ />
668
+ ```
669
+
670
+ > Su un provider un oggetto semplice è **sempre** letto come mappa per chiave, mai come variante oggetto: le due sono strutturalmente identiche. Per fissare una variante oggetto a livello globale, annidala sotto una voce: `variant={{ default: { id: "prod_abc" } }}`.
671
+
672
+ Poiché le chiavi della mappa sono verificate rispetto alle chiavi di dizionario dichiarate, un refuso — o una variante oggetto scritta direttamente, come `variant={{ id: "prod_abc" }}` — è un errore di compilazione.
673
+
467
674
  ## Modalità di caricamento
468
675
 
469
676
  Le varianti oggetto sono spesso caricate in modo differito. Imposta `importMode` sul dizionario per controllarlo:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  createdAt: 2026-06-12
3
- updatedAt: 2026-06-26
3
+ updatedAt: 2026-08-04
4
4
  title: バリアント
5
5
  description: Intlayer のコンテンツファイルで variant メタデータフィールドを使用し、名前付きまたは構造化されたコンテンツの代替(A/B テスト、季節バナー、フィーチャーフラグ付きコピー、CMS レコード、ユーザー固有コンテンツ)を宣言し、コード変更なしにランタイムで切り替えます。
6
6
  keywords:
@@ -22,7 +22,13 @@ history:
22
22
  changes: "バリアント機能のリリース"
23
23
  - version: 9.1.0
24
24
  date: 2026-06-26
25
- changes: "`variant` が文字列またはオブジェクトを受け取るようになりました `meta` / 動的レコードはオブジェクトバリアントとして宣言します"
25
+ changes: "`variant`は文字列またはオブジェクトを受け付けるようになりました以前の `meta` / 動的レコードはオブジェクトバリアントとして宣言されます"
26
+ - version: 9.1.1
27
+ date: 2026-07-31
28
+ changes: "バリアントは上書きするキーのみを宣言します。宣言されていないバリアントはデフォルトのエントリにフォールバックします"
29
+ - version: 9.1.2
30
+ date: 2026-08-04
31
+ changes: "プロバイダーがアンビエントな `variant` プロパティを受け取り、セレクターが順序付きの優先チェーンを受け取れるようになりました"
26
32
  author: aymericzip
27
33
  ---
28
34
 
@@ -77,6 +83,37 @@ const dictionary = {
77
83
  export default dictionary;
78
84
  ```
79
85
 
86
+ ### 部分的なバリアント
87
+
88
+ バリアントは**上書きするキーのみを宣言します**。残りはデフォルトのエントリから継承されます。
89
+
90
+ ```ts fileName="hero-banner.summer.content.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
91
+ import { t, type Dictionary } from "intlayer";
92
+
93
+ const dictionary = {
94
+ key: "hero-banner",
95
+ variant: "summer",
96
+ content: {
97
+ headline: t({
98
+ en: "Build faster all summer",
99
+ fr: "Développez plus vite tout l'été",
100
+ }),
101
+ },
102
+ } satisfies Dictionary;
103
+
104
+ export default dictionary;
105
+ ```
106
+
107
+ ```tsx
108
+ useIntlayer("hero-banner", { variant: "summer" });
109
+ // → { headline: "Développez plus vite tout l'été", cta: "Commencer" } — `cta` は継承されます
110
+
111
+ useIntlayer("hero-banner", { variant: "never-declared" });
112
+ // → デフォルトのエントリ
113
+ ```
114
+
115
+ したがって、実際にテキストが異なる場所にのみバリアントファイルを追加します。バリアントを宣言しているがデフォルトのエントリがない場合にのみ、キーは `null` に解決されます。
116
+
80
117
  ### 名前付きバリアントの利用
81
118
 
82
119
  #### デフォルトバリアント
@@ -464,6 +501,176 @@ const content = useIntlayer("product", {
464
501
  const content = useIntlayer("product", { variant: { id: "prod_abc" } });
465
502
  ```
466
503
 
504
+ ## アンビエントバリアント
505
+
506
+ バリアントの次元の中には、テナント、学校種別、プラン階層のように、セッション全体で固定されるものがあります。これらは一度だけ解決されるものであり、各コンポーネントが手で渡すべきものではありません。
507
+
508
+ > これらを注入するために `useIntlayer` を独自のフックでラップしないでください。ビルド時の最適化は、フレームワークパッケージからインポートされたリテラルな `useIntlayer("key")` の呼び出しだけを書き換えるため、ラッパーの背後にあるものはバンドルされません。
509
+
510
+ 代わりに、`locale` とまったく同じように、プロバイダーで一度だけバリアントを宣言します:
511
+
512
+ <Tabs group="framework">
513
+ <Tab label="React" value="react">
514
+ ```tsx fileName="App.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
515
+ import { IntlayerProvider } from "react-intlayer";
516
+
517
+ export const App = ({ locale, schoolType }) => (
518
+ <IntlayerProvider locale={locale} variant={schoolType}>
519
+ <Hero />
520
+ </IntlayerProvider>
521
+ );
522
+ ```
523
+
524
+ </Tab>
525
+ <Tab label="Next.js" value="nextjs">
526
+ ```tsx fileName="layout.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
527
+ import { IntlayerServerProvider } from "next-intlayer/server";
528
+ import { IntlayerClientProvider } from "next-intlayer";
529
+
530
+ export default async function Layout({ children, params }) {
531
+ const { locale } = await params;
532
+ const schoolType = await getSchoolType();
533
+
534
+ return (
535
+ <IntlayerServerProvider locale={locale} variant={schoolType}>
536
+ <IntlayerClientProvider locale={locale} variant={schoolType}>
537
+ {children}
538
+ </IntlayerClientProvider>
539
+ </IntlayerServerProvider>
540
+ );
541
+ }
542
+ ```
543
+
544
+ </Tab>
545
+ <Tab label="Vue" value="vue">
546
+ ```ts fileName="main.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
547
+ import { createApp } from "vue";
548
+ import { installIntlayer } from "vue-intlayer";
549
+ import App from "./App.vue";
550
+
551
+ const app = createApp(App);
552
+
553
+ installIntlayer(app, { locale: "en", variant: schoolType });
554
+
555
+ app.mount("#app");
556
+ ```
557
+
558
+ </Tab>
559
+ <Tab label="Svelte" value="svelte">
560
+ ```svelte fileName="+layout.svelte" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
561
+ <script lang="ts">
562
+ import { setupIntlayer } from "svelte-intlayer";
563
+
564
+ export let schoolType: string;
565
+
566
+ setupIntlayer("en", schoolType);
567
+ </script>
568
+
569
+ <slot />
570
+ ```
571
+
572
+ </Tab>
573
+ <Tab label="Preact" value="preact">
574
+ ```tsx fileName="App.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
575
+ import { IntlayerProvider } from "preact-intlayer";
576
+
577
+ export const App = ({ locale, schoolType }) => (
578
+ <IntlayerProvider locale={locale} variant={schoolType}>
579
+ <Hero />
580
+ </IntlayerProvider>
581
+ );
582
+ ```
583
+
584
+ </Tab>
585
+ <Tab label="Solid" value="solid">
586
+ ```tsx fileName="App.tsx" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
587
+ import { IntlayerProvider } from "solid-intlayer";
588
+
589
+ export const App = (props) => (
590
+ <IntlayerProvider locale={props.locale} variant={props.schoolType}>
591
+ <Hero />
592
+ </IntlayerProvider>
593
+ );
594
+ ```
595
+
596
+ </Tab>
597
+ <Tab label="Angular" value="angular">
598
+ ```typescript fileName="app.config.ts" contentDeclarationFormat={["typescript", "esm", "commonjs"]}
599
+ import { ApplicationConfig } from "@angular/core";
600
+ import { provideIntlayer } from "angular-intlayer";
601
+
602
+ export const appConfig: ApplicationConfig = {
603
+ providers: [provideIntlayer("en", true, schoolType)],
604
+ };
605
+ ```
606
+
607
+ </Tab>
608
+ <Tab label="Vanilla JS" value="vanilla">
609
+ ```javascript fileName="main.js"
610
+ import { installIntlayer } from "vanilla-intlayer";
611
+
612
+ installIntlayer({ locale: "en", variant: schoolType });
613
+ ```
614
+
615
+ </Tab>
616
+ </Tabs>
617
+
618
+ これでプロバイダー配下のすべての辞書の読み取りがそのバリアントで解決され、呼び出し側のセレクターが常に優先されます:
619
+
620
+ ```tsx
621
+ useIntlayer("hero-banner");
622
+ // → プロバイダーのバリアント
623
+
624
+ useIntlayer("hero-banner", { variant: "summer" });
625
+ // → "summer" — プロバイダーのバリアントを置き換えます(拡張はしません)
626
+ ```
627
+
628
+ ### 形式
629
+
630
+ `variant` プロパティは 3 つの形式を受け取ります:
631
+
632
+ | 形式 | 意味 |
633
+ | --------------------------------------------------------- | --------------------------------------------- |
634
+ | `variant="school1"` | すべてのキーに対する 1 つの名前付きバリアント |
635
+ | `variant={["school1", "default"]}` | 順序付きの優先チェーン |
636
+ | `variant={{ "hero-banner": "school1", default: "base" }}` | 辞書キーごとに 1 つのバリアント |
637
+
638
+ #### 優先チェーン
639
+
640
+ チェーンは各キーが宣言しているエントリーに対して左から右へ順に試され、最初に宣言されているものが採用されます。どれも宣言されていない場合は、単一の値のときとまったく同じく、暗黙のデフォルトエントリーが使われます。
641
+
642
+ ```tsx
643
+ <IntlayerProvider variant={["school1", "school2"]} />
644
+ // `hero-banner` は `school1` エントリーを宣言していないが `school2` を宣言している → "school2"
645
+ // どちらも宣言していないキー → デフォルトエントリー
646
+ ```
647
+
648
+ したがって `["black_friday", "summer"]` は「このキーに black friday があればそれ、なければ summer、それもなければデフォルト」と読めます。チェーンは呼び出し側でも使えます:
649
+
650
+ ```tsx
651
+ useIntlayer("hero-banner", { variant: ["black_friday", "summer"] });
652
+ ```
653
+
654
+ > これはコンテンツファイルの `variant` **フィールド**が受け取る配列とちょうど逆であることに注意してください。あちらでは配列が要素ごとに 1 つのエントリーを*宣言*しますが、こちらでは優先順位に従ってそれらを*消費*します。
655
+
656
+ #### キーごとのマップ
657
+
658
+ 辞書キーごとに個別に指定します。予約された `default` エントリーが、記載されていないすべてのキーをカバーします:
659
+
660
+ ```tsx
661
+ <IntlayerProvider
662
+ variant={{
663
+ "hero-banner": "school1",
664
+ product: ["school1", "default"],
665
+ default: "base",
666
+ }}
667
+ />
668
+ ```
669
+
670
+ > プロバイダーでは、プレーンなオブジェクトは**常に**キーごとのマップとして読み取られ、オブジェクトバリアントとしては解釈されません(両者は構造的に同一のためです)。オブジェクトバリアントをグローバルに指定するには、エントリーの下にネストしてください: `variant={{ default: { id: "prod_abc" } }}`。
671
+
672
+ マップのキーは宣言済みの辞書キーと照合されるため、タイプミス(あるいは `variant={{ id: "prod_abc" }}` のようにオブジェクトバリアントを直接書いた場合)はコンパイルエラーになります。
673
+
467
674
  ## 読み込みモード
468
675
 
469
676
  オブジェクトバリアントはしばしば遅延読み込みされます。これを制御するには辞書に `importMode` を設定します: