@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.
- package/README.md +0 -204
- package/docs/I18N_NUMERIC_INDEXING_PLUGIN.md +225 -0
- package/lib/schemas/defaultConfigValues.js +9 -12
- package/lib/schemas/defaultConfigValuesOnly.js +1 -18
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +124 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +210 -143
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js +47 -12
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js +19 -1
- package/lib/shared/bundler/webpack/jsLoaders.js +9 -4
- package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +12 -3
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +28 -14
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nIndexingPlugin.js +42 -0
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericHtmlInjector.js +92 -0
- package/lib/shared/bundler/webpack/plugins.js +6 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/README_backup.md +0 -202
- package/init/README.md +0 -170
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin.js +0 -49
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/collectAstKeys.js +0 -96
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +0 -84
- package/lib/shared/bundler/webpack/utils/propertiesParser.js +0 -81
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js
CHANGED
|
@@ -1,186 +1,253 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
sources,
|
|
7
|
-
Compilation,
|
|
8
|
-
util
|
|
9
|
-
} = require('webpack');
|
|
8
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
decodeUnicodeEscapes
|
|
13
|
-
} = require('../../utils/propertiesParser');
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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 =
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90
|
+
processI18nFiles(compilation) {
|
|
57
91
|
const {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
75
|
-
const contentHash = this.generateContentHash(fileContent, compilation);
|
|
120
|
+
const jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath); // Parse custom groups from banner markers
|
|
76
121
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
161
|
-
// Determine manifest path
|
|
162
|
-
let manifestPath;
|
|
158
|
+
const belongsToGroup = this.getKeyGroup(key);
|
|
163
159
|
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
manifestPath = this.options.manifestPath;
|
|
160
|
+
if (belongsToGroup) {
|
|
161
|
+
groupData[belongsToGroup][numericKey] = value;
|
|
167
162
|
} else {
|
|
168
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
I18nNumericIndexPlugin
|
|
186
|
-
};
|
|
253
|
+
exports.default = I18nNumericIndexPlugin;
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js
CHANGED
|
@@ -5,13 +5,16 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const {
|
|
8
|
-
|
|
9
|
-
} = require('
|
|
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
|
|
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
|
-
|
|
30
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
|
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
|
|