langsys-js-typescript 0.6.0 → 0.6.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/CHANGELOG.md +24 -1
- package/dist/index.d.mts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
## 0.6.
|
|
1
|
+
## 0.6.2 - 2026-08-16
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **`data-notrans` is now honored as an alias for `translate="no"`.** `langsys-php` accepts it as an author-facing opt-out, for hosts whose templating strips unknown bare attributes or where `translate` collides with another tool — and it survives into `translatePage()` output. On SSR handoff there is one DOM, so content an author marked "do not translate" was being extracted and registered by whichever SDK walked it. Presence is intent; an explicit `"false"`/`"0"` opts out of the opt-out, compared after trimming. `translate="no"` is now matched case-insensitively too, also mirroring PHP.
|
|
6
|
+
- **`isTranslationExcluded(element)`** — exported alongside `isPhraseMarked`, for wrappers that tokenize through their own templating rather than delegating to the DOM class.
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **Phrase-marker values are trimmed before comparison**, so `data-langsys-phrase=" false "` opts out like `="false"` does. This matched `langsys-php` at the time it shipped; auditing the difference showed the gap was drift — their exclusion check gained a trim their phrase check never did — and they normalized on their side, so both SDKs now trim in both places. `isPhraseMarked` is also the method name in both SDKs now, so the correspondence is checkable at a glance.
|
|
11
|
+
|
|
12
|
+
### Notes
|
|
13
|
+
|
|
14
|
+
- Asking `langsys-php` whether to mirror `data-notrans` rather than implementing against their code surfaced a bug on their side: their truthiness test was inverted at both ends — bare `data-notrans` did nothing, while `data-notrans="false"` excluded. Had this been implemented from their source as it stood, it would have faithfully reproduced that. Their fix landed first; this mirrors the corrected semantics.
|
|
15
|
+
|
|
16
|
+
## 0.6.1 - 2026-08-15
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`data-langsys-phrase="false"` / `="0"` are now honored as opt-outs.** 0.6.0 taught the tokenizer to skip subtrees carrying `langsys-php`'s keep-together marker, but matched on attribute presence alone. PHP's `hasPhraseAttribute()` treats presence as intent *while letting an explicit off value opt out* — so we skipped subtrees the author had deliberately un-marked, leaving that content translated by neither SDK. Matching is now value-aware and case-insensitive, mirroring PHP exactly.
|
|
21
|
+
|
|
22
|
+
Found by reading `HtmlParser.php` rather than trusting a summary of it — the same second-hand-documentation error this class of bug keeps producing.
|
|
23
|
+
|
|
24
|
+
## 0.6.0 - 2026-08-15
|
|
2
25
|
|
|
3
26
|
### Fixed
|
|
4
27
|
|
package/dist/index.d.mts
CHANGED
|
@@ -628,7 +628,38 @@ interface iContentBlock {
|
|
|
628
628
|
* additive — `data-langsys-phrase` never appears in DOM our components emit.
|
|
629
629
|
*/
|
|
630
630
|
declare const PHRASE_MARKER_ATTRS: readonly ["data-ls-phrase", "data-langsys-phrase"];
|
|
631
|
-
/**
|
|
631
|
+
/**
|
|
632
|
+
* True when an element opts out of translation entirely — it and its subtree
|
|
633
|
+
* are skipped by the tokenizer.
|
|
634
|
+
*
|
|
635
|
+
* Mirrors `langsys-php`'s `HtmlParser::isTranslationExcluded()` exactly:
|
|
636
|
+
* - `translate="no"` — the HTML standard attribute, matched case-insensitively.
|
|
637
|
+
* - `data-notrans` — PHP's author-facing alias, for hosts whose templating
|
|
638
|
+
* strips unknown bare attributes or where `translate` collides with another
|
|
639
|
+
* tool. Presence is intent; an explicit `"false"`/`"0"` opts out of the
|
|
640
|
+
* opt-out, compared after trimming.
|
|
641
|
+
*
|
|
642
|
+
* Honoring PHP's alias matters for the same reason as the phrase marker: on SSR
|
|
643
|
+
* handoff there is ONE DOM, and content an author marked "do not translate"
|
|
644
|
+
* must not be extracted and registered by whichever SDK happens to walk it.
|
|
645
|
+
*
|
|
646
|
+
* Both this and `isPhraseMarked` trim before comparing. They briefly differed
|
|
647
|
+
* — PHP's phrase check predated the trim its exclusion check gained — and the
|
|
648
|
+
* gap was resolved by asking rather than tidying: mirror exactly, flag the
|
|
649
|
+
* wart, let the owner decide. Divergence-by-tidying is the same failure class
|
|
650
|
+
* as divergence-by-oversight, just better intentioned.
|
|
651
|
+
*/
|
|
652
|
+
declare function isTranslationExcluded(element: Element): boolean;
|
|
653
|
+
/**
|
|
654
|
+
* True when an element is marked as a self-managed phrase by either SDK.
|
|
655
|
+
*
|
|
656
|
+
* Presence alone means intent, like any boolean HTML attribute — but an
|
|
657
|
+
* explicit `="false"` or `="0"` opts OUT, compared after trimming. Mirrors
|
|
658
|
+
* `langsys-php`'s `isPhraseMarked()` exactly (same method name in both SDKs,
|
|
659
|
+
* so the correspondence is checkable at a glance). Without the opt-out we
|
|
660
|
+
* would skip a subtree the author had deliberately un-marked, and it would
|
|
661
|
+
* then be translated by neither SDK.
|
|
662
|
+
*/
|
|
632
663
|
declare function isPhraseMarked(element: Element): boolean;
|
|
633
664
|
/**
|
|
634
665
|
* Compute the deterministic content-block id from a `(category, tokens)` pair.
|
|
@@ -845,4 +876,4 @@ interface iProject {
|
|
|
845
876
|
declare const t: TFunction;
|
|
846
877
|
declare const tSignal: Signal<TFunction>;
|
|
847
878
|
|
|
848
|
-
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, logger, markupTokenValues, md5, md5Legacy, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -628,7 +628,38 @@ interface iContentBlock {
|
|
|
628
628
|
* additive — `data-langsys-phrase` never appears in DOM our components emit.
|
|
629
629
|
*/
|
|
630
630
|
declare const PHRASE_MARKER_ATTRS: readonly ["data-ls-phrase", "data-langsys-phrase"];
|
|
631
|
-
/**
|
|
631
|
+
/**
|
|
632
|
+
* True when an element opts out of translation entirely — it and its subtree
|
|
633
|
+
* are skipped by the tokenizer.
|
|
634
|
+
*
|
|
635
|
+
* Mirrors `langsys-php`'s `HtmlParser::isTranslationExcluded()` exactly:
|
|
636
|
+
* - `translate="no"` — the HTML standard attribute, matched case-insensitively.
|
|
637
|
+
* - `data-notrans` — PHP's author-facing alias, for hosts whose templating
|
|
638
|
+
* strips unknown bare attributes or where `translate` collides with another
|
|
639
|
+
* tool. Presence is intent; an explicit `"false"`/`"0"` opts out of the
|
|
640
|
+
* opt-out, compared after trimming.
|
|
641
|
+
*
|
|
642
|
+
* Honoring PHP's alias matters for the same reason as the phrase marker: on SSR
|
|
643
|
+
* handoff there is ONE DOM, and content an author marked "do not translate"
|
|
644
|
+
* must not be extracted and registered by whichever SDK happens to walk it.
|
|
645
|
+
*
|
|
646
|
+
* Both this and `isPhraseMarked` trim before comparing. They briefly differed
|
|
647
|
+
* — PHP's phrase check predated the trim its exclusion check gained — and the
|
|
648
|
+
* gap was resolved by asking rather than tidying: mirror exactly, flag the
|
|
649
|
+
* wart, let the owner decide. Divergence-by-tidying is the same failure class
|
|
650
|
+
* as divergence-by-oversight, just better intentioned.
|
|
651
|
+
*/
|
|
652
|
+
declare function isTranslationExcluded(element: Element): boolean;
|
|
653
|
+
/**
|
|
654
|
+
* True when an element is marked as a self-managed phrase by either SDK.
|
|
655
|
+
*
|
|
656
|
+
* Presence alone means intent, like any boolean HTML attribute — but an
|
|
657
|
+
* explicit `="false"` or `="0"` opts OUT, compared after trimming. Mirrors
|
|
658
|
+
* `langsys-php`'s `isPhraseMarked()` exactly (same method name in both SDKs,
|
|
659
|
+
* so the correspondence is checkable at a glance). Without the opt-out we
|
|
660
|
+
* would skip a subtree the author had deliberately un-marked, and it would
|
|
661
|
+
* then be translated by neither SDK.
|
|
662
|
+
*/
|
|
632
663
|
declare function isPhraseMarked(element: Element): boolean;
|
|
633
664
|
/**
|
|
634
665
|
* Compute the deterministic content-block id from a `(category, tokens)` pair.
|
|
@@ -845,4 +876,4 @@ interface iProject {
|
|
|
845
876
|
declare const t: TFunction;
|
|
846
877
|
declare const tSignal: Signal<TFunction>;
|
|
847
878
|
|
|
848
|
-
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, logger, markupTokenValues, md5, md5Legacy, normalizeMarkupPlaceholders, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1086,8 +1086,18 @@ var TRANSLATABLE_ATTRIBUTES = [
|
|
|
1086
1086
|
"data-pattern-message"
|
|
1087
1087
|
];
|
|
1088
1088
|
var PHRASE_MARKER_ATTRS = ["data-ls-phrase", "data-langsys-phrase"];
|
|
1089
|
+
function isTranslationExcluded(element) {
|
|
1090
|
+
if ((element.getAttribute("translate") ?? "").toLowerCase() === "no") return true;
|
|
1091
|
+
if (!element.hasAttribute("data-notrans")) return false;
|
|
1092
|
+
const value = (element.getAttribute("data-notrans") ?? "").trim().toLowerCase();
|
|
1093
|
+
return value !== "false" && value !== "0";
|
|
1094
|
+
}
|
|
1089
1095
|
function isPhraseMarked(element) {
|
|
1090
|
-
return PHRASE_MARKER_ATTRS.some((attr) =>
|
|
1096
|
+
return PHRASE_MARKER_ATTRS.some((attr) => {
|
|
1097
|
+
if (!element.hasAttribute(attr)) return false;
|
|
1098
|
+
const value = (element.getAttribute(attr) ?? "").trim().toLowerCase();
|
|
1099
|
+
return value !== "false" && value !== "0";
|
|
1100
|
+
});
|
|
1091
1101
|
}
|
|
1092
1102
|
var VALUE_TRANSLATABLE_ELEMENTS = ["button"];
|
|
1093
1103
|
var VALUE_TRANSLATABLE_INPUT_TYPES = ["submit", "button"];
|
|
@@ -1204,7 +1214,7 @@ function _walkForTokens(liveRoot, cloneNodes, tokens, indices) {
|
|
|
1204
1214
|
cloneNodes.forEach((node, index) => {
|
|
1205
1215
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1206
1216
|
const el = node;
|
|
1207
|
-
if (el
|
|
1217
|
+
if (isTranslationExcluded(el)) return;
|
|
1208
1218
|
if (isPhraseMarked(el)) return;
|
|
1209
1219
|
}
|
|
1210
1220
|
if (node.hasChildNodes()) {
|
|
@@ -1393,7 +1403,7 @@ var Translate = class {
|
|
|
1393
1403
|
nodes.forEach((node) => {
|
|
1394
1404
|
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
1395
1405
|
const element = node;
|
|
1396
|
-
if (element
|
|
1406
|
+
if (isTranslationExcluded(element)) return;
|
|
1397
1407
|
if (isPhraseMarked(element)) return;
|
|
1398
1408
|
this.translateAttributes(node);
|
|
1399
1409
|
}
|
|
@@ -1656,6 +1666,7 @@ exports.isContentBlockKnown = isContentBlockKnown;
|
|
|
1656
1666
|
exports.isEmpty = isEmpty;
|
|
1657
1667
|
exports.isICU = isICU;
|
|
1658
1668
|
exports.isPhraseMarked = isPhraseMarked;
|
|
1669
|
+
exports.isTranslationExcluded = isTranslationExcluded;
|
|
1659
1670
|
exports.logger = logger;
|
|
1660
1671
|
exports.markupTokenValues = markupTokenValues;
|
|
1661
1672
|
exports.md5 = md5;
|