i18next-cli 1.42.5 → 1.42.6
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/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +12 -3
- package/dist/cjs/utils/file-utils.js +38 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +14 -5
- package/dist/esm/utils/file-utils.js +38 -1
- package/package.json +1 -1
- package/types/extractor/core/translation-manager.d.ts.map +1 -1
- package/types/utils/file-utils.d.ts +16 -0
- package/types/utils/file-utils.d.ts.map +1 -1
package/dist/cjs/cli.js
CHANGED
|
@@ -28,7 +28,7 @@ const program = new commander.Command();
|
|
|
28
28
|
program
|
|
29
29
|
.name('i18next-cli')
|
|
30
30
|
.description('A unified, high-performance i18next CLI.')
|
|
31
|
-
.version('1.42.
|
|
31
|
+
.version('1.42.6'); // This string is replaced with the actual version at build time by rollup
|
|
32
32
|
// new: global config override option
|
|
33
33
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
34
34
|
program
|
|
@@ -815,14 +815,23 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
815
815
|
// LOGIC PATH 2: Separate Namespace Files
|
|
816
816
|
}
|
|
817
817
|
else {
|
|
818
|
-
// Find all namespaces that exist on disk for this locale
|
|
818
|
+
// Find all namespaces that exist on disk for this locale.
|
|
819
|
+
// Use '**' so the glob crosses directory boundaries — namespaces
|
|
820
|
+
// can contain '/' and span multiple directory levels.
|
|
819
821
|
const namespacesToProcess = new Set(keysByNS.keys());
|
|
820
|
-
const existingNsPattern = fileUtils.getOutputPath(config.extract.output, locale, '
|
|
822
|
+
const existingNsPattern = fileUtils.getOutputPath(config.extract.output, locale, '**');
|
|
821
823
|
// Ensure glob receives POSIX-style separators so pattern matching works cross-platform (Windows -> backslashes)
|
|
822
824
|
const existingNsGlobPattern = existingNsPattern.replace(/\\/g, '/');
|
|
823
825
|
const existingNsFiles = await glob.glob(existingNsGlobPattern, { ignore: userIgnore });
|
|
824
826
|
for (const file of existingNsFiles) {
|
|
825
|
-
|
|
827
|
+
// Recover the full (possibly multi-segment) namespace from the file path
|
|
828
|
+
// by matching it against the output template.
|
|
829
|
+
const ns = typeof config.extract.output === 'string'
|
|
830
|
+
? fileUtils.extractNamespaceFromPath(config.extract.output, locale, file)
|
|
831
|
+
: undefined;
|
|
832
|
+
if (ns) {
|
|
833
|
+
namespacesToProcess.add(ns);
|
|
834
|
+
}
|
|
826
835
|
}
|
|
827
836
|
// Process each namespace individually and create a result for each one
|
|
828
837
|
for (const ns of namespacesToProcess) {
|
|
@@ -45,6 +45,43 @@ function getOutputPath(outputTemplate, language, namespace) {
|
|
|
45
45
|
out = out.replace(/\/\/+/g, '/');
|
|
46
46
|
return node_path.normalize(out);
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Extracts the namespace value from a concrete file path by matching it against
|
|
50
|
+
* the output template.
|
|
51
|
+
*
|
|
52
|
+
* Given a template like `src/{{namespace}}/locales/{{language}}.json` and a file
|
|
53
|
+
* path like `src/widgets/component/locales/en.json`, this returns `widgets/component`.
|
|
54
|
+
*
|
|
55
|
+
* This handles multi-segment namespaces (namespaces containing `/`) which
|
|
56
|
+
* `basename()` cannot recover.
|
|
57
|
+
*
|
|
58
|
+
* @param outputTemplate - The output path template string (must contain `{{namespace}}`)
|
|
59
|
+
* @param language - The language value used when the file was generated
|
|
60
|
+
* @param filePath - The concrete file path to extract the namespace from
|
|
61
|
+
* @returns The namespace string, or `undefined` if the path doesn't match the template
|
|
62
|
+
*/
|
|
63
|
+
function extractNamespaceFromPath(outputTemplate, language, filePath) {
|
|
64
|
+
// Build a regex from the template by escaping everything except the placeholders.
|
|
65
|
+
// Replace {{language}}/{{lng}} with the literal language value and
|
|
66
|
+
// {{namespace}} with a named capture group that matches one or more path segments.
|
|
67
|
+
const pattern = outputTemplate
|
|
68
|
+
// Normalise to forward slashes for matching
|
|
69
|
+
.replace(/\\/g, '/');
|
|
70
|
+
// Escape regex-special characters (but keep our placeholders intact first)
|
|
71
|
+
const nsPlaceholder = '{{namespace}}';
|
|
72
|
+
const parts = pattern.split(nsPlaceholder);
|
|
73
|
+
// Escape each part individually then rejoin with the capture group
|
|
74
|
+
const escaped = parts.map(p => p
|
|
75
|
+
.replace(/\{\{language\}\}|\{\{lng\}\}/g, () => escapeForRegex(language))
|
|
76
|
+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
77
|
+
// Don't anchor the start — the glob may return absolute or prefixed paths.
|
|
78
|
+
// Anchor only the end so that the namespace capture is unambiguous.
|
|
79
|
+
const regexStr = escaped.join('(.+)') + '$';
|
|
80
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
81
|
+
const m = new RegExp(regexStr).exec(normalized);
|
|
82
|
+
return m?.[1];
|
|
83
|
+
}
|
|
84
|
+
const escapeForRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
48
85
|
/**
|
|
49
86
|
* Dynamically loads a translation file, supporting .json, .js, and .ts formats.
|
|
50
87
|
* @param filePath - The path to the translation file.
|
|
@@ -156,6 +193,7 @@ function inferFormatFromPath(filePath, defaultFormat = 'json') {
|
|
|
156
193
|
return defaultFormat || 'json';
|
|
157
194
|
}
|
|
158
195
|
|
|
196
|
+
exports.extractNamespaceFromPath = extractNamespaceFromPath;
|
|
159
197
|
exports.getOutputPath = getOutputPath;
|
|
160
198
|
exports.inferFormatFromPath = inferFormatFromPath;
|
|
161
199
|
exports.loadRawJson5Content = loadRawJson5Content;
|
package/dist/esm/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ const program = new Command();
|
|
|
26
26
|
program
|
|
27
27
|
.name('i18next-cli')
|
|
28
28
|
.description('A unified, high-performance i18next CLI.')
|
|
29
|
-
.version('1.42.
|
|
29
|
+
.version('1.42.6'); // This string is replaced with the actual version at build time by rollup
|
|
30
30
|
// new: global config override option
|
|
31
31
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
32
32
|
program
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { resolve
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
2
|
import { glob } from 'glob';
|
|
3
3
|
import { getNestedKeys, getNestedValue, setNestedValue } from '../../utils/nested-object.js';
|
|
4
|
-
import { getOutputPath, loadTranslationFile } from '../../utils/file-utils.js';
|
|
4
|
+
import { getOutputPath, loadTranslationFile, extractNamespaceFromPath } from '../../utils/file-utils.js';
|
|
5
5
|
import { resolveDefaultValue } from '../../utils/default-value.js';
|
|
6
6
|
|
|
7
7
|
// used for natural language check
|
|
@@ -813,14 +813,23 @@ async function getTranslations(keys, objectKeys, config, { syncPrimaryWithDefaul
|
|
|
813
813
|
// LOGIC PATH 2: Separate Namespace Files
|
|
814
814
|
}
|
|
815
815
|
else {
|
|
816
|
-
// Find all namespaces that exist on disk for this locale
|
|
816
|
+
// Find all namespaces that exist on disk for this locale.
|
|
817
|
+
// Use '**' so the glob crosses directory boundaries — namespaces
|
|
818
|
+
// can contain '/' and span multiple directory levels.
|
|
817
819
|
const namespacesToProcess = new Set(keysByNS.keys());
|
|
818
|
-
const existingNsPattern = getOutputPath(config.extract.output, locale, '
|
|
820
|
+
const existingNsPattern = getOutputPath(config.extract.output, locale, '**');
|
|
819
821
|
// Ensure glob receives POSIX-style separators so pattern matching works cross-platform (Windows -> backslashes)
|
|
820
822
|
const existingNsGlobPattern = existingNsPattern.replace(/\\/g, '/');
|
|
821
823
|
const existingNsFiles = await glob(existingNsGlobPattern, { ignore: userIgnore });
|
|
822
824
|
for (const file of existingNsFiles) {
|
|
823
|
-
|
|
825
|
+
// Recover the full (possibly multi-segment) namespace from the file path
|
|
826
|
+
// by matching it against the output template.
|
|
827
|
+
const ns = typeof config.extract.output === 'string'
|
|
828
|
+
? extractNamespaceFromPath(config.extract.output, locale, file)
|
|
829
|
+
: undefined;
|
|
830
|
+
if (ns) {
|
|
831
|
+
namespacesToProcess.add(ns);
|
|
832
|
+
}
|
|
824
833
|
}
|
|
825
834
|
// Process each namespace individually and create a result for each one
|
|
826
835
|
for (const ns of namespacesToProcess) {
|
|
@@ -43,6 +43,43 @@ function getOutputPath(outputTemplate, language, namespace) {
|
|
|
43
43
|
out = out.replace(/\/\/+/g, '/');
|
|
44
44
|
return normalize(out);
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Extracts the namespace value from a concrete file path by matching it against
|
|
48
|
+
* the output template.
|
|
49
|
+
*
|
|
50
|
+
* Given a template like `src/{{namespace}}/locales/{{language}}.json` and a file
|
|
51
|
+
* path like `src/widgets/component/locales/en.json`, this returns `widgets/component`.
|
|
52
|
+
*
|
|
53
|
+
* This handles multi-segment namespaces (namespaces containing `/`) which
|
|
54
|
+
* `basename()` cannot recover.
|
|
55
|
+
*
|
|
56
|
+
* @param outputTemplate - The output path template string (must contain `{{namespace}}`)
|
|
57
|
+
* @param language - The language value used when the file was generated
|
|
58
|
+
* @param filePath - The concrete file path to extract the namespace from
|
|
59
|
+
* @returns The namespace string, or `undefined` if the path doesn't match the template
|
|
60
|
+
*/
|
|
61
|
+
function extractNamespaceFromPath(outputTemplate, language, filePath) {
|
|
62
|
+
// Build a regex from the template by escaping everything except the placeholders.
|
|
63
|
+
// Replace {{language}}/{{lng}} with the literal language value and
|
|
64
|
+
// {{namespace}} with a named capture group that matches one or more path segments.
|
|
65
|
+
const pattern = outputTemplate
|
|
66
|
+
// Normalise to forward slashes for matching
|
|
67
|
+
.replace(/\\/g, '/');
|
|
68
|
+
// Escape regex-special characters (but keep our placeholders intact first)
|
|
69
|
+
const nsPlaceholder = '{{namespace}}';
|
|
70
|
+
const parts = pattern.split(nsPlaceholder);
|
|
71
|
+
// Escape each part individually then rejoin with the capture group
|
|
72
|
+
const escaped = parts.map(p => p
|
|
73
|
+
.replace(/\{\{language\}\}|\{\{lng\}\}/g, () => escapeForRegex(language))
|
|
74
|
+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
75
|
+
// Don't anchor the start — the glob may return absolute or prefixed paths.
|
|
76
|
+
// Anchor only the end so that the namespace capture is unambiguous.
|
|
77
|
+
const regexStr = escaped.join('(.+)') + '$';
|
|
78
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
79
|
+
const m = new RegExp(regexStr).exec(normalized);
|
|
80
|
+
return m?.[1];
|
|
81
|
+
}
|
|
82
|
+
const escapeForRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
46
83
|
/**
|
|
47
84
|
* Dynamically loads a translation file, supporting .json, .js, and .ts formats.
|
|
48
85
|
* @param filePath - The path to the translation file.
|
|
@@ -154,4 +191,4 @@ function inferFormatFromPath(filePath, defaultFormat = 'json') {
|
|
|
154
191
|
return defaultFormat || 'json';
|
|
155
192
|
}
|
|
156
193
|
|
|
157
|
-
export { getOutputPath, inferFormatFromPath, loadRawJson5Content, loadTranslationFile, serializeTranslationFile };
|
|
194
|
+
export { extractNamespaceFromPath, getOutputPath, inferFormatFromPath, loadRawJson5Content, loadTranslationFile, serializeTranslationFile };
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAgyBnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,oBAAoB,EAC5B,EACE,uBAA+B,EAC/B,OAAe,EAChB,GAAE;IACD,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAA;CACb,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAgyBnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,oBAAoB,EAC5B,EACE,uBAA+B,EAC/B,OAAe,EAChB,GAAE;IACD,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAA;CACb,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAmJ9B"}
|
|
@@ -48,6 +48,22 @@ export declare function writeFileAsync(filePath: string, data: string): Promise<
|
|
|
48
48
|
* - Normalizes duplicate slashes and returns a platform-correct path.
|
|
49
49
|
*/
|
|
50
50
|
export declare function getOutputPath(outputTemplate: string | ((language: string, namespace?: string) => string) | undefined, language: string, namespace?: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Extracts the namespace value from a concrete file path by matching it against
|
|
53
|
+
* the output template.
|
|
54
|
+
*
|
|
55
|
+
* Given a template like `src/{{namespace}}/locales/{{language}}.json` and a file
|
|
56
|
+
* path like `src/widgets/component/locales/en.json`, this returns `widgets/component`.
|
|
57
|
+
*
|
|
58
|
+
* This handles multi-segment namespaces (namespaces containing `/`) which
|
|
59
|
+
* `basename()` cannot recover.
|
|
60
|
+
*
|
|
61
|
+
* @param outputTemplate - The output path template string (must contain `{{namespace}}`)
|
|
62
|
+
* @param language - The language value used when the file was generated
|
|
63
|
+
* @param filePath - The concrete file path to extract the namespace from
|
|
64
|
+
* @returns The namespace string, or `undefined` if the path doesn't match the template
|
|
65
|
+
*/
|
|
66
|
+
export declare function extractNamespaceFromPath(outputTemplate: string, language: string, filePath: string): string | undefined;
|
|
51
67
|
/**
|
|
52
68
|
* Dynamically loads a translation file, supporting .json, .js, and .ts formats.
|
|
53
69
|
* @param filePath - The path to the translation file.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAKpD;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,EACvF,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA8BR;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CAwChG;AAGD,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,EAChE,WAAW,GAAE,MAAM,GAAG,MAAU,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CA6BR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,GACtE,WAAW,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC,CAO9D"}
|
|
1
|
+
{"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/utils/file-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAKpD;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,EACvF,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA8BR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAwBpB;AAID;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CAwChG;AAGD,wBAAsB,mBAAmB,CAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQnF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,EAChE,WAAW,GAAE,MAAM,GAAG,MAAU,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CA6BR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,GAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAU,GACtE,WAAW,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC,CAO9D"}
|