@lionrapid/core 0.5.0 → 0.6.1
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/base/event-emitter.d.ts.map +1 -1
- package/dist/base/event-emitter.js +2 -1
- package/dist/base/event-emitter.js.map +1 -1
- package/dist/browser/attribute-extractor.d.ts.map +1 -1
- package/dist/browser/attribute-extractor.js +2 -1
- package/dist/browser/attribute-extractor.js.map +1 -1
- package/dist/browser/auto-init.d.ts +73 -0
- package/dist/browser/auto-init.d.ts.map +1 -1
- package/dist/browser/auto-init.js +193 -62
- package/dist/browser/auto-init.js.map +1 -1
- package/dist/browser/block-extractor.d.ts.map +1 -1
- package/dist/browser/block-extractor.js +3 -1
- package/dist/browser/block-extractor.js.map +1 -1
- package/dist/browser/editor/editor-api.d.ts +50 -6
- package/dist/browser/editor/editor-api.d.ts.map +1 -1
- package/dist/browser/editor/editor-api.js +121 -19
- package/dist/browser/editor/editor-api.js.map +1 -1
- package/dist/browser/editor/editor-init.js +4 -1
- package/dist/browser/editor/editor-init.js.map +1 -1
- package/dist/browser/editor/sidebar.d.ts +4 -3
- package/dist/browser/editor/sidebar.d.ts.map +1 -1
- package/dist/browser/editor/sidebar.js +93 -27
- package/dist/browser/editor/sidebar.js.map +1 -1
- package/dist/browser/editor/snapshot-viewer.d.ts.map +1 -1
- package/dist/browser/editor/snapshot-viewer.js +32 -3
- package/dist/browser/editor/snapshot-viewer.js.map +1 -1
- package/dist/browser/lionrapid.auto.dev.js +444 -149
- package/dist/browser/lionrapid.auto.dev.js.map +4 -4
- package/dist/browser/lionrapid.auto.js +444 -149
- package/dist/browser/lionrapid.auto.js.map +4 -4
- package/dist/browser/lionrapid.auto.min.js +4 -3
- package/dist/browser/lionrapid.auto.min.js.map +4 -4
- package/dist/browser/lionrapid.editor.js +1467 -1115
- package/dist/browser/lionrapid.editor.js.map +4 -4
- package/dist/browser/lionrapid.editor.min.js +4 -3
- package/dist/browser/lionrapid.editor.min.js.map +4 -4
- package/dist/browser/lionrapid.esm.js +590 -101
- package/dist/browser/lionrapid.esm.js.map +4 -4
- package/dist/browser/lionrapid.esm.min.js +3 -2
- package/dist/browser/lionrapid.esm.min.js.map +4 -4
- package/dist/browser/lionrapid.iife.dev.js +590 -101
- package/dist/browser/lionrapid.iife.dev.js.map +4 -4
- package/dist/browser/lionrapid.iife.js +590 -101
- package/dist/browser/lionrapid.iife.js.map +4 -4
- package/dist/browser/lionrapid.iife.min.js +3 -2
- package/dist/browser/lionrapid.iife.min.js.map +4 -4
- package/dist/browser/lionrapid.umd.js +590 -101
- package/dist/browser/lionrapid.umd.js.map +4 -4
- package/dist/browser/lionrapid.umd.min.js +3 -2
- package/dist/browser/lionrapid.umd.min.js.map +4 -4
- package/dist/browser/sanitizer.d.ts +37 -0
- package/dist/browser/sanitizer.d.ts.map +1 -0
- package/dist/browser/sanitizer.js +93 -0
- package/dist/browser/sanitizer.js.map +1 -0
- package/dist/builder.d.ts +8 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +33 -5
- package/dist/builder.js.map +1 -1
- package/dist/config/lionrapid.config.d.ts +0 -1
- package/dist/config/lionrapid.config.d.ts.map +1 -1
- package/dist/config/repository.config.d.ts +2 -2
- package/dist/config/repository.config.d.ts.map +1 -1
- package/dist/content/index.d.ts +3 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +5 -0
- package/dist/content/index.js.map +1 -1
- package/dist/content/sanitizer/HTMLSanitizer.d.ts +105 -0
- package/dist/content/sanitizer/HTMLSanitizer.d.ts.map +1 -0
- package/dist/content/sanitizer/HTMLSanitizer.js +257 -0
- package/dist/content/sanitizer/HTMLSanitizer.js.map +1 -0
- package/dist/content/serializer/HTMLSerializer.d.ts +6 -2
- package/dist/content/serializer/HTMLSerializer.d.ts.map +1 -1
- package/dist/content/serializer/HTMLSerializer.js +16 -8
- package/dist/content/serializer/HTMLSerializer.js.map +1 -1
- package/dist/content/splitter/BlockSplitter.d.ts +66 -0
- package/dist/content/splitter/BlockSplitter.d.ts.map +1 -0
- package/dist/content/splitter/BlockSplitter.js +219 -0
- package/dist/content/splitter/BlockSplitter.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/lionRapid.d.ts +2 -2
- package/dist/lionRapid.d.ts.map +1 -1
- package/dist/lionRapid.js +19 -7
- package/dist/lionRapid.js.map +1 -1
- package/dist/repositories/network-repository.d.ts +30 -8
- package/dist/repositories/network-repository.d.ts.map +1 -1
- package/dist/repositories/network-repository.js +88 -74
- package/dist/repositories/network-repository.js.map +1 -1
- package/dist/types/lionRapid.types.d.ts +2 -0
- package/dist/types/lionRapid.types.d.ts.map +1 -1
- package/dist/types/translation.types.d.ts +0 -3
- package/dist/types/translation.types.d.ts.map +1 -1
- package/dist/utils/context-utils.d.ts +1 -2
- package/dist/utils/context-utils.d.ts.map +1 -1
- package/dist/utils/context-utils.js +3 -6
- package/dist/utils/context-utils.js.map +1 -1
- package/dist/utils/locale.d.ts +57 -0
- package/dist/utils/locale.d.ts.map +1 -0
- package/dist/utils/locale.js +116 -0
- package/dist/utils/locale.js.map +1 -0
- package/dist/utils/translation-flow.d.ts +1 -1
- package/dist/utils/translation-flow.d.ts.map +1 -1
- package/dist/utils/translation-flow.js +2 -3
- package/dist/utils/translation-flow.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
2
|
+
var __lionrapidAutoExports = (() => {
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -336,6 +336,49 @@ var LionRapid = (() => {
|
|
|
336
336
|
packageEmoji: "\u{1F981}"
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
+
// src/utils/locale.ts
|
|
340
|
+
var LOCALE_TAG_REGEX = /^[a-z]{2,3}(?:-[a-z0-9]{2,4})*$/i;
|
|
341
|
+
var RTL_LANGUAGES = /* @__PURE__ */ new Set(["ar", "fa", "he", "ur", "yi"]);
|
|
342
|
+
function primarySubtag(tag) {
|
|
343
|
+
return tag.split("-")[0].toLowerCase();
|
|
344
|
+
}
|
|
345
|
+
function normalizeLocale(tag) {
|
|
346
|
+
const trimmed = tag.trim();
|
|
347
|
+
if (!trimmed) return trimmed;
|
|
348
|
+
const [lang, ...rest] = trimmed.split("-");
|
|
349
|
+
const out = [lang.toLowerCase()];
|
|
350
|
+
for (const sub of rest) {
|
|
351
|
+
if (sub.length === 4) {
|
|
352
|
+
out.push(sub[0].toUpperCase() + sub.slice(1).toLowerCase());
|
|
353
|
+
} else if (sub.length === 2) {
|
|
354
|
+
out.push(sub.toUpperCase());
|
|
355
|
+
} else {
|
|
356
|
+
out.push(sub.toLowerCase());
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return out.join("-");
|
|
360
|
+
}
|
|
361
|
+
function matchLocale(requested, available, fallback) {
|
|
362
|
+
if (available.length === 0) return fallback;
|
|
363
|
+
const avail = available.map((raw) => ({
|
|
364
|
+
raw,
|
|
365
|
+
lower: normalizeLocale(raw).toLowerCase()
|
|
366
|
+
}));
|
|
367
|
+
for (const req of requested) {
|
|
368
|
+
if (!req) continue;
|
|
369
|
+
const reqLower = normalizeLocale(req).toLowerCase();
|
|
370
|
+
const exact = avail.find((a) => a.lower === reqLower);
|
|
371
|
+
if (exact) return exact.raw;
|
|
372
|
+
const primary = primarySubtag(req);
|
|
373
|
+
const byPrimary = avail.find((a) => a.lower === primary);
|
|
374
|
+
if (byPrimary) return byPrimary.raw;
|
|
375
|
+
}
|
|
376
|
+
return fallback;
|
|
377
|
+
}
|
|
378
|
+
function localeDir(tag) {
|
|
379
|
+
return RTL_LANGUAGES.has(primarySubtag(tag)) ? "rtl" : "ltr";
|
|
380
|
+
}
|
|
381
|
+
|
|
339
382
|
// src/base/event-emitter.ts
|
|
340
383
|
var EventEmitter = class {
|
|
341
384
|
constructor() {
|
|
@@ -624,7 +667,7 @@ var LionRapid = (() => {
|
|
|
624
667
|
complete: () => {
|
|
625
668
|
this.emit("language:change:success", {
|
|
626
669
|
context: this.createContext(requestId, [...baseTrace, "complete"]),
|
|
627
|
-
payload: { oldLocale, newLocale }
|
|
670
|
+
payload: { oldLocale, newLocale, dir: localeDir(newLocale) }
|
|
628
671
|
});
|
|
629
672
|
},
|
|
630
673
|
error: (error) => {
|
|
@@ -1034,12 +1077,11 @@ var LionRapid = (() => {
|
|
|
1034
1077
|
}
|
|
1035
1078
|
|
|
1036
1079
|
// src/utils/context-utils.ts
|
|
1037
|
-
function createTranslationContext(parsedKey, currentLocale, defaultNamespace
|
|
1080
|
+
function createTranslationContext(parsedKey, currentLocale, defaultNamespace) {
|
|
1038
1081
|
const effectiveNamespace = parsedKey.hasExplicitNamespace ? parsedKey.namespace : defaultNamespace;
|
|
1039
1082
|
return {
|
|
1040
1083
|
locale: currentLocale,
|
|
1041
|
-
namespace: effectiveNamespace
|
|
1042
|
-
fallbackLocale: fallbackLocale || currentLocale
|
|
1084
|
+
namespace: effectiveNamespace
|
|
1043
1085
|
};
|
|
1044
1086
|
}
|
|
1045
1087
|
|
|
@@ -1156,7 +1198,7 @@ var LionRapid = (() => {
|
|
|
1156
1198
|
}
|
|
1157
1199
|
|
|
1158
1200
|
// src/utils/translation-flow.ts
|
|
1159
|
-
function prepareTranslationRequest(key, currentLocale, defaultValueOrParams, paramsOrDefaultValue, defaultNamespace
|
|
1201
|
+
function prepareTranslationRequest(key, currentLocale, defaultValueOrParams, paramsOrDefaultValue, defaultNamespace) {
|
|
1160
1202
|
const startTime = performance.now();
|
|
1161
1203
|
const { defaultValue, params } = parseTranslationParams(
|
|
1162
1204
|
defaultValueOrParams,
|
|
@@ -1166,8 +1208,7 @@ var LionRapid = (() => {
|
|
|
1166
1208
|
const context = createTranslationContext(
|
|
1167
1209
|
parsedKey,
|
|
1168
1210
|
currentLocale,
|
|
1169
|
-
defaultNamespace
|
|
1170
|
-
fallbackLocale
|
|
1211
|
+
defaultNamespace
|
|
1171
1212
|
);
|
|
1172
1213
|
const individualCacheKey = createIndividualCacheKey(
|
|
1173
1214
|
context.locale,
|
|
@@ -1189,7 +1230,6 @@ var LionRapid = (() => {
|
|
|
1189
1230
|
locale: currentLocale,
|
|
1190
1231
|
namespace: context.namespace,
|
|
1191
1232
|
// ✅ Use effective namespace (explicit or default)
|
|
1192
|
-
fallbackLocale,
|
|
1193
1233
|
individualCacheKey,
|
|
1194
1234
|
// ✅ Ready-to-use cache key
|
|
1195
1235
|
namespaceCacheKey,
|
|
@@ -2299,6 +2339,157 @@ var LionRapid = (() => {
|
|
|
2299
2339
|
}
|
|
2300
2340
|
};
|
|
2301
2341
|
|
|
2342
|
+
// src/content/sanitizer/HTMLSanitizer.ts
|
|
2343
|
+
var SAFE_TAGS = /* @__PURE__ */ new Set([
|
|
2344
|
+
"a",
|
|
2345
|
+
"abbr",
|
|
2346
|
+
"b",
|
|
2347
|
+
"bdi",
|
|
2348
|
+
"bdo",
|
|
2349
|
+
"br",
|
|
2350
|
+
"cite",
|
|
2351
|
+
"code",
|
|
2352
|
+
"del",
|
|
2353
|
+
"dfn",
|
|
2354
|
+
"em",
|
|
2355
|
+
"i",
|
|
2356
|
+
"img",
|
|
2357
|
+
"ins",
|
|
2358
|
+
"kbd",
|
|
2359
|
+
"mark",
|
|
2360
|
+
"q",
|
|
2361
|
+
"s",
|
|
2362
|
+
"samp",
|
|
2363
|
+
"small",
|
|
2364
|
+
"span",
|
|
2365
|
+
"strong",
|
|
2366
|
+
"sub",
|
|
2367
|
+
"sup",
|
|
2368
|
+
"time",
|
|
2369
|
+
"u",
|
|
2370
|
+
"var",
|
|
2371
|
+
"wbr"
|
|
2372
|
+
]);
|
|
2373
|
+
var SAFE_ATTRS = /* @__PURE__ */ new Set([
|
|
2374
|
+
"alt",
|
|
2375
|
+
"cite",
|
|
2376
|
+
"class",
|
|
2377
|
+
"datetime",
|
|
2378
|
+
"dir",
|
|
2379
|
+
"height",
|
|
2380
|
+
"href",
|
|
2381
|
+
"id",
|
|
2382
|
+
"lang",
|
|
2383
|
+
"loading",
|
|
2384
|
+
"rel",
|
|
2385
|
+
"role",
|
|
2386
|
+
"src",
|
|
2387
|
+
"target",
|
|
2388
|
+
"title",
|
|
2389
|
+
"width"
|
|
2390
|
+
]);
|
|
2391
|
+
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "cite"]);
|
|
2392
|
+
var SAFE_STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
2393
|
+
"color",
|
|
2394
|
+
"background-color",
|
|
2395
|
+
"font-family",
|
|
2396
|
+
"font-size",
|
|
2397
|
+
"font-style",
|
|
2398
|
+
"font-weight",
|
|
2399
|
+
"letter-spacing",
|
|
2400
|
+
"line-height",
|
|
2401
|
+
"text-decoration",
|
|
2402
|
+
"text-decoration-color",
|
|
2403
|
+
"text-decoration-line",
|
|
2404
|
+
"text-decoration-style",
|
|
2405
|
+
"text-transform",
|
|
2406
|
+
"vertical-align",
|
|
2407
|
+
"white-space",
|
|
2408
|
+
"word-spacing"
|
|
2409
|
+
]);
|
|
2410
|
+
var DANGEROUS_CSS_VALUE = /url\s*\(|expression\s*\(|image-set\s*\(|element\s*\(|paint\s*\(|javascript:|\\|[<>{}@]/i;
|
|
2411
|
+
var SAFE_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
2412
|
+
var NAMED_ENTITIES = {
|
|
2413
|
+
amp: "&",
|
|
2414
|
+
lt: "<",
|
|
2415
|
+
gt: ">",
|
|
2416
|
+
quot: '"',
|
|
2417
|
+
apos: "'",
|
|
2418
|
+
nbsp: "\xA0",
|
|
2419
|
+
colon: ":",
|
|
2420
|
+
semi: ";",
|
|
2421
|
+
sol: "/",
|
|
2422
|
+
bsol: "\\",
|
|
2423
|
+
Tab: " ",
|
|
2424
|
+
NewLine: "\n"
|
|
2425
|
+
};
|
|
2426
|
+
function decodeEntities2(value) {
|
|
2427
|
+
return value.replace(
|
|
2428
|
+
/&(?:#[xX]([0-9a-fA-F]+);?|#(\d+);?|([a-zA-Z]+);)/g,
|
|
2429
|
+
(full, hex, dec, name) => {
|
|
2430
|
+
if (hex) return String.fromCodePoint(parseInt(hex, 16));
|
|
2431
|
+
if (dec) return String.fromCodePoint(parseInt(dec, 10));
|
|
2432
|
+
return NAMED_ENTITIES[name] ?? full;
|
|
2433
|
+
}
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
function escapeHtml(text) {
|
|
2437
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2438
|
+
}
|
|
2439
|
+
function sanitizeUrl(value) {
|
|
2440
|
+
const decoded = decodeEntities2(value);
|
|
2441
|
+
const probe = decoded.replace(/[\u0000-\u0020\u007f]/g, "").toLowerCase();
|
|
2442
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/.exec(probe);
|
|
2443
|
+
if (!scheme) return value;
|
|
2444
|
+
return SAFE_SCHEMES.has(scheme[1]) ? value : null;
|
|
2445
|
+
}
|
|
2446
|
+
function sanitizeStyle(value) {
|
|
2447
|
+
return filterCssDeclarations(value, true);
|
|
2448
|
+
}
|
|
2449
|
+
function filterCssDeclarations(value, requirePropAllowlist) {
|
|
2450
|
+
const decoded = decodeEntities2(value);
|
|
2451
|
+
const kept = [];
|
|
2452
|
+
for (const declaration of decoded.split(";")) {
|
|
2453
|
+
const colon = declaration.indexOf(":");
|
|
2454
|
+
if (colon === -1) continue;
|
|
2455
|
+
const prop = declaration.slice(0, colon).trim().toLowerCase();
|
|
2456
|
+
const val = declaration.slice(colon + 1).trim();
|
|
2457
|
+
if (!/^[a-z-]+$/.test(prop)) continue;
|
|
2458
|
+
if (requirePropAllowlist && !SAFE_STYLE_PROPS.has(prop)) continue;
|
|
2459
|
+
if (val === "" || DANGEROUS_CSS_VALUE.test(val)) continue;
|
|
2460
|
+
kept.push(`${prop}: ${val}`);
|
|
2461
|
+
}
|
|
2462
|
+
return kept.length > 0 ? kept.join("; ") : null;
|
|
2463
|
+
}
|
|
2464
|
+
var ATTR_PATTERN = /([^\s=/>"']+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]*)))?/g;
|
|
2465
|
+
function sanitizeOpeningTag(rawOpen) {
|
|
2466
|
+
if (!rawOpen.startsWith("<")) return null;
|
|
2467
|
+
const tagName = extractTagName(rawOpen);
|
|
2468
|
+
if (!/^[a-z][a-z0-9-]*$/.test(tagName) || !SAFE_TAGS.has(tagName)) {
|
|
2469
|
+
return null;
|
|
2470
|
+
}
|
|
2471
|
+
const end = rawOpen.endsWith("/>") ? rawOpen.length - 2 : rawOpen.endsWith(">") ? rawOpen.length - 1 : rawOpen.length;
|
|
2472
|
+
const attrSource = rawOpen.slice(1 + tagName.length, end);
|
|
2473
|
+
const attrs = {};
|
|
2474
|
+
for (const match of attrSource.matchAll(ATTR_PATTERN)) {
|
|
2475
|
+
const name = match[1].toLowerCase();
|
|
2476
|
+
const value = match[2] ?? match[3] ?? match[4] ?? "";
|
|
2477
|
+
if (name in attrs) continue;
|
|
2478
|
+
if (name === "style") {
|
|
2479
|
+
const cleanStyle = sanitizeStyle(value);
|
|
2480
|
+
if (cleanStyle !== null) attrs[name] = cleanStyle;
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
const allowed = SAFE_ATTRS.has(name) || /^data-[a-z0-9-]*$/.test(name) || /^aria-[a-z0-9-]*$/.test(name);
|
|
2484
|
+
if (!allowed) continue;
|
|
2485
|
+
if (URL_ATTRS.has(name)) {
|
|
2486
|
+
if (sanitizeUrl(value) === null) continue;
|
|
2487
|
+
}
|
|
2488
|
+
attrs[name] = value;
|
|
2489
|
+
}
|
|
2490
|
+
return serializeOpeningTag(tagName, attrs);
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2302
2493
|
// src/content/serializer/HTMLSerializer.ts
|
|
2303
2494
|
var HTMLSerializer = class {
|
|
2304
2495
|
serialize(unit) {
|
|
@@ -2308,24 +2499,25 @@ var LionRapid = (() => {
|
|
|
2308
2499
|
let lastIndex = 0;
|
|
2309
2500
|
let match;
|
|
2310
2501
|
while ((match = tagPattern.exec(text)) !== null) {
|
|
2311
|
-
result += text.slice(lastIndex, match.index);
|
|
2502
|
+
result += escapeHtml(text.slice(lastIndex, match.index));
|
|
2312
2503
|
const [fullMatch, closingSlash, indexStr] = match;
|
|
2313
2504
|
const styleList = styles[indexStr] ?? [];
|
|
2314
2505
|
const rawOpen = readRawStyle(styleList);
|
|
2315
|
-
|
|
2506
|
+
const safeOpen = rawOpen === null ? null : sanitizeOpeningTag(rawOpen);
|
|
2507
|
+
if (rawOpen && safeOpen !== null) {
|
|
2316
2508
|
const tagName = extractTagName(rawOpen);
|
|
2317
2509
|
const isVoid = VOID_TAGS.has(tagName);
|
|
2318
2510
|
if (closingSlash) {
|
|
2319
2511
|
if (!isVoid) result += `</${tagName}>`;
|
|
2320
2512
|
} else {
|
|
2321
|
-
result +=
|
|
2513
|
+
result += safeOpen;
|
|
2322
2514
|
}
|
|
2323
2515
|
} else {
|
|
2324
2516
|
result += fullMatch;
|
|
2325
2517
|
}
|
|
2326
2518
|
lastIndex = match.index + fullMatch.length;
|
|
2327
2519
|
}
|
|
2328
|
-
result += text.slice(lastIndex);
|
|
2520
|
+
result += escapeHtml(text.slice(lastIndex));
|
|
2329
2521
|
return result;
|
|
2330
2522
|
}
|
|
2331
2523
|
};
|
|
@@ -2564,10 +2756,9 @@ var LionRapid = (() => {
|
|
|
2564
2756
|
var INDEXED_TAG_REGEX = /<\/?[0-9]+\/?>/;
|
|
2565
2757
|
var HTML_TAG_REGEX = /<[a-z][a-z0-9]*(?:\s+[^>]*)?\/?>/i;
|
|
2566
2758
|
var LionRapidCore = class {
|
|
2567
|
-
constructor(eventEmitter, handlerChain, defaultLocale = "en",
|
|
2759
|
+
constructor(eventEmitter, handlerChain, defaultLocale = "en", defaultNamespace) {
|
|
2568
2760
|
__publicField(this, "handlerChain");
|
|
2569
2761
|
__publicField(this, "currentLocale");
|
|
2570
|
-
__publicField(this, "fallbackLocale");
|
|
2571
2762
|
__publicField(this, "defaultNamespace");
|
|
2572
2763
|
__publicField(this, "pendingTranslations", /* @__PURE__ */ new Map());
|
|
2573
2764
|
__publicField(this, "bus");
|
|
@@ -2575,6 +2766,7 @@ var LionRapid = (() => {
|
|
|
2575
2766
|
// 🔥 EMBEDDED: Core integration maps
|
|
2576
2767
|
__publicField(this, "formatters", /* @__PURE__ */ new Map());
|
|
2577
2768
|
__publicField(this, "processors", /* @__PURE__ */ new Map());
|
|
2769
|
+
__publicField(this, "warnedNoInterpolate", false);
|
|
2578
2770
|
// 📡 EVENT-DRIVEN: Just track for cleanup
|
|
2579
2771
|
__publicField(this, "eventPlugins", /* @__PURE__ */ new Set());
|
|
2580
2772
|
// 📦 CONTENT: Parser and serializer for ContentUnit processing
|
|
@@ -2585,7 +2777,6 @@ var LionRapid = (() => {
|
|
|
2585
2777
|
this.bus = eventEmitter;
|
|
2586
2778
|
this.handlerChain = handlerChain;
|
|
2587
2779
|
this.currentLocale = defaultLocale;
|
|
2588
|
-
this.fallbackLocale = fallbackLocale;
|
|
2589
2780
|
this.defaultNamespace = defaultNamespace;
|
|
2590
2781
|
this.cache = new CacheManager(
|
|
2591
2782
|
this.handlerChain,
|
|
@@ -2650,8 +2841,7 @@ var LionRapid = (() => {
|
|
|
2650
2841
|
this.currentLocale,
|
|
2651
2842
|
firstArg,
|
|
2652
2843
|
secondArg,
|
|
2653
|
-
this.defaultNamespace
|
|
2654
|
-
this.fallbackLocale
|
|
2844
|
+
this.defaultNamespace
|
|
2655
2845
|
);
|
|
2656
2846
|
logger.debug(`Translation requested: ${request.cleanKey}`);
|
|
2657
2847
|
const translateBus = this.bus.translation(request);
|
|
@@ -2701,8 +2891,7 @@ var LionRapid = (() => {
|
|
|
2701
2891
|
this.currentLocale,
|
|
2702
2892
|
firstArg,
|
|
2703
2893
|
secondArg,
|
|
2704
|
-
this.defaultNamespace
|
|
2705
|
-
this.fallbackLocale
|
|
2894
|
+
this.defaultNamespace
|
|
2706
2895
|
);
|
|
2707
2896
|
logger.debug(`HTML translation requested: ${request.cleanKey}`);
|
|
2708
2897
|
const translateBus = this.bus.translation(request);
|
|
@@ -2870,6 +3059,11 @@ var LionRapid = (() => {
|
|
|
2870
3059
|
resolvedLocale,
|
|
2871
3060
|
resolvedKey
|
|
2872
3061
|
);
|
|
3062
|
+
} else if (!this.warnedNoInterpolate && params && Object.keys(params).length > 0 && processedValue.includes("{")) {
|
|
3063
|
+
this.warnedNoInterpolate = true;
|
|
3064
|
+
console.warn(
|
|
3065
|
+
"[LionRapid] Translation params were passed but no interpolate formatter is registered \u2014 {placeholders} render literally. Install @lionrapid/formatters and add .use(coreFormattersPlugin) to the builder."
|
|
3066
|
+
);
|
|
2873
3067
|
}
|
|
2874
3068
|
if (this.processors.size > 0) {
|
|
2875
3069
|
for (const [name, processor] of this.processors.entries()) {
|
|
@@ -2910,8 +3104,7 @@ var LionRapid = (() => {
|
|
|
2910
3104
|
this.currentLocale,
|
|
2911
3105
|
params,
|
|
2912
3106
|
defaultValue,
|
|
2913
|
-
this.defaultNamespace
|
|
2914
|
-
this.fallbackLocale
|
|
3107
|
+
this.defaultNamespace
|
|
2915
3108
|
);
|
|
2916
3109
|
const syncResult = this.handlerChain.handleSync(request);
|
|
2917
3110
|
const rawValue = syncResult && syncResult.status === 200 ? syncResult.value : request.parsedDefaultValue || request.cleanKey;
|
|
@@ -3032,8 +3225,7 @@ var LionRapid = (() => {
|
|
|
3032
3225
|
this.currentLocale,
|
|
3033
3226
|
void 0,
|
|
3034
3227
|
void 0,
|
|
3035
|
-
this.defaultNamespace
|
|
3036
|
-
this.fallbackLocale
|
|
3228
|
+
this.defaultNamespace
|
|
3037
3229
|
);
|
|
3038
3230
|
return this.handlerChain.isCached(request);
|
|
3039
3231
|
}
|
|
@@ -3454,6 +3646,9 @@ var LionRapid = (() => {
|
|
|
3454
3646
|
};
|
|
3455
3647
|
|
|
3456
3648
|
// src/builder.ts
|
|
3649
|
+
function isLoudPreloadFailure(error) {
|
|
3650
|
+
return !(error instanceof TranslationNotFoundError || error instanceof TranslationLockedError);
|
|
3651
|
+
}
|
|
3457
3652
|
var LionRapidBuilder = class _LionRapidBuilder {
|
|
3458
3653
|
constructor(config) {
|
|
3459
3654
|
__publicField(this, "config");
|
|
@@ -3464,7 +3659,6 @@ var LionRapid = (() => {
|
|
|
3464
3659
|
__publicField(this, "syncQueue", null);
|
|
3465
3660
|
this.config = {
|
|
3466
3661
|
defaultLocale: config.defaultLocale || "en",
|
|
3467
|
-
fallbackLocale: config.fallbackLocale || "en",
|
|
3468
3662
|
namespace: config.namespace || "app"
|
|
3469
3663
|
};
|
|
3470
3664
|
}
|
|
@@ -3565,7 +3759,6 @@ var LionRapid = (() => {
|
|
|
3565
3759
|
eventEmitter,
|
|
3566
3760
|
handlerChain,
|
|
3567
3761
|
this.config.defaultLocale,
|
|
3568
|
-
this.config.fallbackLocale,
|
|
3569
3762
|
this.config.namespace
|
|
3570
3763
|
);
|
|
3571
3764
|
for (const plugin of this.plugins) {
|
|
@@ -3596,7 +3789,17 @@ var LionRapid = (() => {
|
|
|
3596
3789
|
logger.debug("Preload targets", {
|
|
3597
3790
|
namespaces: eagerPreloads.map((c) => `${c.locale}:${c.namespace}`)
|
|
3598
3791
|
});
|
|
3792
|
+
let warnedLoudly = false;
|
|
3793
|
+
const warnLoudlyOnce = (namespaceKey, error) => {
|
|
3794
|
+
if (!isLoudPreloadFailure(error) || warnedLoudly) return;
|
|
3795
|
+
warnedLoudly = true;
|
|
3796
|
+
console.warn(
|
|
3797
|
+
`[LionRapid] Could not load translations for ${namespaceKey} \u2014 the page stays untranslated until the server is reachable.`,
|
|
3798
|
+
error
|
|
3799
|
+
);
|
|
3800
|
+
};
|
|
3599
3801
|
const promises = eagerPreloads.map(async (preloadConfig) => {
|
|
3802
|
+
const namespaceKey = `${preloadConfig.locale}:${preloadConfig.namespace}`;
|
|
3600
3803
|
try {
|
|
3601
3804
|
const result = await networkHandler.loadNamespace(
|
|
3602
3805
|
preloadConfig.locale,
|
|
@@ -3605,19 +3808,19 @@ var LionRapid = (() => {
|
|
|
3605
3808
|
if (result && result.data && result.status === 200) {
|
|
3606
3809
|
const context = {
|
|
3607
3810
|
locale: preloadConfig.locale,
|
|
3608
|
-
namespace: preloadConfig.namespace
|
|
3609
|
-
fallbackLocale: this.config.fallbackLocale
|
|
3811
|
+
namespace: preloadConfig.namespace
|
|
3610
3812
|
};
|
|
3611
3813
|
await handlerChain.saveNamespaceToHandlers(result.data, context);
|
|
3612
|
-
logger.debug(
|
|
3613
|
-
|
|
3614
|
-
|
|
3814
|
+
logger.debug(`Preloaded: ${namespaceKey}`);
|
|
3815
|
+
return;
|
|
3816
|
+
}
|
|
3817
|
+
if (result?.error) {
|
|
3818
|
+
warnLoudlyOnce(namespaceKey, result.error);
|
|
3819
|
+
logger.warn(`Preload failed: ${namespaceKey}`, result.error);
|
|
3615
3820
|
}
|
|
3616
3821
|
} catch (error) {
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
error
|
|
3620
|
-
);
|
|
3822
|
+
warnLoudlyOnce(namespaceKey, error);
|
|
3823
|
+
logger.warn(`Preload failed: ${namespaceKey}`, error);
|
|
3621
3824
|
}
|
|
3622
3825
|
});
|
|
3623
3826
|
await Promise.allSettled(promises);
|
|
@@ -3631,8 +3834,16 @@ var LionRapid = (() => {
|
|
|
3631
3834
|
__publicField(this, "name", "NetworkRepository");
|
|
3632
3835
|
__publicField(this, "type", "network");
|
|
3633
3836
|
__publicField(this, "config", {});
|
|
3634
|
-
/**
|
|
3635
|
-
|
|
3837
|
+
/**
|
|
3838
|
+
* Namespace snapshot per key "locale:namespace": the server version TOGETHER
|
|
3839
|
+
* with the payload it describes, persisted as ONE localStorage entry.
|
|
3840
|
+
*
|
|
3841
|
+
* Canonical 304 contract (same as edge KV bundle + WP transient cache):
|
|
3842
|
+
* never claim freshness you can't back. If-None-Match is only sent when the
|
|
3843
|
+
* snapshot is in hand; a 304 answers with that snapshot's payload; version
|
|
3844
|
+
* and payload can never desync because they live and die together.
|
|
3845
|
+
*/
|
|
3846
|
+
__publicField(this, "namespaceSnapshots", /* @__PURE__ */ new Map());
|
|
3636
3847
|
/**
|
|
3637
3848
|
* Default endpoint templates with placeholder support
|
|
3638
3849
|
* Placeholders: :key, :locale, :namespace
|
|
@@ -3745,36 +3956,50 @@ var LionRapid = (() => {
|
|
|
3745
3956
|
logger.debug("API base URL is not set");
|
|
3746
3957
|
throw new NetworkError("API base URL is not set");
|
|
3747
3958
|
}
|
|
3959
|
+
try {
|
|
3960
|
+
return await this.fetchNamespaceRaw(locale, namespace);
|
|
3961
|
+
} catch (error) {
|
|
3962
|
+
this.handleCaughtError(error);
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
/**
|
|
3966
|
+
* Shared namespace fetch with the canonical 304 contract:
|
|
3967
|
+
* - If-None-Match is sent ONLY when a snapshot (version + payload) is in hand.
|
|
3968
|
+
* - 304 answers with that snapshot's payload — never with "nothing".
|
|
3969
|
+
* - A 304 with no snapshot (server/proxy surprise) refetches unconditionally.
|
|
3970
|
+
* - A 200 stores version + payload atomically before either is trusted.
|
|
3971
|
+
*/
|
|
3972
|
+
async fetchNamespaceRaw(locale, namespace, allowConditional = true) {
|
|
3748
3973
|
const url = this.buildUrl("getNamespace", { locale, namespace });
|
|
3749
3974
|
logger.debug(`API GET Namespace: ${url}`);
|
|
3750
3975
|
const namespaceKey = `${locale}:${namespace}`;
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
if (
|
|
3759
|
-
logger.debug(`Namespace ${namespaceKey}: 304 Not Modified`);
|
|
3760
|
-
return
|
|
3761
|
-
}
|
|
3762
|
-
if (!response.ok) {
|
|
3763
|
-
this.handleHttpError(response, namespaceKey);
|
|
3764
|
-
}
|
|
3765
|
-
const data = await response.json();
|
|
3766
|
-
logger.debug("API response data:", data);
|
|
3767
|
-
if (!data.translations) {
|
|
3768
|
-
throw new TranslationNotFoundError(namespaceKey);
|
|
3769
|
-
}
|
|
3770
|
-
const version = data.meta?.version != null ? String(data.meta.version) : null;
|
|
3771
|
-
if (version) {
|
|
3772
|
-
this.setVersion(namespaceKey, version);
|
|
3976
|
+
const snapshot = allowConditional ? this.getSnapshot(namespaceKey) : void 0;
|
|
3977
|
+
const extraHeaders = {};
|
|
3978
|
+
if (snapshot) {
|
|
3979
|
+
extraHeaders["If-None-Match"] = `W/"${snapshot.version}"`;
|
|
3980
|
+
}
|
|
3981
|
+
const response = await this.makeRequest(url, "GET", void 0, extraHeaders);
|
|
3982
|
+
if (response.status === 304) {
|
|
3983
|
+
if (snapshot) {
|
|
3984
|
+
logger.debug(`Namespace ${namespaceKey}: 304 Not Modified (snapshot served)`);
|
|
3985
|
+
return snapshot.translations;
|
|
3773
3986
|
}
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3987
|
+
logger.debug(`Namespace ${namespaceKey}: unexpected 304 with no snapshot \u2014 refetching`);
|
|
3988
|
+
return this.fetchNamespaceRaw(locale, namespace, false);
|
|
3989
|
+
}
|
|
3990
|
+
if (!response.ok) {
|
|
3991
|
+
this.handleHttpError(response, namespaceKey);
|
|
3992
|
+
}
|
|
3993
|
+
const data = await response.json();
|
|
3994
|
+
logger.debug("API response data:", data);
|
|
3995
|
+
if (!data.translations) {
|
|
3996
|
+
throw new TranslationNotFoundError(namespaceKey);
|
|
3997
|
+
}
|
|
3998
|
+
const version = data.meta?.version != null ? String(data.meta.version) : null;
|
|
3999
|
+
if (version) {
|
|
4000
|
+
this.setSnapshot(namespaceKey, { version, translations: data.translations });
|
|
3777
4001
|
}
|
|
4002
|
+
return data.translations;
|
|
3778
4003
|
}
|
|
3779
4004
|
/**
|
|
3780
4005
|
* Save translation to API (if supported)
|
|
@@ -3859,33 +4084,8 @@ var LionRapid = (() => {
|
|
|
3859
4084
|
logger.debug("API base URL is not set");
|
|
3860
4085
|
throw new NetworkError("API base URL is not set");
|
|
3861
4086
|
}
|
|
3862
|
-
const url = this.buildUrl("getNamespace", { locale, namespace });
|
|
3863
|
-
logger.debug(`API GET Namespace ContentUnits: ${url}`);
|
|
3864
|
-
const namespaceKey = `${locale}:${namespace}`;
|
|
3865
4087
|
try {
|
|
3866
|
-
|
|
3867
|
-
const cachedVersion = this.getVersion(namespaceKey);
|
|
3868
|
-
if (cachedVersion) {
|
|
3869
|
-
extraHeaders["If-None-Match"] = `W/"${cachedVersion}"`;
|
|
3870
|
-
}
|
|
3871
|
-
const response = await this.makeRequest(url, "GET", void 0, extraHeaders);
|
|
3872
|
-
if (response.status === 304) {
|
|
3873
|
-
logger.debug(`Namespace ContentUnits ${namespaceKey}: 304 Not Modified`);
|
|
3874
|
-
return null;
|
|
3875
|
-
}
|
|
3876
|
-
if (!response.ok) {
|
|
3877
|
-
this.handleHttpError(response, namespaceKey);
|
|
3878
|
-
}
|
|
3879
|
-
const data = await response.json();
|
|
3880
|
-
logger.debug("API response data:", data);
|
|
3881
|
-
if (!data.translations) {
|
|
3882
|
-
throw new TranslationNotFoundError(namespaceKey);
|
|
3883
|
-
}
|
|
3884
|
-
const version = data.meta?.version != null ? String(data.meta.version) : null;
|
|
3885
|
-
if (version) {
|
|
3886
|
-
this.setVersion(namespaceKey, version);
|
|
3887
|
-
}
|
|
3888
|
-
return data.translations;
|
|
4088
|
+
return await this.fetchNamespaceRaw(locale, namespace);
|
|
3889
4089
|
} catch (error) {
|
|
3890
4090
|
this.handleCaughtError(error);
|
|
3891
4091
|
}
|
|
@@ -3995,25 +4195,37 @@ var LionRapid = (() => {
|
|
|
3995
4195
|
getConfig() {
|
|
3996
4196
|
return this.config;
|
|
3997
4197
|
}
|
|
3998
|
-
/** Read persisted version
|
|
3999
|
-
|
|
4000
|
-
|
|
4198
|
+
/** Read a persisted snapshot (version + payload), falling back to memory.
|
|
4199
|
+
* Legacy bare-version entries and corrupt snapshots are deleted on sight —
|
|
4200
|
+
* a version without its payload must never drive a conditional request. */
|
|
4201
|
+
getSnapshot(namespaceKey) {
|
|
4202
|
+
const mem = this.namespaceSnapshots.get(namespaceKey);
|
|
4001
4203
|
if (mem) return mem;
|
|
4002
4204
|
try {
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4205
|
+
localStorage.removeItem(_NetworkRepository.LEGACY_VERSION_PREFIX + namespaceKey);
|
|
4206
|
+
const raw = localStorage.getItem(_NetworkRepository.SNAPSHOT_PREFIX + namespaceKey);
|
|
4207
|
+
if (raw) {
|
|
4208
|
+
const parsed = JSON.parse(raw);
|
|
4209
|
+
if (parsed && typeof parsed.version === "string" && parsed.version.length > 0 && parsed.translations && typeof parsed.translations === "object") {
|
|
4210
|
+
this.namespaceSnapshots.set(namespaceKey, parsed);
|
|
4211
|
+
return parsed;
|
|
4212
|
+
}
|
|
4213
|
+
localStorage.removeItem(_NetworkRepository.SNAPSHOT_PREFIX + namespaceKey);
|
|
4007
4214
|
}
|
|
4008
4215
|
} catch {
|
|
4009
4216
|
}
|
|
4010
4217
|
return void 0;
|
|
4011
4218
|
}
|
|
4012
|
-
/** Persist version
|
|
4013
|
-
|
|
4014
|
-
|
|
4219
|
+
/** Persist version + payload as ONE entry (memory + localStorage). On quota
|
|
4220
|
+
* failure the previous stored snapshot stays — still a consistent pair. */
|
|
4221
|
+
setSnapshot(namespaceKey, snapshot) {
|
|
4222
|
+
this.namespaceSnapshots.set(namespaceKey, snapshot);
|
|
4015
4223
|
try {
|
|
4016
|
-
localStorage.setItem(
|
|
4224
|
+
localStorage.setItem(
|
|
4225
|
+
_NetworkRepository.SNAPSHOT_PREFIX + namespaceKey,
|
|
4226
|
+
JSON.stringify(snapshot)
|
|
4227
|
+
);
|
|
4228
|
+
localStorage.removeItem(_NetworkRepository.LEGACY_VERSION_PREFIX + namespaceKey);
|
|
4017
4229
|
} catch {
|
|
4018
4230
|
}
|
|
4019
4231
|
}
|
|
@@ -4102,8 +4314,11 @@ var LionRapid = (() => {
|
|
|
4102
4314
|
}
|
|
4103
4315
|
}
|
|
4104
4316
|
};
|
|
4105
|
-
/** localStorage key prefix for persisted
|
|
4106
|
-
__publicField(_NetworkRepository, "
|
|
4317
|
+
/** localStorage key prefix for persisted snapshots (version + payload) */
|
|
4318
|
+
__publicField(_NetworkRepository, "SNAPSHOT_PREFIX", "lionrapid:ns:");
|
|
4319
|
+
/** Legacy bare-version prefix — a version WITHOUT its payload is exactly the
|
|
4320
|
+
* desync the snapshot store exists to prevent; entries are deleted on sight. */
|
|
4321
|
+
__publicField(_NetworkRepository, "LEGACY_VERSION_PREFIX", "lionrapid:v:");
|
|
4107
4322
|
var NetworkRepository = _NetworkRepository;
|
|
4108
4323
|
|
|
4109
4324
|
// src/browser/exclude-filter.ts
|
|
@@ -4496,7 +4711,7 @@ var LionRapid = (() => {
|
|
|
4496
4711
|
isInsideSkipTag(element) {
|
|
4497
4712
|
let current = element.parentElement;
|
|
4498
4713
|
while (current) {
|
|
4499
|
-
if (SKIP_TAGS.has(current.tagName)) {
|
|
4714
|
+
if (SKIP_TAGS.has(current.tagName.toUpperCase())) {
|
|
4500
4715
|
return true;
|
|
4501
4716
|
}
|
|
4502
4717
|
current = current.parentElement;
|
|
@@ -4610,7 +4825,7 @@ var LionRapid = (() => {
|
|
|
4610
4825
|
isInsideSkipTag(element) {
|
|
4611
4826
|
let current = element.parentElement;
|
|
4612
4827
|
while (current) {
|
|
4613
|
-
if (SKIP_TAGS2.has(current.tagName)) return true;
|
|
4828
|
+
if (SKIP_TAGS2.has(current.tagName.toUpperCase())) return true;
|
|
4614
4829
|
current = current.parentElement;
|
|
4615
4830
|
}
|
|
4616
4831
|
return false;
|
|
@@ -5723,6 +5938,50 @@ var LionRapid = (() => {
|
|
|
5723
5938
|
};
|
|
5724
5939
|
|
|
5725
5940
|
// src/browser/auto-init.ts
|
|
5941
|
+
function resolveWritePolicy(apiKey) {
|
|
5942
|
+
const isPublishable = apiKey.startsWith("pk_");
|
|
5943
|
+
return {
|
|
5944
|
+
syncMissing: { enabled: isPublishable },
|
|
5945
|
+
warnReadOnly: !isPublishable
|
|
5946
|
+
};
|
|
5947
|
+
}
|
|
5948
|
+
function configForWindow(config) {
|
|
5949
|
+
return { ...config, apiKey: "" };
|
|
5950
|
+
}
|
|
5951
|
+
function createDeferredLocaleHandler(effects) {
|
|
5952
|
+
let applyLocale = null;
|
|
5953
|
+
let pendingLocale = null;
|
|
5954
|
+
return {
|
|
5955
|
+
onChange: async (locale) => {
|
|
5956
|
+
effects.setCookie(locale);
|
|
5957
|
+
if (effects.navigate) {
|
|
5958
|
+
effects.navigate(locale);
|
|
5959
|
+
return;
|
|
5960
|
+
}
|
|
5961
|
+
if (applyLocale) {
|
|
5962
|
+
await applyLocale(locale);
|
|
5963
|
+
return;
|
|
5964
|
+
}
|
|
5965
|
+
pendingLocale = locale;
|
|
5966
|
+
},
|
|
5967
|
+
attach: (apply) => {
|
|
5968
|
+
applyLocale = apply;
|
|
5969
|
+
if (pendingLocale !== null) {
|
|
5970
|
+
const replay = pendingLocale;
|
|
5971
|
+
pendingLocale = null;
|
|
5972
|
+
void apply(replay).catch(() => {
|
|
5973
|
+
});
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
};
|
|
5977
|
+
}
|
|
5978
|
+
function onDomReady(fn) {
|
|
5979
|
+
if (document.readyState === "loading") {
|
|
5980
|
+
document.addEventListener("DOMContentLoaded", fn, { once: true });
|
|
5981
|
+
} else {
|
|
5982
|
+
fn();
|
|
5983
|
+
}
|
|
5984
|
+
}
|
|
5726
5985
|
function parseConfig(script) {
|
|
5727
5986
|
const apiKey = script.dataset.apiKey || "";
|
|
5728
5987
|
const baseUrl = script.dataset.baseUrl || "https://api.lionrapid.com";
|
|
@@ -5759,50 +6018,86 @@ var LionRapid = (() => {
|
|
|
5759
6018
|
}
|
|
5760
6019
|
function readLocaleCookie(availableLanguages) {
|
|
5761
6020
|
if (typeof document === "undefined") return null;
|
|
5762
|
-
const match = document.cookie.match(
|
|
6021
|
+
const match = document.cookie.match(
|
|
6022
|
+
/(?:^|;\s*)lionrapid_locale=([a-z]{2,3}(?:-[a-z0-9]{2,4})*)/i
|
|
6023
|
+
);
|
|
5763
6024
|
if (!match) return null;
|
|
5764
|
-
return
|
|
6025
|
+
return matchLocale([match[1]], availableLanguages, "") || null;
|
|
5765
6026
|
}
|
|
5766
6027
|
function detectBrowserLanguage(availableLanguages, defaultLocale) {
|
|
5767
6028
|
if (typeof navigator === "undefined") return defaultLocale;
|
|
5768
|
-
const
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
}
|
|
5773
|
-
return defaultLocale;
|
|
6029
|
+
const requested = navigator.languages && navigator.languages.length > 0 ? [...navigator.languages] : [
|
|
6030
|
+
navigator.language || navigator.userLanguage || ""
|
|
6031
|
+
];
|
|
6032
|
+
return matchLocale(requested, availableLanguages, defaultLocale);
|
|
5774
6033
|
}
|
|
5775
|
-
function
|
|
5776
|
-
const
|
|
6034
|
+
function buildLocaleUrl(pathname, search, locale, defaultLocale, availableLanguages) {
|
|
6035
|
+
const segments = pathname.split("/");
|
|
6036
|
+
const first = segments[1] ?? "";
|
|
6037
|
+
const hasLocalePrefix = LOCALE_TAG_REGEX.test(first) && matchLocale([first], availableLanguages, "") !== "";
|
|
6038
|
+
const path = hasLocalePrefix ? "/" + segments.slice(2).join("/") : pathname;
|
|
5777
6039
|
const prefix = locale === defaultLocale ? "" : "/" + locale;
|
|
5778
|
-
|
|
6040
|
+
return prefix + (path || "/") + search;
|
|
5779
6041
|
}
|
|
5780
|
-
|
|
6042
|
+
function navigateToLocale(locale, defaultLocale, availableLanguages) {
|
|
6043
|
+
window.location.href = buildLocaleUrl(
|
|
6044
|
+
window.location.pathname,
|
|
6045
|
+
window.location.search,
|
|
6046
|
+
locale,
|
|
6047
|
+
defaultLocale,
|
|
6048
|
+
availableLanguages
|
|
6049
|
+
);
|
|
6050
|
+
}
|
|
6051
|
+
function resolveInitialLocale(config) {
|
|
6052
|
+
if (config.mode === "dynamic-only") {
|
|
6053
|
+
const htmlLang = typeof document !== "undefined" ? document.documentElement.lang : "";
|
|
6054
|
+
return htmlLang ? normalizeLocale(htmlLang) : config.defaultLocale;
|
|
6055
|
+
}
|
|
6056
|
+
const cookieLocale = readLocaleCookie(config.languages);
|
|
6057
|
+
if (cookieLocale) return cookieLocale;
|
|
6058
|
+
if (config.autoDetect) {
|
|
6059
|
+
return detectBrowserLanguage(config.languages, config.defaultLocale);
|
|
6060
|
+
}
|
|
6061
|
+
return config.defaultLocale;
|
|
6062
|
+
}
|
|
6063
|
+
function applyLocaleDir(locale) {
|
|
6064
|
+
if (typeof document !== "undefined") {
|
|
6065
|
+
document.documentElement.dir = localeDir(locale);
|
|
6066
|
+
}
|
|
6067
|
+
}
|
|
6068
|
+
function findConfigScript() {
|
|
6069
|
+
const current = document.currentScript;
|
|
6070
|
+
if (current instanceof HTMLScriptElement && current.dataset.apiKey) {
|
|
6071
|
+
return current;
|
|
6072
|
+
}
|
|
5781
6073
|
const scripts = document.querySelectorAll("script[data-api-key]");
|
|
5782
|
-
|
|
6074
|
+
if (scripts.length > 1) {
|
|
6075
|
+
console.warn(
|
|
6076
|
+
`[LionRapid] ${scripts.length} script tags with data-api-key found; using the last one`
|
|
6077
|
+
);
|
|
6078
|
+
}
|
|
6079
|
+
return scripts[scripts.length - 1];
|
|
6080
|
+
}
|
|
6081
|
+
async function autoInit() {
|
|
6082
|
+
const script = findConfigScript();
|
|
5783
6083
|
if (!script) {
|
|
5784
6084
|
console.warn("[LionRapid] No script tag with data-api-key found");
|
|
5785
6085
|
return;
|
|
5786
6086
|
}
|
|
5787
6087
|
const config = parseConfig(script);
|
|
5788
|
-
window.__lionrapidConfig = config;
|
|
6088
|
+
window.__lionrapidConfig = configForWindow(config);
|
|
5789
6089
|
if (!config.apiKey) {
|
|
5790
6090
|
console.warn("[LionRapid] Missing data-api-key attribute");
|
|
5791
6091
|
return;
|
|
5792
6092
|
}
|
|
5793
|
-
|
|
5794
|
-
if (
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
const cookieLocale = readLocaleCookie(config.languages);
|
|
5799
|
-
if (cookieLocale) {
|
|
5800
|
-
initialLocale = cookieLocale;
|
|
5801
|
-
} else if (config.autoDetect) {
|
|
5802
|
-
initialLocale = detectBrowserLanguage(config.languages, config.defaultLocale);
|
|
5803
|
-
}
|
|
6093
|
+
const writePolicy = resolveWritePolicy(config.apiKey);
|
|
6094
|
+
if (writePolicy.warnReadOnly) {
|
|
6095
|
+
console.error(
|
|
6096
|
+
"[LionRapid] data-api-key must be a publishable pk_ key; running read-only"
|
|
6097
|
+
);
|
|
5804
6098
|
}
|
|
5805
|
-
const
|
|
6099
|
+
const initialLocale = resolveInitialLocale(config);
|
|
6100
|
+
applyLocaleDir(initialLocale);
|
|
5806
6101
|
const languageSwitcher = new LanguageSwitcher();
|
|
5807
6102
|
languageSwitcher.setLanguages(config.languages);
|
|
5808
6103
|
languageSwitcher.setInitialLocale(initialLocale);
|
|
@@ -5828,11 +6123,14 @@ var LionRapid = (() => {
|
|
|
5828
6123
|
}
|
|
5829
6124
|
languageSwitcher.initAll();
|
|
5830
6125
|
};
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
6126
|
+
onDomReady(renderSwitcher);
|
|
6127
|
+
const localeHandler = createDeferredLocaleHandler({
|
|
6128
|
+
setCookie: (locale) => {
|
|
6129
|
+
document.cookie = `lionrapid_locale=${locale}; path=/; max-age=31536000; SameSite=Lax`;
|
|
6130
|
+
},
|
|
6131
|
+
navigate: config.mode === "dynamic-only" ? (locale) => navigateToLocale(locale, config.defaultLocale, config.languages) : null
|
|
6132
|
+
});
|
|
6133
|
+
languageSwitcher.setOnLanguageChange(localeHandler.onChange);
|
|
5836
6134
|
try {
|
|
5837
6135
|
const builder = LionRapidBuilder.init({
|
|
5838
6136
|
defaultLocale: initialLocale,
|
|
@@ -5855,11 +6153,9 @@ var LionRapid = (() => {
|
|
|
5855
6153
|
options: {
|
|
5856
6154
|
baseUrl: config.baseUrl,
|
|
5857
6155
|
apiKey: config.apiKey,
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
batchSize: 50
|
|
5862
|
-
},
|
|
6156
|
+
// pk_ keys sync missing source strings (discovery); anything else
|
|
6157
|
+
// runs read-only — see resolveWritePolicy.
|
|
6158
|
+
syncMissing: writePolicy.syncMissing,
|
|
5863
6159
|
preloadNamespaces: [
|
|
5864
6160
|
{
|
|
5865
6161
|
locale: initialLocale,
|
|
@@ -5880,15 +6176,11 @@ var LionRapid = (() => {
|
|
|
5880
6176
|
sourceLocale: config.defaultLocale
|
|
5881
6177
|
});
|
|
5882
6178
|
languageSwitcher.setCore(core);
|
|
5883
|
-
|
|
5884
|
-
document.cookie = `lionrapid_locale=${locale}; path=/; max-age=31536000; SameSite=Lax`;
|
|
5885
|
-
if (config.mode === "dynamic-only") {
|
|
5886
|
-
navigateToLocale(locale, config.defaultLocale);
|
|
5887
|
-
return;
|
|
5888
|
-
}
|
|
6179
|
+
localeHandler.attach(async (locale) => {
|
|
5889
6180
|
await core.changeLanguage(locale, {
|
|
5890
6181
|
preloadNamespaces: [config.namespace]
|
|
5891
6182
|
});
|
|
6183
|
+
applyLocaleDir(locale);
|
|
5892
6184
|
autoTranslator.updateLocale(locale);
|
|
5893
6185
|
autoTranslator.retranslate();
|
|
5894
6186
|
});
|
|
@@ -5935,6 +6227,7 @@ var LionRapid = (() => {
|
|
|
5935
6227
|
await core.changeLanguage(locale, {
|
|
5936
6228
|
preloadNamespaces: [config.namespace]
|
|
5937
6229
|
});
|
|
6230
|
+
applyLocaleDir(locale);
|
|
5938
6231
|
autoTranslator.updateLocale(locale);
|
|
5939
6232
|
autoTranslator.retranslate();
|
|
5940
6233
|
languageSwitcher.initAll();
|
|
@@ -5957,10 +6250,12 @@ var LionRapid = (() => {
|
|
|
5957
6250
|
editorScript.async = true;
|
|
5958
6251
|
document.body.appendChild(editorScript);
|
|
5959
6252
|
}
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
6253
|
+
onDomReady(() => {
|
|
6254
|
+
if (config.mode !== "dynamic-only") {
|
|
6255
|
+
autoTranslator.translatePage();
|
|
6256
|
+
}
|
|
6257
|
+
autoTranslator.observe();
|
|
6258
|
+
});
|
|
5964
6259
|
} catch (error) {
|
|
5965
6260
|
console.error("[LionRapid] Initialization failed:", error);
|
|
5966
6261
|
}
|