@zohodesk/client_build_tool 0.0.1-0.exp.0.0.4 → 0.0.1-0.exp.0.0.9
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/lib/schemas/defaultConfigValues.js +25 -2
- package/lib/schemas/defaultConfigValuesOnly.js +12 -4
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/CLAUDE.md +0 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin.js +2 -2
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +199 -93
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/I18nFilesEmitPlugin.js +66 -5
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/optionsHandler.js +3 -0
- package/lib/shared/bundler/webpack/custom_plugins/getInitialI18nAssetsArrayStr.js +6 -1
- package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +72 -48
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +156 -95
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +1 -1
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nSplitPlugin.js +4 -1
- package/lib/shared/bundler/webpack/utils/propertiesParser.js +103 -0
- package/npm-shrinkwrap.json +8086 -21
- package/package.json +1 -1
|
@@ -174,7 +174,17 @@ var _default = {
|
|
|
174
174
|
localeVarName: 'document.documentElement.lang',
|
|
175
175
|
jsonpFunc: 'console.log',
|
|
176
176
|
jsResource: null,
|
|
177
|
-
propertiesFolder: null
|
|
177
|
+
propertiesFolder: null,
|
|
178
|
+
propertiesPattern: '',
|
|
179
|
+
// NEW OPTIONS FOR NUMERIC INDEXING IN CHUNK SPLIT
|
|
180
|
+
useNumericIndexing: {
|
|
181
|
+
value: false,
|
|
182
|
+
cli: 'i18n_split_use_numeric'
|
|
183
|
+
},
|
|
184
|
+
numericMapPath: {
|
|
185
|
+
value: './deskapp/properties/i18n-numeric-map.json',
|
|
186
|
+
cli: 'i18n_split_numeric_map_path'
|
|
187
|
+
}
|
|
178
188
|
},
|
|
179
189
|
i18nIndexing: {
|
|
180
190
|
enable: {
|
|
@@ -202,7 +212,7 @@ var _default = {
|
|
|
202
212
|
cli: 'i18n_idx_dynamic_filename_template'
|
|
203
213
|
},
|
|
204
214
|
jsonpFunc: {
|
|
205
|
-
value: 'window.
|
|
215
|
+
value: 'window.loadI18nChunk',
|
|
206
216
|
cli: 'i18n_idx_jsonp_func'
|
|
207
217
|
},
|
|
208
218
|
htmlTemplateLabel: {
|
|
@@ -212,6 +222,19 @@ var _default = {
|
|
|
212
222
|
localeVarName: {
|
|
213
223
|
value: 'window.userLangCode',
|
|
214
224
|
cli: 'i18n_idx_locale_var_name'
|
|
225
|
+
},
|
|
226
|
+
// NEW LOADER CONFIGURATION OPTIONS
|
|
227
|
+
retainLines: {
|
|
228
|
+
value: false,
|
|
229
|
+
cli: 'i18n_idx_retain_lines'
|
|
230
|
+
},
|
|
231
|
+
preserveComments: {
|
|
232
|
+
value: true,
|
|
233
|
+
cli: 'i18n_idx_preserve_comments'
|
|
234
|
+
},
|
|
235
|
+
babelPlugins: {
|
|
236
|
+
value: null,
|
|
237
|
+
cli: 'i18n_idx_babel_plugins'
|
|
215
238
|
}
|
|
216
239
|
},
|
|
217
240
|
publicFolders: ['...'],
|
|
@@ -94,18 +94,26 @@ var _default = {
|
|
|
94
94
|
localeVarName: 'document.documentElement.lang',
|
|
95
95
|
jsonpFunc: 'console.log',
|
|
96
96
|
jsResource: null,
|
|
97
|
-
propertiesFolder: null
|
|
97
|
+
propertiesFolder: null,
|
|
98
|
+
propertiesPattern: '',
|
|
99
|
+
// NEW OPTIONS FOR NUMERIC INDEXING IN CHUNK SPLIT
|
|
100
|
+
useNumericIndexing: false,
|
|
101
|
+
numericMapPath: './deskapp/properties/i18n-numeric-map.json'
|
|
98
102
|
},
|
|
99
103
|
i18nIndexing: {
|
|
100
104
|
enable: false,
|
|
101
105
|
jsResourcePath: './deskapp/properties/JSResources.properties',
|
|
102
106
|
propertiesFolderPath: './deskapp/properties',
|
|
103
107
|
numericMapPath: './deskapp/properties/i18n-numeric-map.json',
|
|
104
|
-
numericFilenameTemplate: 'i18n-chunks/
|
|
105
|
-
dynamicFilenameTemplate: 'i18n-chunks/
|
|
108
|
+
numericFilenameTemplate: 'i18n-chunks/[locale]/numeric.[contenthash].js',
|
|
109
|
+
dynamicFilenameTemplate: 'i18n-chunks/[locale]/dynamic.[contenthash].js',
|
|
106
110
|
jsonpFunc: 'window.loadI18nChunk',
|
|
107
111
|
htmlTemplateLabel: '{{--user-locale}}',
|
|
108
|
-
localeVarName: 'window.userLangCode'
|
|
112
|
+
localeVarName: 'window.userLangCode',
|
|
113
|
+
// NEW LOADER CONFIGURATION OPTIONS
|
|
114
|
+
retainLines: false,
|
|
115
|
+
preserveComments: true,
|
|
116
|
+
babelPlugins: null
|
|
109
117
|
},
|
|
110
118
|
publicFolders: ['...', {
|
|
111
119
|
source: './deskapp/tp/',
|
|
File without changes
|
|
@@ -23,8 +23,8 @@ class I18nNumericIndexHtmlInjectorPlugin {
|
|
|
23
23
|
i18nAssetsPublicPathPrefix = ''
|
|
24
24
|
} = this.options;
|
|
25
25
|
const DEFAULT_LOCALE = '{{--user-locale}}';
|
|
26
|
-
const NUMERIC_FILENAME = `i18n-
|
|
27
|
-
const DYNAMIC_FILENAME = `i18n-
|
|
26
|
+
const NUMERIC_FILENAME = `i18n-chunk/${DEFAULT_LOCALE}/numeric.i18n.js`;
|
|
27
|
+
const DYNAMIC_FILENAME = `i18n-chunk/${DEFAULT_LOCALE}/dynamic.i18n.js`;
|
|
28
28
|
const newI18nAssetUrlsToAdd = [];
|
|
29
29
|
const numericAssetUrl = urlConcat('', NUMERIC_FILENAME);
|
|
30
30
|
newI18nAssetUrlsToAdd.push(numericAssetUrl);
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js
CHANGED
|
@@ -9,9 +9,12 @@ const {
|
|
|
9
9
|
Compilation
|
|
10
10
|
} = require('webpack');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const {
|
|
13
|
+
parseProperties,
|
|
14
|
+
decodeUnicodeEscapes
|
|
15
|
+
} = require('../../utils/propertiesParser');
|
|
16
|
+
|
|
17
|
+
let createHashFunction;
|
|
15
18
|
|
|
16
19
|
const {
|
|
17
20
|
createHash
|
|
@@ -23,58 +26,59 @@ const {
|
|
|
23
26
|
} = sources;
|
|
24
27
|
const pluginName = 'I18nNumericIndexPlugin';
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
class I18nNumericIndexPlugin {
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.numericMap = null;
|
|
33
|
+
this.stringPool = new Map(); // String interning for deduplication
|
|
34
|
+
// Default emitLiteralUnicode to true if not specified
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
if (typeof this.options.emitLiteralUnicode === 'undefined') {
|
|
37
|
+
this.options.emitLiteralUnicode = true;
|
|
38
|
+
} // Default to compact mode for memory optimization
|
|
33
39
|
|
|
34
|
-
if (trimmedLine && !trimmedLine.startsWith('#') && !trimmedLine.startsWith('!')) {
|
|
35
|
-
const separatorIndex = trimmedLine.indexOf('=');
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
if (typeof this.options.useCompactFormat === 'undefined') {
|
|
42
|
+
this.options.useCompactFormat = true;
|
|
43
|
+
}
|
|
44
|
+
} // String interning to reduce memory usage
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
value = decodeUnicodeEscapes(value);
|
|
47
|
-
data[key] = value;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
46
|
+
|
|
47
|
+
intern(str) {
|
|
48
|
+
if (typeof str !== 'string') {
|
|
49
|
+
return str;
|
|
50
50
|
}
|
|
51
|
-
}
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
57
|
-
} // This function converts strings like "Hello \\u00E9" to "Hello é"
|
|
52
|
+
if (!this.stringPool.has(str)) {
|
|
53
|
+
this.stringPool.set(str, str);
|
|
54
|
+
}
|
|
58
55
|
|
|
56
|
+
return this.stringPool.get(str);
|
|
57
|
+
} // Generate compact numeric data structure - returns just an array of values
|
|
59
58
|
|
|
60
|
-
function decodeUnicodeEscapes(str) {
|
|
61
|
-
if (typeof str !== 'string') {
|
|
62
|
-
return str;
|
|
63
|
-
}
|
|
64
59
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
60
|
+
generateCompactNumericData(numericDataArray) {
|
|
61
|
+
if (!this.options.useCompactFormat) {
|
|
62
|
+
return numericDataArray;
|
|
63
|
+
} // Create a sparse array with only non-null values
|
|
69
64
|
|
|
70
|
-
class I18nNumericIndexPlugin {
|
|
71
|
-
constructor(options = {}) {
|
|
72
|
-
this.options = options;
|
|
73
|
-
this.numericMap = null; // Default emitLiteralUnicode to true if not specified
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
const compactArray = [];
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < numericDataArray.length; i++) {
|
|
69
|
+
if (numericDataArray[i] !== null) {
|
|
70
|
+
compactArray[i] = this.intern(numericDataArray[i]);
|
|
71
|
+
}
|
|
77
72
|
}
|
|
73
|
+
|
|
74
|
+
return compactArray;
|
|
75
|
+
} // Compact serializer for JSON
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
compactSerialize(data) {
|
|
79
|
+
return JSON.stringify(data, null, 0).replace(/"(\d+)":/g, '$1:') // Remove quotes from numeric keys
|
|
80
|
+
.replace(/,null/g, ',0') // Replace null with 0 for smaller size
|
|
81
|
+
.replace(/null,/g, '0,'); // Replace null with 0 for smaller size
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
loadNumericMapOnce(compilation) {
|
|
@@ -88,16 +92,34 @@ class I18nNumericIndexPlugin {
|
|
|
88
92
|
try {
|
|
89
93
|
if (numericMapPath && fs.existsSync(numericMapPath)) {
|
|
90
94
|
const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
|
|
91
|
-
const parsedData = JSON.parse(fileContent);
|
|
95
|
+
const parsedData = JSON.parse(fileContent); // Handle both old format (sortedOriginalKeys) and new format (originalKeyToNumericId)
|
|
92
96
|
|
|
93
97
|
if (parsedData && parsedData.sortedOriginalKeys && parsedData.totalKeys !== undefined) {
|
|
98
|
+
// Old format
|
|
94
99
|
this.numericMap = {
|
|
95
100
|
sortedOriginalKeys: parsedData.sortedOriginalKeys,
|
|
96
101
|
totalKeys: parsedData.totalKeys
|
|
97
102
|
};
|
|
98
103
|
return this.numericMap;
|
|
104
|
+
} else if (parsedData && parsedData.originalKeyToNumericId && parsedData.totalKeysInMap !== undefined) {
|
|
105
|
+
// New format - convert originalKeyToNumericId to sortedOriginalKeys
|
|
106
|
+
const keyToIdMap = parsedData.originalKeyToNumericId;
|
|
107
|
+
const sortedOriginalKeys = new Array(parsedData.totalKeysInMap); // Fill the array with keys at their numeric positions
|
|
108
|
+
|
|
109
|
+
Object.keys(keyToIdMap).forEach(key => {
|
|
110
|
+
const numericId = keyToIdMap[key];
|
|
111
|
+
|
|
112
|
+
if (numericId >= 0 && numericId < parsedData.totalKeysInMap) {
|
|
113
|
+
sortedOriginalKeys[numericId] = key;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
this.numericMap = {
|
|
117
|
+
sortedOriginalKeys: sortedOriginalKeys,
|
|
118
|
+
totalKeys: parsedData.totalKeysInMap
|
|
119
|
+
};
|
|
120
|
+
return this.numericMap;
|
|
99
121
|
} else {
|
|
100
|
-
compilation.warnings.push(new Error(`${pluginName}: numericMap file (${numericMapPath}) parsed but seems malformed. Using empty map.`));
|
|
122
|
+
compilation.warnings.push(new Error(`${pluginName}: numericMap file (${numericMapPath}) parsed but seems malformed. Expected 'sortedOriginalKeys' + 'totalKeys' or 'originalKeyToNumericId' + 'totalKeysInMap'. Using empty map.`));
|
|
101
123
|
this.numericMap = {
|
|
102
124
|
sortedOriginalKeys: [],
|
|
103
125
|
totalKeys: 0
|
|
@@ -125,11 +147,36 @@ class I18nNumericIndexPlugin {
|
|
|
125
147
|
return this.numericMap;
|
|
126
148
|
}
|
|
127
149
|
|
|
150
|
+
loadJSResourceBaseFile(compilation) {
|
|
151
|
+
const compilerContext = compilation.compiler.context;
|
|
152
|
+
const jsResourcePathOpt = this.options.jsResourcePath;
|
|
153
|
+
|
|
154
|
+
if (!jsResourcePathOpt) {
|
|
155
|
+
return {};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const jsResourcePath = path.resolve(compilerContext, jsResourcePathOpt);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
if (fs.existsSync(jsResourcePath)) {
|
|
162
|
+
const fileContent = fs.readFileSync(jsResourcePath, 'utf-8');
|
|
163
|
+
return parseProperties(fileContent);
|
|
164
|
+
} else {
|
|
165
|
+
compilation.warnings.push(new Error(`${pluginName}: JS Resource base file not found: ${jsResourcePath}`));
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
compilation.errors.push(new Error(`${pluginName}: Error loading JS Resource base file ${jsResourcePath}: ${err.message}`));
|
|
170
|
+
return {};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
128
174
|
loadAllI18nData(compilation) {
|
|
129
175
|
if (this.options.allI18nObject && this.options.locales) {
|
|
130
176
|
return {
|
|
131
177
|
allI18nObject: this.options.allI18nObject,
|
|
132
|
-
locales: this.options.locales
|
|
178
|
+
locales: this.options.locales,
|
|
179
|
+
jsResourceBase: this.loadJSResourceBaseFile(compilation)
|
|
133
180
|
};
|
|
134
181
|
}
|
|
135
182
|
|
|
@@ -143,7 +190,8 @@ class I18nNumericIndexPlugin {
|
|
|
143
190
|
compilation.errors.push(new Error(`${pluginName}: 'propertiesFolderPath' option is missing, cannot load translations.`));
|
|
144
191
|
return {
|
|
145
192
|
allI18nObject,
|
|
146
|
-
locales: []
|
|
193
|
+
locales: [],
|
|
194
|
+
jsResourceBase: {}
|
|
147
195
|
};
|
|
148
196
|
}
|
|
149
197
|
|
|
@@ -154,7 +202,8 @@ class I18nNumericIndexPlugin {
|
|
|
154
202
|
compilation.errors.push(new Error(`${pluginName}: propertiesFolderPath does not exist: ${propertiesFolderPath}`));
|
|
155
203
|
return {
|
|
156
204
|
allI18nObject,
|
|
157
|
-
locales: []
|
|
205
|
+
locales: [],
|
|
206
|
+
jsResourceBase: {}
|
|
158
207
|
};
|
|
159
208
|
}
|
|
160
209
|
|
|
@@ -190,8 +239,7 @@ class I18nNumericIndexPlugin {
|
|
|
190
239
|
if (locale && ext.toLowerCase() === baseExtension.toLowerCase()) {
|
|
191
240
|
try {
|
|
192
241
|
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
193
|
-
|
|
194
|
-
allI18nObject[locale] = parseResult.data;
|
|
242
|
+
allI18nObject[locale] = parseProperties(fileContent);
|
|
195
243
|
discoveredLocales.add(locale);
|
|
196
244
|
} catch (readErr) {
|
|
197
245
|
compilation.errors.push(new Error(`${pluginName}: Error reading/parsing properties file ${filePath}: ${readErr.message}`));
|
|
@@ -201,11 +249,14 @@ class I18nNumericIndexPlugin {
|
|
|
201
249
|
});
|
|
202
250
|
} catch (err) {
|
|
203
251
|
compilation.errors.push(new Error(`${pluginName}: Error reading propertiesFolderPath ${propertiesFolderPath}: ${err.message}`));
|
|
204
|
-
}
|
|
252
|
+
} // Load JS Resource base file separately
|
|
253
|
+
|
|
205
254
|
|
|
255
|
+
const jsResourceBase = this.loadJSResourceBaseFile(compilation);
|
|
206
256
|
return {
|
|
207
257
|
allI18nObject,
|
|
208
|
-
locales: Array.from(discoveredLocales)
|
|
258
|
+
locales: Array.from(discoveredLocales),
|
|
259
|
+
jsResourceBase
|
|
209
260
|
};
|
|
210
261
|
}
|
|
211
262
|
|
|
@@ -219,21 +270,25 @@ class I18nNumericIndexPlugin {
|
|
|
219
270
|
let stringifiedData;
|
|
220
271
|
|
|
221
272
|
try {
|
|
222
|
-
//
|
|
223
|
-
|
|
273
|
+
// Use compact serializer for smaller output
|
|
274
|
+
if (this.options.useCompactFormat) {
|
|
275
|
+
stringifiedData = this.compactSerialize(dataToStringify);
|
|
276
|
+
} else {
|
|
277
|
+
stringifiedData = JSON.stringify(dataToStringify, null, 0);
|
|
278
|
+
} // If emitLiteralUnicode is true (default), convert \uXXXX sequences back to actual Unicode characters
|
|
224
279
|
// This ensures the output file contains literal UTF-8 characters if desired.
|
|
225
280
|
|
|
281
|
+
|
|
226
282
|
if (this.options.emitLiteralUnicode) {
|
|
227
283
|
stringifiedData = decodeUnicodeEscapes(stringifiedData);
|
|
228
284
|
}
|
|
229
285
|
} catch (e) {
|
|
230
286
|
compilation.errors.push(new Error(`${pluginName}: Failed to stringify or post-process data for ${chunkType} chunk (${locale}): ${e.message}`));
|
|
231
287
|
return;
|
|
232
|
-
}
|
|
288
|
+
} // Create content in the format: window.loadI18nChunk({data})
|
|
233
289
|
|
|
234
|
-
const fileContent = `${jsonpFunc}(${stringifiedData});`; // RawSource expects a string. If stringifiedData contains literal Unicode,
|
|
235
|
-
// fileContent will also, and Webpack handles UTF-8 output correctly.
|
|
236
290
|
|
|
291
|
+
const fileContent = `${jsonpFunc}(${stringifiedData});`;
|
|
237
292
|
const source = new RawSource(fileContent);
|
|
238
293
|
const actualContentHash = createHashFunction({
|
|
239
294
|
// Ensure createHashFunction is loaded
|
|
@@ -268,14 +323,15 @@ class I18nNumericIndexPlugin {
|
|
|
268
323
|
name: pluginName,
|
|
269
324
|
stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE // Or a later stage if needed
|
|
270
325
|
|
|
271
|
-
}, (
|
|
326
|
+
}, (unusedAssets, callback) => {
|
|
272
327
|
if (!this.options.enable) {
|
|
273
328
|
return callback();
|
|
274
329
|
}
|
|
275
330
|
|
|
276
331
|
const {
|
|
277
332
|
allI18nObject,
|
|
278
|
-
locales
|
|
333
|
+
locales,
|
|
334
|
+
jsResourceBase
|
|
279
335
|
} = this.loadAllI18nData(compilation);
|
|
280
336
|
|
|
281
337
|
if (!locales || locales.length === 0) {
|
|
@@ -300,8 +356,7 @@ class I18nNumericIndexPlugin {
|
|
|
300
356
|
|
|
301
357
|
const numericFilenameTemplate = this.options.numericFilenameTemplate;
|
|
302
358
|
const dynamicFilenameTemplate = this.options.dynamicFilenameTemplate;
|
|
303
|
-
const
|
|
304
|
-
const dynamicJsonpFunction = this.options.dynamicJsonpFunction || 'loadI18nDynamicChunk';
|
|
359
|
+
const jsonpFunction = this.options.jsonpFunc || 'window.loadI18nChunk';
|
|
305
360
|
|
|
306
361
|
if (!numericFilenameTemplate) {
|
|
307
362
|
compilation.errors.push(new Error(`${pluginName}: Missing required option 'numericFilenameTemplate' in plugin options.`)); // return callback(); // Allow processing other chunks if one template is missing
|
|
@@ -320,7 +375,18 @@ class I18nNumericIndexPlugin {
|
|
|
320
375
|
compilation.warnings.push(new Error(`${pluginName}: The 'dynamicFilenameTemplate' ("${dynamicFilenameTemplate}") ` + `does not include '[locale]', '[name]', or '[id]'. All locales will overwrite the same file if multiple locales exist.`));
|
|
321
376
|
}
|
|
322
377
|
|
|
323
|
-
const numericKeysSet = new Set(sortedOriginalKeys);
|
|
378
|
+
const numericKeysSet = new Set(sortedOriginalKeys); // Use JS resource base file as the master reference for keys
|
|
379
|
+
|
|
380
|
+
const jsResourceBaseTranslations = jsResourceBase || {}; // Get English translations for fallback
|
|
381
|
+
|
|
382
|
+
const englishTranslations = allI18nObject.en || allI18nObject[this.options.defaultLocaleForBaseFile || 'en'] || jsResourceBaseTranslations; // Log base file and numeric map stats
|
|
383
|
+
|
|
384
|
+
console.log(`${pluginName}: === DEBUGGING STATS ===`);
|
|
385
|
+
console.log(`${pluginName}: JS Resource base file keys count: ${Object.keys(jsResourceBaseTranslations).length}`);
|
|
386
|
+
console.log(`${pluginName}: English fallback keys count: ${Object.keys(englishTranslations).length}`);
|
|
387
|
+
console.log(`${pluginName}: Numeric map total keys: ${totalKeys}`);
|
|
388
|
+
console.log(`${pluginName}: Numeric map actual keys: ${sortedOriginalKeys.filter(key => key !== undefined).length}`);
|
|
389
|
+
console.log(`${pluginName}: Global comment keys count: ${globallyUsedCommentKeys.size}`);
|
|
324
390
|
|
|
325
391
|
for (const locale of locales) {
|
|
326
392
|
const localeTranslations = allI18nObject[locale] || {};
|
|
@@ -331,54 +397,94 @@ class I18nNumericIndexPlugin {
|
|
|
331
397
|
// Iterate up to totalKeys (max expected length)
|
|
332
398
|
const originalKey = sortedOriginalKeys[i]; // Get key if available
|
|
333
399
|
// If originalKey is undefined (i < sortedOriginalKeys.length but originalKey is not there due to sparse array or shorter sortedKeys)
|
|
334
|
-
// or if the translation is missing,
|
|
400
|
+
// or if the translation is missing, use English fallback if available, otherwise null.
|
|
401
|
+
|
|
402
|
+
let value = null;
|
|
403
|
+
|
|
404
|
+
if (originalKey !== undefined && jsResourceBaseTranslations[originalKey] !== undefined) {
|
|
405
|
+
if (localeTranslations[originalKey] !== undefined) {
|
|
406
|
+
value = this.intern(localeTranslations[originalKey]);
|
|
407
|
+
} else if (englishTranslations[originalKey] !== undefined) {
|
|
408
|
+
// Fallback to English value for missing key
|
|
409
|
+
value = this.intern(englishTranslations[originalKey]);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
335
412
|
|
|
336
|
-
const value = originalKey !== undefined && localeTranslations[originalKey] !== undefined ? localeTranslations[originalKey] : null;
|
|
337
413
|
numericDataForLocale.push(value);
|
|
338
414
|
}
|
|
339
|
-
}
|
|
415
|
+
} // Convert to compact format
|
|
416
|
+
|
|
340
417
|
|
|
341
|
-
const
|
|
418
|
+
const compactNumericData = this.generateCompactNumericData(numericDataForLocale);
|
|
419
|
+
const dynamicDataForLocale = Object.create(null); // Use null prototype for cleaner object
|
|
420
|
+
// Process globally used comment keys first (only if they exist in JS resource base)
|
|
342
421
|
|
|
343
422
|
globallyUsedCommentKeys.forEach(originalKey => {
|
|
344
|
-
if (!numericKeysSet.has(originalKey)) {
|
|
345
|
-
|
|
346
|
-
|
|
423
|
+
if (!numericKeysSet.has(originalKey) && jsResourceBaseTranslations[originalKey] !== undefined) {
|
|
424
|
+
let value = null;
|
|
425
|
+
|
|
426
|
+
if (localeTranslations[originalKey] !== undefined) {
|
|
427
|
+
value = this.intern(localeTranslations[originalKey]);
|
|
428
|
+
} else if (englishTranslations[originalKey] !== undefined) {
|
|
429
|
+
// Fallback to English value for missing key
|
|
430
|
+
value = this.intern(englishTranslations[originalKey]);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
dynamicDataForLocale[originalKey] = value;
|
|
347
434
|
}
|
|
348
|
-
}); // Process remaining keys from
|
|
435
|
+
}); // Process remaining keys from JS resource base (keys in JS resource but not in numeric mapping)
|
|
436
|
+
// Only include keys that exist in the JS resource base file
|
|
349
437
|
|
|
350
|
-
Object.keys(
|
|
438
|
+
Object.keys(jsResourceBaseTranslations).forEach(originalKey => {
|
|
351
439
|
// Add if not in numeric set AND not already added from globallyUsedCommentKeys
|
|
352
|
-
if (!numericKeysSet.has(originalKey) && !
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
});
|
|
440
|
+
if (!numericKeysSet.has(originalKey) && !Object.prototype.hasOwnProperty.call(dynamicDataForLocale, originalKey)) {
|
|
441
|
+
let value = null;
|
|
356
442
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
443
|
+
if (localeTranslations[originalKey] !== undefined) {
|
|
444
|
+
value = this.intern(localeTranslations[originalKey]);
|
|
445
|
+
} else if (englishTranslations[originalKey] !== undefined) {
|
|
446
|
+
// Use English fallback for missing key
|
|
447
|
+
value = this.intern(englishTranslations[originalKey]);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
dynamicDataForLocale[originalKey] = value;
|
|
451
|
+
}
|
|
452
|
+
}); // Log per-locale stats
|
|
453
|
+
|
|
454
|
+
const numericKeysWithValues = numericDataForLocale.filter(v => v !== null).length;
|
|
455
|
+
const numericKeysWithFallbacks = numericDataForLocale.filter((v, i) => {
|
|
456
|
+
const originalKey = sortedOriginalKeys[i];
|
|
457
|
+
return v !== null && originalKey && localeTranslations[originalKey] === undefined && englishTranslations[originalKey] !== undefined;
|
|
458
|
+
}).length;
|
|
459
|
+
const dynamicKeysCount = Object.keys(dynamicDataForLocale).length;
|
|
460
|
+
const dynamicKeysWithFallbacks = Object.keys(dynamicDataForLocale).filter(key => localeTranslations[key] === undefined && englishTranslations[key] !== undefined).length; // Count how many locale keys are filtered out (not in JS resource base)
|
|
461
|
+
|
|
462
|
+
const localeKeysCount = Object.keys(localeTranslations).length;
|
|
463
|
+
const localeKeysInBase = Object.keys(localeTranslations).filter(key => jsResourceBaseTranslations[key] !== undefined).length;
|
|
464
|
+
const localeKeysFiltered = localeKeysCount - localeKeysInBase;
|
|
465
|
+
console.log(`${pluginName}: ${locale} - Total keys in locale file: ${localeKeysCount}`);
|
|
466
|
+
console.log(`${pluginName}: ${locale} - Keys from locale matching JS resource base: ${localeKeysInBase}`);
|
|
467
|
+
console.log(`${pluginName}: ${locale} - Keys filtered out (not in base): ${localeKeysFiltered}`);
|
|
468
|
+
console.log(`${pluginName}: ${locale} - Numeric: ${numericKeysWithValues}/${numericDataForLocale.length} (${numericKeysWithFallbacks} fallbacks)`);
|
|
469
|
+
console.log(`${pluginName}: ${locale} - Dynamic: ${dynamicKeysCount} (${dynamicKeysWithFallbacks} fallbacks)`);
|
|
470
|
+
console.log(`${pluginName}: ${locale} - Total emitted: ${numericKeysWithValues + dynamicKeysCount}`);
|
|
471
|
+
console.log(`${pluginName}: ---`); // Keep original debug logging for compatibility
|
|
472
|
+
|
|
473
|
+
if (this.options.logKeyCounts && this.options.isDevelopment && locale === (this.options.logKeyCountsForLocale || 'en_US')) {
|
|
474
|
+
console.log(`${pluginName}: ${locale} - Numeric: ${numericKeysWithValues}/${numericDataForLocale.length}, Dynamic: ${Object.keys(dynamicDataForLocale).length}`);
|
|
371
475
|
}
|
|
372
476
|
|
|
373
|
-
if (numericFilenameTemplate && numericDataForLocale.length > 0) {
|
|
374
|
-
this.emitChunkFile(compilation, numericFilenameTemplate, locale, numericDataForLocale,
|
|
477
|
+
if (numericFilenameTemplate && (this.options.useCompactFormat ? compactNumericData.length > 0 : numericDataForLocale.length > 0)) {
|
|
478
|
+
this.emitChunkFile(compilation, numericFilenameTemplate, locale, this.options.useCompactFormat ? compactNumericData : numericDataForLocale, jsonpFunction, 'numeric');
|
|
375
479
|
}
|
|
376
480
|
|
|
377
481
|
if (dynamicFilenameTemplate && Object.keys(dynamicDataForLocale).length > 0) {
|
|
378
|
-
this.emitChunkFile(compilation, dynamicFilenameTemplate, locale, dynamicDataForLocale,
|
|
482
|
+
this.emitChunkFile(compilation, dynamicFilenameTemplate, locale, dynamicDataForLocale, jsonpFunction, 'dynamic');
|
|
379
483
|
}
|
|
380
|
-
}
|
|
484
|
+
} // Clear string pool after processing to free memory
|
|
485
|
+
|
|
381
486
|
|
|
487
|
+
this.stringPool.clear();
|
|
382
488
|
callback();
|
|
383
489
|
});
|
|
384
490
|
});
|
|
@@ -7,6 +7,10 @@ exports.I18nFilesEmitPlugin = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _webpack = require("webpack");
|
|
9
9
|
|
|
10
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
11
|
+
|
|
12
|
+
var _path = _interopRequireDefault(require("path"));
|
|
13
|
+
|
|
10
14
|
var _createHash = require("./createHash");
|
|
11
15
|
|
|
12
16
|
var _pathCreator = require("./pathCreator");
|
|
@@ -15,6 +19,8 @@ var _propertiesUtils = require("./utils/propertiesUtils");
|
|
|
15
19
|
|
|
16
20
|
var _LocaleChunkAssetsStore = require("./LocaleChunkAssetsStore");
|
|
17
21
|
|
|
22
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
|
+
|
|
18
24
|
/* eslint-disable no-restricted-syntax */
|
|
19
25
|
const pluginName = 'I18nFilesEmitPlugin';
|
|
20
26
|
const {
|
|
@@ -23,18 +29,57 @@ const {
|
|
|
23
29
|
|
|
24
30
|
class I18nFilesEmitPlugin {
|
|
25
31
|
constructor(options) {
|
|
26
|
-
this.options = options;
|
|
32
|
+
this.options = options;
|
|
33
|
+
this.numericIdMap = null; // this.options = {
|
|
27
34
|
// locales: options.locales,
|
|
28
35
|
// chunkFilename: options.chunkFilename,
|
|
29
36
|
// filename: options.filename,
|
|
30
37
|
// allI18nObject: options.allI18nObject,
|
|
31
|
-
// jsonpFunc: options.jsonpFunc
|
|
38
|
+
// jsonpFunc: options.jsonpFunc,
|
|
39
|
+
// useNumericIndexing: options.useNumericIndexing, // NEW OPTION
|
|
40
|
+
// numericMapPath: options.numericMapPath // NEW OPTION
|
|
32
41
|
// };
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
loadNumericIdMap(compilation) {
|
|
45
|
+
if (this.numericIdMap || !this.options.useNumericIndexing || !this.options.numericMapPath) {
|
|
46
|
+
return this.numericIdMap;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const absoluteMapPath = _path.default.isAbsolute(this.options.numericMapPath) ? this.options.numericMapPath : _path.default.resolve(compilation.compiler.context, this.options.numericMapPath);
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
if (_fs.default.existsSync(absoluteMapPath)) {
|
|
53
|
+
const fileContent = _fs.default.readFileSync(absoluteMapPath, 'utf-8');
|
|
54
|
+
|
|
55
|
+
const parsedData = JSON.parse(fileContent);
|
|
56
|
+
|
|
57
|
+
if (parsedData && parsedData.originalKeyToNumericId && typeof parsedData.originalKeyToNumericId === 'object') {
|
|
58
|
+
this.numericIdMap = parsedData.originalKeyToNumericId;
|
|
59
|
+
} else {
|
|
60
|
+
compilation.warnings.push(new Error(`${pluginName}: Invalid numeric map format in ${absoluteMapPath}`));
|
|
61
|
+
this.numericIdMap = {};
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
compilation.warnings.push(new Error(`${pluginName}: Numeric map file not found: ${absoluteMapPath}`));
|
|
65
|
+
this.numericIdMap = {};
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
compilation.errors.push(new Error(`${pluginName}: Error loading numeric map from ${absoluteMapPath}: ${err.message}`));
|
|
69
|
+
this.numericIdMap = {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return this.numericIdMap;
|
|
73
|
+
}
|
|
74
|
+
|
|
35
75
|
apply(compiler) {
|
|
36
76
|
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
37
|
-
//
|
|
77
|
+
// Load numeric map if needed
|
|
78
|
+
if (this.options.useNumericIndexing) {
|
|
79
|
+
this.loadNumericIdMap(compilation);
|
|
80
|
+
} // Get store for cache
|
|
81
|
+
|
|
82
|
+
|
|
38
83
|
this.store = (0, _LocaleChunkAssetsStore.getLocaleChunkAssetsStore)(compilation);
|
|
39
84
|
const i18nStore = this.store;
|
|
40
85
|
compilation.hooks.beforeHash.tap(pluginName, () => {
|
|
@@ -150,12 +195,28 @@ class I18nFilesEmitPlugin {
|
|
|
150
195
|
|
|
151
196
|
getI18nContentForkeys(i18nKeys, locale) {
|
|
152
197
|
const {
|
|
153
|
-
allI18nObject
|
|
198
|
+
allI18nObject,
|
|
199
|
+
useNumericIndexing
|
|
154
200
|
} = this.options;
|
|
155
201
|
const data = {};
|
|
156
202
|
|
|
157
203
|
for (const key of i18nKeys) {
|
|
158
|
-
|
|
204
|
+
const value = allI18nObject[locale][key];
|
|
205
|
+
|
|
206
|
+
if (useNumericIndexing && this.numericIdMap) {
|
|
207
|
+
// Use numeric ID as key if available, otherwise keep original key
|
|
208
|
+
const numericId = this.numericIdMap[key];
|
|
209
|
+
|
|
210
|
+
if (numericId !== undefined) {
|
|
211
|
+
data[numericId] = value;
|
|
212
|
+
} else {
|
|
213
|
+
// Keep original key if no numeric mapping exists
|
|
214
|
+
data[key] = value;
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
// Default behavior: use original key
|
|
218
|
+
data[key] = value;
|
|
219
|
+
}
|
|
159
220
|
}
|
|
160
221
|
|
|
161
222
|
return data;
|
|
@@ -40,6 +40,9 @@ function optionsHandler(options) {
|
|
|
40
40
|
jsResourceI18nKeys,
|
|
41
41
|
allI18nObject,
|
|
42
42
|
locales,
|
|
43
|
+
// NEW OPTIONS FOR NUMERIC INDEXING
|
|
44
|
+
useNumericIndexing: options.useNumericIndexing,
|
|
45
|
+
numericMapPath: options.numericMapPath,
|
|
43
46
|
// template: (object, locale) => `window.loadI18n(${JSON.stringify(object)}, ${JSON.stringify(locale)})`,
|
|
44
47
|
runtime: true,
|
|
45
48
|
runtimeOptions: {
|