@dabalabs/lang 0.0.6-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 +202 -14
- 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 +200 -15
- 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.d.cts
CHANGED
|
@@ -30,6 +30,17 @@ interface DabaLangOptions {
|
|
|
30
30
|
* project's languages directly, anchored to the trigger — for sites
|
|
31
31
|
* that want the languages visible in one click, no overlay. */
|
|
32
32
|
view?: "modal" | "dropdown";
|
|
33
|
+
/**
|
|
34
|
+
* Translate each page into every configured language in the background,
|
|
35
|
+
* not just the one the visitor is reading.
|
|
36
|
+
*
|
|
37
|
+
* The gateway caches by content hash, so this makes the first visitor to
|
|
38
|
+
* a page pay for it once and everyone after — in any language — get it
|
|
39
|
+
* instantly. Off by default: it is extra requests per page view, and
|
|
40
|
+
* that should be a site owner's decision rather than something the
|
|
41
|
+
* widget starts doing on its own.
|
|
42
|
+
*/
|
|
43
|
+
prewarm?: boolean;
|
|
33
44
|
onError?: (error: DabaLangError) => void;
|
|
34
45
|
onLanguageChange?: (langCode: string) => void;
|
|
35
46
|
}
|
|
@@ -89,6 +100,7 @@ declare class DabaLang {
|
|
|
89
100
|
private metadata;
|
|
90
101
|
private pathRouter;
|
|
91
102
|
private navWatcher;
|
|
103
|
+
private prewarmer;
|
|
92
104
|
/** Tail of the re-translation chain; see retranslateCurrentPage. */
|
|
93
105
|
private retranslating;
|
|
94
106
|
/** Currently active language (null = original). Tracked here rather
|
|
@@ -104,6 +116,8 @@ declare class DabaLang {
|
|
|
104
116
|
* A no-op in the source language — there is nothing to apply — and on
|
|
105
117
|
* a site-translated page, where the page already *is* the translation.
|
|
106
118
|
*/
|
|
119
|
+
/** Warms every configured language except the one on screen. */
|
|
120
|
+
private startPrewarm;
|
|
107
121
|
private retranslateCurrentPage;
|
|
108
122
|
/** True when the site serves this language's content itself (CMS,
|
|
109
123
|
* localized build): a pre-translated ("developer") language. Combined
|
|
@@ -180,4 +194,19 @@ declare class PathRouter {
|
|
|
180
194
|
private matchPrefix;
|
|
181
195
|
}
|
|
182
196
|
|
|
183
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Call as early as possible — ideally an inline `<script>` in `<head>`,
|
|
199
|
+
* before the body parses. Safe to call more than once.
|
|
200
|
+
*
|
|
201
|
+
* Text is hidden with `visibility`, not `display`: layout still happens,
|
|
202
|
+
* so nothing reflows when it is revealed and the page does not jump.
|
|
203
|
+
*/
|
|
204
|
+
declare function preventFlash(projectId: string): void;
|
|
205
|
+
/** Reveals the page. Idempotent, and safe to call before preventFlash. */
|
|
206
|
+
declare function reveal(): void;
|
|
207
|
+
/** The inline snippet, for sites that want this without loading the widget
|
|
208
|
+
* first. Kept in one place so the published copy cannot drift from the
|
|
209
|
+
* implementation above. */
|
|
210
|
+
declare function inlineSnippet(projectId: string): string;
|
|
211
|
+
|
|
212
|
+
export { DabaLang, type DabaLangError, type DabaLangOptions, type LanguageConfig, PathRouter, type ProjectMetadata, type TranslationResult, inlineSnippet, preventFlash, reveal };
|
package/dist/dabalang.d.ts
CHANGED
|
@@ -30,6 +30,17 @@ interface DabaLangOptions {
|
|
|
30
30
|
* project's languages directly, anchored to the trigger — for sites
|
|
31
31
|
* that want the languages visible in one click, no overlay. */
|
|
32
32
|
view?: "modal" | "dropdown";
|
|
33
|
+
/**
|
|
34
|
+
* Translate each page into every configured language in the background,
|
|
35
|
+
* not just the one the visitor is reading.
|
|
36
|
+
*
|
|
37
|
+
* The gateway caches by content hash, so this makes the first visitor to
|
|
38
|
+
* a page pay for it once and everyone after — in any language — get it
|
|
39
|
+
* instantly. Off by default: it is extra requests per page view, and
|
|
40
|
+
* that should be a site owner's decision rather than something the
|
|
41
|
+
* widget starts doing on its own.
|
|
42
|
+
*/
|
|
43
|
+
prewarm?: boolean;
|
|
33
44
|
onError?: (error: DabaLangError) => void;
|
|
34
45
|
onLanguageChange?: (langCode: string) => void;
|
|
35
46
|
}
|
|
@@ -89,6 +100,7 @@ declare class DabaLang {
|
|
|
89
100
|
private metadata;
|
|
90
101
|
private pathRouter;
|
|
91
102
|
private navWatcher;
|
|
103
|
+
private prewarmer;
|
|
92
104
|
/** Tail of the re-translation chain; see retranslateCurrentPage. */
|
|
93
105
|
private retranslating;
|
|
94
106
|
/** Currently active language (null = original). Tracked here rather
|
|
@@ -104,6 +116,8 @@ declare class DabaLang {
|
|
|
104
116
|
* A no-op in the source language — there is nothing to apply — and on
|
|
105
117
|
* a site-translated page, where the page already *is* the translation.
|
|
106
118
|
*/
|
|
119
|
+
/** Warms every configured language except the one on screen. */
|
|
120
|
+
private startPrewarm;
|
|
107
121
|
private retranslateCurrentPage;
|
|
108
122
|
/** True when the site serves this language's content itself (CMS,
|
|
109
123
|
* localized build): a pre-translated ("developer") language. Combined
|
|
@@ -180,4 +194,19 @@ declare class PathRouter {
|
|
|
180
194
|
private matchPrefix;
|
|
181
195
|
}
|
|
182
196
|
|
|
183
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Call as early as possible — ideally an inline `<script>` in `<head>`,
|
|
199
|
+
* before the body parses. Safe to call more than once.
|
|
200
|
+
*
|
|
201
|
+
* Text is hidden with `visibility`, not `display`: layout still happens,
|
|
202
|
+
* so nothing reflows when it is revealed and the page does not jump.
|
|
203
|
+
*/
|
|
204
|
+
declare function preventFlash(projectId: string): void;
|
|
205
|
+
/** Reveals the page. Idempotent, and safe to call before preventFlash. */
|
|
206
|
+
declare function reveal(): void;
|
|
207
|
+
/** The inline snippet, for sites that want this without loading the widget
|
|
208
|
+
* first. Kept in one place so the published copy cannot drift from the
|
|
209
|
+
* implementation above. */
|
|
210
|
+
declare function inlineSnippet(projectId: string): string;
|
|
211
|
+
|
|
212
|
+
export { DabaLang, type DabaLangError, type DabaLangOptions, type LanguageConfig, PathRouter, type ProjectMetadata, type TranslationResult, inlineSnippet, preventFlash, reveal };
|
package/dist/dabalang.js
CHANGED
|
@@ -143,6 +143,35 @@ async function submitCorrection(gatewayUrl, projectId, apiKey, targetLang, sourc
|
|
|
143
143
|
await parseOrThrow(response);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// src/translation-cache.ts
|
|
147
|
+
var PREFIX = "dabalang:tcache:";
|
|
148
|
+
var VERSION = 1;
|
|
149
|
+
var MAX_ENTRY_BYTES = 512 * 1024;
|
|
150
|
+
function key(projectId, lang) {
|
|
151
|
+
return `${PREFIX}${projectId}:${lang}`;
|
|
152
|
+
}
|
|
153
|
+
function readCachedTranslations(projectId, lang) {
|
|
154
|
+
try {
|
|
155
|
+
const raw = window.localStorage.getItem(key(projectId, lang));
|
|
156
|
+
if (!raw) return null;
|
|
157
|
+
const parsed = JSON.parse(raw);
|
|
158
|
+
if (!parsed || parsed.v !== VERSION || typeof parsed.t !== "object") return null;
|
|
159
|
+
return new Map(Object.entries(parsed.t));
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function writeCachedTranslations(projectId, lang, translations) {
|
|
165
|
+
try {
|
|
166
|
+
if (translations.size === 0) return;
|
|
167
|
+
const payload = { v: VERSION, t: Object.fromEntries(translations) };
|
|
168
|
+
const serialised = JSON.stringify(payload);
|
|
169
|
+
if (serialised.length > MAX_ENTRY_BYTES) return;
|
|
170
|
+
window.localStorage.setItem(key(projectId, lang), serialised);
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
146
175
|
// src/dom/ignore.ts
|
|
147
176
|
var IGNORE_ATTR = "data-dabalang-ignore";
|
|
148
177
|
var SENSITIVE_ELEMENT_SELECTOR = `[${IGNORE_ATTR}], input[type="password"], input[type="hidden"], [autocomplete="current-password"], [autocomplete="new-password"], [autocomplete^="cc-"]`;
|
|
@@ -273,8 +302,63 @@ var TranslationApplier = class {
|
|
|
273
302
|
}
|
|
274
303
|
return null;
|
|
275
304
|
}
|
|
305
|
+
/** Writes every translation this map covers into the DOM. */
|
|
306
|
+
paint(translations) {
|
|
307
|
+
let painted = 0;
|
|
308
|
+
for (const [sourceText, nodes] of this.groups) {
|
|
309
|
+
const translated = translations.get(sourceText);
|
|
310
|
+
if (translated === void 0) continue;
|
|
311
|
+
for (const node of nodes) node.textContent = translated;
|
|
312
|
+
painted += 1;
|
|
313
|
+
}
|
|
314
|
+
return painted;
|
|
315
|
+
}
|
|
316
|
+
/** True when every string on this page is already translated for
|
|
317
|
+
* `targetLang`, so warming it would be a request that changes nothing. */
|
|
318
|
+
isFullyCached(targetLang) {
|
|
319
|
+
const cached = this.fetchedLanguages.get(targetLang) ?? readCachedTranslations(this.projectId, targetLang) ?? void 0;
|
|
320
|
+
if (!cached) return false;
|
|
321
|
+
return this.sourceTexts().every((t) => cached.has(t));
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Translates this page into `targetLang` WITHOUT touching the DOM.
|
|
325
|
+
*
|
|
326
|
+
* Used to warm languages the visitor is not reading. The gateway caches
|
|
327
|
+
* by content hash, so the first visitor to a page pays for it and every
|
|
328
|
+
* later visitor — in any language — gets it instantly. Painting here
|
|
329
|
+
* would replace the text the visitor is currently reading with a
|
|
330
|
+
* language they did not ask for, so this is deliberately fetch-only and
|
|
331
|
+
* shares nothing with applyLanguage beyond the cache it fills.
|
|
332
|
+
*/
|
|
333
|
+
async warmLanguage(targetLang) {
|
|
334
|
+
const existing = this.fetchedLanguages.get(targetLang) ?? readCachedTranslations(this.projectId, targetLang) ?? /* @__PURE__ */ new Map();
|
|
335
|
+
const missing = this.sourceTexts().filter((t) => !existing.has(t));
|
|
336
|
+
if (missing.length === 0) {
|
|
337
|
+
this.fetchedLanguages.set(targetLang, existing);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const results = await batchTranslate(
|
|
341
|
+
this.gatewayUrl,
|
|
342
|
+
this.projectId,
|
|
343
|
+
this.apiKey,
|
|
344
|
+
targetLang,
|
|
345
|
+
missing
|
|
346
|
+
);
|
|
347
|
+
for (const r of results) existing.set(r.sourceText, r.translatedText);
|
|
348
|
+
this.fetchedLanguages.set(targetLang, existing);
|
|
349
|
+
writeCachedTranslations(this.projectId, targetLang, existing);
|
|
350
|
+
}
|
|
276
351
|
async applyLanguage(targetLang) {
|
|
277
|
-
|
|
352
|
+
let cached = this.fetchedLanguages.get(targetLang);
|
|
353
|
+
if (!cached) {
|
|
354
|
+
const stored = readCachedTranslations(this.projectId, targetLang);
|
|
355
|
+
if (stored && stored.size > 0) {
|
|
356
|
+
cached = stored;
|
|
357
|
+
this.fetchedLanguages.set(targetLang, stored);
|
|
358
|
+
this.paint(stored);
|
|
359
|
+
this.activeLang = targetLang;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
278
362
|
const allTexts = this.sourceTexts();
|
|
279
363
|
const missing = cached ? allTexts.filter((t) => !cached.has(t)) : allTexts;
|
|
280
364
|
if (missing.length > 0) {
|
|
@@ -282,13 +366,9 @@ var TranslationApplier = class {
|
|
|
282
366
|
const map = cached ?? /* @__PURE__ */ new Map();
|
|
283
367
|
for (const r of results) map.set(r.sourceText, r.translatedText);
|
|
284
368
|
this.fetchedLanguages.set(targetLang, map);
|
|
369
|
+
writeCachedTranslations(this.projectId, targetLang, map);
|
|
285
370
|
}
|
|
286
|
-
|
|
287
|
-
for (const [sourceText, nodes] of this.groups) {
|
|
288
|
-
const translated = translations.get(sourceText);
|
|
289
|
-
if (translated === void 0) continue;
|
|
290
|
-
for (const node of nodes) node.textContent = translated;
|
|
291
|
-
}
|
|
371
|
+
this.paint(this.fetchedLanguages.get(targetLang));
|
|
292
372
|
this.activeLang = targetLang;
|
|
293
373
|
}
|
|
294
374
|
restoreOriginal() {
|
|
@@ -600,10 +680,10 @@ var PathRouter = class {
|
|
|
600
680
|
};
|
|
601
681
|
|
|
602
682
|
// src/preference.ts
|
|
603
|
-
var
|
|
683
|
+
var PREFIX2 = "dabalang:lang:";
|
|
604
684
|
function readPreferredLanguage(projectId) {
|
|
605
685
|
try {
|
|
606
|
-
return window.localStorage.getItem(
|
|
686
|
+
return window.localStorage.getItem(PREFIX2 + projectId);
|
|
607
687
|
} catch {
|
|
608
688
|
return null;
|
|
609
689
|
}
|
|
@@ -611,14 +691,101 @@ function readPreferredLanguage(projectId) {
|
|
|
611
691
|
function writePreferredLanguage(projectId, langCode) {
|
|
612
692
|
try {
|
|
613
693
|
if (langCode === null) {
|
|
614
|
-
window.localStorage.removeItem(
|
|
694
|
+
window.localStorage.removeItem(PREFIX2 + projectId);
|
|
615
695
|
} else {
|
|
616
|
-
window.localStorage.setItem(
|
|
696
|
+
window.localStorage.setItem(PREFIX2 + projectId, langCode);
|
|
617
697
|
}
|
|
618
698
|
} catch {
|
|
619
699
|
}
|
|
620
700
|
}
|
|
621
701
|
|
|
702
|
+
// src/prevent-flash.ts
|
|
703
|
+
var STYLE_ID = "dabalang-prevent-flash";
|
|
704
|
+
var READY_ATTR = "data-dabalang-ready";
|
|
705
|
+
var PREF_PREFIX = "dabalang:lang:";
|
|
706
|
+
var MAX_HIDE_MS = 1200;
|
|
707
|
+
function preventFlash(projectId) {
|
|
708
|
+
if (typeof document === "undefined") return;
|
|
709
|
+
let preferred = null;
|
|
710
|
+
try {
|
|
711
|
+
preferred = window.localStorage.getItem(PREF_PREFIX + projectId);
|
|
712
|
+
} catch {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (!preferred) return;
|
|
716
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
717
|
+
const style = document.createElement("style");
|
|
718
|
+
style.id = STYLE_ID;
|
|
719
|
+
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; }`;
|
|
720
|
+
(document.head || document.documentElement).appendChild(style);
|
|
721
|
+
window.setTimeout(reveal, MAX_HIDE_MS);
|
|
722
|
+
}
|
|
723
|
+
function reveal() {
|
|
724
|
+
if (typeof document === "undefined") return;
|
|
725
|
+
document.documentElement.setAttribute(READY_ATTR, "");
|
|
726
|
+
}
|
|
727
|
+
function inlineSnippet(projectId) {
|
|
728
|
+
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>`;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/prewarm.ts
|
|
732
|
+
var GAP_MS = 600;
|
|
733
|
+
function whenIdle(fn) {
|
|
734
|
+
const ric = window.requestIdleCallback;
|
|
735
|
+
if (typeof ric === "function") {
|
|
736
|
+
ric(fn, { timeout: 3e3 });
|
|
737
|
+
} else {
|
|
738
|
+
window.setTimeout(fn, 1200);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function warmOrder(targetLanguages, activeLang) {
|
|
742
|
+
return targetLanguages.filter((code) => code !== activeLang);
|
|
743
|
+
}
|
|
744
|
+
var Prewarmer = class {
|
|
745
|
+
constructor(target) {
|
|
746
|
+
this.target = target;
|
|
747
|
+
this.running = false;
|
|
748
|
+
this.cancelled = false;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Warms `languages` one at a time. Calling again while a run is in
|
|
752
|
+
* flight cancels the old one first, so a visitor clicking quickly
|
|
753
|
+
* through pages warms the page they landed on rather than queueing up
|
|
754
|
+
* every page they passed through.
|
|
755
|
+
*/
|
|
756
|
+
start(languages) {
|
|
757
|
+
this.cancel();
|
|
758
|
+
const queue = languages.filter((lang) => !this.target.isFullyCached(lang));
|
|
759
|
+
if (queue.length === 0) return;
|
|
760
|
+
this.cancelled = false;
|
|
761
|
+
whenIdle(() => {
|
|
762
|
+
if (this.cancelled) return;
|
|
763
|
+
void this.run(queue);
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
async run(queue) {
|
|
767
|
+
if (this.running) return;
|
|
768
|
+
this.running = true;
|
|
769
|
+
try {
|
|
770
|
+
for (const lang of queue) {
|
|
771
|
+
if (this.cancelled) return;
|
|
772
|
+
try {
|
|
773
|
+
await this.target.warm(lang);
|
|
774
|
+
} catch {
|
|
775
|
+
}
|
|
776
|
+
if (this.cancelled) return;
|
|
777
|
+
await new Promise((r) => window.setTimeout(r, GAP_MS));
|
|
778
|
+
}
|
|
779
|
+
} finally {
|
|
780
|
+
this.running = false;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
/** Stops after the language currently in flight. */
|
|
784
|
+
cancel() {
|
|
785
|
+
this.cancelled = true;
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
622
789
|
// src/ui/flag-svgs.ts
|
|
623
790
|
var FLAG_IMAGES = {
|
|
624
791
|
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==",
|
|
@@ -670,7 +837,7 @@ var FLAG_IMAGES = {
|
|
|
670
837
|
};
|
|
671
838
|
|
|
672
839
|
// src/ui/language-switcher.ts
|
|
673
|
-
var
|
|
840
|
+
var STYLE_ID2 = "dabalang-switcher-styles";
|
|
674
841
|
var LANGUAGE_LABELS = {
|
|
675
842
|
en: "English",
|
|
676
843
|
es: "Espa\xF1ol",
|
|
@@ -764,9 +931,9 @@ var ALL_WORLD_LANGUAGES = [
|
|
|
764
931
|
{ code: "is", label: "\xCDslenska", flag: "is" }
|
|
765
932
|
];
|
|
766
933
|
function injectStyles() {
|
|
767
|
-
if (document.getElementById(
|
|
934
|
+
if (document.getElementById(STYLE_ID2)) return;
|
|
768
935
|
const style = document.createElement("style");
|
|
769
|
-
style.id =
|
|
936
|
+
style.id = STYLE_ID2;
|
|
770
937
|
style.textContent = `
|
|
771
938
|
.dabalang-trigger {
|
|
772
939
|
appearance: none;
|
|
@@ -1380,6 +1547,7 @@ var DabaLang = class {
|
|
|
1380
1547
|
this.metadata = null;
|
|
1381
1548
|
this.pathRouter = null;
|
|
1382
1549
|
this.navWatcher = null;
|
|
1550
|
+
this.prewarmer = null;
|
|
1383
1551
|
/** Tail of the re-translation chain; see retranslateCurrentPage. */
|
|
1384
1552
|
this.retranslating = Promise.resolve();
|
|
1385
1553
|
/** Currently active language (null = original). Tracked here rather
|
|
@@ -1395,9 +1563,11 @@ var DabaLang = class {
|
|
|
1395
1563
|
this.ready = this.init();
|
|
1396
1564
|
}
|
|
1397
1565
|
async init() {
|
|
1566
|
+
preventFlash(this.options.projectId);
|
|
1398
1567
|
try {
|
|
1399
1568
|
this.metadata = await fetchProjectMetadata(this.gatewayUrl, this.options.projectId, this.options.apiKey);
|
|
1400
1569
|
} catch (err) {
|
|
1570
|
+
reveal();
|
|
1401
1571
|
this.handleError(err);
|
|
1402
1572
|
return;
|
|
1403
1573
|
}
|
|
@@ -1449,6 +1619,8 @@ var DabaLang = class {
|
|
|
1449
1619
|
}
|
|
1450
1620
|
}
|
|
1451
1621
|
}
|
|
1622
|
+
reveal();
|
|
1623
|
+
this.startPrewarm();
|
|
1452
1624
|
}
|
|
1453
1625
|
/**
|
|
1454
1626
|
* Re-applies the active language to content that arrived after init.
|
|
@@ -1456,6 +1628,16 @@ var DabaLang = class {
|
|
|
1456
1628
|
* A no-op in the source language — there is nothing to apply — and on
|
|
1457
1629
|
* a site-translated page, where the page already *is* the translation.
|
|
1458
1630
|
*/
|
|
1631
|
+
/** Warms every configured language except the one on screen. */
|
|
1632
|
+
startPrewarm() {
|
|
1633
|
+
if (!this.options.prewarm || !this.applier) return;
|
|
1634
|
+
const applier = this.applier;
|
|
1635
|
+
this.prewarmer ?? (this.prewarmer = new Prewarmer({
|
|
1636
|
+
warm: (lang) => applier.warmLanguage(lang),
|
|
1637
|
+
isFullyCached: (lang) => applier.isFullyCached(lang)
|
|
1638
|
+
}));
|
|
1639
|
+
this.prewarmer.start(warmOrder(this.readyLanguages(), this.activeLang));
|
|
1640
|
+
}
|
|
1459
1641
|
async retranslateCurrentPage() {
|
|
1460
1642
|
if (!this.applier || this.activeLang === null) return;
|
|
1461
1643
|
if (this.isSiteTranslated(this.activeLang)) return;
|
|
@@ -1467,6 +1649,7 @@ var DabaLang = class {
|
|
|
1467
1649
|
} catch (err) {
|
|
1468
1650
|
this.handleError(err);
|
|
1469
1651
|
}
|
|
1652
|
+
this.startPrewarm();
|
|
1470
1653
|
});
|
|
1471
1654
|
await this.retranslating;
|
|
1472
1655
|
}
|
|
@@ -1517,6 +1700,7 @@ var DabaLang = class {
|
|
|
1517
1700
|
this.activeLang = langCode;
|
|
1518
1701
|
this.switcher.setActive(langCode);
|
|
1519
1702
|
writePreferredLanguage(this.options.projectId, langCode);
|
|
1703
|
+
this.startPrewarm();
|
|
1520
1704
|
this.options.onLanguageChange?.(langCode ?? this.metadata?.sourceLang ?? "");
|
|
1521
1705
|
} catch (err) {
|
|
1522
1706
|
this.handleError(err);
|
|
@@ -1559,6 +1743,7 @@ var DabaLang = class {
|
|
|
1559
1743
|
}
|
|
1560
1744
|
destroy() {
|
|
1561
1745
|
this.navWatcher?.stop();
|
|
1746
|
+
this.prewarmer?.cancel();
|
|
1562
1747
|
this.navWatcher = null;
|
|
1563
1748
|
this.editor?.detach();
|
|
1564
1749
|
this.switcher?.destroy();
|
|
@@ -1566,6 +1751,6 @@ var DabaLang = class {
|
|
|
1566
1751
|
}
|
|
1567
1752
|
};
|
|
1568
1753
|
|
|
1569
|
-
export { DabaLang, PathRouter };
|
|
1754
|
+
export { DabaLang, PathRouter, inlineSnippet, preventFlash, reveal };
|
|
1570
1755
|
//# sourceMappingURL=dabalang.js.map
|
|
1571
1756
|
//# sourceMappingURL=dabalang.js.map
|