webpack-easyi18n 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +73 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack-easyi18n",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Go from gettext catalog (.po files) to embeded localization in your Webpack bundles",
5
5
  "engines": {
6
6
  "node": ">=4.3.0 <5.0.0 || >=5.10"
package/src/index.js CHANGED
@@ -39,6 +39,73 @@ class EasyI18nPlugin {
39
39
  }
40
40
  };
41
41
 
42
+ /**
43
+ * Decode (a small subset of) JavaScript-style escape sequences from *bundle text* into
44
+ * their real runtime characters.
45
+ *
46
+ * Why this exists:
47
+ * - When targeting older environments, Babel/minifiers sometimes emit non-ASCII
48
+ * characters using unicode escapes, e.g. "don\u2019t" instead of "don’t".
49
+ * - If we then call escapeNuggets(), it will escape backslashes, turning "\u2019" into
50
+ * "\\u2019".
51
+ * - In the final JS bundle, "\\u2019" is *not* a unicode escape anymore; it becomes a
52
+ * literal backslash-u sequence and the browser renders "don\u2019t".
53
+ *
54
+ * Example:
55
+ * - Bundle text contains: "don\u2019t"
56
+ * - Without decode: escapeNuggets => "don\\u2019t" (renders as don\u2019t)
57
+ * - With decode first: unescapeJsLike => "don’t"; then escapeNuggets keeps it as don’t
58
+ *
59
+ * Notes:
60
+ * - This intentionally decodes only "\uXXXX" and "\xXX" sequences.
61
+ * - It avoids decoding when the backslash itself is escaped (e.g. "\\u2019"), because
62
+ * that usually means the author intended a literal "\u2019" to be displayed.
63
+ */
64
+ const unescapeJsLike = (value) => {
65
+ if (typeof value !== 'string') return value;
66
+
67
+ const isHex = (c) => (c >= '0' && c <= '9')
68
+ || (c >= 'a' && c <= 'f')
69
+ || (c >= 'A' && c <= 'F');
70
+
71
+ let out = '';
72
+ for (let i = 0; i < value.length; i++) {
73
+ const ch = value[i];
74
+ if (ch !== '\\') {
75
+ out += ch;
76
+ continue;
77
+ }
78
+
79
+ // If we have an escaped backslash ("\\u...." in text), do not decode.
80
+ if (i > 0 && value[i - 1] === '\\') {
81
+ out += ch;
82
+ continue;
83
+ }
84
+
85
+ const next = value[i + 1];
86
+ if (next === 'u') {
87
+ const a = value[i + 2], b = value[i + 3], c = value[i + 4], d = value[i + 5];
88
+ if (isHex(a) && isHex(b) && isHex(c) && isHex(d)) {
89
+ out += String.fromCharCode(parseInt(`${a}${b}${c}${d}`, 16));
90
+ i += 5;
91
+ continue;
92
+ }
93
+ } else if (next === 'x') {
94
+ const a = value[i + 2], b = value[i + 3];
95
+ if (isHex(a) && isHex(b)) {
96
+ out += String.fromCharCode(parseInt(`${a}${b}`, 16));
97
+ i += 3;
98
+ continue;
99
+ }
100
+ }
101
+
102
+ // Not a recognized escape; keep the backslash.
103
+ out += ch;
104
+ }
105
+
106
+ return out;
107
+ };
108
+
42
109
  compiler.hooks.thisCompilation.tap('EasyI18nPlugin', (compilation) => {
43
110
  compilation.hooks.processAssets.tapPromise(
44
111
  {
@@ -100,10 +167,14 @@ class EasyI18nPlugin {
100
167
  }
101
168
  } else {
102
169
  // .po files use \n notation for line breaks
103
- const translationKey = nuggetSyntaxRemoved.replace('\r\n', '\n');
170
+ const translationKeyRaw = nuggetSyntaxRemoved.replace(/\r\n/g, '\n');
171
+ const translationKey = unescapeJsLike(translationKeyRaw);
104
172
 
105
173
  // find this nugget in the locale's array of translations
106
174
  replacement = translationLookup[translationKey];
175
+ if (typeof (replacement) === "undefined") {
176
+ replacement = translationLookup[translationKeyRaw];
177
+ }
107
178
  if (typeof (replacement) === "undefined" || replacement === "") {
108
179
  if (this.options.warnOnMissingTranslations) {
109
180
  compilation.warnings.push(
@@ -119,7 +190,7 @@ class EasyI18nPlugin {
119
190
  }
120
191
 
121
192
  // Escape the translated text BEFORE formatting/splicing
122
- replacement = EasyI18nPlugin.escapeNuggets(replacement);
193
+ replacement = EasyI18nPlugin.escapeNuggets(unescapeJsLike(replacement));
123
194
 
124
195
  // format nuggets
125
196
  var formatItemsMatch = originalText.match(/\|\|\|(.+?)(?:\/\/\/.+?)?\]\]\]/s)