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/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
- node.nodeValue = node.originalNodeValue.replace(contentToken, translation);
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