@lovalingo/lovalingo 0.5.25 → 0.5.28
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/__tests__/languageFlags.test.d.ts +1 -0
- package/dist/__tests__/languageFlags.test.js +42 -0
- package/dist/__tests__/mergeEntitlements.test.d.ts +1 -0
- package/dist/__tests__/mergeEntitlements.test.js +27 -0
- package/dist/components/LanguageSwitcher.js +80 -53
- package/dist/components/LovalingoProvider.js +18 -473
- package/dist/components/provider/__tests__/seoUtils.test.d.ts +1 -0
- package/dist/components/provider/__tests__/seoUtils.test.js +13 -0
- package/dist/components/provider/editModeUtils.d.ts +6 -0
- package/dist/components/provider/editModeUtils.js +59 -0
- package/dist/components/provider/localeUtils.d.ts +8 -0
- package/dist/components/provider/localeUtils.js +46 -0
- package/dist/components/provider/providerConstants.d.ts +12 -0
- package/dist/components/provider/providerConstants.js +11 -0
- package/dist/components/provider/seoUtils.d.ts +8 -0
- package/dist/components/provider/seoUtils.js +118 -0
- package/dist/components/provider/useEditModeOverlay.d.ts +7 -0
- package/dist/components/provider/useEditModeOverlay.js +134 -0
- package/dist/components/provider/useHistoryNavigationPatch.d.ts +3 -0
- package/dist/components/provider/useHistoryNavigationPatch.js +47 -0
- package/dist/components/provider/useProviderCache.d.ts +12 -0
- package/dist/components/provider/useProviderCache.js +82 -0
- package/dist/hooks/provider/useBundleLoading.d.ts +2 -1
- package/dist/hooks/provider/useBundleLoading.js +15 -3
- package/dist/utils/api.d.ts +3 -78
- package/dist/utils/api.js +1 -53
- package/dist/utils/apiTypes.d.ts +78 -0
- package/dist/utils/apiTypes.js +1 -0
- package/dist/utils/apiUtils.d.ts +4 -0
- package/dist/utils/apiUtils.js +54 -0
- package/dist/utils/languageFlags.d.ts +7 -0
- package/dist/utils/languageFlags.js +90 -0
- package/dist/utils/markerEngine.d.ts +8 -66
- package/dist/utils/markerEngine.js +19 -703
- package/dist/utils/markerEngineApply.d.ts +3 -0
- package/dist/utils/markerEngineApply.js +136 -0
- package/dist/utils/markerEngineConstants.d.ts +10 -0
- package/dist/utils/markerEngineConstants.js +12 -0
- package/dist/utils/markerEngineCritical.d.ts +2 -0
- package/dist/utils/markerEngineCritical.js +98 -0
- package/dist/utils/markerEngineDomUtils.d.ts +8 -0
- package/dist/utils/markerEngineDomUtils.js +74 -0
- package/dist/utils/markerEngineFilters.d.ts +2 -0
- package/dist/utils/markerEngineFilters.js +26 -0
- package/dist/utils/markerEngineMisses.d.ts +5 -0
- package/dist/utils/markerEngineMisses.js +81 -0
- package/dist/utils/markerEngineOriginals.d.ts +5 -0
- package/dist/utils/markerEngineOriginals.js +29 -0
- package/dist/utils/markerEngineScan.d.ts +5 -0
- package/dist/utils/markerEngineScan.js +162 -0
- package/dist/utils/markerEngineState.d.ts +4 -0
- package/dist/utils/markerEngineState.js +14 -0
- package/dist/utils/markerEngineStats.d.ts +3 -0
- package/dist/utils/markerEngineStats.js +28 -0
- package/dist/utils/markerEngineTranslations.d.ts +3 -0
- package/dist/utils/markerEngineTranslations.js +49 -0
- package/dist/utils/markerEngineTypes.d.ts +62 -0
- package/dist/utils/markerEngineTypes.js +1 -0
- package/dist/utils/markerEngineViewport.d.ts +2 -0
- package/dist/utils/markerEngineViewport.js +27 -0
- package/dist/utils/mergeEntitlements.d.ts +2 -0
- package/dist/utils/mergeEntitlements.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/utils/translator.d.ts +0 -80
- package/dist/utils/translator.js +0 -802
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function getCustomExcludeSelector(): string | null;
|
|
2
|
+
export declare function setCustomExcludeSelector(value: string | null): void;
|
|
3
|
+
export declare function getActiveTranslationMap(): Map<string, string> | null;
|
|
4
|
+
export declare function setActiveTranslationMap(value: Map<string, string> | null): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
let customExcludeSelector = null;
|
|
2
|
+
let activeTranslationMap = null;
|
|
3
|
+
export function getCustomExcludeSelector() {
|
|
4
|
+
return customExcludeSelector;
|
|
5
|
+
}
|
|
6
|
+
export function setCustomExcludeSelector(value) {
|
|
7
|
+
customExcludeSelector = value;
|
|
8
|
+
}
|
|
9
|
+
export function getActiveTranslationMap() {
|
|
10
|
+
return activeTranslationMap;
|
|
11
|
+
}
|
|
12
|
+
export function setActiveTranslationMap(value) {
|
|
13
|
+
activeTranslationMap = value;
|
|
14
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function buildEmptyStats() {
|
|
2
|
+
return {
|
|
3
|
+
totalTextNodes: 0,
|
|
4
|
+
markedNodes: 0,
|
|
5
|
+
skippedUnsafeNodes: 0,
|
|
6
|
+
skippedExcludedNodes: 0,
|
|
7
|
+
skippedNonTranslatableNodes: 0,
|
|
8
|
+
totalChars: 0,
|
|
9
|
+
markedChars: 0,
|
|
10
|
+
skippedUnsafeChars: 0,
|
|
11
|
+
skippedExcludedChars: 0,
|
|
12
|
+
skippedNonTranslatableChars: 0,
|
|
13
|
+
coverageRatio: 0,
|
|
14
|
+
coverageRatioChars: 0,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function finalizeStats(stats) {
|
|
18
|
+
const eligibleNodes = stats.totalTextNodes -
|
|
19
|
+
stats.skippedUnsafeNodes -
|
|
20
|
+
stats.skippedExcludedNodes -
|
|
21
|
+
stats.skippedNonTranslatableNodes;
|
|
22
|
+
const eligibleChars = stats.totalChars -
|
|
23
|
+
stats.skippedUnsafeChars -
|
|
24
|
+
stats.skippedExcludedChars -
|
|
25
|
+
stats.skippedNonTranslatableChars;
|
|
26
|
+
stats.coverageRatio = eligibleNodes > 0 ? stats.markedNodes / eligibleNodes : 1;
|
|
27
|
+
stats.coverageRatioChars = eligibleChars > 0 ? stats.markedChars / eligibleChars : 1;
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { normalizeWhitespace } from "./markerEngineDomUtils";
|
|
2
|
+
import { getActiveTranslationMap, setActiveTranslationMap } from "./markerEngineState";
|
|
3
|
+
export function setActiveTranslations(translations) {
|
|
4
|
+
if (!translations || translations.length === 0) {
|
|
5
|
+
setActiveTranslationMap(null);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const map = new Map();
|
|
9
|
+
for (const t of translations) {
|
|
10
|
+
const source = normalizeWhitespace((t?.source_text || "").toString());
|
|
11
|
+
const translated = (t?.translated_text ?? "").toString();
|
|
12
|
+
if (!source || !translated)
|
|
13
|
+
continue;
|
|
14
|
+
map.set(source, translated);
|
|
15
|
+
}
|
|
16
|
+
setActiveTranslationMap(map);
|
|
17
|
+
}
|
|
18
|
+
export function addActiveTranslations(translations) {
|
|
19
|
+
if (!translations)
|
|
20
|
+
return 0;
|
|
21
|
+
const map = getActiveTranslationMap() ?? new Map();
|
|
22
|
+
let added = 0;
|
|
23
|
+
if (Array.isArray(translations)) {
|
|
24
|
+
for (const t of translations) {
|
|
25
|
+
const source = normalizeWhitespace((t?.source_text || "").toString());
|
|
26
|
+
const translated = (t?.translated_text ?? "").toString();
|
|
27
|
+
if (!source || !translated)
|
|
28
|
+
continue;
|
|
29
|
+
if (map.get(source) === translated)
|
|
30
|
+
continue;
|
|
31
|
+
map.set(source, translated);
|
|
32
|
+
added += 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
for (const [keyRaw, valueRaw] of Object.entries(translations || {})) {
|
|
37
|
+
const source = normalizeWhitespace((keyRaw || "").toString());
|
|
38
|
+
const translated = (valueRaw ?? "").toString();
|
|
39
|
+
if (!source || !translated)
|
|
40
|
+
continue;
|
|
41
|
+
if (map.get(source) === translated)
|
|
42
|
+
continue;
|
|
43
|
+
map.set(source, translated);
|
|
44
|
+
added += 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
setActiveTranslationMap(map);
|
|
48
|
+
return added;
|
|
49
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type MarkerStats = {
|
|
2
|
+
totalTextNodes: number;
|
|
3
|
+
markedNodes: number;
|
|
4
|
+
skippedUnsafeNodes: number;
|
|
5
|
+
skippedExcludedNodes: number;
|
|
6
|
+
skippedNonTranslatableNodes: number;
|
|
7
|
+
totalChars: number;
|
|
8
|
+
markedChars: number;
|
|
9
|
+
skippedUnsafeChars: number;
|
|
10
|
+
skippedExcludedChars: number;
|
|
11
|
+
skippedNonTranslatableChars: number;
|
|
12
|
+
coverageRatio: number;
|
|
13
|
+
coverageRatioChars: number;
|
|
14
|
+
};
|
|
15
|
+
export type MarkerEngineOptions = {
|
|
16
|
+
throttleMs?: number;
|
|
17
|
+
};
|
|
18
|
+
export type DomScanOccurrence = {
|
|
19
|
+
source_text: string;
|
|
20
|
+
semantic_context?: string;
|
|
21
|
+
};
|
|
22
|
+
export type DomScanSegment = {
|
|
23
|
+
kind: "text" | "title" | "aria-label" | "placeholder";
|
|
24
|
+
selector: string | null;
|
|
25
|
+
original: string | null;
|
|
26
|
+
current: string | null;
|
|
27
|
+
html: null;
|
|
28
|
+
};
|
|
29
|
+
export type DomScanResult = {
|
|
30
|
+
version: 1;
|
|
31
|
+
stats: MarkerStats;
|
|
32
|
+
segments: DomScanSegment[];
|
|
33
|
+
occurrences: DomScanOccurrence[];
|
|
34
|
+
critical_occurrences?: DomScanOccurrence[];
|
|
35
|
+
viewport?: {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
truncated: boolean;
|
|
40
|
+
};
|
|
41
|
+
export type DomMiss = {
|
|
42
|
+
source_text: string;
|
|
43
|
+
semantic_context: string;
|
|
44
|
+
};
|
|
45
|
+
export type DomMissScanResult = {
|
|
46
|
+
misses: DomMiss[];
|
|
47
|
+
};
|
|
48
|
+
export type CriticalFingerprint = {
|
|
49
|
+
critical_count: number;
|
|
50
|
+
critical_hash: string;
|
|
51
|
+
viewport: {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export type TextOriginal = {
|
|
57
|
+
raw: string;
|
|
58
|
+
trimmed: string;
|
|
59
|
+
leading: string;
|
|
60
|
+
trailing: string;
|
|
61
|
+
id: string;
|
|
62
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function isInViewport(rect, viewportHeight, bufferPx) {
|
|
2
|
+
if (!rect)
|
|
3
|
+
return false;
|
|
4
|
+
if (!Number.isFinite(rect.top) || !Number.isFinite(rect.bottom))
|
|
5
|
+
return false;
|
|
6
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
7
|
+
return false;
|
|
8
|
+
return rect.bottom > -bufferPx && rect.top < viewportHeight + bufferPx;
|
|
9
|
+
}
|
|
10
|
+
export function getTextNodeRect(node) {
|
|
11
|
+
try {
|
|
12
|
+
const range = document.createRange();
|
|
13
|
+
range.selectNodeContents(node);
|
|
14
|
+
const rect = range.getBoundingClientRect();
|
|
15
|
+
if (rect && rect.width > 0 && rect.height > 0)
|
|
16
|
+
return rect;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// ignore
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return node.parentElement ? node.parentElement.getBoundingClientRect() : null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.5.
|
|
1
|
+
export declare const VERSION = "0.5.28";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.5.
|
|
1
|
+
export const VERSION = "0.5.28";
|
package/package.json
CHANGED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { Translation, Exclusion } from '../types';
|
|
2
|
-
export declare class Translator {
|
|
3
|
-
private translationMap;
|
|
4
|
-
private exclusions;
|
|
5
|
-
private nonTranslatableTerms;
|
|
6
|
-
constructor();
|
|
7
|
-
/**
|
|
8
|
-
* Check if element is interactive (should be preserved as island)
|
|
9
|
-
*/
|
|
10
|
-
private isInteractive;
|
|
11
|
-
/**
|
|
12
|
-
* Check if text contains actual translatable content
|
|
13
|
-
*/
|
|
14
|
-
private isTranslatableText;
|
|
15
|
-
/**
|
|
16
|
-
* Check if element should be treated as semantic boundary
|
|
17
|
-
* even though it's not in the predefined SEMANTIC_BOUNDARIES set
|
|
18
|
-
*
|
|
19
|
-
* Phase 1: Conservative approach - only leaf elements (no element children)
|
|
20
|
-
* This catches cases like: <span>text</span>, <div>text</div>, etc.
|
|
21
|
-
*/
|
|
22
|
-
private shouldTreatAsSemantic;
|
|
23
|
-
private isLargeInteractiveContainer;
|
|
24
|
-
private sanitizePlaceholderMap;
|
|
25
|
-
private cleanupPreserveIds;
|
|
26
|
-
setTranslations(translations: Translation[]): void;
|
|
27
|
-
setExclusions(exclusions: Exclusion[]): void;
|
|
28
|
-
private isExcluded;
|
|
29
|
-
/**
|
|
30
|
-
* DOM-BASED EXTRACTION
|
|
31
|
-
* Uses DOM APIs to reliably parse and tokenize HTML
|
|
32
|
-
*/
|
|
33
|
-
private extractTranslatableContent;
|
|
34
|
-
/**
|
|
35
|
-
* RECONSTRUCTION: Replace tokens with original HTML
|
|
36
|
-
*/
|
|
37
|
-
private reconstructHTML;
|
|
38
|
-
/**
|
|
39
|
-
* Restore element to original HTML while preserving event listeners
|
|
40
|
-
*/
|
|
41
|
-
private restoreElement;
|
|
42
|
-
/**
|
|
43
|
-
* Restore all elements to original state
|
|
44
|
-
*/
|
|
45
|
-
restoreDOM(): void;
|
|
46
|
-
/**
|
|
47
|
-
* Mark all descendant elements with unique IDs before translation
|
|
48
|
-
* This allows us to reuse original DOM nodes (preserving event listeners)
|
|
49
|
-
*/
|
|
50
|
-
private markElements;
|
|
51
|
-
/**
|
|
52
|
-
* After reconstruction, transplant original LIVE elements into new structure
|
|
53
|
-
* This preserves event listeners and framework connections
|
|
54
|
-
*/
|
|
55
|
-
private transplantOriginalElements;
|
|
56
|
-
/**
|
|
57
|
-
* Directly update the parent element's children by transplanting from temp
|
|
58
|
-
* This avoids using innerHTML which would destroy event listeners
|
|
59
|
-
*/
|
|
60
|
-
private updateElementChildren;
|
|
61
|
-
/**
|
|
62
|
-
* Update only text nodes within an element, preserving child elements
|
|
63
|
-
* Enhanced to handle nested structures recursively
|
|
64
|
-
*/
|
|
65
|
-
private updateTextNodesOnly;
|
|
66
|
-
translateElement(element: HTMLElement): void;
|
|
67
|
-
/**
|
|
68
|
-
* Restore attribute to original value
|
|
69
|
-
*/
|
|
70
|
-
private restoreAttribute;
|
|
71
|
-
/**
|
|
72
|
-
* Translate interactive element safely (preserves event handlers)
|
|
73
|
-
*/
|
|
74
|
-
private translateInteractive;
|
|
75
|
-
/**
|
|
76
|
-
* Translate individual attributes
|
|
77
|
-
*/
|
|
78
|
-
private translateAttribute;
|
|
79
|
-
translateDOM(): void;
|
|
80
|
-
}
|