langsys-js-typescript 0.4.1 → 0.4.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/dist/index.mjs CHANGED
@@ -266,6 +266,25 @@ var ICU_PATTERN = /\{[^{}]+,\s*(plural|select|selectordinal|number|date|time)\s*
266
266
  function normalizeMarkupPlaceholders(text) {
267
267
  return text.replace(/%([A-Za-z_][A-Za-z0-9_]*)%/g, "{$1}");
268
268
  }
269
+ function escapeForRegExp(key) {
270
+ return key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
271
+ }
272
+ function findUnusedParamKeys(texts, params) {
273
+ if (!params) return [];
274
+ const keys = Object.keys(params);
275
+ if (!keys.length || !texts.length) return keys;
276
+ const haystack = texts.join("\0");
277
+ return keys.filter((key) => !new RegExp(`\\{\\s*${escapeForRegExp(key)}\\s*[,}]`).test(haystack));
278
+ }
279
+ function warnUnmatchedParams(source, texts, params, context) {
280
+ if (!logger.debugEnabled) return;
281
+ const unused = findUnusedParamKeys(texts, params);
282
+ if (!unused.length) return;
283
+ const keyList = unused.map((key) => `%${key}%`).join(", ");
284
+ logger.warn(
285
+ `${source} received params with no matching placeholder in its content: ${keyList}. If you wrote {${unused[0]}} in markup, the framework compiler (Svelte/JSX) substituted it before Langsys saw the text \u2014 write %${unused[0]}% instead.` + (context ? ` [${context}]` : "")
286
+ );
287
+ }
269
288
  function interpolate(template, params, locale) {
270
289
  if (isICU(template)) {
271
290
  try {
@@ -1246,6 +1265,8 @@ var Translate = class {
1246
1265
  this.isTokenizing = false;
1247
1266
  this.lastTranslatedLocale = "";
1248
1267
  this.unsubscribeLocale = null;
1268
+ /** Sorted params key-set already checked, so value-only updates don't re-warn. */
1269
+ this.checkedParamKeys = null;
1249
1270
  this.element = element;
1250
1271
  this.options = { ...options };
1251
1272
  this.custom_id = options.custom_id || "";
@@ -1257,6 +1278,7 @@ var Translate = class {
1257
1278
  /** Update interpolation params (e.g. a changed count) and re-render. Mirrors `Phrase.setParams`. */
1258
1279
  setParams(params) {
1259
1280
  this.options.params = params;
1281
+ this.checkParams();
1260
1282
  if (!this.parseComplete) return;
1261
1283
  this.lastTranslatedLocale = "";
1262
1284
  const locale = currentlyLoadedLocale.get();
@@ -1290,6 +1312,7 @@ var Translate = class {
1290
1312
  this.isTokenizing = true;
1291
1313
  const { tokens, content } = tokenizeElement(this.element);
1292
1314
  this.tokens = tokens;
1315
+ this.checkParams();
1293
1316
  const { category = "" } = this.options;
1294
1317
  if (this.tokens.length === 1) {
1295
1318
  this.element.innerText = this.applyParams(LangsysApp.Translations.t(this.tokens[0], category));
@@ -1363,6 +1386,19 @@ var Translate = class {
1363
1386
  const { category = "" } = this.options;
1364
1387
  return LangsysApp.Translations.lookupContent(category, this.custom_id, token);
1365
1388
  }
1389
+ /**
1390
+ * Debug-time check that every supplied param has a placeholder to land in.
1391
+ * Re-runs only when the key SET changes — a changing `count` value must not
1392
+ * re-warn on every render.
1393
+ */
1394
+ checkParams() {
1395
+ if (!this.tokens.length) return;
1396
+ const { params, category = "" } = this.options;
1397
+ const signature = params ? Object.keys(params).sort().join(",") : "";
1398
+ if (signature === this.checkedParamKeys) return;
1399
+ this.checkedParamKeys = signature;
1400
+ warnUnmatchedParams("<Translate>", this.tokens, params, category || void 0);
1401
+ }
1366
1402
  /**
1367
1403
  * Interpolate `{name}`-style params into a resolved text, matching `t()`:
1368
1404
  * unknown keys fall through untouched, and the untranslated fallback is
@@ -1497,6 +1533,8 @@ var Phrase = class {
1497
1533
  this.slots = [];
1498
1534
  this.unsubscribers = [];
1499
1535
  this.ready = false;
1536
+ /** Sorted params key-set already checked, so value-only updates don't re-warn. */
1537
+ this.checkedParamKeys = null;
1500
1538
  this.host = host;
1501
1539
  this.category = options.category ?? "";
1502
1540
  this.params = options.params ?? {};
@@ -1510,7 +1548,9 @@ var Phrase = class {
1510
1548
  /** Update interpolation params (e.g. a changed count) and re-render. */
1511
1549
  setParams(params) {
1512
1550
  this.params = params ?? {};
1513
- if (this.ready) this._render();
1551
+ this._checkParams();
1552
+ if (!this.ready) return;
1553
+ this._render();
1514
1554
  }
1515
1555
  /** Stop reacting to locale/translation changes. Safe to call multiple times. */
1516
1556
  destroy() {
@@ -1525,11 +1565,24 @@ var Phrase = class {
1525
1565
  const { phrase, slots } = encodeRichText(this.host);
1526
1566
  this.phrase = phrase;
1527
1567
  this.slots = slots;
1568
+ this._checkParams();
1528
1569
  await LangsysApp.Translations.ready();
1529
1570
  LangsysApp.Translations.t(this.phrase, this.category);
1530
1571
  this.ready = true;
1531
1572
  this._render();
1532
1573
  }
1574
+ /**
1575
+ * Debug-time check that every supplied param has a placeholder to land in.
1576
+ * Re-runs only when the key SET changes — a changing `n` value must not
1577
+ * re-warn on every render.
1578
+ */
1579
+ _checkParams() {
1580
+ if (!this.phrase) return;
1581
+ const signature = Object.keys(this.params).sort().join(",");
1582
+ if (signature === this.checkedParamKeys) return;
1583
+ this.checkedParamKeys = signature;
1584
+ warnUnmatchedParams("<Phrase>", [this.phrase], this.params, this.category || void 0);
1585
+ }
1533
1586
  _render() {
1534
1587
  if (!this.phrase) return;
1535
1588
  const raw = LangsysApp.Translations.lookup(this.phrase, this.category) ?? this.phrase;
@@ -1546,6 +1599,6 @@ var t = ((phrase, ...rest) => {
1546
1599
  });
1547
1600
  var tSignal = LangsysApp.Translations.tSignal;
1548
1601
 
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 };
1602
+ export { LangsysApp, LangsysAppAPI, Logger, PHRASE_MARKER_ATTR, Phrase, Translate, Translations, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, findUnusedParamKeys, generateCustomId, getValue, interpolate, isContentBlockKnown, isEmpty, isICU, logger, markupTokenValues, md5, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
1550
1603
  //# sourceMappingURL=index.mjs.map
1551
1604
  //# sourceMappingURL=index.mjs.map