langsys-js-typescript 0.3.0 → 0.4.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.
- package/CHANGELOG.md +20 -0
- package/README.md +37 -0
- package/dist/index.d.mts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +41 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -263,6 +263,9 @@ function isICU(template) {
|
|
|
263
263
|
return ICU_PATTERN.test(template);
|
|
264
264
|
}
|
|
265
265
|
var ICU_PATTERN = /\{[^{}]+,\s*(plural|select|selectordinal|number|date|time)\s*[,}]/;
|
|
266
|
+
function normalizeMarkupPlaceholders(text) {
|
|
267
|
+
return text.replace(/%([A-Za-z_][A-Za-z0-9_]*)%/g, "{$1}");
|
|
268
|
+
}
|
|
266
269
|
function interpolate(template, params, locale) {
|
|
267
270
|
if (isICU(template)) {
|
|
268
271
|
try {
|
|
@@ -1139,7 +1142,7 @@ function tokenizeElement(element) {
|
|
|
1139
1142
|
const tokens = [];
|
|
1140
1143
|
const clone = element.cloneNode(true);
|
|
1141
1144
|
_walkForTokens(element, Array.from(clone.childNodes), tokens, []);
|
|
1142
|
-
return { tokens, content: clone.outerHTML };
|
|
1145
|
+
return { tokens, content: normalizeMarkupPlaceholders(clone.outerHTML) };
|
|
1143
1146
|
}
|
|
1144
1147
|
function _writeKnownContentBlockToCache(category, customId) {
|
|
1145
1148
|
const lookupCat = category || "__uncategorized__";
|
|
@@ -1169,7 +1172,7 @@ function _walkForTokens(liveRoot, cloneNodes, tokens, indices) {
|
|
|
1169
1172
|
}
|
|
1170
1173
|
const contentToken = node.nodeValue?.replace(/\s+/g, " ").trim();
|
|
1171
1174
|
if (node.nodeType === Node.TEXT_NODE && contentToken) {
|
|
1172
|
-
tokens.push(contentToken);
|
|
1175
|
+
tokens.push(normalizeMarkupPlaceholders(contentToken));
|
|
1173
1176
|
return;
|
|
1174
1177
|
}
|
|
1175
1178
|
if (!node.hasChildNodes()) return;
|
|
@@ -1184,24 +1187,24 @@ function _tokenizeAttributes(element, tokens) {
|
|
|
1184
1187
|
}
|
|
1185
1188
|
for (const attr of TRANSLATABLE_ATTRIBUTES) {
|
|
1186
1189
|
const value = element.getAttribute(attr)?.trim();
|
|
1187
|
-
if (value) tokens.push(value);
|
|
1190
|
+
if (value) tokens.push(normalizeMarkupPlaceholders(value));
|
|
1188
1191
|
}
|
|
1189
1192
|
if (VALUE_TRANSLATABLE_ELEMENTS.includes(tagName)) {
|
|
1190
1193
|
const value = element.getAttribute("value")?.trim();
|
|
1191
|
-
if (value) tokens.push(value);
|
|
1194
|
+
if (value) tokens.push(normalizeMarkupPlaceholders(value));
|
|
1192
1195
|
}
|
|
1193
1196
|
if (tagName === "input") {
|
|
1194
1197
|
const inputType = element.getAttribute("type")?.toLowerCase();
|
|
1195
1198
|
if (inputType && VALUE_TRANSLATABLE_INPUT_TYPES.includes(inputType)) {
|
|
1196
1199
|
const value = element.getAttribute("value")?.trim();
|
|
1197
|
-
if (value) tokens.push(value);
|
|
1200
|
+
if (value) tokens.push(normalizeMarkupPlaceholders(value));
|
|
1198
1201
|
}
|
|
1199
1202
|
}
|
|
1200
1203
|
if (tagName === "select") {
|
|
1201
1204
|
const options = element.querySelectorAll("option");
|
|
1202
1205
|
options.forEach((option) => {
|
|
1203
1206
|
const optionText = option.textContent?.trim();
|
|
1204
|
-
if (optionText) tokens.push(optionText);
|
|
1207
|
+
if (optionText) tokens.push(normalizeMarkupPlaceholders(optionText));
|
|
1205
1208
|
});
|
|
1206
1209
|
}
|
|
1207
1210
|
}
|
|
@@ -1251,6 +1254,14 @@ var Translate = class {
|
|
|
1251
1254
|
if (locale && this.parseComplete) this.translateUpdate(locale);
|
|
1252
1255
|
});
|
|
1253
1256
|
}
|
|
1257
|
+
/** Update interpolation params (e.g. a changed count) and re-render. Mirrors `Phrase.setParams`. */
|
|
1258
|
+
setParams(params) {
|
|
1259
|
+
this.options.params = params;
|
|
1260
|
+
if (!this.parseComplete) return;
|
|
1261
|
+
this.lastTranslatedLocale = "";
|
|
1262
|
+
const locale = currentlyLoadedLocale.get();
|
|
1263
|
+
if (locale) this.translateUpdate(locale);
|
|
1264
|
+
}
|
|
1254
1265
|
/** Stop reacting to locale changes. Safe to call multiple times. */
|
|
1255
1266
|
destroy() {
|
|
1256
1267
|
if (this.unsubscribeLocale) {
|
|
@@ -1263,7 +1274,7 @@ var Translate = class {
|
|
|
1263
1274
|
if (this.lastTranslatedLocale === currentLocale) return;
|
|
1264
1275
|
const { category = "" } = this.options;
|
|
1265
1276
|
if (this.tokens.length === 1) {
|
|
1266
|
-
this.element.innerText = LangsysApp.Translations.t(this.tokens[0], category);
|
|
1277
|
+
this.element.innerText = this.applyParams(LangsysApp.Translations.t(this.tokens[0], category));
|
|
1267
1278
|
} else {
|
|
1268
1279
|
this.translate(Array.from(this.element.childNodes));
|
|
1269
1280
|
this.lastTranslatedLocale = currentLocale;
|
|
@@ -1281,7 +1292,7 @@ var Translate = class {
|
|
|
1281
1292
|
this.tokens = tokens;
|
|
1282
1293
|
const { category = "" } = this.options;
|
|
1283
1294
|
if (this.tokens.length === 1) {
|
|
1284
|
-
this.element.innerText = LangsysApp.Translations.t(this.tokens[0], category);
|
|
1295
|
+
this.element.innerText = this.applyParams(LangsysApp.Translations.t(this.tokens[0], category));
|
|
1285
1296
|
} else {
|
|
1286
1297
|
const contentBlock = {
|
|
1287
1298
|
custom_id: "",
|
|
@@ -1317,7 +1328,7 @@ var Translate = class {
|
|
|
1317
1328
|
}
|
|
1318
1329
|
translate(nodes) {
|
|
1319
1330
|
const currentLocale = currentlyLoadedLocale.get();
|
|
1320
|
-
if (currentLocale === config.baseLocale && (this.lastTranslatedLocale === "" || this.lastTranslatedLocale === config.baseLocale)) {
|
|
1331
|
+
if (currentLocale === config.baseLocale && (this.lastTranslatedLocale === "" || this.lastTranslatedLocale === config.baseLocale) && isEmpty(this.options.params)) {
|
|
1321
1332
|
return;
|
|
1322
1333
|
}
|
|
1323
1334
|
nodes.forEach((node) => {
|
|
@@ -1334,15 +1345,16 @@ var Translate = class {
|
|
|
1334
1345
|
}
|
|
1335
1346
|
if (isEmpty(node.nodeValue?.trim())) return;
|
|
1336
1347
|
if (isEmpty(node.originalNodeValue)) {
|
|
1337
|
-
node.originalNodeValue = node.nodeValue;
|
|
1348
|
+
node.originalNodeValue = normalizeMarkupPlaceholders(node.nodeValue);
|
|
1338
1349
|
}
|
|
1339
1350
|
const contentToken = node.originalNodeValue?.replace(/\s+/g, " ").trim();
|
|
1340
1351
|
if (!contentToken) return;
|
|
1341
1352
|
const translation = this.getTranslation(contentToken);
|
|
1342
1353
|
if (translation && contentToken && node.originalNodeValue) {
|
|
1343
|
-
|
|
1354
|
+
const resolved = this.applyParams(translation);
|
|
1355
|
+
node.nodeValue = node.originalNodeValue.replace(contentToken, () => resolved);
|
|
1344
1356
|
} else if (node.originalNodeValue) {
|
|
1345
|
-
node.nodeValue = node.originalNodeValue;
|
|
1357
|
+
node.nodeValue = this.applyParams(node.originalNodeValue);
|
|
1346
1358
|
}
|
|
1347
1359
|
});
|
|
1348
1360
|
}
|
|
@@ -1351,6 +1363,16 @@ var Translate = class {
|
|
|
1351
1363
|
const { category = "" } = this.options;
|
|
1352
1364
|
return LangsysApp.Translations.lookupContent(category, this.custom_id, token);
|
|
1353
1365
|
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Interpolate `{name}`-style params into a resolved text, matching `t()`:
|
|
1368
|
+
* unknown keys fall through untouched, and the untranslated fallback is
|
|
1369
|
+
* interpolated too. No-op when no params were supplied.
|
|
1370
|
+
*/
|
|
1371
|
+
applyParams(text) {
|
|
1372
|
+
const { params } = this.options;
|
|
1373
|
+
if (isEmpty(params)) return text;
|
|
1374
|
+
return interpolate(text, params, currentlyLoadedLocale.get());
|
|
1375
|
+
}
|
|
1354
1376
|
translateAttributes(element) {
|
|
1355
1377
|
const tagName = element.tagName.toLowerCase();
|
|
1356
1378
|
if (!element.originalAttributes) element.originalAttributes = {};
|
|
@@ -1372,12 +1394,12 @@ var Translate = class {
|
|
|
1372
1394
|
const optionEl = option;
|
|
1373
1395
|
if (!optionEl.originalAttributes) optionEl.originalAttributes = {};
|
|
1374
1396
|
if (optionEl.originalAttributes["textContent"] === void 0) {
|
|
1375
|
-
optionEl.originalAttributes["textContent"] = option.textContent || "";
|
|
1397
|
+
optionEl.originalAttributes["textContent"] = normalizeMarkupPlaceholders(option.textContent || "");
|
|
1376
1398
|
}
|
|
1377
1399
|
const originalText = optionEl.originalAttributes["textContent"].trim();
|
|
1378
1400
|
if (originalText) {
|
|
1379
1401
|
const translation = this.getTranslation(originalText);
|
|
1380
|
-
option.textContent = translation || optionEl.originalAttributes["textContent"];
|
|
1402
|
+
option.textContent = this.applyParams(translation || optionEl.originalAttributes["textContent"]);
|
|
1381
1403
|
}
|
|
1382
1404
|
});
|
|
1383
1405
|
}
|
|
@@ -1386,13 +1408,13 @@ var Translate = class {
|
|
|
1386
1408
|
const currentValue = element.getAttribute(attr);
|
|
1387
1409
|
if (!currentValue) return;
|
|
1388
1410
|
if (element.originalAttributes[attr] === void 0) {
|
|
1389
|
-
element.originalAttributes[attr] = currentValue;
|
|
1411
|
+
element.originalAttributes[attr] = normalizeMarkupPlaceholders(currentValue);
|
|
1390
1412
|
}
|
|
1391
1413
|
const originalValue = element.originalAttributes[attr].trim();
|
|
1392
1414
|
if (!originalValue) return;
|
|
1393
1415
|
const translation = this.getTranslation(originalValue);
|
|
1394
|
-
if (translation) element.setAttribute(attr, translation);
|
|
1395
|
-
else element.setAttribute(attr, element.originalAttributes[attr]);
|
|
1416
|
+
if (translation) element.setAttribute(attr, this.applyParams(translation));
|
|
1417
|
+
else element.setAttribute(attr, this.applyParams(element.originalAttributes[attr]));
|
|
1396
1418
|
}
|
|
1397
1419
|
};
|
|
1398
1420
|
|
|
@@ -1405,7 +1427,7 @@ var SENT_SCAN = /\uE000(\d+)\uE001|\uE002(\d+)\uE003/g;
|
|
|
1405
1427
|
function encodeRichText(root) {
|
|
1406
1428
|
const slots = [];
|
|
1407
1429
|
const phrase = _encodeNodes(Array.from(root.childNodes), slots);
|
|
1408
|
-
return { phrase: phrase.replace(/\s+/g, " ").trim(), slots };
|
|
1430
|
+
return { phrase: normalizeMarkupPlaceholders(phrase.replace(/\s+/g, " ").trim()), slots };
|
|
1409
1431
|
}
|
|
1410
1432
|
function _encodeNodes(nodes, slots) {
|
|
1411
1433
|
let out = "";
|
|
@@ -1524,6 +1546,6 @@ var t = ((phrase, ...rest) => {
|
|
|
1524
1546
|
});
|
|
1525
1547
|
var tSignal = LangsysApp.Translations.tSignal;
|
|
1526
1548
|
|
|
1527
|
-
export { LangsysApp, LangsysAppAPI, Logger, PHRASE_MARKER_ATTR, Phrase, Translate, Translations, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, generateCustomId, getValue, interpolate, isContentBlockKnown, isEmpty, isICU, logger, markupTokenValues, md5, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
1549
|
+
export { LangsysApp, LangsysAppAPI, Logger, PHRASE_MARKER_ATTR, Phrase, Translate, Translations, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, generateCustomId, getValue, interpolate, isContentBlockKnown, isEmpty, isICU, logger, markupTokenValues, md5, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
1528
1550
|
//# sourceMappingURL=index.mjs.map
|
|
1529
1551
|
//# sourceMappingURL=index.mjs.map
|