@zohodesk/client_build_tool 0.0.18 → 0.0.19-i18n-exp.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +34 -0
- package/lib/schemas/defaultConfigValues.js +21 -0
- package/lib/schemas/defaultConfigValuesOnly.js +18 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +195 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js +115 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js +5 -0
- package/lib/shared/bundler/webpack/jsLoaders.js +11 -3
- package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +62 -0
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +116 -0
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +75 -0
- package/lib/shared/bundler/webpack/plugins.js +3 -2
- package/lib/shared/bundler/webpack/utils/propertiesParser.js +36 -0
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(cat:*)",
|
|
5
|
+
"Bash(ls:*)",
|
|
6
|
+
"Bash(npm root:*)",
|
|
7
|
+
"Bash(grep:*)",
|
|
8
|
+
"Bash(source:*)",
|
|
9
|
+
"Bash(echo $CLAUDE_CODE_SUBAGENT_MODEL)",
|
|
10
|
+
"Bash(npm run devMode:*)",
|
|
11
|
+
"Bash(npm run start:*)",
|
|
12
|
+
"Bash(npm run build:*)",
|
|
13
|
+
"Bash(lsof:*)",
|
|
14
|
+
"Bash(xargs kill:*)",
|
|
15
|
+
"Bash(pkill:*)",
|
|
16
|
+
"Bash(node:*)",
|
|
17
|
+
"Bash(git -C /Users/ramaraj-14019/i18nIndexing_plugin/packages/client_build_tool status --short)",
|
|
18
|
+
"Bash(git -C /Users/ramaraj-14019/zoho_iM/zohoim_client/jsapps/imclient status --short)",
|
|
19
|
+
"Bash(git -C /Users/ramaraj-14019/zoho_iM/zohoim_client/jsapps/imclient diff --name-only)",
|
|
20
|
+
"Bash(git -C /Users/ramaraj-14019/zoho_iM/zohoim_client/jsapps/imclient status numericMap.json)",
|
|
21
|
+
"Bash(git -C /Users/ramaraj-14019/i18nIndexing_plugin/packages/client_build_tool status src/shared/bundler/webpack/utils/)",
|
|
22
|
+
"Bash(git -C /Users/ramaraj-14019/i18nIndexing_plugin/packages/client_build_tool show 3.0.0:src/shared/bundler/webpack/utils/)",
|
|
23
|
+
"Bash(git -C /Users/ramaraj-14019/i18nIndexing_plugin/packages/client_build_tool ls-tree:*)",
|
|
24
|
+
"Bash(git -C /Users/ramaraj-14019/i18nIndexing_plugin/packages/client_build_tool show 3.0.0:src/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js)",
|
|
25
|
+
"Bash(curl:*)",
|
|
26
|
+
"Skill(ralph-loop:ralph-loop)",
|
|
27
|
+
"Bash(kill:*)",
|
|
28
|
+
"Bash(npm install:*)",
|
|
29
|
+
"Bash(npm run createEfc:*)",
|
|
30
|
+
"Bash(npm run i18n-sync:*)",
|
|
31
|
+
"Bash(git push:*)"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -179,6 +179,27 @@ var _default = {
|
|
|
179
179
|
jsResource: null,
|
|
180
180
|
propertiesFolder: null
|
|
181
181
|
},
|
|
182
|
+
i18nIndexing: {
|
|
183
|
+
enable: {
|
|
184
|
+
value: false,
|
|
185
|
+
cli: 'i18n_indexing'
|
|
186
|
+
},
|
|
187
|
+
jsResourcePath: null,
|
|
188
|
+
propertiesFolderPath: null,
|
|
189
|
+
numericMapPath: null,
|
|
190
|
+
outputFolder: 'i18n-chunk',
|
|
191
|
+
numericFilenameTemplate: 'i18n-chunk/[locale]/numeric.i18n.js',
|
|
192
|
+
dynamicFilenameTemplate: 'i18n-chunk/[locale]/dynamic.i18n.js',
|
|
193
|
+
singleFile: false,
|
|
194
|
+
singleFileTemplate: '[locale].js',
|
|
195
|
+
jsonpFunc: 'window.loadI18nChunk',
|
|
196
|
+
htmlTemplateLabel: '{{--user-locale}}',
|
|
197
|
+
localeVarName: 'window.userLangCode',
|
|
198
|
+
includeContentHash: false,
|
|
199
|
+
emitFiles: true,
|
|
200
|
+
generateManifest: false,
|
|
201
|
+
manifestPath: null
|
|
202
|
+
},
|
|
182
203
|
publicFolders: {
|
|
183
204
|
dev: ['...'],
|
|
184
205
|
prod: ['...']
|
|
@@ -96,6 +96,24 @@ var _default = {
|
|
|
96
96
|
jsResource: null,
|
|
97
97
|
propertiesFolder: null
|
|
98
98
|
},
|
|
99
|
+
i18nIndexing: {
|
|
100
|
+
enable: false,
|
|
101
|
+
jsResourcePath: null,
|
|
102
|
+
propertiesFolderPath: null,
|
|
103
|
+
numericMapPath: null,
|
|
104
|
+
outputFolder: 'i18n-chunk',
|
|
105
|
+
numericFilenameTemplate: 'i18n-chunk/[locale]/numeric.i18n.js',
|
|
106
|
+
dynamicFilenameTemplate: 'i18n-chunk/[locale]/dynamic.i18n.js',
|
|
107
|
+
singleFile: false,
|
|
108
|
+
singleFileTemplate: '[locale].js',
|
|
109
|
+
jsonpFunc: 'window.loadI18nChunk',
|
|
110
|
+
htmlTemplateLabel: '{{--user-locale}}',
|
|
111
|
+
localeVarName: 'window.userLangCode',
|
|
112
|
+
includeContentHash: false,
|
|
113
|
+
emitFiles: true,
|
|
114
|
+
generateManifest: false,
|
|
115
|
+
manifestPath: null
|
|
116
|
+
},
|
|
99
117
|
publicFolders: {
|
|
100
118
|
dev: ['...'],
|
|
101
119
|
prod: ['...']
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
sources,
|
|
7
|
+
Compilation
|
|
8
|
+
} = require('webpack');
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
decodeUnicodeEscapes
|
|
12
|
+
} = require('../I18nSplitPlugin/utils/propertiesUtils');
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
loadNumericMap,
|
|
16
|
+
loadI18nData
|
|
17
|
+
} = require('./utils/i18nDataLoader');
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
createHash
|
|
21
|
+
} = require('../I18nSplitPlugin/createHash');
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
pathCreator
|
|
25
|
+
} = require('../I18nSplitPlugin/pathCreator');
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
RawSource
|
|
29
|
+
} = sources;
|
|
30
|
+
const pluginName = 'I18nNumericIndexPlugin';
|
|
31
|
+
|
|
32
|
+
class I18nNumericIndexPlugin {
|
|
33
|
+
constructor(options) {
|
|
34
|
+
if (!options) {
|
|
35
|
+
throw new Error('[I18nNumericIndexPlugin] options is required');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.options = options;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
emitChunk(compilation, filename, locale, data, manifest) {
|
|
42
|
+
const content = decodeUnicodeEscapes(JSON.stringify(data));
|
|
43
|
+
const fileContent = `${this.options.jsonpFunc}(${content});`;
|
|
44
|
+
const contentHash = createHash({
|
|
45
|
+
outputOptions: compilation.outputOptions,
|
|
46
|
+
content: fileContent
|
|
47
|
+
});
|
|
48
|
+
let outputPath = pathCreator(filename, compilation, {
|
|
49
|
+
hash: compilation.hash,
|
|
50
|
+
locale,
|
|
51
|
+
chunkName: null,
|
|
52
|
+
chunkId: locale,
|
|
53
|
+
chunkHash: null,
|
|
54
|
+
contentHash
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (this.options.includeContentHash && !filename.includes('[contenthash]')) {
|
|
58
|
+
outputPath = outputPath.replace(/\.js$/, `.${contentHash}.js`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this.options.generateManifest) {
|
|
62
|
+
manifest[locale] = outputPath.split('/').pop();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.options.emitFiles) {
|
|
66
|
+
compilation.emitAsset(outputPath, new RawSource(fileContent));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return outputPath;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
buildNumericData(sortedKeys, totalKeys, jsResourceBase, localeData, englishData) {
|
|
73
|
+
const numericData = {};
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < totalKeys; i++) {
|
|
76
|
+
const key = sortedKeys[i];
|
|
77
|
+
|
|
78
|
+
if (key && jsResourceBase[key] !== undefined) {
|
|
79
|
+
numericData[i] = localeData[key] ?? englishData[key];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return numericData;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
buildDynamicData(numericKeysSet, jsResourceBase, localeData, englishData) {
|
|
87
|
+
const dynamicData = {};
|
|
88
|
+
|
|
89
|
+
for (const key of Object.keys(jsResourceBase)) {
|
|
90
|
+
if (!numericKeysSet.has(key)) {
|
|
91
|
+
dynamicData[key] = localeData[key] ?? englishData[key];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return dynamicData;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
processLocaleData(compilation, locale, {
|
|
99
|
+
sortedKeys,
|
|
100
|
+
totalKeys,
|
|
101
|
+
numericKeysSet,
|
|
102
|
+
jsResourceBase,
|
|
103
|
+
allI18n,
|
|
104
|
+
englishData,
|
|
105
|
+
manifest
|
|
106
|
+
}) {
|
|
107
|
+
const localeData = allI18n[locale] || {};
|
|
108
|
+
const numericData = this.buildNumericData(sortedKeys, totalKeys, jsResourceBase, localeData, englishData);
|
|
109
|
+
const dynamicData = this.buildDynamicData(numericKeysSet, jsResourceBase, localeData, englishData);
|
|
110
|
+
|
|
111
|
+
if (this.options.singleFile) {
|
|
112
|
+
const combinedData = { ...numericData,
|
|
113
|
+
...dynamicData
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (Object.keys(combinedData).length > 0) {
|
|
117
|
+
const filename = `${this.options.outputFolder}/${this.options.singleFileTemplate}`;
|
|
118
|
+
this.emitChunk(compilation, filename, locale, combinedData, manifest);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
if (Object.keys(numericData).length > 0) {
|
|
122
|
+
this.emitChunk(compilation, this.options.numericFilenameTemplate, locale, numericData, manifest);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (Object.keys(dynamicData).length > 0) {
|
|
126
|
+
this.emitChunk(compilation, this.options.dynamicFilenameTemplate, locale, dynamicData, manifest);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
emitManifest(compilation, manifest) {
|
|
132
|
+
if (!this.options.generateManifest || Object.keys(manifest).length === 0) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const manifestContent = JSON.stringify(manifest, null, 2);
|
|
137
|
+
|
|
138
|
+
if (this.options.emitFiles) {
|
|
139
|
+
compilation.emitAsset(this.options.manifestPath, new RawSource(manifestContent));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
processI18nAssets(compilation) {
|
|
144
|
+
const mapPath = path.resolve(compilation.compiler.context, this.options.numericMapPath);
|
|
145
|
+
const {
|
|
146
|
+
sortedKeys,
|
|
147
|
+
totalKeys
|
|
148
|
+
} = loadNumericMap(mapPath, compilation);
|
|
149
|
+
const {
|
|
150
|
+
jsResourceBase,
|
|
151
|
+
allI18n,
|
|
152
|
+
locales
|
|
153
|
+
} = loadI18nData(this.options, compilation);
|
|
154
|
+
if (!locales.length) return;
|
|
155
|
+
const manifest = {};
|
|
156
|
+
const numericKeysSet = new Set(sortedKeys);
|
|
157
|
+
const englishData = allI18n.en_US || jsResourceBase;
|
|
158
|
+
const context = {
|
|
159
|
+
sortedKeys,
|
|
160
|
+
totalKeys,
|
|
161
|
+
numericKeysSet,
|
|
162
|
+
jsResourceBase,
|
|
163
|
+
allI18n,
|
|
164
|
+
englishData,
|
|
165
|
+
manifest
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
for (const locale of locales) {
|
|
169
|
+
this.processLocaleData(compilation, locale, context);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.emitManifest(compilation, manifest);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
apply(compiler) {
|
|
176
|
+
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
177
|
+
compilation.hooks.processAssets.tapAsync({
|
|
178
|
+
name: pluginName,
|
|
179
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
|
|
180
|
+
}, (_assets, callback) => {
|
|
181
|
+
if (!this.options.enable) {
|
|
182
|
+
return callback();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.processI18nAssets(compilation);
|
|
186
|
+
callback();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
I18nNumericIndexPlugin
|
|
195
|
+
};
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
parseProperties
|
|
9
|
+
} = require('../../../utils/propertiesParser');
|
|
10
|
+
|
|
11
|
+
function loadPropertiesFile(filePath, compilation, description) {
|
|
12
|
+
try {
|
|
13
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
+
return parseProperties(content);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
if (compilation) {
|
|
17
|
+
compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading ${description}: ${err.message}`));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadNumericMap(numericMapPath, compilation) {
|
|
25
|
+
try {
|
|
26
|
+
const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
|
|
27
|
+
const parsedData = JSON.parse(fileContent);
|
|
28
|
+
const numericIds = Object.values(parsedData.originalKeyToNumericId);
|
|
29
|
+
const maxId = numericIds.length > 0 ? Math.max(...numericIds) : -1;
|
|
30
|
+
const totalKeys = maxId + 1;
|
|
31
|
+
const sortedKeys = new Array(totalKeys);
|
|
32
|
+
Object.entries(parsedData.originalKeyToNumericId).forEach(([key, id]) => {
|
|
33
|
+
sortedKeys[id] = key;
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
sortedKeys,
|
|
37
|
+
totalKeys
|
|
38
|
+
};
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (compilation) {
|
|
41
|
+
compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading numeric map: ${err.message}`));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
sortedKeys: [],
|
|
46
|
+
totalKeys: 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function loadAllLocaleFiles(propertiesPath, baseFileName, compilation) {
|
|
52
|
+
const allI18n = {};
|
|
53
|
+
const locales = [];
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const files = fs.readdirSync(propertiesPath);
|
|
57
|
+
files.forEach(file => {
|
|
58
|
+
if (file === baseFileName + '.properties') {
|
|
59
|
+
const filePath = path.join(propertiesPath, file);
|
|
60
|
+
const baseData = loadPropertiesFile(filePath, compilation, 'JSResources base');
|
|
61
|
+
allI18n['en_US'] = baseData;
|
|
62
|
+
locales.push('en_US');
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
files.forEach(file => {
|
|
66
|
+
if (!file.endsWith('.properties')) return;
|
|
67
|
+
const match = file.match(/^ApplicationResources_([a-z]{2}_[A-Z]{2})\.properties$/);
|
|
68
|
+
|
|
69
|
+
if (match) {
|
|
70
|
+
const locale = match[1];
|
|
71
|
+
const filePath = path.join(propertiesPath, file);
|
|
72
|
+
const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`);
|
|
73
|
+
allI18n[locale] = { ...allI18n['en_US'],
|
|
74
|
+
...localeData
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (!locales.includes(locale)) {
|
|
78
|
+
locales.push(locale);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (compilation) {
|
|
84
|
+
compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error reading properties folder: ${err.message}`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
allI18n,
|
|
90
|
+
locales
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function loadI18nData(options, compilation) {
|
|
95
|
+
const jsResourcePath = path.resolve(compilation.compiler.context, options.jsResourcePath);
|
|
96
|
+
const propertiesPath = path.resolve(compilation.compiler.context, options.propertiesFolderPath);
|
|
97
|
+
const baseFileName = path.basename(options.jsResourcePath, '.properties');
|
|
98
|
+
const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources');
|
|
99
|
+
const {
|
|
100
|
+
allI18n,
|
|
101
|
+
locales
|
|
102
|
+
} = loadAllLocaleFiles(propertiesPath, baseFileName, compilation);
|
|
103
|
+
return {
|
|
104
|
+
jsResourceBase,
|
|
105
|
+
allI18n,
|
|
106
|
+
locales
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
loadPropertiesFile,
|
|
112
|
+
loadNumericMap,
|
|
113
|
+
loadAllLocaleFiles,
|
|
114
|
+
loadI18nData
|
|
115
|
+
};
|
|
@@ -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;
|
|
@@ -13,6 +14,10 @@ var _path = require("path");
|
|
|
13
14
|
|
|
14
15
|
var _constants = require("../../../../../constants");
|
|
15
16
|
|
|
17
|
+
function decodeUnicodeEscapes(str) {
|
|
18
|
+
return str.replace(/\\u([0-9A-Fa-f]{4})/g, (_, code) => String.fromCharCode(parseInt(code, 16)));
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
function isComment(line) {
|
|
17
22
|
return line[0] === '#';
|
|
18
23
|
}
|
|
@@ -5,13 +5,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.jsLoaders = jsLoaders;
|
|
7
7
|
|
|
8
|
-
var _babelLoaderConfig = require("./loaderConfigs/babelLoaderConfig");
|
|
8
|
+
var _babelLoaderConfig = require("./loaderConfigs/babelLoaderConfig.js");
|
|
9
|
+
|
|
10
|
+
var _i18nIdReplaceLoaderConfig = require("./loaderConfigs/i18nIdReplaceLoaderConfig.js");
|
|
9
11
|
|
|
10
12
|
function jsLoaders(options) {
|
|
13
|
+
const useLoaders = [(0, _babelLoaderConfig.babelLoaderConfig)(options)];
|
|
14
|
+
const shouldUseNumericIndexing = options.i18nIndexing?.enable || options.i18nChunkSplit?.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing;
|
|
15
|
+
|
|
16
|
+
if (shouldUseNumericIndexing) {
|
|
17
|
+
useLoaders.push((0, _i18nIdReplaceLoaderConfig.i18nIdReplaceLoaderConfig)(options));
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
return [{
|
|
12
21
|
test: /\.js$/,
|
|
13
22
|
exclude: /node_modules/,
|
|
14
|
-
use:
|
|
15
|
-
|
|
23
|
+
use: useLoaders
|
|
16
24
|
}];
|
|
17
25
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.i18nIdReplaceLoaderConfig = i18nIdReplaceLoaderConfig;
|
|
7
|
+
|
|
8
|
+
var _propertiesUtils = require("../custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js");
|
|
9
|
+
|
|
10
|
+
function getI18nConfig(options) {
|
|
11
|
+
if (options.i18nIndexing?.enable) {
|
|
12
|
+
return {
|
|
13
|
+
jsResourcePath: options.i18nIndexing.jsResourcePath,
|
|
14
|
+
numericMapPath: options.i18nIndexing.numericMapPath
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (options.i18nChunkSplit?.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
|
|
19
|
+
return {
|
|
20
|
+
jsResourcePath: options.i18nChunkSplit.jsResource,
|
|
21
|
+
numericMapPath: options.i18nChunkSplit.numericMapPath
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadJSResources(jsResourcePath) {
|
|
29
|
+
const i18nData = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
|
|
30
|
+
|
|
31
|
+
if (Object.keys(i18nData).length === 0) {
|
|
32
|
+
console.warn(`[i18nIdReplaceLoaderConfig] Warning: No i18n data found in JSResource file: ${jsResourcePath}`);
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return i18nData;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function i18nIdReplaceLoaderConfig(options) {
|
|
40
|
+
const config = getI18nConfig(options);
|
|
41
|
+
|
|
42
|
+
if (!config) {
|
|
43
|
+
throw new Error('i18nIdReplaceLoader requires either i18nIndexing to be enabled or i18nChunkSplit with useNumericIndexing');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!config.jsResourcePath) {
|
|
47
|
+
throw new Error('Missing required jsResourcePath in i18n options');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!config.numericMapPath) {
|
|
51
|
+
throw new Error('numericMapPath is required in i18nIndexing or i18nChunkSplit config');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
loader: require.resolve('../loaders/i18nIdReplaceLoader.js'),
|
|
56
|
+
options: {
|
|
57
|
+
allI18nData: loadJSResources(config.jsResourcePath),
|
|
58
|
+
numericMapPath: config.numericMapPath,
|
|
59
|
+
sourceMaps: false
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const parser = require('@babel/parser');
|
|
6
|
+
|
|
7
|
+
const traverse = require('@babel/traverse').default;
|
|
8
|
+
|
|
9
|
+
const generator = require('@babel/generator').default;
|
|
10
|
+
|
|
11
|
+
const t = require('@babel/types');
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
getOptions
|
|
15
|
+
} = require('loader-utils');
|
|
16
|
+
|
|
17
|
+
function loadNumericMap(numericMapPath) {
|
|
18
|
+
if (!numericMapPath) {
|
|
19
|
+
console.warn('[i18nIdReplaceLoader] numericMapPath option is missing');
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(numericMapPath)) {
|
|
24
|
+
console.warn(`[i18nIdReplaceLoader] Numeric map file not found: ${numericMapPath}`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
|
|
30
|
+
const parsedData = JSON.parse(fileContent);
|
|
31
|
+
|
|
32
|
+
if (!parsedData?.originalKeyToNumericId) {
|
|
33
|
+
console.warn(`[i18nIdReplaceLoader] Invalid numeric map format: missing 'originalKeyToNumericId'`);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return parsedData.originalKeyToNumericId;
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.warn(`[i18nIdReplaceLoader] Error reading numeric map: ${err.message}`);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function transformSource(source, resourcePath, numericIdMap, allI18nData, options) {
|
|
45
|
+
const ast = parser.parse(source, {
|
|
46
|
+
sourceType: 'module',
|
|
47
|
+
plugins: ['jsx', 'typescript', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
|
|
48
|
+
sourceFilename: resourcePath
|
|
49
|
+
});
|
|
50
|
+
let hasTransformations = false;
|
|
51
|
+
traverse(ast, {
|
|
52
|
+
StringLiteral(path) {
|
|
53
|
+
const {
|
|
54
|
+
node
|
|
55
|
+
} = path;
|
|
56
|
+
|
|
57
|
+
if (!(node.value in allI18nData)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (node.value in numericIdMap) {
|
|
62
|
+
path.replaceWith(t.stringLiteral(String(numericIdMap[node.value])));
|
|
63
|
+
hasTransformations = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!hasTransformations) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return generator(ast, {
|
|
74
|
+
sourceMaps: !!options.sourceMaps,
|
|
75
|
+
sourceFileName: resourcePath,
|
|
76
|
+
retainLines: false,
|
|
77
|
+
comments: true
|
|
78
|
+
}, source);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = function i18nIdReplaceLoader(source, map) {
|
|
82
|
+
const resourcePath = this.resourcePath;
|
|
83
|
+
if (this.cacheable) this.cacheable();
|
|
84
|
+
const options = getOptions(this) || {};
|
|
85
|
+
const callback = this.async();
|
|
86
|
+
|
|
87
|
+
if (resourcePath.includes('node_modules') || !resourcePath.includes('src')) {
|
|
88
|
+
return callback(null, source, map);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!options.allI18nData) {
|
|
92
|
+
return callback(null, source, map);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (options.numericMapPath) {
|
|
96
|
+
this.addDependency(options.numericMapPath);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const numericIdMap = loadNumericMap(options.numericMapPath);
|
|
100
|
+
|
|
101
|
+
if (!numericIdMap) {
|
|
102
|
+
return callback(null, source, map);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const output = transformSource(source, resourcePath, numericIdMap, options.allI18nData, options);
|
|
107
|
+
|
|
108
|
+
if (output) {
|
|
109
|
+
callback(null, output.code, output.map);
|
|
110
|
+
} else {
|
|
111
|
+
callback(null, source, map);
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
callback(err);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.configI18nNumericIndexPlugin = configI18nNumericIndexPlugin;
|
|
7
|
+
|
|
8
|
+
var _I18nNumericIndexPlugin = require("../custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js");
|
|
9
|
+
|
|
10
|
+
var _readI18nValues = require("../custom_plugins/I18nSplitPlugin/readI18nValues.js");
|
|
11
|
+
|
|
12
|
+
function configI18nNumericIndexPlugin(options) {
|
|
13
|
+
if (!options.i18nIndexing?.enable) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const i18nOpts = options.i18nIndexing;
|
|
18
|
+
const requiredOptions = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath', 'jsonpFunc', 'localeVarName'];
|
|
19
|
+
const missingOptions = requiredOptions.filter(opt => !i18nOpts[opt]);
|
|
20
|
+
|
|
21
|
+
if (missingOptions.length > 0) {
|
|
22
|
+
console.warn(`[configI18nNumericIndexPlugin] Missing required options: ${missingOptions.join(', ')}`);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (i18nOpts.singleFile) {
|
|
27
|
+
if (!i18nOpts.singleFileTemplate) {
|
|
28
|
+
console.warn('[configI18nNumericIndexPlugin] singleFileTemplate is required when singleFile is true');
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
if (!i18nOpts.numericFilenameTemplate || !i18nOpts.numericFilenameTemplate.includes('[locale]')) {
|
|
33
|
+
console.warn('[configI18nNumericIndexPlugin] numericFilenameTemplate must include [locale] placeholder');
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!i18nOpts.dynamicFilenameTemplate || !i18nOpts.dynamicFilenameTemplate.includes('[locale]')) {
|
|
38
|
+
console.warn('[configI18nNumericIndexPlugin] dynamicFilenameTemplate must include [locale] placeholder');
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (i18nOpts.generateManifest && !i18nOpts.manifestPath) {
|
|
44
|
+
console.warn('[configI18nNumericIndexPlugin] manifestPath is required when generateManifest is true');
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const {
|
|
49
|
+
locales,
|
|
50
|
+
allI18nObject
|
|
51
|
+
} = (0, _readI18nValues.readI18nValues)({
|
|
52
|
+
jsResource: i18nOpts.jsResourcePath,
|
|
53
|
+
propertiesFolder: i18nOpts.propertiesFolderPath,
|
|
54
|
+
disableDefault: false
|
|
55
|
+
});
|
|
56
|
+
const numericIndexPluginOptions = {
|
|
57
|
+
enable: i18nOpts.enable,
|
|
58
|
+
jsResourcePath: i18nOpts.jsResourcePath,
|
|
59
|
+
propertiesFolderPath: i18nOpts.propertiesFolderPath,
|
|
60
|
+
numericMapPath: i18nOpts.numericMapPath,
|
|
61
|
+
locales,
|
|
62
|
+
allI18nObject,
|
|
63
|
+
outputFolder: i18nOpts.outputFolder,
|
|
64
|
+
numericFilenameTemplate: i18nOpts.numericFilenameTemplate,
|
|
65
|
+
dynamicFilenameTemplate: i18nOpts.dynamicFilenameTemplate,
|
|
66
|
+
singleFile: i18nOpts.singleFile,
|
|
67
|
+
singleFileTemplate: i18nOpts.singleFileTemplate,
|
|
68
|
+
jsonpFunc: i18nOpts.jsonpFunc,
|
|
69
|
+
includeContentHash: i18nOpts.includeContentHash,
|
|
70
|
+
emitFiles: i18nOpts.emitFiles,
|
|
71
|
+
generateManifest: i18nOpts.generateManifest,
|
|
72
|
+
manifestPath: i18nOpts.manifestPath
|
|
73
|
+
};
|
|
74
|
+
return new _I18nNumericIndexPlugin.I18nNumericIndexPlugin(numericIndexPluginOptions);
|
|
75
|
+
}
|
|
@@ -57,10 +57,11 @@ var _configInjectChunkGraphPlugin = require("./pluginConfigs/configInjectChunkGr
|
|
|
57
57
|
|
|
58
58
|
var _configCssCustomOrderPlugin = require("./pluginConfigs/configCssCustomOrderPlugin");
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
var _configI18nNumericIndexPlugin = require("./pluginConfigs/configI18nNumericIndexPlugin");
|
|
61
|
+
|
|
61
62
|
function plugins(options) {
|
|
62
63
|
const {
|
|
63
64
|
webpackPlugins
|
|
64
65
|
} = options;
|
|
65
|
-
return [(0, _configEnvVariables.configEnvVariables)(options), (0, _configCustomAttributesPlugin.configCustomAttributesPlugin)(options), (0, _configTPHashMappingPlugin.configTPHashMappingPlugin)(options), (0, _configCopyPublicFolders.configCopyPublicFolders)(options), (0, _configIgnorePlugin.configIgnorePlugin)(options), (0, _configMiniCSSExtractPlugin.configMiniCSSExtractPlugin)(options), (0, _configSelectorWeightPlugin.configSelectorWeightPlugin)(options), (0, _configVariableConversionPlugin.configVariableConversionPlugin)(options), (0, _configI18nSplitPlugin.configI18nSplitPlugin)(options), (0, _configRtlCssPlugin.configRtlCssPlugin)(options), (0, _configHtmlWebpackPlugin.configHtmlWebpackPlugin)(options), (0, _configCustomScriptLoadingStrategyPlugin.configCustomScriptLoadingStrategyPlugin)(options), (0, _configCdnChangePlugin.configCdnChangePlugin)(options), (0, _configServiceWorkerPlugin.configServiceWorkerPlugin)(options), (0, _configEFCTemplatePlugin.configEFCTemplatePlugin)(options), (0, _configResourceHintsPlugin.configResourceHintsPlugin)(options), (0, _configBundleAnalyzer.configBundleAnalyzer)(options), (0, _configManifestJsonPlugin.configManifestJsonPlugin)(options), (0, _configSourceMapPlugin.configSourceMapPlugin)(options), (0, _configProgressPlugin.configProgressPlugin)(options), (0, _configBundleIntegrityReport.configBundleIntegrityReport)(options), (0, _configRuntimeResourceCleanup.configRuntimeResourceCleanup)(options), (0, _configMurphyInjectorPlugin.configMurphyInjectorPlugin)(options), (0, _configCssCustomOrderPlugin.configCssCustomOrderPlugin)(options), (0, _configChunkHierarchyPlugin.configChunkHierarchyPlugin)(options), ...webpackPlugins].filter(Boolean);
|
|
66
|
+
return [(0, _configEnvVariables.configEnvVariables)(options), (0, _configCustomAttributesPlugin.configCustomAttributesPlugin)(options), (0, _configTPHashMappingPlugin.configTPHashMappingPlugin)(options), (0, _configCopyPublicFolders.configCopyPublicFolders)(options), (0, _configIgnorePlugin.configIgnorePlugin)(options), (0, _configMiniCSSExtractPlugin.configMiniCSSExtractPlugin)(options), (0, _configSelectorWeightPlugin.configSelectorWeightPlugin)(options), (0, _configVariableConversionPlugin.configVariableConversionPlugin)(options), (0, _configI18nSplitPlugin.configI18nSplitPlugin)(options), (0, _configRtlCssPlugin.configRtlCssPlugin)(options), (0, _configHtmlWebpackPlugin.configHtmlWebpackPlugin)(options), (0, _configI18nNumericIndexPlugin.configI18nNumericIndexPlugin)(options), (0, _configCustomScriptLoadingStrategyPlugin.configCustomScriptLoadingStrategyPlugin)(options), (0, _configCdnChangePlugin.configCdnChangePlugin)(options), (0, _configServiceWorkerPlugin.configServiceWorkerPlugin)(options), (0, _configEFCTemplatePlugin.configEFCTemplatePlugin)(options), (0, _configResourceHintsPlugin.configResourceHintsPlugin)(options), (0, _configBundleAnalyzer.configBundleAnalyzer)(options), (0, _configManifestJsonPlugin.configManifestJsonPlugin)(options), (0, _configSourceMapPlugin.configSourceMapPlugin)(options), (0, _configProgressPlugin.configProgressPlugin)(options), (0, _configBundleIntegrityReport.configBundleIntegrityReport)(options), (0, _configRuntimeResourceCleanup.configRuntimeResourceCleanup)(options), (0, _configMurphyInjectorPlugin.configMurphyInjectorPlugin)(options), (0, _configCssCustomOrderPlugin.configCssCustomOrderPlugin)(options), (0, _configChunkHierarchyPlugin.configChunkHierarchyPlugin)(options), ...webpackPlugins].filter(Boolean);
|
|
66
67
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
function decodeUnicodeEscapes(str) {
|
|
6
|
+
return str.replace(/\\u([0-9A-Fa-f]{4})/g, (_, code) => String.fromCharCode(parseInt(code, 16)));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parseProperties(content) {
|
|
10
|
+
const result = {};
|
|
11
|
+
const lines = content.split(/\r?\n/);
|
|
12
|
+
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
const trimmed = line.trim();
|
|
15
|
+
|
|
16
|
+
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('!')) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const separatorIndex = trimmed.search(/[=:]/);
|
|
21
|
+
if (separatorIndex === -1) continue;
|
|
22
|
+
const key = trimmed.slice(0, separatorIndex).trim().replace(/\\ /g, ' ');
|
|
23
|
+
const value = trimmed.slice(separatorIndex + 1).trim();
|
|
24
|
+
|
|
25
|
+
if (key) {
|
|
26
|
+
result[key] = decodeUnicodeEscapes(value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
decodeUnicodeEscapes,
|
|
35
|
+
parseProperties
|
|
36
|
+
};
|