langsys-js-typescript 0.6.2 → 0.6.4
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 +48 -0
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +95 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
## 0.6.4 - 2026-08-16
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **A missing ICU argument no longer dumps the message source to the page.** `intl-messageformat` throws when an argument is absent, and the fallback ran the simple interpolator — whose pattern deliberately cannot match an ICU slot, so the entire construct passed through untouched. A phrase promoted to `{name_gender, select, …}` rendered as literal ICU source in front of users.
|
|
6
|
+
|
|
7
|
+
This is reachable with no caller mistake: langsys-ai's ICU promoter *introduces* a select argument the source phrase never had — a plain `{name}` becomes `{name_gender, select, …}` in gendered target locales. The application cannot supply `name_gender`; it doesn't exist in the phrase the developer wrote, and nothing tells them the target grew one. Every app translating into a gendered locale hits it.
|
|
8
|
+
|
|
9
|
+
Recovery, agreed with `langsys-php` so a shared catalog cannot render two different sentences for one input:
|
|
10
|
+
|
|
11
|
+
| slot | missing-argument behavior | outcome |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `select` | take the `other` branch | a correct sentence — `other` is what an unknown gender should render |
|
|
14
|
+
| `plural` | take `other`, render `#` as `{argName}` | sentence survives with a visible gap; nothing can be inferred for a count |
|
|
15
|
+
| `{name}`, `{n, number}`, dates | render as `{argName}` | matches the simple path's rule that unknown keys stay visible |
|
|
16
|
+
|
|
17
|
+
The asymmetry is deliberate: `select` is genuinely recoverable, `plural` is only made less bad. Arguments that ARE supplied are untouched and recursed into, so a missing argument nested inside a satisfied branch still recovers. `plural`/`select` without an `other` branch is malformed ICU and is left alone, falling through to the simple path as before.
|
|
18
|
+
|
|
19
|
+
**A `null` argument counts as missing**, matching `langsys-php`. It doesn't throw — `intl-messageformat` coerces null to `0` — so `{count, plural, …}` rendered `0 items`. That is worse than it looks: a genuine `0` also renders `0 items`, so a bug and valid data became indistinguishable, in the output, in a screenshot, and in a bug report. A real `0` still renders `0 items`; only null and absent produce `{count} items`.
|
|
20
|
+
|
|
21
|
+
Found by `langsys-php` from a report against their SDK; both implementations had it, by different mechanisms. Verified against their shipped v1.3.1 behavior, including a nested case where an *unsatisfied* branch's missing argument correctly costs nothing (`{g, select, f {Ella} other {{n, plural …}}}` with `g='f'` renders `Ella`).
|
|
22
|
+
|
|
23
|
+
## 0.6.3 - 2026-08-16
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **`<select>` option text was harvested twice, diverging `custom_id` from every other Langsys SDK.** A `<select>` special case pushed each `<option>`'s text, and the ordinary text-node walk then pushed it again — `<option>` cannot contain elements, so the walker already covered it. Any content block containing a `<select>` therefore produced `["S","S"]` where `langsys-php` produced `["S"]`, and had a different `custom_id` in the two SDKs since before either was audited. The fixture suite could not see it: those fixtures are synthetic token lists that never touch a DOM.
|
|
28
|
+
|
|
29
|
+
**Migration is automatic and lookup-only**, extending the mechanism 0.6.0 introduced. Three historical id shapes are now tried before registering:
|
|
30
|
+
|
|
31
|
+
| tokens | hash | registered by |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| corrected | corrected | 0.6.3+ (current) |
|
|
34
|
+
| legacy (duplicated) | corrected | 0.6.0 – 0.6.2 |
|
|
35
|
+
| legacy (duplicated) | legacy | before 0.6.0 |
|
|
36
|
+
|
|
37
|
+
Registration always uses the corrected id, so the legacy-keyed population can only shrink. Content without a `<select>` is unaffected — its token list is identical either way, so nothing needlessly rebases. Without this fallback the upgrade would have been silent in the worst direction: a dropdown quietly reverting to source text, with nothing erroring to explain it.
|
|
38
|
+
|
|
39
|
+
- **`legacyTokenizeElement(element)`** — the pre-0.6.3 token list, exported for migration tooling. Lookup-only; never register under an id derived from it. Deprecated on arrival.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- **Four standard attributes are now harvested: `label`, `aria-description`, `aria-valuetext`, `aria-roledescription`.** `langsys-php` harvests a superset of our list; diffing the two showed nothing JS-only and 16 PHP-only, of which these four carry real user-visible text. `label` is what a user reads in a `<select>` picker (`<option>`, `<optgroup>`, `<track>`) and is invisible to sighted testing that only inspects option text; the three ARIA attributes are read aloud by screen readers, so leaving them untranslated degrades accessibility specifically for the users least able to work around it.
|
|
44
|
+
|
|
45
|
+
The remaining twelve PHP-only attributes are framework conventions (Bootstrap `data-bs-*`, Rails-style `data-confirm`, loading/notification text) and are deliberately **not** mirrored — they're written by server-rendered templates, and a JS app renders those strings through its own components.
|
|
46
|
+
|
|
47
|
+
**Note:** a content block containing one of these attributes now yields a different token list, and therefore a different `custom_id`. Such blocks re-register and their existing translations orphan. The 0.6.0 legacy-id fallback cannot help here — it re-hashes the *same* tokens, and this changes the tokens themselves. Affected population is only blocks that use these four attributes.
|
|
48
|
+
|
|
1
49
|
## 0.6.2 - 2026-08-16
|
|
2
50
|
|
|
3
51
|
### Added
|
package/dist/index.d.mts
CHANGED
|
@@ -738,6 +738,23 @@ declare function tokenizeElement(element: HTMLElement): {
|
|
|
738
738
|
tokens: string[];
|
|
739
739
|
content: string;
|
|
740
740
|
};
|
|
741
|
+
/**
|
|
742
|
+
* The token list this element would have produced before 0.6.3, when every
|
|
743
|
+
* `<option>`'s text was harvested twice — once by a `<select>` special case and
|
|
744
|
+
* again by the ordinary text-node walk.
|
|
745
|
+
*
|
|
746
|
+
* **Lookup only.** Content blocks registered by an older SDK are keyed by an id
|
|
747
|
+
* derived from this list, so `Translate` falls back to it when the corrected id
|
|
748
|
+
* misses; without that, every block containing a `<select>` would silently
|
|
749
|
+
* re-key and lose its translations on upgrade. Registration always uses the
|
|
750
|
+
* corrected list, so the legacy-keyed population can only shrink.
|
|
751
|
+
*
|
|
752
|
+
* Skips the computed-style capture that `tokenizeElement` performs — this is a
|
|
753
|
+
* cache-key computation, not a snapshot for translators.
|
|
754
|
+
*
|
|
755
|
+
* @deprecated Migration aid; will be removed once catalogs have been rebased.
|
|
756
|
+
*/
|
|
757
|
+
declare function legacyTokenizeElement(element: HTMLElement): string[];
|
|
741
758
|
|
|
742
759
|
/**
|
|
743
760
|
* Create a Signal whose value is persisted to localStorage (when available).
|
|
@@ -876,4 +893,4 @@ interface iProject {
|
|
|
876
893
|
declare const t: TFunction;
|
|
877
894
|
declare const tSignal: Signal<TFunction>;
|
|
878
895
|
|
|
879
|
-
export { type EncodedRichText, type ExtractParamKeys, LangsysApp, LangsysAppAPI, type LocaleSource, Logger, PHRASE_MARKER_ATTR, PHRASE_MARKER_ATTRS, type ParamPrimitive, type ParamsFor, Phrase, type PhraseOptions, type RichSlot, type Signal, type Subscriber, type TArgs, type TFunction, Translate, type TranslateOptions, type TranslationParams, Translations, type Unsubscriber, type Updater, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, findUnusedParamKeys, generateCustomId, generateLegacyCustomId, getValue, type iCategories, type iContentBlock, type iCountry, type iCountryDialCode, type iCountryList, type iCurrency, type iCurrencyList, type iLangsysConfig, type iLangsysInitConfig, type ResponseObject as iLangsysResponse, type iLanguageName, type iLocaleData, type iLocaleDefault, type iLocaleFlat, type iProject, type iTranslations, interpolate, isContentBlockKnown, isEmpty, isICU, isPhraseMarked, isTranslationExcluded, logger, markupTokenValues, md5, md5Legacy, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
896
|
+
export { type EncodedRichText, type ExtractParamKeys, LangsysApp, LangsysAppAPI, type LocaleSource, Logger, PHRASE_MARKER_ATTR, PHRASE_MARKER_ATTRS, type ParamPrimitive, type ParamsFor, Phrase, type PhraseOptions, type RichSlot, type Signal, type Subscriber, type TArgs, type TFunction, Translate, type TranslateOptions, type TranslationParams, Translations, type Unsubscriber, type Updater, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, findUnusedParamKeys, generateCustomId, generateLegacyCustomId, getValue, type iCategories, type iContentBlock, type iCountry, type iCountryDialCode, type iCountryList, type iCurrency, type iCurrencyList, type iLangsysConfig, type iLangsysInitConfig, type ResponseObject as iLangsysResponse, type iLanguageName, type iLocaleData, type iLocaleDefault, type iLocaleFlat, type iProject, type iTranslations, interpolate, isContentBlockKnown, isEmpty, isICU, isPhraseMarked, isTranslationExcluded, legacyTokenizeElement, logger, markupTokenValues, md5, md5Legacy, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
package/dist/index.d.ts
CHANGED
|
@@ -738,6 +738,23 @@ declare function tokenizeElement(element: HTMLElement): {
|
|
|
738
738
|
tokens: string[];
|
|
739
739
|
content: string;
|
|
740
740
|
};
|
|
741
|
+
/**
|
|
742
|
+
* The token list this element would have produced before 0.6.3, when every
|
|
743
|
+
* `<option>`'s text was harvested twice — once by a `<select>` special case and
|
|
744
|
+
* again by the ordinary text-node walk.
|
|
745
|
+
*
|
|
746
|
+
* **Lookup only.** Content blocks registered by an older SDK are keyed by an id
|
|
747
|
+
* derived from this list, so `Translate` falls back to it when the corrected id
|
|
748
|
+
* misses; without that, every block containing a `<select>` would silently
|
|
749
|
+
* re-key and lose its translations on upgrade. Registration always uses the
|
|
750
|
+
* corrected list, so the legacy-keyed population can only shrink.
|
|
751
|
+
*
|
|
752
|
+
* Skips the computed-style capture that `tokenizeElement` performs — this is a
|
|
753
|
+
* cache-key computation, not a snapshot for translators.
|
|
754
|
+
*
|
|
755
|
+
* @deprecated Migration aid; will be removed once catalogs have been rebased.
|
|
756
|
+
*/
|
|
757
|
+
declare function legacyTokenizeElement(element: HTMLElement): string[];
|
|
741
758
|
|
|
742
759
|
/**
|
|
743
760
|
* Create a Signal whose value is persisted to localStorage (when available).
|
|
@@ -876,4 +893,4 @@ interface iProject {
|
|
|
876
893
|
declare const t: TFunction;
|
|
877
894
|
declare const tSignal: Signal<TFunction>;
|
|
878
895
|
|
|
879
|
-
export { type EncodedRichText, type ExtractParamKeys, LangsysApp, LangsysAppAPI, type LocaleSource, Logger, PHRASE_MARKER_ATTR, PHRASE_MARKER_ATTRS, type ParamPrimitive, type ParamsFor, Phrase, type PhraseOptions, type RichSlot, type Signal, type Subscriber, type TArgs, type TFunction, Translate, type TranslateOptions, type TranslationParams, Translations, type Unsubscriber, type Updater, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, findUnusedParamKeys, generateCustomId, generateLegacyCustomId, getValue, type iCategories, type iContentBlock, type iCountry, type iCountryDialCode, type iCountryList, type iCurrency, type iCurrencyList, type iLangsysConfig, type iLangsysInitConfig, type ResponseObject as iLangsysResponse, type iLanguageName, type iLocaleData, type iLocaleDefault, type iLocaleFlat, type iProject, type iTranslations, interpolate, isContentBlockKnown, isEmpty, isICU, isPhraseMarked, isTranslationExcluded, logger, markupTokenValues, md5, md5Legacy, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
896
|
+
export { type EncodedRichText, type ExtractParamKeys, LangsysApp, LangsysAppAPI, type LocaleSource, Logger, PHRASE_MARKER_ATTR, PHRASE_MARKER_ATTRS, type ParamPrimitive, type ParamsFor, Phrase, type PhraseOptions, type RichSlot, type Signal, type Subscriber, type TArgs, type TFunction, Translate, type TranslateOptions, type TranslationParams, Translations, type Unsubscriber, type Updater, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, findUnusedParamKeys, generateCustomId, generateLegacyCustomId, getValue, type iCategories, type iContentBlock, type iCountry, type iCountryDialCode, type iCountryList, type iCurrency, type iCurrencyList, type iLangsysConfig, type iLangsysInitConfig, type ResponseObject as iLangsysResponse, type iLanguageName, type iLocaleData, type iLocaleDefault, type iLocaleFlat, type iProject, type iTranslations, interpolate, isContentBlockKnown, isEmpty, isICU, isPhraseMarked, isTranslationExcluded, legacyTokenizeElement, logger, markupTokenValues, md5, md5Legacy, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
package/dist/index.js
CHANGED
|
@@ -289,14 +289,76 @@ function warnUnmatchedParams(source, texts, params, context) {
|
|
|
289
289
|
}
|
|
290
290
|
function interpolate(template, params, locale) {
|
|
291
291
|
if (isICU(template)) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
292
|
+
const resolved = locale || "en";
|
|
293
|
+
const hasNullParam = !!params && Object.values(params).some((value) => value === null);
|
|
294
|
+
if (!hasNullParam) {
|
|
295
|
+
try {
|
|
296
|
+
return new intlMessageformat.IntlMessageFormat(template, resolved).format(params);
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
{
|
|
301
|
+
try {
|
|
302
|
+
const ast = new intlMessageformat.IntlMessageFormat(template, resolved).ast;
|
|
303
|
+
if (!Array.isArray(ast)) return simpleInterpolate(template, params, locale);
|
|
304
|
+
const recovered = _recoverMissingArgs(JSON.parse(JSON.stringify(ast)), params);
|
|
305
|
+
return new intlMessageformat.IntlMessageFormat(recovered, resolved).format(params);
|
|
306
|
+
} catch {
|
|
307
|
+
return simpleInterpolate(template, params, locale);
|
|
308
|
+
}
|
|
296
309
|
}
|
|
297
310
|
}
|
|
298
311
|
return simpleInterpolate(template, params, locale);
|
|
299
312
|
}
|
|
313
|
+
var ICU_LITERAL = 0;
|
|
314
|
+
var ICU_ARGUMENT = 1;
|
|
315
|
+
var ICU_NUMBER = 2;
|
|
316
|
+
var ICU_DATE = 3;
|
|
317
|
+
var ICU_TIME = 4;
|
|
318
|
+
var ICU_SELECT = 5;
|
|
319
|
+
var ICU_PLURAL = 6;
|
|
320
|
+
var ICU_POUND = 7;
|
|
321
|
+
var ICU_TAG = 8;
|
|
322
|
+
function _recoverMissingArgs(nodes, params) {
|
|
323
|
+
const out = [];
|
|
324
|
+
for (const node of nodes) {
|
|
325
|
+
const name = node.value;
|
|
326
|
+
const value = name !== void 0 && name in params ? params[name] : void 0;
|
|
327
|
+
const supplied = value !== void 0 && value !== null;
|
|
328
|
+
if (node.type === ICU_TAG) {
|
|
329
|
+
out.push({ ...node, children: _recoverMissingArgs(node.children ?? [], params) });
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (node.type === ICU_SELECT || node.type === ICU_PLURAL) {
|
|
333
|
+
if (supplied) {
|
|
334
|
+
const options = {};
|
|
335
|
+
for (const [key, branch2] of Object.entries(node.options ?? {})) {
|
|
336
|
+
options[key] = { value: _recoverMissingArgs(branch2.value, params) };
|
|
337
|
+
}
|
|
338
|
+
out.push({ ...node, options });
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const other = node.options?.other;
|
|
342
|
+
if (!other) {
|
|
343
|
+
out.push(node);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const branch = _recoverMissingArgs(other.value, params);
|
|
347
|
+
out.push(
|
|
348
|
+
...branch.map(
|
|
349
|
+
(child) => child.type === ICU_POUND ? { type: ICU_LITERAL, value: `{${name}}` } : child
|
|
350
|
+
)
|
|
351
|
+
);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (!supplied && (node.type === ICU_ARGUMENT || node.type === ICU_NUMBER || node.type === ICU_DATE || node.type === ICU_TIME)) {
|
|
355
|
+
out.push({ type: ICU_LITERAL, value: `{${name}}` });
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
out.push(node);
|
|
359
|
+
}
|
|
360
|
+
return out;
|
|
361
|
+
}
|
|
300
362
|
function simpleInterpolate(template, params, locale) {
|
|
301
363
|
return template.replace(/\{([^{},]+)\}/g, (match, rawKey) => {
|
|
302
364
|
const key = rawKey.trim();
|
|
@@ -1076,8 +1138,14 @@ var TRANSLATABLE_ATTRIBUTES = [
|
|
|
1076
1138
|
"placeholder",
|
|
1077
1139
|
"alt",
|
|
1078
1140
|
"title",
|
|
1141
|
+
"label",
|
|
1142
|
+
// <option>, <optgroup>, <track> — the text a user reads in the picker
|
|
1079
1143
|
"aria-label",
|
|
1080
1144
|
"aria-placeholder",
|
|
1145
|
+
"aria-description",
|
|
1146
|
+
"aria-valuetext",
|
|
1147
|
+
// the spoken value of a slider/meter
|
|
1148
|
+
"aria-roledescription",
|
|
1081
1149
|
"data-error",
|
|
1082
1150
|
"data-error-message",
|
|
1083
1151
|
"data-validation-message",
|
|
@@ -1194,9 +1262,15 @@ async function registerContentBlock(contentBlock) {
|
|
|
1194
1262
|
function tokenizeElement(element) {
|
|
1195
1263
|
const tokens = [];
|
|
1196
1264
|
const clone = element.cloneNode(true);
|
|
1197
|
-
_walkForTokens(element, Array.from(clone.childNodes), tokens, []);
|
|
1265
|
+
_walkForTokens(element, Array.from(clone.childNodes), tokens, [], false, true);
|
|
1198
1266
|
return { tokens, content: normalizeMarkupPlaceholders(clone.outerHTML) };
|
|
1199
1267
|
}
|
|
1268
|
+
function legacyTokenizeElement(element) {
|
|
1269
|
+
const tokens = [];
|
|
1270
|
+
const clone = element.cloneNode(true);
|
|
1271
|
+
_walkForTokens(element, Array.from(clone.childNodes), tokens, [], true, false);
|
|
1272
|
+
return tokens;
|
|
1273
|
+
}
|
|
1200
1274
|
function _writeKnownContentBlockToCache(category, customId) {
|
|
1201
1275
|
const lookupCat = category || "__uncategorized__";
|
|
1202
1276
|
sTranslations.update((current) => {
|
|
@@ -1210,18 +1284,18 @@ function _writeKnownContentBlockToCache(category, customId) {
|
|
|
1210
1284
|
return { ...current };
|
|
1211
1285
|
});
|
|
1212
1286
|
}
|
|
1213
|
-
function _walkForTokens(liveRoot, cloneNodes, tokens, indices) {
|
|
1287
|
+
function _walkForTokens(liveRoot, cloneNodes, tokens, indices, duplicateSelectOptions, applyStyles) {
|
|
1214
1288
|
cloneNodes.forEach((node, index) => {
|
|
1215
1289
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1216
1290
|
const el = node;
|
|
1217
1291
|
if (isTranslationExcluded(el)) return;
|
|
1218
1292
|
if (isPhraseMarked(el)) return;
|
|
1219
1293
|
}
|
|
1220
|
-
if (node.hasChildNodes()) {
|
|
1294
|
+
if (applyStyles && node.hasChildNodes()) {
|
|
1221
1295
|
_applyStylesToClone(liveRoot, node, [...indices, index]);
|
|
1222
1296
|
}
|
|
1223
1297
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1224
|
-
_tokenizeAttributes(node, tokens);
|
|
1298
|
+
_tokenizeAttributes(node, tokens, duplicateSelectOptions);
|
|
1225
1299
|
}
|
|
1226
1300
|
const contentToken = node.nodeValue?.replace(/\s+/g, " ").trim();
|
|
1227
1301
|
if (node.nodeType === Node.TEXT_NODE && contentToken) {
|
|
@@ -1229,10 +1303,10 @@ function _walkForTokens(liveRoot, cloneNodes, tokens, indices) {
|
|
|
1229
1303
|
return;
|
|
1230
1304
|
}
|
|
1231
1305
|
if (!node.hasChildNodes()) return;
|
|
1232
|
-
_walkForTokens(liveRoot, Array.from(node.childNodes), tokens, [...indices, index]);
|
|
1306
|
+
_walkForTokens(liveRoot, Array.from(node.childNodes), tokens, [...indices, index], duplicateSelectOptions, applyStyles);
|
|
1233
1307
|
});
|
|
1234
1308
|
}
|
|
1235
|
-
function _tokenizeAttributes(element, tokens) {
|
|
1309
|
+
function _tokenizeAttributes(element, tokens, duplicateSelectOptions) {
|
|
1236
1310
|
const tagName = element.tagName.toLowerCase();
|
|
1237
1311
|
if (tagName === "img") {
|
|
1238
1312
|
const img = element;
|
|
@@ -1253,7 +1327,7 @@ function _tokenizeAttributes(element, tokens) {
|
|
|
1253
1327
|
if (value) tokens.push(normalizeMarkupPlaceholders(value));
|
|
1254
1328
|
}
|
|
1255
1329
|
}
|
|
1256
|
-
if (tagName === "select") {
|
|
1330
|
+
if (duplicateSelectOptions && tagName === "select") {
|
|
1257
1331
|
const options = element.querySelectorAll("option");
|
|
1258
1332
|
options.forEach((option) => {
|
|
1259
1333
|
const optionText = option.textContent?.trim();
|
|
@@ -1379,9 +1453,15 @@ var Translate = class {
|
|
|
1379
1453
|
return;
|
|
1380
1454
|
}
|
|
1381
1455
|
if (derivedId) {
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1456
|
+
const legacyTokens = legacyTokenizeElement(this.element);
|
|
1457
|
+
const candidates = [
|
|
1458
|
+
generateCustomId(contentBlock.category, legacyTokens),
|
|
1459
|
+
generateLegacyCustomId(contentBlock.category, legacyTokens)
|
|
1460
|
+
];
|
|
1461
|
+
for (const candidate of candidates) {
|
|
1462
|
+
if (candidate === this.custom_id) continue;
|
|
1463
|
+
if (!isContentBlockKnown(contentBlock.category, candidate)) continue;
|
|
1464
|
+
this.custom_id = candidate;
|
|
1385
1465
|
if (this.tokens.length > 1 && this.element) {
|
|
1386
1466
|
this.translate(Array.from(this.element.childNodes));
|
|
1387
1467
|
this.lastTranslatedLocale = currentlyLoadedLocale.get();
|
|
@@ -1667,6 +1747,7 @@ exports.isEmpty = isEmpty;
|
|
|
1667
1747
|
exports.isICU = isICU;
|
|
1668
1748
|
exports.isPhraseMarked = isPhraseMarked;
|
|
1669
1749
|
exports.isTranslationExcluded = isTranslationExcluded;
|
|
1750
|
+
exports.legacyTokenizeElement = legacyTokenizeElement;
|
|
1670
1751
|
exports.logger = logger;
|
|
1671
1752
|
exports.markupTokenValues = markupTokenValues;
|
|
1672
1753
|
exports.md5 = md5;
|