@intlayer/sync-po-plugin 9.5.3 → 9.5.4-canary.0

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://intlayer.org" rel="">
3
- <img src="https://raw.githubusercontent.com/aymericzip/intlayer/main/docs/assets/cover.png" width="60%" alt="Intlayer Logo" />
3
+ <img src="https://raw.githubusercontent.com/aymericzip/intlayer/main/docs/assets/cover.webp" width="60%" alt="Intlayer Logo" />
4
4
  </a>
5
5
  </p>
6
6
 
@@ -49,10 +49,10 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
49
49
 
50
50
  | Feature | Description |
51
51
  | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.png?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
52
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.webp?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
53
53
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/javascript_content_management.jpg?raw=true" alt="Feature" width="700"> | **JavaScript-Powered Content Management**<br><br>Harness the flexibility of JavaScript to define and manage your content efficiently. <br><br> - [Content declaration](https://intlayer.org/doc/concept/content) |
54
54
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/per_locale_content_declaration_file.png?raw=true" alt="Feature" width="700"> | **Per-Locale Content Declaration File**<br><br>Speed up your development by declaring your content once, before auto generation.<br><br> - [Per-Locale Content Declaration File](https://intlayer.org/doc/concept/per-locale-file) |
55
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/compiler.jpg?raw=true" alt="Feature" width="700"> | **Compiler**<br><br>The Intlayer Compiler extract automatically the content from the components and generate the dictionary files.<br><br> - [Compiler](https://intlayer.org/doc/compiler) |
55
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/compiler.webp?raw=true" alt="Feature" width="700"> | **Compiler**<br><br>The Intlayer Compiler extract automatically the content from the components and generate the dictionary files.<br><br> - [Compiler](https://intlayer.org/doc/compiler) |
56
56
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true" alt="Feature" width="700"> | **Type-Safe Environment**<br><br>Leverage TypeScript to ensure your content definitions and code are error-free, while also benefiting from IDE autocompletion.<br><br> - [TypeScript configuration](https://intlayer.org/doc/environment/vite-and-react#configure-typescript) |
57
57
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/config_file.png?raw=true" alt="Feature" width="700"> | **Simplified Setup**<br><br>Get up and running quickly with minimal configuration. Adjust settings for internationalization, routing, AI, build, and content handling with ease. <br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
58
58
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/content_retrieval.png?raw=true" alt="Feature" width="700"> | **Simplified Content Retrieval**<br><br>No need to call your `t` function for each piece of content. Retrieve all your content directly using a single hook.<br><br> - [React integration](https://intlayer.org/doc/environment/create-react-app) |
@@ -60,12 +60,12 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
60
60
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/file_tree.png?raw=true" alt="Feature" width="700"> | **Organized Codebase**<br><br>Keep your codebase more organized: 1 component = 1 dictionary in the same folder. Translations close to their respective components, enhance maintainability and clarity. <br><br> - [How Intlayer works](https://intlayer.org/doc/concept/how-works-intlayer) |
61
61
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/url_routing.png?raw=true" alt="Feature" width="700"> | **Enhanced Routing**<br><br>Full support of app routing, adapting seamlessly to complex application structures, for Next.js, React, Vite, Vue.js, etc.<br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
62
62
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/markdown.png?raw=true" alt="Feature" width="700"> | **Markdown Support**<br><br>Import and interpret, locale files and remote Markdown for multilingual content like privacy policies, documentation, etc. Interpret and make Markdown metadata accessible in your code.<br><br> - [Content files](https://intlayer.org/doc/concept/content/file) |
63
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.png?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
64
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
65
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.png?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
63
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.webp?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
64
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.webp?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
65
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.webp?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
66
66
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/AI_translation.png?raw=true" alt="Feature" width="700"> | **AI-Powered Translation**<br><br>Transform your website into 231 languages with just one click using Intlayer's advanced AI-powered translation tools using your own AI provider / API key. <br><br> - [CI/CD integration](https://intlayer.org/doc/concept/ci-cd) <br> - [Intlayer CLI](https://intlayer.org/doc/concept/cli) <br> - [Auto fill](https://intlayer.org/doc/concept/auto-fill) |
67
67
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/mcp.png?raw=true" alt="Feature" width="700"> | **MCP Server Integration**<br><br>Provides an MCP (Model Context Protocol) server for IDE automation, enabling seamless content management and i18n workflows directly within your development environment. <br><br> - [MCP Server](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/mcp_server.md) |
68
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.png?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
68
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.webp?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
69
69
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/interoperability.png?raw=true" alt="Feature" width="700"> | **Interoperability**<br><br>Allow interoperability with react-i18next, next-i18next, next-intl, react-intl, vue-i18n. <br><br> - [Intlayer and react-intl](https://intlayer.org/blog/intlayer-with-react-intl) <br> - [Intlayer and next-intl](https://intlayer.org/blog/intlayer-with-next-intl) <br> - [Intlayer and next-i18next](https://intlayer.org/blog/intlayer-with-next-i18next) <br> - [Intlayer and vue-i18n](https://intlayer.org/blog/intlayer-with-vue-i18n) <br> - [Intlayer compat adapters](https://intlayer.org/doc/compatibility) |
70
70
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/benchmark.png?raw=true" alt="Feature" width="700"> | **Performances & Benchmark**<br><br>Uses advanced tree-shaking and dynamic loading to boost performances and keep the solution as light as possible. <br><br> - [Performances & Benchmark](https://intlayer.org/doc/benchmark) |
71
71
 
@@ -262,7 +262,7 @@ Intlayer connects **developers**, **copywriters**, and **AI agents** in one work
262
262
  - Feature flags
263
263
  - Headless CMSs (e.g. Contentful, Strapi, Sanity)
264
264
 
265
- ![CMS Preview](https://github.com/aymericzip/intlayer/blob/main/docs/assets/CMS.png?raw=true)
265
+ ![CMS Preview](https://github.com/aymericzip/intlayer/blob/main/docs/assets/CMS.webp?raw=true)
266
266
 
267
267
  ## 🌐 Readme in other languages
268
268
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/sync-po-plugin",
3
- "version": "9.5.3",
3
+ "version": "9.5.4-canary.0",
4
4
  "private": false,
5
5
  "description": "A plugin for Intlayer that syncs Portable Object (.po) files to dictionaries.",
6
6
  "keywords": [
@@ -66,16 +66,17 @@
66
66
  "prepublish": "cp -f ../../README.md ./README.md",
67
67
  "publish": "bun publish || true",
68
68
  "publish:canary": "bun publish --access public --tag canary || true",
69
+ "publish:ci": "bun ../../scripts/publish-package.mjs",
69
70
  "publish:latest": "bun publish --access public --tag latest || true",
70
71
  "test": "bun --bun vitest run",
71
72
  "test:watch": "bun --bun vitest",
72
73
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
73
74
  },
74
75
  "dependencies": {
75
- "@intlayer/config": "9.5.3",
76
- "@intlayer/core": "9.5.3",
77
- "@intlayer/engine": "9.5.3",
78
- "@intlayer/types": "9.5.3",
76
+ "@intlayer/config": "9.5.4-canary.0",
77
+ "@intlayer/core": "9.5.4-canary.0",
78
+ "@intlayer/engine": "9.5.4-canary.0",
79
+ "@intlayer/types": "9.5.4-canary.0",
79
80
  "fast-glob": "3.3.3"
80
81
  },
81
82
  "devDependencies": {
@@ -86,7 +87,7 @@
86
87
  "rimraf": "6.1.3",
87
88
  "tsdown": "0.23.0",
88
89
  "typescript": "7.0.2",
89
- "vitest": "5.0.0"
90
+ "vitest": "5.0.1"
90
91
  },
91
92
  "engines": {
92
93
  "node": ">=14.18"
@@ -1,29 +0,0 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) {
13
- __defProp(to, key, {
14
- get: ((k) => from[k]).bind(null, key),
15
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
- });
17
- }
18
- }
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
- value: mod,
24
- enumerable: true
25
- }) : target, mod));
26
-
27
- //#endregion
28
-
29
- exports.__toESM = __toESM;
@@ -1,95 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
3
- const require_syncJSON = require('./syncJSON.cjs');
4
- let node_path = require("node:path");
5
- let _intlayer_config_file = require("@intlayer/config/file");
6
- let _intlayer_config_utils = require("@intlayer/config/utils");
7
- let fast_glob = require("fast-glob");
8
- fast_glob = require_runtime.__toESM(fast_glob);
9
-
10
- //#region src/loadJSON.ts
11
- const listMessages = async (source, configuration) => {
12
- const { system, internationalization } = configuration;
13
- const { baseDir } = system;
14
- const { locales } = internationalization;
15
- const result = {};
16
- for (const locale of locales) {
17
- const globPatternLocale = await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
18
- key: "**",
19
- locale
20
- });
21
- const maskPatternLocale = await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
22
- key: "{{__KEY__}}",
23
- locale
24
- });
25
- if (!globPatternLocale || !maskPatternLocale) continue;
26
- const files = await (0, fast_glob.default)(globPatternLocale.startsWith("./") ? globPatternLocale.slice(2) : globPatternLocale, { cwd: baseDir });
27
- for (const file of files) {
28
- const hasLocaleInMask = maskPatternLocale.includes("{{__LOCALE__}}");
29
- const hasKeyInMask = maskPatternLocale.includes("{{__KEY__}}");
30
- let key;
31
- let extractedLocale;
32
- if (hasLocaleInMask || hasKeyInMask) {
33
- const extraction = require_syncJSON.extractKeyAndLocaleFromPath(file, maskPatternLocale, locales, locale);
34
- if (!extraction) continue;
35
- key = extraction.key;
36
- extractedLocale = extraction.locale;
37
- } else {
38
- key = "index";
39
- extractedLocale = locale;
40
- }
41
- const absolutePath = (0, node_path.isAbsolute)(file) ? file : (0, node_path.resolve)(baseDir, file);
42
- const usedLocale = extractedLocale;
43
- if (!result[usedLocale]) result[usedLocale] = {};
44
- result[usedLocale][key] = absolutePath;
45
- }
46
- }
47
- return result;
48
- };
49
- const loadMessagePathMap = async (source, configuration) => {
50
- const messages = await listMessages(source, configuration);
51
- return Object.entries(messages).flatMap(([locale, keysRecord]) => Object.entries(keysRecord).map(([key, path]) => {
52
- return {
53
- path: (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(configuration.system.baseDir, path),
54
- locale,
55
- key
56
- };
57
- }));
58
- };
59
- const loadJSON = (options) => {
60
- const { location, priority, locale, format } = {
61
- location: "plugin",
62
- priority: 0,
63
- ...options
64
- };
65
- return {
66
- name: "load-json",
67
- loadDictionaries: async ({ configuration }) => {
68
- const dictionariesMap = await loadMessagePathMap(options.source, configuration);
69
- const dictionaries = [];
70
- for (const { path, key, locale: entryLocale } of dictionariesMap) {
71
- const json = await (0, _intlayer_config_file.loadExternalFile)(path, { logError: false }) ?? {};
72
- const filePath = (0, node_path.relative)(configuration.system.baseDir, path);
73
- const entryUsedLocale = locale ?? entryLocale;
74
- const dictionary = {
75
- key,
76
- locale: entryUsedLocale,
77
- fill: filePath,
78
- format,
79
- localId: `${key}::${location}::${filePath}`,
80
- location,
81
- filled: entryUsedLocale !== configuration.internationalization.defaultLocale ? true : void 0,
82
- content: json,
83
- filePath,
84
- priority
85
- };
86
- dictionaries.push(dictionary);
87
- }
88
- return dictionaries;
89
- }
90
- };
91
- };
92
-
93
- //#endregion
94
- exports.loadJSON = loadJSON;
95
- //# sourceMappingURL=loadJSON.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loadJSON.cjs","names":["extractKeyAndLocaleFromPath","sourcePattern"],"sources":["../../src/loadJSON.ts"],"sourcesContent":["import { isAbsolute, relative, resolve } from 'node:path';\nimport { loadExternalFile } from '@intlayer/config/file';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n} from '@intlayer/types/filePathPattern';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport fg from 'fast-glob';\nimport { extractKeyAndLocaleFromPath } from './syncJSON';\n\ntype JSONContent = Record<string, any>;\n\ntype FilePath = string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst listMessages = async (\n source: FilePathPattern,\n configuration: IntlayerConfig\n): Promise<MessagesRecord> => {\n const { system, internationalization } = configuration;\n\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(source, {\n key: '**',\n locale,\n } as any as FilePathPatternContext);\n\n const maskPatternLocale = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale,\n } as any as FilePathPatternContext);\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const normalizedGlobPattern = globPatternLocale.startsWith('./')\n ? globPatternLocale.slice(2)\n : globPatternLocale;\n\n const files = await fg(normalizedGlobPattern, {\n cwd: baseDir,\n });\n\n for (const file of files) {\n // extractKeyAndLocaleFromPath requires at least one named capture group\n // ({{__LOCALE__}} or {{__KEY__}}) in the mask to return a non-null result.\n // When the mask is fully concrete (e.g. `messages_ICU/en.json` — the source\n // has {{locale}} but no {{key}}), no groups exist and it returns null.\n // In that case, fall back directly to the loop locale and key = 'index'.\n const hasLocaleInMask = maskPatternLocale.includes('{{__LOCALE__}}');\n const hasKeyInMask = maskPatternLocale.includes('{{__KEY__}}');\n\n let key: string;\n let extractedLocale: Locale;\n\n if (hasLocaleInMask || hasKeyInMask) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n key = extraction.key;\n extractedLocale = extraction.locale;\n } else {\n // Mask has no placeholders — the file was found via a concrete locale\n // glob. Attribute it directly to the current loop locale.\n key = 'index';\n extractedLocale = locale;\n }\n\n const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);\n\n const usedLocale = extractedLocale as Locale;\n if (!result[usedLocale]) {\n result[usedLocale] = {};\n }\n\n result[usedLocale][key as Dictionary['key']] = absolutePath;\n }\n }\n\n // For the load plugin we only use actual discovered files; do not fabricate\n // missing locales or keys, since we don't write outputs.\n return result;\n};\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = async (\n source: MessagesRecord | FilePathPattern,\n configuration: IntlayerConfig\n) => {\n const sourcePattern = source as FilePathPattern;\n const messages: MessagesRecord = await listMessages(\n sourcePattern,\n configuration\n );\n\n // Always include all discovered locales — loadJSON is read-only and should\n // ingest every locale file that exists, just like syncJSON does.\n const entries = Object.entries(messages) as [\n Locale,\n Record<Dictionary['key'], FilePath>,\n ][];\n\n const dictionariesPathMap: DictionariesMap = entries.flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.system.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype LoadJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * })\n * ```\n */\n source: FilePathPattern;\n\n /**\n * Locale\n *\n * If not provided, the plugin will consider the default locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * locale: Locales.ENGLISH,\n * })\n * ```\n */\n locale?: Locale;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * @example\n * ```ts\n * const config = {\n * plugins: [\n * loadJSON({\n * source: ({ key }) => `./resources/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * loadJSON({\n * source: ({ key }) => `./messages/${key}.json`,\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: string;\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n\n /**\n * The format of the dictionary content.\n *\n * @example\n * ```ts\n * loadJSON({\n * format: 'icu',\n * })\n * ```\n */\n format?: DictionaryFormat;\n};\n\nexport const loadJSON = (options: LoadJSONPluginOptions): Plugin => {\n const { location, priority, locale, format } = {\n location: 'plugin',\n priority: 0,\n ...options,\n } as const;\n\n return {\n name: 'load-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = await loadMessagePathMap(\n options.source,\n configuration\n );\n\n const dictionaries: Dictionary[] = [];\n\n for (const { path, key, locale: entryLocale } of dictionariesMap) {\n // loadExternalFile swallows errors and returns undefined for missing files;\n // use ?? {} to guarantee a plain object regardless.\n const json: JSONContent =\n (await loadExternalFile(path, { logError: false })) ?? {};\n\n const filePath = relative(configuration.system.baseDir, path);\n\n // Use the per-entry locale discovered from the file path. If a fixed\n // locale override was provided, use it only as a fallback.\n const entryUsedLocale = (locale ?? entryLocale) as Locale;\n\n const dictionary: Dictionary = {\n key,\n locale: entryUsedLocale,\n fill: filePath,\n format,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n entryUsedLocale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n };\n};\n"],"mappings":";;;;;;;;;;AAwBA,MAAM,eAAe,OACnB,QACA,kBAC4B;CAC5B,MAAM,EAAE,QAAQ,yBAAyB;CAEzC,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,YAAY;CAEpB,MAAM,SAAyB,EAAE;CAEjC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,oBAAoB,uDAA2B,QAAQ;GAC3D,KAAK;GACL;GACD,CAAkC;EAEnC,MAAM,oBAAoB,uDAA2B,QAAQ;GAC3D,KAAK;GACL;GACD,CAAkC;EAEnC,IAAI,CAAC,qBAAqB,CAAC,mBACzB;EAOF,MAAM,QAAQ,6BAJgB,kBAAkB,WAAW,KAAK,GAC5D,kBAAkB,MAAM,EAAE,GAC1B,mBAE0C,EAC5C,KAAK,SACN,CAAC;EAEF,KAAK,MAAM,QAAQ,OAAO;GAMxB,MAAM,kBAAkB,kBAAkB,SAAS,iBAAiB;GACpE,MAAM,eAAe,kBAAkB,SAAS,cAAc;GAE9D,IAAI;GACJ,IAAI;GAEJ,IAAI,mBAAmB,cAAc;IACnC,MAAM,aAAaA,6CACjB,MACA,mBACA,SACA,OACD;IAED,IAAI,CAAC,YACH;IAGF,MAAM,WAAW;IACjB,kBAAkB,WAAW;UACxB;IAGL,MAAM;IACN,kBAAkB;;GAGpB,MAAM,yCAA0B,KAAK,GAAG,8BAAe,SAAS,KAAK;GAErE,MAAM,aAAa;GACnB,IAAI,CAAC,OAAO,aACV,OAAO,cAAc,EAAE;GAGzB,OAAO,YAAY,OAA4B;;;CAMnD,OAAO;;AAKT,MAAM,qBAAqB,OACzB,QACA,kBACG;CAEH,MAAM,WAA2B,MAAM,aACrCC,QACA,cACD;CAwBD,OApBgB,OAAO,QAAQ,SAKqB,CAAC,SAClD,CAAC,QAAQ,gBACR,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,KAAK,UAAU;EAK9C,OAAO;GACL,gCAL8B,KAAK,GACjC,8BACQ,cAAc,OAAO,SAAS,KAAK;GAI7C;GACA;GACD;GACD,CAGoB;;AA+E5B,MAAa,YAAY,YAA2C;CAClE,MAAM,EAAE,UAAU,UAAU,QAAQ,WAAW;EAC7C,UAAU;EACV,UAAU;EACV,GAAG;EACJ;CAED,OAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,EAAE,oBAAoB;GAC7C,MAAM,kBAAmC,MAAM,mBAC7C,QAAQ,QACR,cACD;GAED,MAAM,eAA6B,EAAE;GAErC,KAAK,MAAM,EAAE,MAAM,KAAK,QAAQ,iBAAiB,iBAAiB;IAGhE,MAAM,OACH,kDAAuB,MAAM,EAAE,UAAU,OAAO,CAAC,IAAK,EAAE;IAE3D,MAAM,mCAAoB,cAAc,OAAO,SAAS,KAAK;IAI7D,MAAM,kBAAmB,UAAU;IAEnC,MAAM,aAAyB;KAC7B;KACA,QAAQ;KACR,MAAM;KACN;KACA,SAAS,GAAG,IAAI,IAAI,SAAS,IAAI;KACvB;KACV,QACE,oBAAoB,cAAc,qBAAqB,gBACnD,OACA;KACN,SAAS;KACT;KACA;KACD;IAED,aAAa,KAAK,WAAW;;GAG/B,OAAO;;EAEV"}
@@ -1,173 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
3
- let node_path = require("node:path");
4
- let _intlayer_config_file = require("@intlayer/config/file");
5
- let _intlayer_config_utils = require("@intlayer/config/utils");
6
- let fast_glob = require("fast-glob");
7
- fast_glob = require_runtime.__toESM(fast_glob);
8
- let node_fs_promises = require("node:fs/promises");
9
-
10
- //#region src/syncJSON.ts
11
- const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12
- const extractKeyAndLocaleFromPath = (filePath, maskPattern, locales, defaultLocale) => {
13
- const keyPlaceholder = "{{__KEY__}}";
14
- const localePlaceholder = "{{__LOCALE__}}";
15
- const normalize = (path) => path.startsWith("./") ? path.slice(2) : path;
16
- const normalizedFilePath = normalize(filePath);
17
- const normalizedMask = normalize(maskPattern);
18
- const localesAlternation = locales.join("|");
19
- let regexStr = `^${escapeRegex(normalizedMask)}$`;
20
- regexStr = regexStr.replace(/\\\*\\\*/g, ".*");
21
- regexStr = regexStr.replace(/\\\*/g, "[^/]*");
22
- regexStr = regexStr.replace(escapeRegex(localePlaceholder), `(?<locale>${localesAlternation})`);
23
- if (normalizedMask.includes(keyPlaceholder)) regexStr = regexStr.replace(escapeRegex(keyPlaceholder), "(?<key>.+)");
24
- const match = new RegExp(regexStr).exec(normalizedFilePath);
25
- if (!match?.groups) return null;
26
- let locale = match.groups.locale;
27
- let key = match.groups.key ?? "index";
28
- if (typeof key === "undefined") key = "index";
29
- if (typeof locale === "undefined") locale = defaultLocale;
30
- return {
31
- key,
32
- locale
33
- };
34
- };
35
- const listMessages = async (source, configuration) => {
36
- const { system, internationalization } = configuration;
37
- const { baseDir } = system;
38
- const { locales } = internationalization;
39
- const result = {};
40
- for (const locale of locales) {
41
- const globPatternLocale = await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
42
- key: "**",
43
- locale
44
- });
45
- const maskPatternLocale = await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
46
- key: "{{__KEY__}}",
47
- locale
48
- });
49
- if (!globPatternLocale || !maskPatternLocale) continue;
50
- const files = await (0, fast_glob.default)(globPatternLocale.startsWith("./") ? globPatternLocale.slice(2) : globPatternLocale, { cwd: baseDir });
51
- for (const file of files) {
52
- const extraction = extractKeyAndLocaleFromPath(file, maskPatternLocale, locales, locale);
53
- if (!extraction) continue;
54
- const { key, locale: extractedLocale } = extraction;
55
- const expectedPath = await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
56
- key,
57
- locale: extractedLocale
58
- });
59
- const absoluteFoundPath = (0, node_path.isAbsolute)(file) ? file : (0, node_path.resolve)(baseDir, file);
60
- if (absoluteFoundPath !== ((0, node_path.isAbsolute)(expectedPath) ? expectedPath : (0, node_path.resolve)(baseDir, expectedPath))) continue;
61
- const usedLocale = extractedLocale;
62
- if (!result[usedLocale]) result[usedLocale] = {};
63
- result[usedLocale][key] = absoluteFoundPath;
64
- }
65
- }
66
- const hasKeyInMask = (await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
67
- key: "{{__KEY__}}",
68
- locale: locales[0]
69
- })).includes("{{__KEY__}}");
70
- const discoveredKeys = /* @__PURE__ */ new Set();
71
- for (const locale of Object.keys(result)) for (const key of Object.keys(result[locale] ?? {})) discoveredKeys.add(key);
72
- if (!hasKeyInMask) discoveredKeys.add("index");
73
- const keysToEnsure = discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];
74
- for (const locale of locales) {
75
- if (!result[locale]) result[locale] = {};
76
- for (const key of keysToEnsure) if (!result[locale][key]) {
77
- const builtPath = await (0, _intlayer_config_utils.parseFilePathPattern)(source, {
78
- key,
79
- locale
80
- });
81
- const absoluteBuiltPath = (0, node_path.isAbsolute)(builtPath) ? builtPath : (0, node_path.resolve)(baseDir, builtPath);
82
- result[locale][key] = absoluteBuiltPath;
83
- }
84
- }
85
- return result;
86
- };
87
- const loadMessagePathMap = async (source, configuration) => {
88
- const messages = await listMessages(source, configuration);
89
- return Object.entries(messages).flatMap(([locale, keysRecord]) => Object.entries(keysRecord).map(([key, path]) => {
90
- return {
91
- path: (0, node_path.isAbsolute)(path) ? path : (0, node_path.resolve)(configuration.system.baseDir, path),
92
- locale,
93
- key
94
- };
95
- }));
96
- };
97
- const syncJSON = async (options) => {
98
- const { location, priority, format } = {
99
- location: `sync-json::${await (0, _intlayer_config_utils.parseFilePathPattern)(options.source, {
100
- key: "{{key}}",
101
- locale: "{{locale}}"
102
- })}`,
103
- priority: 0,
104
- ...options
105
- };
106
- return {
107
- name: "sync-json",
108
- loadDictionaries: async ({ configuration }) => {
109
- const dictionariesMap = await loadMessagePathMap(options.source, configuration);
110
- let fill = await (0, _intlayer_config_utils.parseFilePathPattern)(options.source, {
111
- key: "{{key}}",
112
- locale: "{{locale}}"
113
- });
114
- if (fill) fill = (0, node_path.relative)(configuration.system.baseDir, (0, node_path.resolve)(configuration.system.baseDir, fill));
115
- const dictionaries = [];
116
- for (const { locale, path, key } of dictionariesMap) {
117
- const json = await (0, _intlayer_config_file.loadExternalFile)(path, { logError: false }) ?? {};
118
- const filePath = (0, node_path.relative)(configuration.system.baseDir, path);
119
- const dictionary = {
120
- key,
121
- locale,
122
- fill,
123
- format,
124
- localId: `${key}::${location}::${filePath}`,
125
- location,
126
- filled: locale !== configuration.internationalization.defaultLocale ? true : void 0,
127
- content: json,
128
- filePath,
129
- priority
130
- };
131
- dictionaries.push(dictionary);
132
- }
133
- return dictionaries;
134
- },
135
- formatOutput: async ({ dictionary, configuration }) => {
136
- const { formatDictionaryOutput } = await import("@intlayer/chokidar/build");
137
- if (!dictionary.filePath || !dictionary.locale) return dictionary;
138
- const builderPath = await (0, _intlayer_config_utils.parseFilePathPattern)(options.source, {
139
- key: dictionary.key,
140
- locale: dictionary.locale
141
- });
142
- if ((0, node_path.resolve)(configuration.system.baseDir, builderPath) !== (0, node_path.resolve)(configuration.system.baseDir, dictionary.filePath)) return dictionary;
143
- return formatDictionaryOutput(dictionary, format).content;
144
- },
145
- afterBuild: async ({ dictionaries, configuration }) => {
146
- const { getPerLocaleDictionary } = await import("@intlayer/core/plugins");
147
- const { parallelize } = await import("@intlayer/chokidar/utils");
148
- const { formatDictionaryOutput } = await import("@intlayer/chokidar/build");
149
- const { locales } = configuration.internationalization;
150
- await parallelize(Object.entries(dictionaries.mergedDictionaries).flatMap(([key, dictionary]) => locales.map((locale) => ({
151
- key,
152
- dictionary: dictionary.dictionary,
153
- locale
154
- }))), async ({ key, dictionary, locale }) => {
155
- if (dictionary.location !== location) return;
156
- const builderPath = await (0, _intlayer_config_utils.parseFilePathPattern)(options.source, {
157
- key,
158
- locale
159
- });
160
- const formattedOutput = formatDictionaryOutput(getPerLocaleDictionary(dictionary, locale), format);
161
- const content = JSON.parse(JSON.stringify(formattedOutput.content));
162
- if (typeof content === "undefined" || typeof content === "object" && Object.keys(content).length === 0) return;
163
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(builderPath), { recursive: true });
164
- await (0, node_fs_promises.writeFile)(builderPath, `${JSON.stringify(content, null, 2)}\n`, "utf-8");
165
- });
166
- }
167
- };
168
- };
169
-
170
- //#endregion
171
- exports.extractKeyAndLocaleFromPath = extractKeyAndLocaleFromPath;
172
- exports.syncJSON = syncJSON;
173
- //# sourceMappingURL=syncJSON.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"syncJSON.cjs","names":[],"sources":["../../src/syncJSON.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { loadExternalFile } from '@intlayer/config/file';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n DictionaryLocation,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n} from '@intlayer/types/filePathPattern';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype FilePath = string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: Locale\n): { key: string; locale: Locale } | null => {\n const keyPlaceholder = '{{__KEY__}}';\n const localePlaceholder = '{{__LOCALE__}}';\n\n // fast-glob strips leading \"./\" from returned paths; normalize both sides\n const normalize = (path: string) =>\n path.startsWith('./') ? path.slice(2) : path;\n\n const normalizedFilePath = normalize(filePath);\n const normalizedMask = normalize(maskPattern);\n\n const localesAlternation = locales.join('|');\n\n // Escape special regex chars, then convert glob wildcards to regex equivalents.\n // Must replace ** before * to avoid double-replacing.\n let regexStr = `^${escapeRegex(normalizedMask)}$`;\n regexStr = regexStr.replace(/\\\\\\*\\\\\\*/g, '.*'); // ** → match any path segments\n regexStr = regexStr.replace(/\\\\\\*/g, '[^/]*'); // * → match within a single segment\n\n regexStr = regexStr.replace(\n escapeRegex(localePlaceholder),\n `(?<locale>${localesAlternation})`\n );\n\n if (normalizedMask.includes(keyPlaceholder)) {\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>.+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n const match = maskRegex.exec(normalizedFilePath);\n\n if (!match?.groups) {\n return null;\n }\n\n let locale = match.groups.locale as Locale | undefined;\n let key = (match.groups.key as string | undefined) ?? 'index';\n\n if (typeof key === 'undefined') {\n key = 'index';\n }\n\n if (typeof locale === 'undefined') {\n locale = defaultLocale;\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = async (\n source: FilePathPattern,\n configuration: IntlayerConfig\n): Promise<MessagesRecord> => {\n const { system, internationalization } = configuration;\n\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(source, {\n key: '**',\n locale,\n } as any as FilePathPatternContext);\n\n const maskPatternLocale = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale,\n } as any as FilePathPatternContext);\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const normalizedGlobPattern = globPatternLocale.startsWith('./')\n ? globPatternLocale.slice(2)\n : globPatternLocale;\n\n const files = await fg(normalizedGlobPattern, {\n cwd: baseDir,\n });\n\n for (const file of files) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n const { key, locale: extractedLocale } = extraction;\n\n // Generate what the path SHOULD be for this key/locale using the current builder\n const expectedPath = await parseFilePathPattern(source, {\n key,\n locale: extractedLocale,\n } as any as FilePathPatternContext);\n\n // Resolve both to absolute paths to ensure safe comparison\n const absoluteFoundPath = isAbsolute(file)\n ? file\n : resolve(baseDir, file);\n const absoluteExpectedPath = isAbsolute(expectedPath)\n ? expectedPath\n : resolve(baseDir, expectedPath);\n\n // If the file found doesn't exactly match the file expected, it belongs to another plugin/structure\n if (absoluteFoundPath !== absoluteExpectedPath) {\n continue;\n }\n\n const usedLocale = extractedLocale as Locale;\n if (!result[usedLocale]) {\n result[usedLocale] = {};\n }\n\n result[usedLocale][key as Dictionary['key']] = absoluteFoundPath;\n }\n }\n\n // Ensure all declared locales are present even if the file doesn't exist yet\n const maskWithKey = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale: locales[0],\n } as any as FilePathPatternContext);\n\n const hasKeyInMask = maskWithKey.includes('{{__KEY__}}');\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result)) {\n for (const key of Object.keys(result[locale as Locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n const keysToEnsure =\n discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];\n\n for (const locale of locales) {\n if (!result[locale]) {\n result[locale] = {} as Record<Dictionary['key'], FilePath>;\n }\n\n for (const key of keysToEnsure) {\n if (!result[locale][key as Dictionary['key']]) {\n const builtPath = await parseFilePathPattern(source, {\n key,\n locale,\n } as any as FilePathPatternContext);\n const absoluteBuiltPath = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n\n result[locale][key as Dictionary['key']] = absoluteBuiltPath;\n }\n }\n }\n\n return result;\n};\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = async (\n source: MessagesRecord | FilePathPattern,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = await listMessages(\n source as FilePathPattern,\n configuration\n );\n\n const dictionariesPathMap: DictionariesMap = Object.entries(messages).flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.system.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype SyncJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * ```ts\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * })\n * ```\n */\n source: FilePathPattern;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * ```ts\n * // Example usage:\n * const config = {\n * plugins: [\n * syncJSON({\n * source: ({ key, locale }) => `./resources/${locale}/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`,\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: DictionaryLocation | (string & {});\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n\n /**\n * The format of the dictionaries created by the plugin.\n *\n * Default: 'intlayer'\n *\n * The format of the dictionaries created by the plugin.\n */\n format?: DictionaryFormat;\n};\n\nexport const syncJSON = async (\n options: SyncJSONPluginOptions\n): Promise<Plugin> => {\n // Generate a unique default location based on the source pattern.\n // This ensures that if you have multiple plugins, they don't share the same 'plugin' ID.\n const patternMarker = await parseFilePathPattern(options.source, {\n key: '{{key}}',\n locale: '{{locale}}',\n } as any as FilePathPatternContext);\n const defaultLocation = `sync-json::${patternMarker}`;\n\n const { location, priority, format } = {\n location: defaultLocation,\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = await loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = await parseFilePathPattern(options.source, {\n key: '{{key}}',\n locale: '{{locale}}',\n } as any as FilePathPatternContext);\n\n if (fill) {\n fill = relative(\n configuration.system.baseDir,\n resolve(configuration.system.baseDir, fill)\n );\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n // loadExternalFile swallows errors and returns undefined for missing files;\n // the try/catch does not help here — use ?? {} to guarantee a plain object.\n const json: JSONContent =\n (await loadExternalFile(path, { logError: false })) ?? {};\n\n const filePath = relative(configuration.system.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n format,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n locale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n\n formatOutput: async ({ dictionary, configuration }) => {\n // Lazy import intlayer modules to avoid circular dependencies\n const { formatDictionaryOutput } = await import(\n '@intlayer/chokidar/build'\n );\n\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = await parseFilePathPattern(options.source, {\n key: dictionary.key,\n locale: dictionary.locale,\n } as FilePathPatternContext);\n\n // Verification to ensure we are formatting the correct file\n if (\n resolve(configuration.system.baseDir, builderPath) !==\n resolve(configuration.system.baseDir, dictionary.filePath)\n ) {\n return dictionary;\n }\n\n const formattedOutput = formatDictionaryOutput(dictionary, format);\n\n return formattedOutput.content;\n },\n\n afterBuild: async ({ dictionaries, configuration }) => {\n // Lazy import intlayer modules to avoid circular dependencies\n const { getPerLocaleDictionary } = await import('@intlayer/core/plugins');\n const { parallelize } = await import('@intlayer/chokidar/utils');\n const { formatDictionaryOutput } = await import(\n '@intlayer/chokidar/build'\n );\n\n const { locales } = configuration.internationalization;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n // We get all dictionaries, but we need to filter them\n const recordList: RecordList[] = Object.entries(\n dictionaries.mergedDictionaries\n ).flatMap(([key, dictionary]) =>\n locales.map((locale) => ({\n key,\n dictionary: dictionary.dictionary as Dictionary,\n locale,\n }))\n );\n\n await parallelize(recordList, async ({ key, dictionary, locale }) => {\n // Only process dictionaries that belong to THIS plugin instance.\n if (dictionary.location !== location) {\n return;\n }\n\n const builderPath = await parseFilePathPattern(options.source, {\n key,\n locale,\n } as any as FilePathPatternContext);\n\n const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n const formattedOutput = formatDictionaryOutput(\n localizedDictionary,\n format\n );\n\n const content = JSON.parse(JSON.stringify(formattedOutput.content));\n\n if (\n typeof content === 'undefined' ||\n (typeof content === 'object' &&\n Object.keys(content as Record<string, unknown>).length === 0)\n ) {\n return;\n }\n\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(content, null, 2);\n\n await writeFile(builderPath, `${stringContent}\\n`, 'utf-8');\n });\n },\n };\n};\n"],"mappings":"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"}
@@ -1,92 +0,0 @@
1
- import { extractKeyAndLocaleFromPath } from "./syncJSON.mjs";
2
- import { isAbsolute, relative, resolve } from "node:path";
3
- import { loadExternalFile } from "@intlayer/config/file";
4
- import { parseFilePathPattern } from "@intlayer/config/utils";
5
- import fg from "fast-glob";
6
-
7
- //#region src/loadJSON.ts
8
- const listMessages = async (source, configuration) => {
9
- const { system, internationalization } = configuration;
10
- const { baseDir } = system;
11
- const { locales } = internationalization;
12
- const result = {};
13
- for (const locale of locales) {
14
- const globPatternLocale = await parseFilePathPattern(source, {
15
- key: "**",
16
- locale
17
- });
18
- const maskPatternLocale = await parseFilePathPattern(source, {
19
- key: "{{__KEY__}}",
20
- locale
21
- });
22
- if (!globPatternLocale || !maskPatternLocale) continue;
23
- const files = await fg(globPatternLocale.startsWith("./") ? globPatternLocale.slice(2) : globPatternLocale, { cwd: baseDir });
24
- for (const file of files) {
25
- const hasLocaleInMask = maskPatternLocale.includes("{{__LOCALE__}}");
26
- const hasKeyInMask = maskPatternLocale.includes("{{__KEY__}}");
27
- let key;
28
- let extractedLocale;
29
- if (hasLocaleInMask || hasKeyInMask) {
30
- const extraction = extractKeyAndLocaleFromPath(file, maskPatternLocale, locales, locale);
31
- if (!extraction) continue;
32
- key = extraction.key;
33
- extractedLocale = extraction.locale;
34
- } else {
35
- key = "index";
36
- extractedLocale = locale;
37
- }
38
- const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);
39
- const usedLocale = extractedLocale;
40
- if (!result[usedLocale]) result[usedLocale] = {};
41
- result[usedLocale][key] = absolutePath;
42
- }
43
- }
44
- return result;
45
- };
46
- const loadMessagePathMap = async (source, configuration) => {
47
- const messages = await listMessages(source, configuration);
48
- return Object.entries(messages).flatMap(([locale, keysRecord]) => Object.entries(keysRecord).map(([key, path]) => {
49
- return {
50
- path: isAbsolute(path) ? path : resolve(configuration.system.baseDir, path),
51
- locale,
52
- key
53
- };
54
- }));
55
- };
56
- const loadJSON = (options) => {
57
- const { location, priority, locale, format } = {
58
- location: "plugin",
59
- priority: 0,
60
- ...options
61
- };
62
- return {
63
- name: "load-json",
64
- loadDictionaries: async ({ configuration }) => {
65
- const dictionariesMap = await loadMessagePathMap(options.source, configuration);
66
- const dictionaries = [];
67
- for (const { path, key, locale: entryLocale } of dictionariesMap) {
68
- const json = await loadExternalFile(path, { logError: false }) ?? {};
69
- const filePath = relative(configuration.system.baseDir, path);
70
- const entryUsedLocale = locale ?? entryLocale;
71
- const dictionary = {
72
- key,
73
- locale: entryUsedLocale,
74
- fill: filePath,
75
- format,
76
- localId: `${key}::${location}::${filePath}`,
77
- location,
78
- filled: entryUsedLocale !== configuration.internationalization.defaultLocale ? true : void 0,
79
- content: json,
80
- filePath,
81
- priority
82
- };
83
- dictionaries.push(dictionary);
84
- }
85
- return dictionaries;
86
- }
87
- };
88
- };
89
-
90
- //#endregion
91
- export { loadJSON };
92
- //# sourceMappingURL=loadJSON.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loadJSON.mjs","names":["sourcePattern"],"sources":["../../src/loadJSON.ts"],"sourcesContent":["import { isAbsolute, relative, resolve } from 'node:path';\nimport { loadExternalFile } from '@intlayer/config/file';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n} from '@intlayer/types/filePathPattern';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport fg from 'fast-glob';\nimport { extractKeyAndLocaleFromPath } from './syncJSON';\n\ntype JSONContent = Record<string, any>;\n\ntype FilePath = string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst listMessages = async (\n source: FilePathPattern,\n configuration: IntlayerConfig\n): Promise<MessagesRecord> => {\n const { system, internationalization } = configuration;\n\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(source, {\n key: '**',\n locale,\n } as any as FilePathPatternContext);\n\n const maskPatternLocale = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale,\n } as any as FilePathPatternContext);\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const normalizedGlobPattern = globPatternLocale.startsWith('./')\n ? globPatternLocale.slice(2)\n : globPatternLocale;\n\n const files = await fg(normalizedGlobPattern, {\n cwd: baseDir,\n });\n\n for (const file of files) {\n // extractKeyAndLocaleFromPath requires at least one named capture group\n // ({{__LOCALE__}} or {{__KEY__}}) in the mask to return a non-null result.\n // When the mask is fully concrete (e.g. `messages_ICU/en.json` — the source\n // has {{locale}} but no {{key}}), no groups exist and it returns null.\n // In that case, fall back directly to the loop locale and key = 'index'.\n const hasLocaleInMask = maskPatternLocale.includes('{{__LOCALE__}}');\n const hasKeyInMask = maskPatternLocale.includes('{{__KEY__}}');\n\n let key: string;\n let extractedLocale: Locale;\n\n if (hasLocaleInMask || hasKeyInMask) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n key = extraction.key;\n extractedLocale = extraction.locale;\n } else {\n // Mask has no placeholders — the file was found via a concrete locale\n // glob. Attribute it directly to the current loop locale.\n key = 'index';\n extractedLocale = locale;\n }\n\n const absolutePath = isAbsolute(file) ? file : resolve(baseDir, file);\n\n const usedLocale = extractedLocale as Locale;\n if (!result[usedLocale]) {\n result[usedLocale] = {};\n }\n\n result[usedLocale][key as Dictionary['key']] = absolutePath;\n }\n }\n\n // For the load plugin we only use actual discovered files; do not fabricate\n // missing locales or keys, since we don't write outputs.\n return result;\n};\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = async (\n source: MessagesRecord | FilePathPattern,\n configuration: IntlayerConfig\n) => {\n const sourcePattern = source as FilePathPattern;\n const messages: MessagesRecord = await listMessages(\n sourcePattern,\n configuration\n );\n\n // Always include all discovered locales — loadJSON is read-only and should\n // ingest every locale file that exists, just like syncJSON does.\n const entries = Object.entries(messages) as [\n Locale,\n Record<Dictionary['key'], FilePath>,\n ][];\n\n const dictionariesPathMap: DictionariesMap = entries.flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.system.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype LoadJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * })\n * ```\n */\n source: FilePathPattern;\n\n /**\n * Locale\n *\n * If not provided, the plugin will consider the default locale.\n *\n * @example\n * ```ts\n * loadJSON({\n * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,\n * locale: Locales.ENGLISH,\n * })\n * ```\n */\n locale?: Locale;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * @example\n * ```ts\n * const config = {\n * plugins: [\n * loadJSON({\n * source: ({ key }) => `./resources/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * loadJSON({\n * source: ({ key }) => `./messages/${key}.json`,\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: string;\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n\n /**\n * The format of the dictionary content.\n *\n * @example\n * ```ts\n * loadJSON({\n * format: 'icu',\n * })\n * ```\n */\n format?: DictionaryFormat;\n};\n\nexport const loadJSON = (options: LoadJSONPluginOptions): Plugin => {\n const { location, priority, locale, format } = {\n location: 'plugin',\n priority: 0,\n ...options,\n } as const;\n\n return {\n name: 'load-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = await loadMessagePathMap(\n options.source,\n configuration\n );\n\n const dictionaries: Dictionary[] = [];\n\n for (const { path, key, locale: entryLocale } of dictionariesMap) {\n // loadExternalFile swallows errors and returns undefined for missing files;\n // use ?? {} to guarantee a plain object regardless.\n const json: JSONContent =\n (await loadExternalFile(path, { logError: false })) ?? {};\n\n const filePath = relative(configuration.system.baseDir, path);\n\n // Use the per-entry locale discovered from the file path. If a fixed\n // locale override was provided, use it only as a fallback.\n const entryUsedLocale = (locale ?? entryLocale) as Locale;\n\n const dictionary: Dictionary = {\n key,\n locale: entryUsedLocale,\n fill: filePath,\n format,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n entryUsedLocale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n };\n};\n"],"mappings":";;;;;;;AAwBA,MAAM,eAAe,OACnB,QACA,kBAC4B;CAC5B,MAAM,EAAE,QAAQ,yBAAyB;CAEzC,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,YAAY;CAEpB,MAAM,SAAyB,EAAE;CAEjC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,oBAAoB,MAAM,qBAAqB,QAAQ;GAC3D,KAAK;GACL;GACD,CAAkC;EAEnC,MAAM,oBAAoB,MAAM,qBAAqB,QAAQ;GAC3D,KAAK;GACL;GACD,CAAkC;EAEnC,IAAI,CAAC,qBAAqB,CAAC,mBACzB;EAOF,MAAM,QAAQ,MAAM,GAJU,kBAAkB,WAAW,KAAK,GAC5D,kBAAkB,MAAM,EAAE,GAC1B,mBAE0C,EAC5C,KAAK,SACN,CAAC;EAEF,KAAK,MAAM,QAAQ,OAAO;GAMxB,MAAM,kBAAkB,kBAAkB,SAAS,iBAAiB;GACpE,MAAM,eAAe,kBAAkB,SAAS,cAAc;GAE9D,IAAI;GACJ,IAAI;GAEJ,IAAI,mBAAmB,cAAc;IACnC,MAAM,aAAa,4BACjB,MACA,mBACA,SACA,OACD;IAED,IAAI,CAAC,YACH;IAGF,MAAM,WAAW;IACjB,kBAAkB,WAAW;UACxB;IAGL,MAAM;IACN,kBAAkB;;GAGpB,MAAM,eAAe,WAAW,KAAK,GAAG,OAAO,QAAQ,SAAS,KAAK;GAErE,MAAM,aAAa;GACnB,IAAI,CAAC,OAAO,aACV,OAAO,cAAc,EAAE;GAGzB,OAAO,YAAY,OAA4B;;;CAMnD,OAAO;;AAKT,MAAM,qBAAqB,OACzB,QACA,kBACG;CAEH,MAAM,WAA2B,MAAM,aACrCA,QACA,cACD;CAwBD,OApBgB,OAAO,QAAQ,SAKqB,CAAC,SAClD,CAAC,QAAQ,gBACR,OAAO,QAAQ,WAAW,CAAC,KAAK,CAAC,KAAK,UAAU;EAK9C,OAAO;GACL,MALmB,WAAW,KAAK,GACjC,OACA,QAAQ,cAAc,OAAO,SAAS,KAAK;GAI7C;GACA;GACD;GACD,CAGoB;;AA+E5B,MAAa,YAAY,YAA2C;CAClE,MAAM,EAAE,UAAU,UAAU,QAAQ,WAAW;EAC7C,UAAU;EACV,UAAU;EACV,GAAG;EACJ;CAED,OAAO;EACL,MAAM;EAEN,kBAAkB,OAAO,EAAE,oBAAoB;GAC7C,MAAM,kBAAmC,MAAM,mBAC7C,QAAQ,QACR,cACD;GAED,MAAM,eAA6B,EAAE;GAErC,KAAK,MAAM,EAAE,MAAM,KAAK,QAAQ,iBAAiB,iBAAiB;IAGhE,MAAM,OACH,MAAM,iBAAiB,MAAM,EAAE,UAAU,OAAO,CAAC,IAAK,EAAE;IAE3D,MAAM,WAAW,SAAS,cAAc,OAAO,SAAS,KAAK;IAI7D,MAAM,kBAAmB,UAAU;IAEnC,MAAM,aAAyB;KAC7B;KACA,QAAQ;KACR,MAAM;KACN;KACA,SAAS,GAAG,IAAI,IAAI,SAAS,IAAI;KACvB;KACV,QACE,oBAAoB,cAAc,qBAAqB,gBACnD,OACA;KACN,SAAS;KACT;KACA;KACD;IAED,aAAa,KAAK,WAAW;;GAG/B,OAAO;;EAEV"}
@@ -1,169 +0,0 @@
1
- import { dirname, isAbsolute, relative, resolve } from "node:path";
2
- import { loadExternalFile } from "@intlayer/config/file";
3
- import { parseFilePathPattern } from "@intlayer/config/utils";
4
- import fg from "fast-glob";
5
- import { mkdir, writeFile } from "node:fs/promises";
6
-
7
- //#region src/syncJSON.ts
8
- const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9
- const extractKeyAndLocaleFromPath = (filePath, maskPattern, locales, defaultLocale) => {
10
- const keyPlaceholder = "{{__KEY__}}";
11
- const localePlaceholder = "{{__LOCALE__}}";
12
- const normalize = (path) => path.startsWith("./") ? path.slice(2) : path;
13
- const normalizedFilePath = normalize(filePath);
14
- const normalizedMask = normalize(maskPattern);
15
- const localesAlternation = locales.join("|");
16
- let regexStr = `^${escapeRegex(normalizedMask)}$`;
17
- regexStr = regexStr.replace(/\\\*\\\*/g, ".*");
18
- regexStr = regexStr.replace(/\\\*/g, "[^/]*");
19
- regexStr = regexStr.replace(escapeRegex(localePlaceholder), `(?<locale>${localesAlternation})`);
20
- if (normalizedMask.includes(keyPlaceholder)) regexStr = regexStr.replace(escapeRegex(keyPlaceholder), "(?<key>.+)");
21
- const match = new RegExp(regexStr).exec(normalizedFilePath);
22
- if (!match?.groups) return null;
23
- let locale = match.groups.locale;
24
- let key = match.groups.key ?? "index";
25
- if (typeof key === "undefined") key = "index";
26
- if (typeof locale === "undefined") locale = defaultLocale;
27
- return {
28
- key,
29
- locale
30
- };
31
- };
32
- const listMessages = async (source, configuration) => {
33
- const { system, internationalization } = configuration;
34
- const { baseDir } = system;
35
- const { locales } = internationalization;
36
- const result = {};
37
- for (const locale of locales) {
38
- const globPatternLocale = await parseFilePathPattern(source, {
39
- key: "**",
40
- locale
41
- });
42
- const maskPatternLocale = await parseFilePathPattern(source, {
43
- key: "{{__KEY__}}",
44
- locale
45
- });
46
- if (!globPatternLocale || !maskPatternLocale) continue;
47
- const files = await fg(globPatternLocale.startsWith("./") ? globPatternLocale.slice(2) : globPatternLocale, { cwd: baseDir });
48
- for (const file of files) {
49
- const extraction = extractKeyAndLocaleFromPath(file, maskPatternLocale, locales, locale);
50
- if (!extraction) continue;
51
- const { key, locale: extractedLocale } = extraction;
52
- const expectedPath = await parseFilePathPattern(source, {
53
- key,
54
- locale: extractedLocale
55
- });
56
- const absoluteFoundPath = isAbsolute(file) ? file : resolve(baseDir, file);
57
- if (absoluteFoundPath !== (isAbsolute(expectedPath) ? expectedPath : resolve(baseDir, expectedPath))) continue;
58
- const usedLocale = extractedLocale;
59
- if (!result[usedLocale]) result[usedLocale] = {};
60
- result[usedLocale][key] = absoluteFoundPath;
61
- }
62
- }
63
- const hasKeyInMask = (await parseFilePathPattern(source, {
64
- key: "{{__KEY__}}",
65
- locale: locales[0]
66
- })).includes("{{__KEY__}}");
67
- const discoveredKeys = /* @__PURE__ */ new Set();
68
- for (const locale of Object.keys(result)) for (const key of Object.keys(result[locale] ?? {})) discoveredKeys.add(key);
69
- if (!hasKeyInMask) discoveredKeys.add("index");
70
- const keysToEnsure = discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];
71
- for (const locale of locales) {
72
- if (!result[locale]) result[locale] = {};
73
- for (const key of keysToEnsure) if (!result[locale][key]) {
74
- const builtPath = await parseFilePathPattern(source, {
75
- key,
76
- locale
77
- });
78
- const absoluteBuiltPath = isAbsolute(builtPath) ? builtPath : resolve(baseDir, builtPath);
79
- result[locale][key] = absoluteBuiltPath;
80
- }
81
- }
82
- return result;
83
- };
84
- const loadMessagePathMap = async (source, configuration) => {
85
- const messages = await listMessages(source, configuration);
86
- return Object.entries(messages).flatMap(([locale, keysRecord]) => Object.entries(keysRecord).map(([key, path]) => {
87
- return {
88
- path: isAbsolute(path) ? path : resolve(configuration.system.baseDir, path),
89
- locale,
90
- key
91
- };
92
- }));
93
- };
94
- const syncJSON = async (options) => {
95
- const { location, priority, format } = {
96
- location: `sync-json::${await parseFilePathPattern(options.source, {
97
- key: "{{key}}",
98
- locale: "{{locale}}"
99
- })}`,
100
- priority: 0,
101
- ...options
102
- };
103
- return {
104
- name: "sync-json",
105
- loadDictionaries: async ({ configuration }) => {
106
- const dictionariesMap = await loadMessagePathMap(options.source, configuration);
107
- let fill = await parseFilePathPattern(options.source, {
108
- key: "{{key}}",
109
- locale: "{{locale}}"
110
- });
111
- if (fill) fill = relative(configuration.system.baseDir, resolve(configuration.system.baseDir, fill));
112
- const dictionaries = [];
113
- for (const { locale, path, key } of dictionariesMap) {
114
- const json = await loadExternalFile(path, { logError: false }) ?? {};
115
- const filePath = relative(configuration.system.baseDir, path);
116
- const dictionary = {
117
- key,
118
- locale,
119
- fill,
120
- format,
121
- localId: `${key}::${location}::${filePath}`,
122
- location,
123
- filled: locale !== configuration.internationalization.defaultLocale ? true : void 0,
124
- content: json,
125
- filePath,
126
- priority
127
- };
128
- dictionaries.push(dictionary);
129
- }
130
- return dictionaries;
131
- },
132
- formatOutput: async ({ dictionary, configuration }) => {
133
- const { formatDictionaryOutput } = await import("@intlayer/chokidar/build");
134
- if (!dictionary.filePath || !dictionary.locale) return dictionary;
135
- const builderPath = await parseFilePathPattern(options.source, {
136
- key: dictionary.key,
137
- locale: dictionary.locale
138
- });
139
- if (resolve(configuration.system.baseDir, builderPath) !== resolve(configuration.system.baseDir, dictionary.filePath)) return dictionary;
140
- return formatDictionaryOutput(dictionary, format).content;
141
- },
142
- afterBuild: async ({ dictionaries, configuration }) => {
143
- const { getPerLocaleDictionary } = await import("@intlayer/core/plugins");
144
- const { parallelize } = await import("@intlayer/chokidar/utils");
145
- const { formatDictionaryOutput } = await import("@intlayer/chokidar/build");
146
- const { locales } = configuration.internationalization;
147
- await parallelize(Object.entries(dictionaries.mergedDictionaries).flatMap(([key, dictionary]) => locales.map((locale) => ({
148
- key,
149
- dictionary: dictionary.dictionary,
150
- locale
151
- }))), async ({ key, dictionary, locale }) => {
152
- if (dictionary.location !== location) return;
153
- const builderPath = await parseFilePathPattern(options.source, {
154
- key,
155
- locale
156
- });
157
- const formattedOutput = formatDictionaryOutput(getPerLocaleDictionary(dictionary, locale), format);
158
- const content = JSON.parse(JSON.stringify(formattedOutput.content));
159
- if (typeof content === "undefined" || typeof content === "object" && Object.keys(content).length === 0) return;
160
- await mkdir(dirname(builderPath), { recursive: true });
161
- await writeFile(builderPath, `${JSON.stringify(content, null, 2)}\n`, "utf-8");
162
- });
163
- }
164
- };
165
- };
166
-
167
- //#endregion
168
- export { extractKeyAndLocaleFromPath, syncJSON };
169
- //# sourceMappingURL=syncJSON.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"syncJSON.mjs","names":[],"sources":["../../src/syncJSON.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\nimport { loadExternalFile } from '@intlayer/config/file';\nimport { parseFilePathPattern } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryFormat,\n DictionaryLocation,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport type {\n FilePathPattern,\n FilePathPatternContext,\n} from '@intlayer/types/filePathPattern';\nimport type { Plugin } from '@intlayer/types/plugin';\nimport fg from 'fast-glob';\n\ntype JSONContent = Record<string, any>;\n\ntype FilePath = string;\n\ntype MessagesRecord = Record<Locale, Record<Dictionary['key'], FilePath>>;\n\nconst escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const extractKeyAndLocaleFromPath = (\n filePath: string,\n maskPattern: string,\n locales: Locale[],\n defaultLocale: Locale\n): { key: string; locale: Locale } | null => {\n const keyPlaceholder = '{{__KEY__}}';\n const localePlaceholder = '{{__LOCALE__}}';\n\n // fast-glob strips leading \"./\" from returned paths; normalize both sides\n const normalize = (path: string) =>\n path.startsWith('./') ? path.slice(2) : path;\n\n const normalizedFilePath = normalize(filePath);\n const normalizedMask = normalize(maskPattern);\n\n const localesAlternation = locales.join('|');\n\n // Escape special regex chars, then convert glob wildcards to regex equivalents.\n // Must replace ** before * to avoid double-replacing.\n let regexStr = `^${escapeRegex(normalizedMask)}$`;\n regexStr = regexStr.replace(/\\\\\\*\\\\\\*/g, '.*'); // ** → match any path segments\n regexStr = regexStr.replace(/\\\\\\*/g, '[^/]*'); // * → match within a single segment\n\n regexStr = regexStr.replace(\n escapeRegex(localePlaceholder),\n `(?<locale>${localesAlternation})`\n );\n\n if (normalizedMask.includes(keyPlaceholder)) {\n regexStr = regexStr.replace(escapeRegex(keyPlaceholder), '(?<key>.+)');\n }\n\n const maskRegex = new RegExp(regexStr);\n const match = maskRegex.exec(normalizedFilePath);\n\n if (!match?.groups) {\n return null;\n }\n\n let locale = match.groups.locale as Locale | undefined;\n let key = (match.groups.key as string | undefined) ?? 'index';\n\n if (typeof key === 'undefined') {\n key = 'index';\n }\n\n if (typeof locale === 'undefined') {\n locale = defaultLocale;\n }\n\n return {\n key,\n locale,\n };\n};\n\nconst listMessages = async (\n source: FilePathPattern,\n configuration: IntlayerConfig\n): Promise<MessagesRecord> => {\n const { system, internationalization } = configuration;\n\n const { baseDir } = system;\n const { locales } = internationalization;\n\n const result: MessagesRecord = {} as MessagesRecord;\n\n for (const locale of locales) {\n const globPatternLocale = await parseFilePathPattern(source, {\n key: '**',\n locale,\n } as any as FilePathPatternContext);\n\n const maskPatternLocale = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale,\n } as any as FilePathPatternContext);\n\n if (!globPatternLocale || !maskPatternLocale) {\n continue;\n }\n\n const normalizedGlobPattern = globPatternLocale.startsWith('./')\n ? globPatternLocale.slice(2)\n : globPatternLocale;\n\n const files = await fg(normalizedGlobPattern, {\n cwd: baseDir,\n });\n\n for (const file of files) {\n const extraction = extractKeyAndLocaleFromPath(\n file,\n maskPatternLocale,\n locales,\n locale\n );\n\n if (!extraction) {\n continue;\n }\n\n const { key, locale: extractedLocale } = extraction;\n\n // Generate what the path SHOULD be for this key/locale using the current builder\n const expectedPath = await parseFilePathPattern(source, {\n key,\n locale: extractedLocale,\n } as any as FilePathPatternContext);\n\n // Resolve both to absolute paths to ensure safe comparison\n const absoluteFoundPath = isAbsolute(file)\n ? file\n : resolve(baseDir, file);\n const absoluteExpectedPath = isAbsolute(expectedPath)\n ? expectedPath\n : resolve(baseDir, expectedPath);\n\n // If the file found doesn't exactly match the file expected, it belongs to another plugin/structure\n if (absoluteFoundPath !== absoluteExpectedPath) {\n continue;\n }\n\n const usedLocale = extractedLocale as Locale;\n if (!result[usedLocale]) {\n result[usedLocale] = {};\n }\n\n result[usedLocale][key as Dictionary['key']] = absoluteFoundPath;\n }\n }\n\n // Ensure all declared locales are present even if the file doesn't exist yet\n const maskWithKey = await parseFilePathPattern(source, {\n key: '{{__KEY__}}',\n locale: locales[0],\n } as any as FilePathPatternContext);\n\n const hasKeyInMask = maskWithKey.includes('{{__KEY__}}');\n const discoveredKeys = new Set<string>();\n\n for (const locale of Object.keys(result)) {\n for (const key of Object.keys(result[locale as Locale] ?? {})) {\n discoveredKeys.add(key);\n }\n }\n\n if (!hasKeyInMask) {\n discoveredKeys.add('index');\n }\n\n const keysToEnsure =\n discoveredKeys.size > 0 ? Array.from(discoveredKeys) : [];\n\n for (const locale of locales) {\n if (!result[locale]) {\n result[locale] = {} as Record<Dictionary['key'], FilePath>;\n }\n\n for (const key of keysToEnsure) {\n if (!result[locale][key as Dictionary['key']]) {\n const builtPath = await parseFilePathPattern(source, {\n key,\n locale,\n } as any as FilePathPatternContext);\n const absoluteBuiltPath = isAbsolute(builtPath)\n ? builtPath\n : resolve(baseDir, builtPath);\n\n result[locale][key as Dictionary['key']] = absoluteBuiltPath;\n }\n }\n }\n\n return result;\n};\n\ntype DictionariesMap = { path: string; locale: Locale; key: string }[];\n\nconst loadMessagePathMap = async (\n source: MessagesRecord | FilePathPattern,\n configuration: IntlayerConfig\n) => {\n const messages: MessagesRecord = await listMessages(\n source as FilePathPattern,\n configuration\n );\n\n const dictionariesPathMap: DictionariesMap = Object.entries(messages).flatMap(\n ([locale, keysRecord]) =>\n Object.entries(keysRecord).map(([key, path]) => {\n const absolutePath = isAbsolute(path)\n ? path\n : resolve(configuration.system.baseDir, path);\n\n return {\n path: absolutePath,\n locale,\n key,\n } as DictionariesMap[number];\n })\n );\n\n return dictionariesPathMap;\n};\n\ntype SyncJSONPluginOptions = {\n /**\n * The source of the plugin.\n * Is a function to build the source from the key and locale.\n *\n * ```ts\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`\n * })\n * ```\n */\n source: FilePathPattern;\n\n /**\n * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.\n * Used to identify the plugin in the dictionary.\n *\n * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.\n *\n * ```ts\n * // Example usage:\n * const config = {\n * plugins: [\n * syncJSON({\n * source: ({ key, locale }) => `./resources/${locale}/${key}.json`,\n * location: 'plugin-i18next',\n * }),\n * syncJSON({\n * source: ({ key, locale }) => `./messages/${locale}/${key}.json`,\n * location: 'plugin-next-intl',\n * }),\n * ]\n * }\n * ```\n */\n location?: DictionaryLocation | (string & {});\n\n /**\n * The priority of the dictionaries created by the plugin.\n *\n * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.\n *\n * Default is -1. (.content file priority is 0)\n *\n */\n priority?: number;\n\n /**\n * The format of the dictionaries created by the plugin.\n *\n * Default: 'intlayer'\n *\n * The format of the dictionaries created by the plugin.\n */\n format?: DictionaryFormat;\n};\n\nexport const syncJSON = async (\n options: SyncJSONPluginOptions\n): Promise<Plugin> => {\n // Generate a unique default location based on the source pattern.\n // This ensures that if you have multiple plugins, they don't share the same 'plugin' ID.\n const patternMarker = await parseFilePathPattern(options.source, {\n key: '{{key}}',\n locale: '{{locale}}',\n } as any as FilePathPatternContext);\n const defaultLocation = `sync-json::${patternMarker}`;\n\n const { location, priority, format } = {\n location: defaultLocation,\n priority: 0,\n ...options,\n };\n\n return {\n name: 'sync-json',\n\n loadDictionaries: async ({ configuration }) => {\n const dictionariesMap: DictionariesMap = await loadMessagePathMap(\n options.source,\n configuration\n );\n\n let fill: string = await parseFilePathPattern(options.source, {\n key: '{{key}}',\n locale: '{{locale}}',\n } as any as FilePathPatternContext);\n\n if (fill) {\n fill = relative(\n configuration.system.baseDir,\n resolve(configuration.system.baseDir, fill)\n );\n }\n\n const dictionaries: Dictionary[] = [];\n\n for (const { locale, path, key } of dictionariesMap) {\n // loadExternalFile swallows errors and returns undefined for missing files;\n // the try/catch does not help here — use ?? {} to guarantee a plain object.\n const json: JSONContent =\n (await loadExternalFile(path, { logError: false })) ?? {};\n\n const filePath = relative(configuration.system.baseDir, path);\n\n const dictionary: Dictionary = {\n key,\n locale,\n fill,\n format,\n localId: `${key}::${location}::${filePath}` as LocalDictionaryId,\n location: location as Dictionary['location'],\n filled:\n locale !== configuration.internationalization.defaultLocale\n ? true\n : undefined,\n content: json,\n filePath,\n priority,\n };\n\n dictionaries.push(dictionary);\n }\n\n return dictionaries;\n },\n\n formatOutput: async ({ dictionary, configuration }) => {\n // Lazy import intlayer modules to avoid circular dependencies\n const { formatDictionaryOutput } = await import(\n '@intlayer/chokidar/build'\n );\n\n if (!dictionary.filePath || !dictionary.locale) return dictionary;\n\n const builderPath = await parseFilePathPattern(options.source, {\n key: dictionary.key,\n locale: dictionary.locale,\n } as FilePathPatternContext);\n\n // Verification to ensure we are formatting the correct file\n if (\n resolve(configuration.system.baseDir, builderPath) !==\n resolve(configuration.system.baseDir, dictionary.filePath)\n ) {\n return dictionary;\n }\n\n const formattedOutput = formatDictionaryOutput(dictionary, format);\n\n return formattedOutput.content;\n },\n\n afterBuild: async ({ dictionaries, configuration }) => {\n // Lazy import intlayer modules to avoid circular dependencies\n const { getPerLocaleDictionary } = await import('@intlayer/core/plugins');\n const { parallelize } = await import('@intlayer/chokidar/utils');\n const { formatDictionaryOutput } = await import(\n '@intlayer/chokidar/build'\n );\n\n const { locales } = configuration.internationalization;\n\n type RecordList = {\n key: string;\n dictionary: Dictionary;\n locale: Locale;\n };\n\n // We get all dictionaries, but we need to filter them\n const recordList: RecordList[] = Object.entries(\n dictionaries.mergedDictionaries\n ).flatMap(([key, dictionary]) =>\n locales.map((locale) => ({\n key,\n dictionary: dictionary.dictionary as Dictionary,\n locale,\n }))\n );\n\n await parallelize(recordList, async ({ key, dictionary, locale }) => {\n // Only process dictionaries that belong to THIS plugin instance.\n if (dictionary.location !== location) {\n return;\n }\n\n const builderPath = await parseFilePathPattern(options.source, {\n key,\n locale,\n } as any as FilePathPatternContext);\n\n const localizedDictionary = getPerLocaleDictionary(dictionary, locale);\n\n const formattedOutput = formatDictionaryOutput(\n localizedDictionary,\n format\n );\n\n const content = JSON.parse(JSON.stringify(formattedOutput.content));\n\n if (\n typeof content === 'undefined' ||\n (typeof content === 'object' &&\n Object.keys(content as Record<string, unknown>).length === 0)\n ) {\n return;\n }\n\n await mkdir(dirname(builderPath), { recursive: true });\n\n const stringContent = JSON.stringify(content, null, 2);\n\n await writeFile(builderPath, `${stringContent}\\n`, 'utf-8');\n });\n },\n };\n};\n"],"mappings":"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"}
@@ -1,81 +0,0 @@
1
- import { Locale } from "@intlayer/types/allLocales";
2
- import { DictionaryFormat } from "@intlayer/types/dictionary";
3
- import { FilePathPattern } from "@intlayer/types/filePathPattern";
4
- import { Plugin } from "@intlayer/types/plugin";
5
-
6
- //#region src/loadJSON.d.ts
7
- type LoadJSONPluginOptions = {
8
- /**
9
- * The source of the plugin.
10
- * Is a function to build the source from the key and locale.
11
- *
12
- * @example
13
- * ```ts
14
- * loadJSON({
15
- * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,
16
- * })
17
- * ```
18
- */
19
- source: FilePathPattern;
20
- /**
21
- * Locale
22
- *
23
- * If not provided, the plugin will consider the default locale.
24
- *
25
- * @example
26
- * ```ts
27
- * loadJSON({
28
- * source: ({ key }) => `blog/${'**'}/${key}.i18n.json`,
29
- * locale: Locales.ENGLISH,
30
- * })
31
- * ```
32
- */
33
- locale?: Locale;
34
- /**
35
- * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.
36
- * Used to identify the plugin in the dictionary.
37
- *
38
- * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.
39
- *
40
- * @example
41
- * ```ts
42
- * const config = {
43
- * plugins: [
44
- * loadJSON({
45
- * source: ({ key }) => `./resources/${key}.json`,
46
- * location: 'plugin-i18next',
47
- * }),
48
- * loadJSON({
49
- * source: ({ key }) => `./messages/${key}.json`,
50
- * location: 'plugin-next-intl',
51
- * }),
52
- * ]
53
- * }
54
- * ```
55
- */
56
- location?: string;
57
- /**
58
- * The priority of the dictionaries created by the plugin.
59
- *
60
- * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.
61
- *
62
- * Default is -1. (.content file priority is 0)
63
- *
64
- */
65
- priority?: number;
66
- /**
67
- * The format of the dictionary content.
68
- *
69
- * @example
70
- * ```ts
71
- * loadJSON({
72
- * format: 'icu',
73
- * })
74
- * ```
75
- */
76
- format?: DictionaryFormat;
77
- };
78
- declare const loadJSON: (options: LoadJSONPluginOptions) => Plugin;
79
- //#endregion
80
- export { loadJSON };
81
- //# sourceMappingURL=loadJSON.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loadJSON.d.ts","names":[],"sources":["../../src/loadJSON.ts"],"mappings":";;;;;;KAgJK,qBAAA;;AAlIgD;;;;;;;;;;EA8InD,MAAA,EAAQ,eAAA;EAeC;;;;;;;AAiDX;;;;;;EAjDE,MAAA,GAAS,MAAA;EAqGV;;;;;;;;;;;;;;;;;;;;;;EA7EC,QAAA;;;;;;;;;EAUA,QAAA;;;;;;;;;;;EAYA,MAAA,GAAS,gBAAA;AAAA;AAAA,cAGE,QAAA,GAAY,OAAA,EAAS,qBAAA,KAAwB,MAAA"}
@@ -1,67 +0,0 @@
1
- import { Locale } from "@intlayer/types/allLocales";
2
- import { DictionaryFormat, DictionaryLocation } from "@intlayer/types/dictionary";
3
- import { FilePathPattern } from "@intlayer/types/filePathPattern";
4
- import { Plugin } from "@intlayer/types/plugin";
5
-
6
- //#region src/syncJSON.d.ts
7
- declare const extractKeyAndLocaleFromPath: (filePath: string, maskPattern: string, locales: Locale[], defaultLocale: Locale) => {
8
- key: string;
9
- locale: Locale;
10
- } | null;
11
- type SyncJSONPluginOptions = {
12
- /**
13
- * The source of the plugin.
14
- * Is a function to build the source from the key and locale.
15
- *
16
- * ```ts
17
- * syncJSON({
18
- * source: ({ key, locale }) => `./messages/${locale}/${key}.json`
19
- * })
20
- * ```
21
- */
22
- source: FilePathPattern;
23
- /**
24
- * Because Intlayer transform the JSON files into Dictionary, we need to identify the plugin in the dictionary.
25
- * Used to identify the plugin in the dictionary.
26
- *
27
- * In the case you have multiple plugins, you can use this to identify the plugin in the dictionary.
28
- *
29
- * ```ts
30
- * // Example usage:
31
- * const config = {
32
- * plugins: [
33
- * syncJSON({
34
- * source: ({ key, locale }) => `./resources/${locale}/${key}.json`,
35
- * location: 'plugin-i18next',
36
- * }),
37
- * syncJSON({
38
- * source: ({ key, locale }) => `./messages/${locale}/${key}.json`,
39
- * location: 'plugin-next-intl',
40
- * }),
41
- * ]
42
- * }
43
- * ```
44
- */
45
- location?: DictionaryLocation | (string & {});
46
- /**
47
- * The priority of the dictionaries created by the plugin.
48
- *
49
- * In the case of conflicts with remote dictionaries, or .content files, the dictionary with the highest priority will override the other dictionaries.
50
- *
51
- * Default is -1. (.content file priority is 0)
52
- *
53
- */
54
- priority?: number;
55
- /**
56
- * The format of the dictionaries created by the plugin.
57
- *
58
- * Default: 'intlayer'
59
- *
60
- * The format of the dictionaries created by the plugin.
61
- */
62
- format?: DictionaryFormat;
63
- };
64
- declare const syncJSON: (options: SyncJSONPluginOptions) => Promise<Plugin>;
65
- //#endregion
66
- export { extractKeyAndLocaleFromPath, syncJSON };
67
- //# sourceMappingURL=syncJSON.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"syncJSON.d.ts","names":[],"sources":["../../src/syncJSON.ts"],"mappings":";;;;;;cA2Ba,2BAAA,GACX,QAAA,UACA,WAAA,UACA,OAAA,EAAS,MAAA,IACT,aAAA,EAAe,MAAA;EACZ,GAAA;EAAa,MAAA,EAAQ,MAAA;AAAA;AAAA,KA0MrB,qBAAA;EA5MM;;;;;;;;;;EAuNT,MAAA,EAAQ,eAAA;EArNL;;;;;AAkDH;;;;;;;;;;;;;;;;;EA2LA,QAAA,GAAW,kBAAA;EAqLZ;;;;;;;;EA3KC,QAAA;EAcC;;;;;;;EALD,MAAA,GAAS,gBAAA;AAAA;AAAA,cAGE,QAAA,GACX,OAAA,EAAS,qBAAA,KACR,OAAA,CAAQ,MAAA"}