@atlassian/i18n-properties-loader 2.0.1 → 2.0.2
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/CHANGELOG.md +9 -0
- package/index.js +39 -8
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.0.2](https://bitbucket.org/atlassianlabs/fe-server/branches/compare/@atlassian/i18n-properties-loader@2.0.2..@atlassian/i18n-properties-loader@2.0.1) (2026-05-29)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **@atlassian/i18n-properties-loader:** escape substitutions inside string literals ([6f1ac57](https://bitbucket.org/atlassianlabs/fe-server/commits/6f1ac575a4f7dbc2de1143bc7f77a8592e56698d))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
6
15
|
## [2.0.1](https://bitbucket.org/atlassianlabs/fe-server/branches/compare/@atlassian/i18n-properties-loader@2.0.1..@atlassian/i18n-properties-loader@2.0.0) (2026-05-12)
|
|
7
16
|
|
|
8
17
|
**Note:** Version bump only for package @atlassian/i18n-properties-loader
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-env node */
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const debounce = require('lodash
|
|
3
|
+
const debounce = require('lodash/debounce');
|
|
4
4
|
const { PropertiesFile } = require('java-properties');
|
|
5
5
|
const { getOptions } = require('loader-utils');
|
|
6
6
|
const { validate } = require('schema-utils');
|
|
@@ -97,6 +97,29 @@ const clearCacheWhenIdle = debounce(() => {
|
|
|
97
97
|
i18nCache = new Map();
|
|
98
98
|
}, accessThreshold);
|
|
99
99
|
|
|
100
|
+
// Returns the quote character (' or ") enclosing `offset` in `source`, or null if not in a string literal.
|
|
101
|
+
function getEnclosingStringQuote(source, offset) {
|
|
102
|
+
let openQuote = null; // currently open string delimiter, or null
|
|
103
|
+
let escaped = false;
|
|
104
|
+
for (let i = 0; i < offset; i++) {
|
|
105
|
+
const ch = source[i];
|
|
106
|
+
if (escaped) {
|
|
107
|
+
escaped = false;
|
|
108
|
+
} else if (ch === '\\') {
|
|
109
|
+
escaped = true;
|
|
110
|
+
} else if (openQuote) {
|
|
111
|
+
if (ch === openQuote) openQuote = null;
|
|
112
|
+
} else if (ch === "'" || ch === '"') {
|
|
113
|
+
openQuote = ch;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return openQuote;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function escapeForStringLiteral(value, quote) {
|
|
120
|
+
return value.split(quote).join('\\' + quote);
|
|
121
|
+
}
|
|
122
|
+
|
|
100
123
|
function processI18nFiles(i18nFiles) {
|
|
101
124
|
clearCacheWhenIdle();
|
|
102
125
|
|
|
@@ -152,8 +175,18 @@ module.exports = function (source, map) {
|
|
|
152
175
|
|
|
153
176
|
// Replace "I18n.getText('some.translation.key', [param1], [paramN])" with the real translation string
|
|
154
177
|
const properties = processI18nFiles(i18nFiles);
|
|
155
|
-
const newSource = source.replace(i18nRegExp, (...args) => {
|
|
156
|
-
|
|
178
|
+
const newSource = source.replace(i18nRegExp, (match, ...args) => {
|
|
179
|
+
// replace() callback: (match, ...captures, offset, source, groups)
|
|
180
|
+
const groups = args[args.length - 1];
|
|
181
|
+
const matchSource = args[args.length - 2];
|
|
182
|
+
const offset = args[args.length - 3];
|
|
183
|
+
|
|
184
|
+
// Escape substitutions inside string literals (e.g. babel-plugin-istanbul's
|
|
185
|
+
// embedded inputSourceMap.sourcesContent) to avoid breaking the surrounding
|
|
186
|
+
// literal with unescaped quotes from JSON.stringify(message). See: DCA11Y-3344
|
|
187
|
+
const enclosingQuote = getEnclosingStringQuote(matchSource, offset);
|
|
188
|
+
|
|
189
|
+
const { namespace, key, endParam } = groups;
|
|
157
190
|
const escapedKey = key.replace(/\s/g, '\\ ');
|
|
158
191
|
|
|
159
192
|
let message = properties.get(escapedKey);
|
|
@@ -171,12 +204,10 @@ module.exports = function (source, map) {
|
|
|
171
204
|
// Output string
|
|
172
205
|
const escapedMessage = JSON.stringify(message);
|
|
173
206
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return `${formatFunctionCall}(${escapedMessage},`;
|
|
177
|
-
}
|
|
207
|
+
// For endParam === ',' we don't unquote — WRM.format handles unquoting
|
|
208
|
+
const result = endParam === ',' ? `${formatFunctionCall}(${escapedMessage},` : unquoteMessage(escapedMessage);
|
|
178
209
|
|
|
179
|
-
return
|
|
210
|
+
return enclosingQuote ? escapeForStringLiteral(result, enclosingQuote) : result;
|
|
180
211
|
});
|
|
181
212
|
|
|
182
213
|
callback(null, newSource, map);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlassian/i18n-properties-loader",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A webpack loader for i18n *.properties files that can be used in Atlassian Server products",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"java-properties": "^1.0.2",
|
|
41
41
|
"loader-utils": "2.0.4",
|
|
42
|
-
"lodash
|
|
42
|
+
"lodash": "^4.18.1",
|
|
43
43
|
"schema-utils": "^3.0.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=12"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "d517f4b17c906fc9ea8a1fb5911f454a9ee41429"
|
|
57
57
|
}
|