@zohodesk/client_build_tool 0.0.11-exp.15.1 → 0.0.11-exp.15.4

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.
Files changed (22) hide show
  1. package/README.md +0 -204
  2. package/docs/I18N_NUMERIC_INDEXING_PLUGIN.md +225 -0
  3. package/lib/schemas/defaultConfigValues.js +9 -12
  4. package/lib/schemas/defaultConfigValuesOnly.js +1 -18
  5. package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +124 -0
  6. package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +210 -143
  7. package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js +47 -12
  8. package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js +19 -1
  9. package/lib/shared/bundler/webpack/jsLoaders.js +9 -4
  10. package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +12 -3
  11. package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +28 -14
  12. package/lib/shared/bundler/webpack/pluginConfigs/configI18nIndexingPlugin.js +42 -0
  13. package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericHtmlInjector.js +92 -0
  14. package/lib/shared/bundler/webpack/plugins.js +6 -2
  15. package/npm-shrinkwrap.json +2 -2
  16. package/package.json +1 -1
  17. package/README_backup.md +0 -202
  18. package/init/README.md +0 -170
  19. package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin.js +0 -49
  20. package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/collectAstKeys.js +0 -96
  21. package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +0 -84
  22. package/lib/shared/bundler/webpack/utils/propertiesParser.js +0 -81
@@ -1,186 +1,253 @@
1
1
  "use strict";
2
2
 
3
- const path = require('path');
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
4
7
 
5
- const {
6
- sources,
7
- Compilation,
8
- util
9
- } = require('webpack');
8
+ var _fs = _interopRequireDefault(require("fs"));
10
9
 
11
- const {
12
- decodeUnicodeEscapes
13
- } = require('../../utils/propertiesParser');
10
+ var _path = _interopRequireDefault(require("path"));
14
11
 
15
- const {
16
- loadNumericMap,
17
- loadI18nData
18
- } = require('./utils/i18nDataLoader');
12
+ var _webpack = require("webpack");
13
+
14
+ var _propertiesUtils = require("../I18nSplitPlugin/utils/propertiesUtils");
15
+
16
+ var _I18nGroupRuntimeModule = require("./I18nGroupRuntimeModule");
17
+
18
+ var _i18nDataLoader = require("./utils/i18nDataLoader");
19
+
20
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
21
 
20
22
  const {
21
23
  RawSource
22
- } = sources;
24
+ } = _webpack.sources;
23
25
  const pluginName = 'I18nNumericIndexPlugin';
24
26
 
25
27
  class I18nNumericIndexPlugin {
26
- constructor(options = {}) {
27
- this.options = { ...options,
28
- singleFile: options.singleFile || false,
29
- singleFileTemplate: options.singleFileTemplate,
30
- includeContentHash: options.includeContentHash || false,
31
- generateManifest: options.generateManifest || false,
32
- manifestPath: options.manifestPath || 'i18n/manifest.json'
33
- };
34
- this.numericMap = null;
35
- this.i18nData = null;
36
- this.manifest = {};
28
+ constructor(options) {
29
+ this.options = options;
30
+ this.numericMap = {};
31
+ this.customGroups = {};
37
32
  }
38
33
 
39
- getNumericMap(compilation) {
40
- if (!this.numericMap) {
41
- const mapPath = path.resolve(compilation.compiler.context, this.options.numericMapPath);
42
- this.numericMap = loadNumericMap(mapPath, compilation);
43
- }
34
+ apply(compiler) {
35
+ // Detect webpackI18nGroup comments in code
36
+ this.detectI18nGroupComments(compiler);
37
+ compiler.hooks.thisCompilation.tap(pluginName, compilation => {
38
+ // Add runtime module for group-based loading
39
+ if (this.options.customGroups) {
40
+ compilation.hooks.runtimeRequirementInTree.for(_webpack.RuntimeGlobals.ensureChunk).tap(pluginName, chunk => {
41
+ compilation.addRuntimeModule(chunk, new _I18nGroupRuntimeModule.I18nGroupRuntimeModule({
42
+ customGroups: this.options.customGroups,
43
+ localeVarName: this.options.localeVarName,
44
+ jsonpFunc: this.options.jsonpFunc
45
+ }));
46
+ });
47
+ }
44
48
 
45
- return this.numericMap;
49
+ compilation.hooks.processAssets.tap({
50
+ name: pluginName,
51
+ stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE
52
+ }, () => {
53
+ this.processI18nFiles(compilation);
54
+ });
55
+ });
46
56
  }
47
57
 
48
- getI18nData(compilation) {
49
- if (!this.i18nData) {
50
- this.i18nData = loadI18nData(this.options, compilation);
51
- }
58
+ detectI18nGroupComments(compiler) {
59
+ compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
60
+ factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
61
+ parser.hooks.importCall.tap(pluginName, expr => {
62
+ // Check for webpackI18nGroup comment
63
+ const comments = expr.leadingComments || [];
64
+ comments.forEach(comment => {
65
+ if (comment.value && comment.value.includes('webpackI18nGroup')) {
66
+ const match = comment.value.match(/webpackI18nGroup:\s*["']([^"']+)["']/);
67
+
68
+ if (match) {
69
+ const groupName = match[1]; // Store this information for later use
70
+
71
+ if (!this.detectedGroups) {
72
+ this.detectedGroups = {};
73
+ } // Extract chunk name from webpackChunkName comment
74
+
52
75
 
53
- return this.i18nData;
76
+ const chunkNameMatch = comment.value.match(/webpackChunkName:\s*["']([^"']+)["']/);
77
+
78
+ if (chunkNameMatch) {
79
+ const chunkName = chunkNameMatch[1];
80
+ this.detectedGroups[chunkName] = groupName;
81
+ }
82
+ }
83
+ }
84
+ });
85
+ });
86
+ });
87
+ });
54
88
  }
55
89
 
56
- generateContentHash(content, compilation) {
90
+ processI18nFiles(compilation) {
57
91
  const {
58
- hashFunction,
59
- hashDigest,
60
- hashDigestLength
61
- } = compilation.outputOptions;
62
- const hash = util.createHash(hashFunction || 'xxhash64');
63
- hash.update(content);
64
- return hash.digest(hashDigest || 'hex').substring(0, hashDigestLength || 20);
65
- }
92
+ jsResourcePath,
93
+ propertiesFolderPath,
94
+ numericMapPath,
95
+ customGroups,
96
+ jsonpFunc,
97
+ numericFilenameTemplate,
98
+ dynamicFilenameTemplate
99
+ } = this.options;
100
+
101
+ if (!jsResourcePath || !propertiesFolderPath) {
102
+ return;
103
+ } // Load existing numeric map if available
104
+
105
+
106
+ if (numericMapPath) {
107
+ const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
108
+
109
+ if (mapData && mapData.sortedKeys) {
110
+ // Initialize numericMap from existing data
111
+ mapData.sortedKeys.forEach((key, id) => {
112
+ if (key) {
113
+ this.numericMap[key] = id;
114
+ }
115
+ });
116
+ }
117
+ } // Read JSResources.properties
66
118
 
67
- emitChunk(compilation, filename, locale, data, fileType = null) {
68
- const content = decodeUnicodeEscapes(JSON.stringify(data));
69
- const fileContent = `${this.options.jsonpFunc}(${content});`;
70
- let outputPath = filename.replace(/\[locale\]/g, locale);
71
- const hasContentHashPlaceholder = filename.includes('[contenthash]');
72
- const shouldIncludeHash = hasContentHashPlaceholder || this.options.includeContentHash;
73
119
 
74
- if (shouldIncludeHash) {
75
- const contentHash = this.generateContentHash(fileContent, compilation);
120
+ const jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath); // Parse custom groups from banner markers
76
121
 
77
- if (hasContentHashPlaceholder) {
78
- outputPath = outputPath.replace(/\[contenthash\]/g, contentHash);
79
- } else {
80
- outputPath = outputPath.replace(/\.js$/, `.${contentHash}.js`);
81
- }
82
- }
122
+ if (customGroups) {
123
+ this.parseCustomGroups(jsResourcePath, customGroups);
124
+ } // Get all locale translations
83
125
 
84
- if (this.options.generateManifest) {
85
- const cleanName = filename.replace(/\[locale\]/g, locale).replace(/\.\[contenthash\]/g, '').replace(/\.js$/, '.js');
86
- const cleanNameWithType = fileType ? cleanName.replace(/\.js$/, `.${fileType}.js`) : cleanName;
87
- const manifestKey = cleanNameWithType.split('/').pop();
88
- this.manifest[manifestKey] = outputPath.split('/').pop();
89
- }
90
126
 
91
- compilation.emitAsset(outputPath, new RawSource(fileContent));
92
- return outputPath;
93
- }
127
+ const allI18nObject = (0, _propertiesUtils.getAllI18n)({
128
+ folderPath: propertiesFolderPath,
129
+ disableDefault: false,
130
+ jsResourceI18nKeys: jsResourceKeys
131
+ }); // For en_US, use only JSResources (don't merge with ApplicationResources_en_US)
94
132
 
95
- apply(compiler) {
96
- compiler.hooks.thisCompilation.tap(pluginName, compilation => {
97
- compilation.hooks.processAssets.tapAsync({
98
- name: pluginName,
99
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
100
- }, (assets, callback) => {
101
- if (!this.options.enable) {
102
- return callback();
103
- }
133
+ if (allI18nObject['en_US']) {
134
+ allI18nObject['en_US'] = jsResourceKeys;
135
+ } else {
136
+ // If en_US doesn't exist in the folder, create it from JSResources
137
+ allI18nObject['en_US'] = jsResourceKeys;
138
+ }
104
139
 
105
- const {
106
- sortedKeys,
107
- totalKeys,
108
- originalKeyToNumericId
109
- } = this.getNumericMap(compilation);
110
- const {
111
- jsResourceBase,
112
- allI18n,
113
- locales
114
- } = this.getI18nData(compilation);
115
- if (!locales.length) return callback();
116
- const numericKeysSet = new Set(sortedKeys);
117
- const englishData = allI18n.en_US || jsResourceBase;
118
- const isDevMode = this.options.devMode || process.env.NODE_ENV === 'development';
119
- locales.forEach(locale => {
120
- const localeData = allI18n[locale] || {};
121
- const numericData = {};
122
- const unmappedData = {};
123
- Object.keys(jsResourceBase).forEach(key => {
124
- const translation = localeData[key] ?? englishData[key];
125
-
126
- if (originalKeyToNumericId && originalKeyToNumericId.hasOwnProperty(key)) {
127
- const numericId = originalKeyToNumericId[key];
128
- numericData[numericId] = translation;
129
- } else if (numericKeysSet.has(key)) {
130
- const index = sortedKeys.indexOf(key);
131
-
132
- if (index !== -1) {
133
- numericData[index] = translation;
134
- }
135
- } else {
136
- unmappedData[key] = translation;
137
- }
138
- });
140
+ const locales = Object.keys(allI18nObject); // Process each locale
139
141
 
140
- if (this.options.singleFile) {
141
- const combinedData = { ...numericData,
142
- ...unmappedData
143
- };
142
+ locales.forEach(locale => {
143
+ const localeData = allI18nObject[locale];
144
+ const numericData = {};
145
+ const dynamicData = {};
146
+ const groupData = {}; // Initialize custom groups
144
147
 
145
- if (Object.keys(combinedData).length > 0) {
146
- const filename = this.options.singleFileTemplate || this.options.numericFilenameTemplate || this.options.dynamicFilenameTemplate;
147
- this.emitChunk(compilation, filename, locale, combinedData);
148
- }
149
- } else {
150
- if (Object.keys(numericData).length > 0) {
151
- this.emitChunk(compilation, this.options.numericFilenameTemplate, locale, numericData, 'numeric');
152
- }
148
+ Object.keys(customGroups || {}).forEach(groupName => {
149
+ groupData[groupName] = {};
150
+ }); // Process each key
153
151
 
154
- if (Object.keys(unmappedData).length > 0) {
155
- this.emitChunk(compilation, this.options.dynamicFilenameTemplate, locale, unmappedData, 'dynamic');
156
- }
157
- }
158
- });
152
+ Object.keys(localeData).forEach(key => {
153
+ const value = localeData[key]; // Simple logic: if has numeric ID use it, otherwise it's dynamic
154
+
155
+ if (this.numericMap[key]) {
156
+ const numericKey = String(this.numericMap[key]); // Check if belongs to a custom group
159
157
 
160
- if (this.options.generateManifest && Object.keys(this.manifest).length > 0) {
161
- // Determine manifest path
162
- let manifestPath;
158
+ const belongsToGroup = this.getKeyGroup(key);
163
159
 
164
- if (this.options.manifestPath) {
165
- // Use explicitly configured path
166
- manifestPath = this.options.manifestPath;
160
+ if (belongsToGroup) {
161
+ groupData[belongsToGroup][numericKey] = value;
167
162
  } else {
168
- // Default to same directory as i18n files with manifest.json name
169
- const template = this.options.singleFileTemplate || this.options.numericFilenameTemplate;
170
- manifestPath = path.dirname(template) + '/manifest.json';
163
+ numericData[numericKey] = value;
171
164
  }
165
+ } else {
166
+ // No numeric ID = dynamic key (regardless of placeholders)
167
+ dynamicData[key] = value;
168
+ }
169
+ }); // Emit numeric chunk
170
+
171
+ this.emitChunk(compilation, numericFilenameTemplate, locale, numericData, jsonpFunc); // Emit dynamic chunk
172
172
 
173
- const manifestContent = JSON.stringify(this.manifest, null, 2);
174
- compilation.emitAsset(manifestPath, new RawSource(manifestContent));
173
+ this.emitChunk(compilation, dynamicFilenameTemplate, locale, dynamicData, jsonpFunc); // Emit custom group chunks
174
+
175
+ Object.entries(groupData).forEach(([groupName, data]) => {
176
+ const groupConfig = customGroups[groupName];
177
+
178
+ if (groupConfig && Object.keys(data).length > 0) {
179
+ this.emitChunk(compilation, groupConfig.filenameTemplate || `i18n-chunk/[locale]/${groupName}.i18n.js`, locale, data, jsonpFunc, groupName);
175
180
  }
181
+ });
182
+ }); // Don't save numeric map - it should only be generated by the external script
183
+ }
176
184
 
177
- callback();
185
+ parseCustomGroups(jsResourcePath, customGroups) {
186
+ const content = _fs.default.readFileSync(jsResourcePath, 'utf-8');
187
+
188
+ const lines = content.split('\n');
189
+ Object.entries(customGroups).forEach(([groupName, config]) => {
190
+ const {
191
+ bannerStart,
192
+ bannerEnd
193
+ } = config;
194
+ let inGroup = false;
195
+ const groupKeys = [];
196
+ lines.forEach(line => {
197
+ if (line.includes(bannerStart)) {
198
+ inGroup = true;
199
+ } else if (line.includes(bannerEnd)) {
200
+ inGroup = false;
201
+ } else if (inGroup && line.includes('=')) {
202
+ const key = line.split('=')[0].trim();
203
+
204
+ if (key && !key.startsWith('#')) {
205
+ groupKeys.push(key);
206
+ }
207
+ }
178
208
  });
209
+ this.customGroups[groupName] = groupKeys;
179
210
  });
180
211
  }
181
212
 
213
+ getKeyGroup(key) {
214
+ for (const [groupName, keys] of Object.entries(this.customGroups)) {
215
+ if (keys.includes(key)) {
216
+ return groupName;
217
+ }
218
+ }
219
+
220
+ return null;
221
+ }
222
+
223
+ isDynamicKey(value) {
224
+ // Check if value contains placeholders like {0}, {1}, etc.
225
+ return /\{\d+\}/.test(value);
226
+ }
227
+
228
+ getNumericKey(key) {
229
+ // Return numeric ID if it exists, null otherwise
230
+ return this.numericMap[key] ? String(this.numericMap[key]) : null;
231
+ }
232
+
233
+ emitChunk(compilation, filenameTemplate, locale, data, jsonpFunc, groupName = null) {
234
+ const filename = filenameTemplate.replace('[locale]', locale);
235
+ const content = this.generateChunkContent(data, jsonpFunc, groupName);
236
+ compilation.emitAsset(filename, new RawSource(content));
237
+ }
238
+
239
+ generateChunkContent(data, jsonpFunc, groupName) {
240
+ // Decode Unicode escapes to convert \uXXXX to actual characters
241
+ const jsonString = (0, _propertiesUtils.decodeUnicodeEscapes)(JSON.stringify(data));
242
+
243
+ if (groupName) {
244
+ // Include group name for lazy loading identification
245
+ return `${jsonpFunc}(${jsonString}, "${groupName}");`;
246
+ }
247
+
248
+ return `${jsonpFunc}(${jsonString});`;
249
+ }
250
+
182
251
  }
183
252
 
184
- module.exports = {
185
- I18nNumericIndexPlugin
186
- };
253
+ exports.default = I18nNumericIndexPlugin;
@@ -5,13 +5,16 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
 
7
7
  const {
8
- parseProperties
9
- } = require('../../../utils/propertiesParser');
8
+ getPropertiesAsJSON
9
+ } = require('../../I18nSplitPlugin/utils/propertiesUtils');
10
+ /**
11
+ * Load and parse a properties file
12
+ */
13
+
10
14
 
11
15
  function loadPropertiesFile(filePath, compilation, description) {
12
16
  try {
13
- const content = fs.readFileSync(filePath, 'utf-8');
14
- const parsed = parseProperties(content);
17
+ const parsed = getPropertiesAsJSON(filePath);
15
18
  return parsed;
16
19
  } catch (err) {
17
20
  if (compilation) {
@@ -21,18 +24,37 @@ function loadPropertiesFile(filePath, compilation, description) {
21
24
  return {};
22
25
  }
23
26
  }
27
+ /**
28
+ * Load numeric mapping from JSON file
29
+ */
30
+
24
31
 
25
32
  function loadNumericMap(numericMapPath, compilation) {
26
33
  try {
27
34
  const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
28
35
  const parsedData = JSON.parse(fileContent);
29
- const sortedKeys = new Array(parsedData.totalKeysInMap);
30
- Object.entries(parsedData.originalKeyToNumericId).forEach(([key, id]) => {
36
+ let numericMap;
37
+ let totalKeys; // Handle both wrapped and flat formats
38
+
39
+ if (parsedData.originalKeyToNumericId) {
40
+ // New format with metadata
41
+ numericMap = parsedData.originalKeyToNumericId;
42
+ totalKeys = parsedData.totalKeysInMap || Object.keys(numericMap).length;
43
+ } else {
44
+ // Flat format - use directly
45
+ numericMap = parsedData;
46
+ totalKeys = Object.keys(numericMap).length;
47
+ } // Create sorted array for numeric ID lookups
48
+
49
+
50
+ const maxId = Math.max(...Object.values(numericMap));
51
+ const sortedKeys = new Array(maxId + 1);
52
+ Object.entries(numericMap).forEach(([key, id]) => {
31
53
  sortedKeys[id] = key;
32
54
  });
33
55
  return {
34
56
  sortedKeys,
35
- totalKeys: parsedData.totalKeysInMap
57
+ totalKeys
36
58
  };
37
59
  } catch (err) {
38
60
  if (compilation) {
@@ -45,23 +67,30 @@ function loadNumericMap(numericMapPath, compilation) {
45
67
  };
46
68
  }
47
69
  }
70
+ /**
71
+ * Load all locale files from properties directory
72
+ */
73
+
48
74
 
49
75
  function loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase) {
50
76
  const allI18n = {};
51
- const locales = [];
77
+ const locales = []; // Start with English base
78
+
52
79
  allI18n['en_US'] = jsResourceBase;
53
80
  locales.push('en_US');
54
81
 
55
82
  try {
56
83
  const files = fs.readdirSync(propertiesPath);
57
84
  files.forEach(file => {
58
- if (!file.endsWith('.properties')) return;
85
+ if (!file.endsWith('.properties')) return; // Match locale-specific property files
86
+
59
87
  const match = file.match(/^ApplicationResources_([a-z]{2}_[A-Z]{2})\.properties$/);
60
88
 
61
89
  if (match) {
62
90
  const locale = match[1];
63
91
  const filePath = path.join(propertiesPath, file);
64
- const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`);
92
+ const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`); // Merge with base resources
93
+
65
94
  allI18n[locale] = { ...jsResourceBase,
66
95
  ...localeData
67
96
  };
@@ -82,11 +111,17 @@ function loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase) {
82
111
  locales
83
112
  };
84
113
  }
114
+ /**
115
+ * Main loader function for i18n data
116
+ */
117
+
85
118
 
86
119
  function loadI18nData(options, compilation) {
87
120
  const jsResourcePath = path.resolve(compilation.compiler.context, options.jsResourcePath);
88
- const propertiesPath = path.resolve(compilation.compiler.context, options.propertiesFolderPath);
89
- const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources');
121
+ const propertiesPath = path.resolve(compilation.compiler.context, options.propertiesFolderPath); // Load base JS resources
122
+
123
+ const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources'); // Load all locale files
124
+
90
125
  const {
91
126
  allI18n,
92
127
  locales
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.decodeUnicodeEscapes = decodeUnicodeEscapes;
6
7
  exports.getAllI18n = getAllI18n;
7
8
  exports.getPropertiesAsJSON = getPropertiesAsJSON;
8
9
  exports.jsonToString = jsonToString;
@@ -16,6 +17,21 @@ var _constants = require("../../../../../constants");
16
17
  function isComment(line) {
17
18
  return line[0] === '#';
18
19
  }
20
+ /**
21
+ * Decode Unicode escape sequences in a string
22
+ * Converts \uXXXX to the actual character
23
+ */
24
+
25
+
26
+ function decodeUnicodeEscapes(str) {
27
+ if (typeof str !== 'string') {
28
+ return str;
29
+ }
30
+
31
+ return str.replace(/\\u([0-9a-fA-F]{4})/g, (match, hex) => {
32
+ return String.fromCharCode(parseInt(hex, 16));
33
+ });
34
+ }
19
35
 
20
36
  function getPropertiesAsJSON(filePath) {
21
37
  try {
@@ -31,9 +47,11 @@ function getPropertiesAsJSON(filePath) {
31
47
 
32
48
  const ind = line.indexOf('=');
33
49
  const key = line.slice(0, ind).replace(/\\ /g, ' ');
34
- const value = line.slice(ind + 1);
50
+ let value = line.slice(ind + 1);
35
51
 
36
52
  if (key && value) {
53
+ // Decode Unicode escapes in the value
54
+ value = decodeUnicodeEscapes(value);
37
55
  i18nObj[key] = value;
38
56
  }
39
57
  }, {});
@@ -12,8 +12,10 @@ const {
12
12
  } = require('./loaderConfigs/i18nIdReplaceLoaderConfig');
13
13
 
14
14
  function jsLoaders(options) {
15
- const useLoaders = [];
16
- useLoaders.push((0, _babelLoaderConfig.babelLoaderConfig)(options));
15
+ const useLoaders = []; // Always add babel loader first
16
+
17
+ useLoaders.push((0, _babelLoaderConfig.babelLoaderConfig)(options)); // Add i18n ID replace loader if numeric indexing is enabled
18
+
17
19
  const shouldUseNumericIndexing = options.i18nIndexing && options.i18nIndexing.enable || options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing;
18
20
 
19
21
  if (shouldUseNumericIndexing) {
@@ -23,13 +25,16 @@ function jsLoaders(options) {
23
25
  if (loaderConfig) {
24
26
  useLoaders.push(loaderConfig);
25
27
  }
26
- } catch (err) {// Silently skip if configuration fails
28
+ } catch (err) {
29
+ // Silently skip if configuration fails
30
+ console.warn('[jsLoaders] Failed to configure i18n ID replace loader:', err.message);
27
31
  }
28
32
  }
29
33
 
30
34
  return [{
31
35
  test: /\.js$/,
32
36
  exclude: /node_modules/,
33
- use: useLoaders
37
+ use: useLoaders // include: path.join(appPath, folder)
38
+
34
39
  }];
35
40
  }
@@ -3,9 +3,13 @@
3
3
  const {
4
4
  getPropertiesAsJSON
5
5
  } = require('../custom_plugins/I18nSplitPlugin/utils/propertiesUtils');
6
+ /**
7
+ * Load i18n data from JSResources file once for all chunks
8
+ */
9
+
6
10
 
7
11
  function loadJSResourcesOnce(options) {
8
- let jsResourcePath;
12
+ let jsResourcePath; // Determine the JSResource path based on configuration
9
13
 
10
14
  if (options.i18nIndexing && options.i18nIndexing.enable) {
11
15
  jsResourcePath = options.i18nIndexing.jsResourcePath;
@@ -32,9 +36,13 @@ function loadJSResourcesOnce(options) {
32
36
  throw new Error(`Error reading JSResource file ${jsResourcePath}: ${err.message}`);
33
37
  }
34
38
  }
39
+ /**
40
+ * Configure the i18n ID replace loader
41
+ */
42
+
35
43
 
36
44
  function i18nIdReplaceLoaderConfig(options, webpackContext) {
37
- let numericMapPath;
45
+ let numericMapPath; // Determine the numeric map path based on configuration
38
46
 
39
47
  if (options.i18nIndexing && options.i18nIndexing.enable) {
40
48
  numericMapPath = options.i18nIndexing.numericMapPath;
@@ -46,7 +54,8 @@ function i18nIdReplaceLoaderConfig(options, webpackContext) {
46
54
 
47
55
  if (!numericMapPath) {
48
56
  throw new Error('numericMapPath is required in i18nIndexing or i18nChunkSplit config');
49
- }
57
+ } // Load all i18n data for key detection
58
+
50
59
 
51
60
  const allI18nData = loadJSResourcesOnce(options);
52
61