webpack-easyi18n 0.4.0 → 0.5.1

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 +72 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webpack-easyi18n",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
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,7 +167,7 @@ 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 translationKey = nuggetSyntaxRemoved.replace(/\r\n/g, '\n');
104
171
 
105
172
  // find this nugget in the locale's array of translations
106
173
  replacement = translationLookup[translationKey];
@@ -118,6 +185,9 @@ class EasyI18nPlugin {
118
185
  }
119
186
  }
120
187
 
188
+ // Escape the translated text BEFORE formatting/splicing
189
+ replacement = EasyI18nPlugin.escapeNuggets(unescapeJsLike(replacement));
190
+
121
191
  // format nuggets
122
192
  var formatItemsMatch = originalText.match(/\|\|\|(.+?)(?:\/\/\/.+?)?\]\]\]/s)
123
193
  if (formatItemsMatch) {
@@ -134,7 +204,7 @@ class EasyI18nPlugin {
134
204
  });
135
205
  }
136
206
 
137
- return EasyI18nPlugin.escapeNuggets(replacement);
207
+ return replacement;
138
208
  });
139
209
 
140
210
  compilation.updateAsset(filename, new SourceMapSource(