@dabalabs/lang 0.0.7-beta → 0.0.8-beta
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/dabalang.cjs +146 -3
- package/dist/dabalang.cjs.map +1 -1
- package/dist/dabalang.d.cts +30 -1
- package/dist/dabalang.d.ts +30 -1
- package/dist/dabalang.js +144 -4
- package/dist/dabalang.js.map +1 -1
- package/dist/dabalang.min.js +2 -2
- package/dist/dabalang.min.js.map +1 -1
- package/package.json +1 -1
package/dist/dabalang.cjs
CHANGED
|
@@ -315,6 +315,41 @@ var TranslationApplier = class {
|
|
|
315
315
|
}
|
|
316
316
|
return painted;
|
|
317
317
|
}
|
|
318
|
+
/** True when every string on this page is already translated for
|
|
319
|
+
* `targetLang`, so warming it would be a request that changes nothing. */
|
|
320
|
+
isFullyCached(targetLang) {
|
|
321
|
+
const cached = this.fetchedLanguages.get(targetLang) ?? readCachedTranslations(this.projectId, targetLang) ?? void 0;
|
|
322
|
+
if (!cached) return false;
|
|
323
|
+
return this.sourceTexts().every((t) => cached.has(t));
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Translates this page into `targetLang` WITHOUT touching the DOM.
|
|
327
|
+
*
|
|
328
|
+
* Used to warm languages the visitor is not reading. The gateway caches
|
|
329
|
+
* by content hash, so the first visitor to a page pays for it and every
|
|
330
|
+
* later visitor — in any language — gets it instantly. Painting here
|
|
331
|
+
* would replace the text the visitor is currently reading with a
|
|
332
|
+
* language they did not ask for, so this is deliberately fetch-only and
|
|
333
|
+
* shares nothing with applyLanguage beyond the cache it fills.
|
|
334
|
+
*/
|
|
335
|
+
async warmLanguage(targetLang) {
|
|
336
|
+
const existing = this.fetchedLanguages.get(targetLang) ?? readCachedTranslations(this.projectId, targetLang) ?? /* @__PURE__ */ new Map();
|
|
337
|
+
const missing = this.sourceTexts().filter((t) => !existing.has(t));
|
|
338
|
+
if (missing.length === 0) {
|
|
339
|
+
this.fetchedLanguages.set(targetLang, existing);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const results = await batchTranslate(
|
|
343
|
+
this.gatewayUrl,
|
|
344
|
+
this.projectId,
|
|
345
|
+
this.apiKey,
|
|
346
|
+
targetLang,
|
|
347
|
+
missing
|
|
348
|
+
);
|
|
349
|
+
for (const r of results) existing.set(r.sourceText, r.translatedText);
|
|
350
|
+
this.fetchedLanguages.set(targetLang, existing);
|
|
351
|
+
writeCachedTranslations(this.projectId, targetLang, existing);
|
|
352
|
+
}
|
|
318
353
|
async applyLanguage(targetLang) {
|
|
319
354
|
let cached = this.fetchedLanguages.get(targetLang);
|
|
320
355
|
if (!cached) {
|
|
@@ -666,6 +701,93 @@ function writePreferredLanguage(projectId, langCode) {
|
|
|
666
701
|
}
|
|
667
702
|
}
|
|
668
703
|
|
|
704
|
+
// src/prevent-flash.ts
|
|
705
|
+
var STYLE_ID = "dabalang-prevent-flash";
|
|
706
|
+
var READY_ATTR = "data-dabalang-ready";
|
|
707
|
+
var PREF_PREFIX = "dabalang:lang:";
|
|
708
|
+
var MAX_HIDE_MS = 1200;
|
|
709
|
+
function preventFlash(projectId) {
|
|
710
|
+
if (typeof document === "undefined") return;
|
|
711
|
+
let preferred = null;
|
|
712
|
+
try {
|
|
713
|
+
preferred = window.localStorage.getItem(PREF_PREFIX + projectId);
|
|
714
|
+
} catch {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (!preferred) return;
|
|
718
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
719
|
+
const style = document.createElement("style");
|
|
720
|
+
style.id = STYLE_ID;
|
|
721
|
+
style.textContent = `html:not([${READY_ATTR}]) body :not([data-dabalang-ignore]):not([data-dabalang-ignore] *) { visibility: hidden !important; }html:not([${READY_ATTR}]) body [data-dabalang-ignore], html:not([${READY_ATTR}]) body [data-dabalang-ignore] * { visibility: visible !important; }`;
|
|
722
|
+
(document.head || document.documentElement).appendChild(style);
|
|
723
|
+
window.setTimeout(reveal, MAX_HIDE_MS);
|
|
724
|
+
}
|
|
725
|
+
function reveal() {
|
|
726
|
+
if (typeof document === "undefined") return;
|
|
727
|
+
document.documentElement.setAttribute(READY_ATTR, "");
|
|
728
|
+
}
|
|
729
|
+
function inlineSnippet(projectId) {
|
|
730
|
+
return `<script>(function(){try{if(!localStorage.getItem('${PREF_PREFIX}${projectId}'))return;var s=document.createElement('style');s.id='${STYLE_ID}';s.textContent='html:not([${READY_ATTR}]) body :not([data-dabalang-ignore]):not([data-dabalang-ignore] *){visibility:hidden!important}';(document.head||document.documentElement).appendChild(s);setTimeout(function(){document.documentElement.setAttribute('${READY_ATTR}','')},${MAX_HIDE_MS});}catch(e){}})();</script>`;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/prewarm.ts
|
|
734
|
+
var GAP_MS = 600;
|
|
735
|
+
function whenIdle(fn) {
|
|
736
|
+
const ric = window.requestIdleCallback;
|
|
737
|
+
if (typeof ric === "function") {
|
|
738
|
+
ric(fn, { timeout: 3e3 });
|
|
739
|
+
} else {
|
|
740
|
+
window.setTimeout(fn, 1200);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function warmOrder(targetLanguages, activeLang) {
|
|
744
|
+
return targetLanguages.filter((code) => code !== activeLang);
|
|
745
|
+
}
|
|
746
|
+
var Prewarmer = class {
|
|
747
|
+
constructor(target) {
|
|
748
|
+
this.target = target;
|
|
749
|
+
this.running = false;
|
|
750
|
+
this.cancelled = false;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Warms `languages` one at a time. Calling again while a run is in
|
|
754
|
+
* flight cancels the old one first, so a visitor clicking quickly
|
|
755
|
+
* through pages warms the page they landed on rather than queueing up
|
|
756
|
+
* every page they passed through.
|
|
757
|
+
*/
|
|
758
|
+
start(languages) {
|
|
759
|
+
this.cancel();
|
|
760
|
+
const queue = languages.filter((lang) => !this.target.isFullyCached(lang));
|
|
761
|
+
if (queue.length === 0) return;
|
|
762
|
+
this.cancelled = false;
|
|
763
|
+
whenIdle(() => {
|
|
764
|
+
if (this.cancelled) return;
|
|
765
|
+
void this.run(queue);
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
async run(queue) {
|
|
769
|
+
if (this.running) return;
|
|
770
|
+
this.running = true;
|
|
771
|
+
try {
|
|
772
|
+
for (const lang of queue) {
|
|
773
|
+
if (this.cancelled) return;
|
|
774
|
+
try {
|
|
775
|
+
await this.target.warm(lang);
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
if (this.cancelled) return;
|
|
779
|
+
await new Promise((r) => window.setTimeout(r, GAP_MS));
|
|
780
|
+
}
|
|
781
|
+
} finally {
|
|
782
|
+
this.running = false;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
/** Stops after the language currently in flight. */
|
|
786
|
+
cancel() {
|
|
787
|
+
this.cancelled = true;
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
|
|
669
791
|
// src/ui/flag-svgs.ts
|
|
670
792
|
var FLAG_IMAGES = {
|
|
671
793
|
us: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAtCAMAAADFqPh+AAACClBMVEUZL13jrrHw0tQaMF7nurzsxsj46uv///+9PUT03t++rLjhqKv68PCcOkkfNWEwQ20lOmUcMV8dMmAvQ2wxRW4nPGc6TXXR1d9hcJAeNGE4S3MeM2A3SnI2SXEkOWV+iqQtQWs+UHdwfptndZQrP2o8T3Zba4s5THQoPWi+xNFSYoUgNmJKW39jcpEhNmPx8vVPYINZaYozR29ebY1reZc9UHbh5OpYaIkpPmiYordfb49IWX5MXYGTnbPS1t98iaM0R3BVZYcwRG4uQmwyRm8sQGqbpLh/i6WosMEqPmnn6e6Qm7FDVXtNXoLV2eFCVXqAjKU1SHE5THMmOmZod5XQ1d6apLiMl64iN2OGkqqHk6vc3+Z9iqTJztmcpbnN0tyPmrBUZIbf4ui5j5w/UXjNydO+nqnIu8UbMV7AxtNWZojCp7N4haD9/f7o6u6NmK8bMV+1vMvZ3eTM0dv8/f16hqGiq75sepccMl+eqLvr7fHL0Nr7/PyUnrNzgZ2WoLU7TnVldJJGWH3k5+yEkKmCjqeRm7G7ws/X2+Owt8dgb497iKJOX4JBU3nx8/Xi5etAUnh5haDHzNi6wc9ebo6OmbDFy9b09fd0gZ3Fsb2ep7onO2dicZAjOGSmrsBHWX3U2OFxf5tEVnustMW3vsyIk6tJWn9RYoTCyNR3hJ+oscJygJydprrny88smqUCAAAACXBIWXMAAC4jAAAuIwF4pT92AAACiUlEQVRIx9WURXMbQRCFOxk7tgMLkqyILLAtiy2DZEuKZWaMmSnMzMzMzMzwH/NGqcptd6uyB5Xf4aup3p6dhukh0tSafCXhY5nAXSwc21KcvY5/Bk61zaUb2hlR3e4yuO7dIxKVP3loJyqKdsDQES1SP3lTnIzkdphAr0cGPR5A8PpAn1egjauUlInNdrCYhz8aJpKnAm1Epgc3EU1wNMi/rs1TEj4ysp+uBAfGWsCRzwB7bAFtYzZQdbO0Kw4Pt0vkITS8JApZmpCz39UIQ6PLT5tXKwm+9ePw8jytA13TTkTy6SOWhd08leLuQvWCiSKZKeFsA0W3zIb+GoQEp5gQ1DcTDR04DKZqcLicnnUTVdVPIpWmu02al8RAhoYgeM/VAj56RjJj92uxdMw4QNqSoyRckvQOnOJN3sZ/Ks6ibBMN7604PInGUThZpVpttn09vIpqesEzJ2XwzRwg9JeAJf2Cep+dEq0jJ+OUJmTDB5IkesusQsYgWGlrgZIyBTNfvASej/ahYC/etaC901eQSu2dWs2CFZIhEgbPWdxgMEWv7ezVLSwHbwyC6lO1vxinXAj08RSvomChuR4+VYFqGKoDGlPVfhRove4BI0fwH5riFbR2VoKVnVbamaskPhisPObHMAzHnIyONUcExkIjpfuISTEJZvVqExlPXOYhnhoAj18bJuqq6eFdP1ShOZImsttsYKupCzSbyV/OfD4sjc1GkJ4vVxJR27cZJPql/isfotlmPEM/fuGGOeYzl2TeoV6wyQgvWDrOH5NxfsO+/+Q3bGERXFzQnKr/fnpXauq3cs55OqRvc64OUb4OZXFzgQ5lsVU5OrRUW7VCh7LYqmU6tERb9Qf3Y5Nqb/wo6gAAAABJRU5ErkJggg==",
|
|
@@ -717,7 +839,7 @@ var FLAG_IMAGES = {
|
|
|
717
839
|
};
|
|
718
840
|
|
|
719
841
|
// src/ui/language-switcher.ts
|
|
720
|
-
var
|
|
842
|
+
var STYLE_ID2 = "dabalang-switcher-styles";
|
|
721
843
|
var LANGUAGE_LABELS = {
|
|
722
844
|
en: "English",
|
|
723
845
|
es: "Espa\xF1ol",
|
|
@@ -811,9 +933,9 @@ var ALL_WORLD_LANGUAGES = [
|
|
|
811
933
|
{ code: "is", label: "\xCDslenska", flag: "is" }
|
|
812
934
|
];
|
|
813
935
|
function injectStyles() {
|
|
814
|
-
if (document.getElementById(
|
|
936
|
+
if (document.getElementById(STYLE_ID2)) return;
|
|
815
937
|
const style = document.createElement("style");
|
|
816
|
-
style.id =
|
|
938
|
+
style.id = STYLE_ID2;
|
|
817
939
|
style.textContent = `
|
|
818
940
|
.dabalang-trigger {
|
|
819
941
|
appearance: none;
|
|
@@ -1427,6 +1549,7 @@ var DabaLang = class {
|
|
|
1427
1549
|
this.metadata = null;
|
|
1428
1550
|
this.pathRouter = null;
|
|
1429
1551
|
this.navWatcher = null;
|
|
1552
|
+
this.prewarmer = null;
|
|
1430
1553
|
/** Tail of the re-translation chain; see retranslateCurrentPage. */
|
|
1431
1554
|
this.retranslating = Promise.resolve();
|
|
1432
1555
|
/** Currently active language (null = original). Tracked here rather
|
|
@@ -1442,9 +1565,11 @@ var DabaLang = class {
|
|
|
1442
1565
|
this.ready = this.init();
|
|
1443
1566
|
}
|
|
1444
1567
|
async init() {
|
|
1568
|
+
preventFlash(this.options.projectId);
|
|
1445
1569
|
try {
|
|
1446
1570
|
this.metadata = await fetchProjectMetadata(this.gatewayUrl, this.options.projectId, this.options.apiKey);
|
|
1447
1571
|
} catch (err) {
|
|
1572
|
+
reveal();
|
|
1448
1573
|
this.handleError(err);
|
|
1449
1574
|
return;
|
|
1450
1575
|
}
|
|
@@ -1496,6 +1621,8 @@ var DabaLang = class {
|
|
|
1496
1621
|
}
|
|
1497
1622
|
}
|
|
1498
1623
|
}
|
|
1624
|
+
reveal();
|
|
1625
|
+
this.startPrewarm();
|
|
1499
1626
|
}
|
|
1500
1627
|
/**
|
|
1501
1628
|
* Re-applies the active language to content that arrived after init.
|
|
@@ -1503,6 +1630,16 @@ var DabaLang = class {
|
|
|
1503
1630
|
* A no-op in the source language — there is nothing to apply — and on
|
|
1504
1631
|
* a site-translated page, where the page already *is* the translation.
|
|
1505
1632
|
*/
|
|
1633
|
+
/** Warms every configured language except the one on screen. */
|
|
1634
|
+
startPrewarm() {
|
|
1635
|
+
if (!this.options.prewarm || !this.applier) return;
|
|
1636
|
+
const applier = this.applier;
|
|
1637
|
+
this.prewarmer ?? (this.prewarmer = new Prewarmer({
|
|
1638
|
+
warm: (lang) => applier.warmLanguage(lang),
|
|
1639
|
+
isFullyCached: (lang) => applier.isFullyCached(lang)
|
|
1640
|
+
}));
|
|
1641
|
+
this.prewarmer.start(warmOrder(this.readyLanguages(), this.activeLang));
|
|
1642
|
+
}
|
|
1506
1643
|
async retranslateCurrentPage() {
|
|
1507
1644
|
if (!this.applier || this.activeLang === null) return;
|
|
1508
1645
|
if (this.isSiteTranslated(this.activeLang)) return;
|
|
@@ -1514,6 +1651,7 @@ var DabaLang = class {
|
|
|
1514
1651
|
} catch (err) {
|
|
1515
1652
|
this.handleError(err);
|
|
1516
1653
|
}
|
|
1654
|
+
this.startPrewarm();
|
|
1517
1655
|
});
|
|
1518
1656
|
await this.retranslating;
|
|
1519
1657
|
}
|
|
@@ -1564,6 +1702,7 @@ var DabaLang = class {
|
|
|
1564
1702
|
this.activeLang = langCode;
|
|
1565
1703
|
this.switcher.setActive(langCode);
|
|
1566
1704
|
writePreferredLanguage(this.options.projectId, langCode);
|
|
1705
|
+
this.startPrewarm();
|
|
1567
1706
|
this.options.onLanguageChange?.(langCode ?? this.metadata?.sourceLang ?? "");
|
|
1568
1707
|
} catch (err) {
|
|
1569
1708
|
this.handleError(err);
|
|
@@ -1606,6 +1745,7 @@ var DabaLang = class {
|
|
|
1606
1745
|
}
|
|
1607
1746
|
destroy() {
|
|
1608
1747
|
this.navWatcher?.stop();
|
|
1748
|
+
this.prewarmer?.cancel();
|
|
1609
1749
|
this.navWatcher = null;
|
|
1610
1750
|
this.editor?.detach();
|
|
1611
1751
|
this.switcher?.destroy();
|
|
@@ -1615,5 +1755,8 @@ var DabaLang = class {
|
|
|
1615
1755
|
|
|
1616
1756
|
exports.DabaLang = DabaLang;
|
|
1617
1757
|
exports.PathRouter = PathRouter;
|
|
1758
|
+
exports.inlineSnippet = inlineSnippet;
|
|
1759
|
+
exports.preventFlash = preventFlash;
|
|
1760
|
+
exports.reveal = reveal;
|
|
1618
1761
|
//# sourceMappingURL=dabalang.cjs.map
|
|
1619
1762
|
//# sourceMappingURL=dabalang.cjs.map
|