@intlayer/docs 9.0.0-canary.2 → 9.0.0-canary.3

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: 2025-03-13
3
- updatedAt: 2025-10-05
3
+ updatedAt: 2026-06-21
4
4
  title: Sync JSON plugin
5
5
  description: Synchronise Intlayer dictionaries with third‑party i18n JSON files (i18next, next-intl, react-intl, vue-i18n, and more). Keep your existing i18n while using Intlayer to manage, translate, and test your messages.
6
6
  keywords:
@@ -24,6 +24,9 @@ slugs:
24
24
  - sync-json
25
25
  youtubeVideo: https://www.youtube.com/watch?v=MpGMxniDHNg
26
26
  history:
27
+ - version: 9.0.0
28
+ date: 2026-06-21
29
+ changes: "Add splitKeys option (one dictionary per top-level namespace key) for next-intl / react-intl single-file layouts"
27
30
  - version: 7.5.0
28
31
  date: 2025-12-13
29
32
  changes: "Add ICU and i18next format support"
@@ -41,7 +44,7 @@ Use Intlayer as an add‑on to your existing i18n stack. This plugin keeps your
41
44
 
42
45
  - Keep i18next, next‑intl, react‑intl, vue‑i18n, next‑translate, nuxt‑i18n, Solid‑i18next, svelte‑i18n, etc.
43
46
  - Manage and translate your messages with Intlayer (CLI, CI, providers, CMS), without refactoring your app.
44
- - Deliver tutorials and SEO content targeting each ecosystem, while recommending Intlayer as the JSON management layer.
47
+ - Deliver tutorials and SEO content targeting each ecosystem, whilst recommending Intlayer as the JSON management layer.
45
48
 
46
49
  Notes and current scope:
47
50
 
@@ -62,7 +65,63 @@ pnpm add -D @intlayer/sync-json-plugin
62
65
  npm i -D @intlayer/sync-json-plugin
63
66
  ```
64
67
 
65
- ## Quick start
68
+ ## Plugins
69
+
70
+ This package provides two plugins:
71
+
72
+ - `loadJSON`: Load JSON files into Intlayer dictionaries.
73
+ - This plugin is used to load JSON files from a source and will be loaded into Intlayer dictionaries. It can scan all the codebase and search for specific JSON files.
74
+ This plugin can be used
75
+ - if you use an i18n library that imposes a specific location for your JSON to be loaded (ex: `next-intl`, `i18next`, `react-intl`, `vue-i18n`, etc.), but you want to place your content declaration where you want in your code base.
76
+ - It can also be used if you want to fetch your messages from a remote source (ex: a CMS, an API, etc.) and store your messages in JSON files.
77
+
78
+ > Under the hood, this plugin will scan all the codebase and search for specific JSON files and load them into Intlayer dictionaries.
79
+ > Note that this plugin will not write the output and translations back to the JSON files.
80
+
81
+ - `syncJSON`: Synchronise JSON files with Intlayer dictionaries.
82
+ - This plugin is used to synchronise JSON files with Intlayer dictionaries. It can scan the given location and load the JSON that match the pattern for specific JSON files. This plugin is useful if you want to get the benefits of Intlayer whilst using another i18n library.
83
+
84
+ ## Using both plugins
85
+
86
+ ```ts fileName="intlayer.config.ts"
87
+ import { Locales, type IntlayerConfig } from "intlayer";
88
+ import { loadJSON, syncJSON } from "@intlayer/sync-json-plugin";
89
+
90
+ const config: IntlayerConfig = {
91
+ internationalization: {
92
+ locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
93
+ defaultLocale: Locales.ENGLISH,
94
+ },
95
+
96
+ // Keep your current JSON files in sync with Intlayer dictionaries
97
+ plugins: [
98
+ /**
99
+ * Will load all the JSON files in the src that match the pattern {key}.i18n json
100
+ */
101
+ loadJSON({
102
+ source: ({ key }) => `./src/**/${key}.i18n.json`,
103
+ locale: Locales.ENGLISH,
104
+ priority: 1, // Ensures these JSON files take precedence over files at `./locales/en/${key}.json`
105
+ format: "intlayer", // Format of the JSON content
106
+ }),
107
+ /**
108
+ * Will load, and write the output and translations back to the JSON files in the locales directory
109
+ */
110
+ syncJSON({
111
+ format: "i18next",
112
+ source: ({ key, locale }) => `./locales/${locale}/${key}.json`,
113
+ priority: 0,
114
+ format: "i18next",
115
+ }),
116
+ ],
117
+ };
118
+
119
+ export default config;
120
+ ```
121
+
122
+ ## `syncJSON` plugin
123
+
124
+ ### Quick start
66
125
 
67
126
  Add the plugin to your `intlayer.config.ts` and point it at your existing JSON structure.
68
127
 
@@ -81,6 +140,7 @@ const config: IntlayerConfig = {
81
140
  syncJSON({
82
141
  // Per-locale, per-namespace layout (e.g., next-intl, i18next with namespaces)
83
142
  source: ({ key, locale }) => `./locales/${locale}/${key}.json`,
143
+ format: "icu",
84
144
  }),
85
145
  ],
86
146
  };
@@ -91,14 +151,27 @@ export default config;
91
151
  Alternative: single file per locale (common with i18next/react-intl setups):
92
152
 
93
153
  ```ts fileName="intlayer.config.ts"
94
- plugins: [
95
- syncJSON({
96
- source: ({ locale }) => `./locales/${locale}.json`,
97
- }),
98
- ];
154
+ import { Locales, type IntlayerConfig } from "intlayer";
155
+ import { syncJSON } from "@intlayer/sync-json-plugin";
156
+
157
+ const config: IntlayerConfig = {
158
+ internationalization: {
159
+ locales: [Locales.ENGLISH, Locales.FRENCH],
160
+ defaultLocale: Locales.ENGLISH,
161
+ },
162
+ plugins: [
163
+ syncJSON({
164
+ format: "i18next",
165
+ source: ({ locale }) => `./locales/${locale}.json`,
166
+ format: "i18next",
167
+ }),
168
+ ],
169
+ };
170
+
171
+ export default config;
99
172
  ```
100
173
 
101
- ### How it works
174
+ #### How it works
102
175
 
103
176
  - Read: the plugin discovers JSON files from your `source` builder and loads them as Intlayer dictionaries.
104
177
  - Write: after builds and fills, it writes localised JSON back to the same paths (with a final newline to avoid formatting issues).
@@ -112,6 +185,7 @@ syncJSON({
112
185
  location?: string, // optional label, default: "plugin"
113
186
  priority?: number, // optional priority for conflict resolution, default: 0
114
187
  format?: 'intlayer' | 'icu' | 'i18next', // optional formatter, used for intlayer runtime compatibility
188
+ splitKeys?: boolean, // optional, split a single file into one dictionary per top-level key (auto-detected)
115
189
  });
116
190
  ```
117
191
 
@@ -136,17 +210,49 @@ syncJSON({
136
210
  }),
137
211
  ```
138
212
 
139
- ## Multiple JSON sources and priority
213
+ #### `splitKeys` (boolean)
214
+
215
+ Controls whether a single JSON file whose **first-level keys are namespaces** should become one dictionary per top-level key, instead of a single dictionary holding the whole file.
216
+
217
+ This matches the namespace model of libraries like `next-intl` and `react-intl`, where one `messages/{locale}.json` file groups several namespaces by its first-level keys, each addressed independently (e.g. `useTranslations('Hero')` resolves to the `Hero` dictionary).
218
+
219
+ - `undefined` (default): **auto-detected** — the file is split when the `source` pattern has no `{key}` segment (one file holds every namespace), and kept as a single dictionary otherwise (one file per key).
220
+ - `true`: always split each top-level key into its own dictionary.
221
+ - `false`: never split; the whole file becomes a single dictionary.
222
+
223
+ Given a single `messages/{locale}.json` file:
224
+
225
+ ```json fileName="messages/en.json"
226
+ {
227
+ "Hero": { "title": "Full-stack developer" },
228
+ "Nav": { "work": "Work", "about": "About" },
229
+ "About": { "lead": "I build apps end to end." }
230
+ }
231
+ ```
232
+
233
+ ```ts fileName="intlayer.config.ts"
234
+ syncJSON({
235
+ format: "icu",
236
+ source: ({ locale }) => `./messages/${locale}.json`,
237
+ // splitKeys: true, // implied because the pattern has no `{key}` segment
238
+ }),
239
+ ```
240
+
241
+ This produces three dictionaries — `Hero`, `Nav`, and `About` — so `useTranslations('Hero')` (next-intl) resolves correctly. On write-back, all namespaces are re-assembled into the same per-locale file.
242
+
243
+ > When you keep the explicit `{key}` segment in your `source` (e.g. `./locales/${locale}/${key}.json`), each file is already one namespace, so splitting is disabled by default.
244
+
245
+ ### Multiple JSON sources and priority
140
246
 
141
247
  You can add multiple `syncJSON` plugins to synchronise different JSON sources. This is useful when you have multiple i18n libraries or different JSON structures in your project.
142
248
 
143
- ### Priority system
249
+ #### Priority system
144
250
 
145
251
  When multiple plugins target the same dictionary key, the `priority` parameter determines which plugin takes precedence:
146
252
 
147
253
  - Higher priority numbers win over lower ones
148
254
  - Default priority of `.content` files is `0`
149
- - Default priority of plugins content files is `-1`
255
+ - Default priority of plugins is `0`
150
256
  - Plugins with the same priority are processed in the order they appear in the configuration
151
257
 
152
258
  ```ts fileName="intlayer.config.ts"
@@ -189,7 +295,133 @@ const config: IntlayerConfig = {
189
295
  export default config;
190
296
  ```
191
297
 
192
- ### Conflict resolution
298
+ ## Load JSON plugin
299
+
300
+ ### Quick start
301
+
302
+ Add the plugin to your `intlayer.config.ts` to ingest existing JSON files as Intlayer dictionaries. This plugin is read‑only (no writes to disk):
303
+
304
+ ```ts fileName="intlayer.config.ts"
305
+ import { Locales, type IntlayerConfig } from "intlayer";
306
+ import { loadJSON } from "@intlayer/sync-json-plugin";
307
+
308
+ const config: IntlayerConfig = {
309
+ internationalization: {
310
+ locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
311
+ defaultLocale: Locales.ENGLISH,
312
+ },
313
+
314
+ plugins: [
315
+ // Ingest JSON messages located anywhere in your source tree
316
+ loadJSON({
317
+ source: ({ key }) => `./src/**/${key}.i18n.json`,
318
+ // Load a single locale per plugin instance (defaults to the config defaultLocale)
319
+ locale: Locales.ENGLISH,
320
+ priority: 0,
321
+ }),
322
+ ],
323
+ };
324
+
325
+ export default config;
326
+ ```
327
+
328
+ Alternative: per‑locale layout, still read‑only (only the selected locale is loaded):
329
+
330
+ ```ts fileName="intlayer.config.ts"
331
+ import { Locales, type IntlayerConfig } from "intlayer";
332
+ import { loadJSON } from "@intlayer/sync-json-plugin";
333
+
334
+ const config: IntlayerConfig = {
335
+ internationalization: {
336
+ locales: [Locales.ENGLISH, Locales.FRENCH],
337
+ defaultLocale: Locales.ENGLISH,
338
+ },
339
+ plugins: [
340
+ loadJSON({
341
+ // Only files for Locales.FRENCH will be loaded from this pattern
342
+ source: ({ key, locale }) => `./locales/${locale}/${key}.json`,
343
+ locale: Locales.FRENCH,
344
+ }),
345
+ ],
346
+ };
347
+
348
+ export default config;
349
+ ```
350
+
351
+ ### How it works
352
+
353
+ - Discover: builds a glob from your `source` builder and collects matching JSON files.
354
+ - Ingest: loads each JSON file as an Intlayer dictionary with the provided `locale`.
355
+ - Read‑only: does not write or format output files; use `syncJSON` if you need round‑trip sync.
356
+ - Auto‑fill ready: defines a `fill` pattern so `intlayer content fill` can populate missing keys.
357
+
358
+ ### API
359
+
360
+ ```ts
361
+ loadJSON({
362
+ // Build paths to your JSON. `locale` is optional if your structure has no locale segment
363
+ source: ({ key, locale }) => string,
364
+
365
+ // Target locale for the dictionaries loaded by this plugin instance
366
+ // Defaults to configuration.internationalization.defaultLocale
367
+ locale?: Locale,
368
+
369
+ // Optional label to identify the source
370
+ location?: string, // default: "plugin"
371
+
372
+ // Priority used for conflict resolution against other sources
373
+ priority?: number, // default: 0
374
+
375
+ // Optional formatter for the JSON content
376
+ format?: 'intlayer' | 'icu' | 'i18next', // default: 'intlayer'
377
+
378
+ // Split a single file into one dictionary per top-level key (auto-detected)
379
+ splitKeys?: boolean,
380
+ });
381
+ ```
382
+
383
+ #### `format` ('intlayer' | 'icu' | 'i18next')
384
+
385
+ Specifies the formatter to use for the dictionary content when loading JSON files. This allows using different message formatting syntaxes compatible with various i18n libraries.
386
+
387
+ - `'intlayer'`: The default Intlayer formatter (default).
388
+ - `'icu'`: Uses ICU message formatting (compatible with libraries like react-intl, vue-i18n).
389
+ - `'i18next'`: Uses i18next message formatting (compatible with i18next, next-i18next, Solid-i18next).
390
+
391
+ **Example:**
392
+
393
+ ```ts
394
+ loadJSON({
395
+ source: ({ key }) => `./src/**/${key}.i18n.json`,
396
+ locale: Locales.ENGLISH,
397
+ format: "icu", // Use ICU formatting for compatibility
398
+ }),
399
+ ```
400
+
401
+ #### `splitKeys` (boolean)
402
+
403
+ Same behaviour as in [`syncJSON`](#splitkeys-boolean): when a single JSON file groups several namespaces by its first-level keys, each top-level key becomes its own dictionary.
404
+
405
+ - `undefined` (default): **auto-detected** — split when the `source` pattern has no `{key}` segment, single dictionary otherwise.
406
+ - `true` / `false`: force or disable splitting.
407
+
408
+ ```ts
409
+ loadJSON({
410
+ source: ({ locale }) => `./messages/${locale}.json`,
411
+ format: "icu",
412
+ // splitKeys auto-enabled: `Hero`, `Nav`, `About`, … each become a dictionary
413
+ }),
414
+ ```
415
+
416
+ ### Behavior and conventions
417
+
418
+ - If your `source` mask includes a locale placeholder, only files for the selected `locale` are ingested.
419
+ - If there is no `{key}` segment in your mask, each top-level key of the file becomes its own dictionary by default (see [`splitKeys`](#splitkeys-boolean)). Set `splitKeys: false` to instead load the whole file as a single `index` dictionary.
420
+ - Keys are derived from file paths by substituting the `{key}` placeholder in your `source` builder.
421
+ - The plugin only uses discovered files and does not fabricate missing locales or keys.
422
+ - The `fill` path is inferred from your `source` and used to update missing values via CLI when you opt‑in.
423
+
424
+ ## Conflict resolution
193
425
 
194
426
  When the same translation key exists in multiple JSON sources:
195
427