@lovalingo/lovalingo 0.5.29 → 0.6.0
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/README.md +34 -0
- package/dist/chunk-2FZR2AKF.mjs +88 -0
- package/dist/chunk-7D5LBV45.mjs +46 -0
- package/dist/chunk-CJOSN7RA.mjs +90 -0
- package/dist/chunk-VAHA2TOX.mjs +3440 -0
- package/dist/chunk-ZMRCSUM7.mjs +26 -0
- package/dist/chunk-ZVYKEEUF.mjs +220 -0
- package/dist/core.d.mts +131 -0
- package/dist/core.d.ts +131 -0
- package/dist/core.js +3561 -0
- package/dist/core.mjs +19 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -25
- package/dist/index.js +3885 -28
- package/dist/index.mjs +33 -0
- package/dist/react-router.d.mts +101 -0
- package/dist/react-router.d.ts +101 -0
- package/dist/react-router.js +353 -0
- package/dist/react-router.mjs +14 -0
- package/dist/tanstack-router.d.mts +22 -0
- package/dist/tanstack-router.d.ts +22 -0
- package/dist/tanstack-router.js +162 -0
- package/dist/tanstack-router.mjs +8 -0
- package/package.json +34 -3
- package/dist/__tests__/languageFlags.test.d.ts +0 -1
- package/dist/__tests__/languageFlags.test.js +0 -42
- package/dist/__tests__/mergeEntitlements.test.d.ts +0 -1
- package/dist/__tests__/mergeEntitlements.test.js +0 -27
- package/dist/components/AixsterProvider.d.ts +0 -1
- package/dist/components/AixsterProvider.js +0 -1
- package/dist/components/LangLink.d.ts +0 -20
- package/dist/components/LangLink.js +0 -38
- package/dist/components/LangRouter.d.ts +0 -37
- package/dist/components/LangRouter.js +0 -191
- package/dist/components/LanguageSwitcher.d.ts +0 -17
- package/dist/components/LanguageSwitcher.js +0 -257
- package/dist/components/LovalingoProvider.d.ts +0 -10
- package/dist/components/LovalingoProvider.js +0 -413
- package/dist/components/NavigationOverlay.d.ts +0 -6
- package/dist/components/NavigationOverlay.js +0 -22
- package/dist/components/provider/__tests__/seoUtils.test.d.ts +0 -1
- package/dist/components/provider/__tests__/seoUtils.test.js +0 -13
- package/dist/components/provider/editModeUtils.d.ts +0 -6
- package/dist/components/provider/editModeUtils.js +0 -59
- package/dist/components/provider/localeUtils.d.ts +0 -8
- package/dist/components/provider/localeUtils.js +0 -46
- package/dist/components/provider/providerConstants.d.ts +0 -12
- package/dist/components/provider/providerConstants.js +0 -11
- package/dist/components/provider/seoUtils.d.ts +0 -8
- package/dist/components/provider/seoUtils.js +0 -118
- package/dist/components/provider/useEditModeOverlay.d.ts +0 -7
- package/dist/components/provider/useEditModeOverlay.js +0 -134
- package/dist/components/provider/useHistoryNavigationPatch.d.ts +0 -3
- package/dist/components/provider/useHistoryNavigationPatch.js +0 -47
- package/dist/components/provider/useProviderCache.d.ts +0 -12
- package/dist/components/provider/useProviderCache.js +0 -82
- package/dist/context/AixsterContext.d.ts +0 -3
- package/dist/context/AixsterContext.js +0 -2
- package/dist/context/LangContext.d.ts +0 -1
- package/dist/context/LangContext.js +0 -2
- package/dist/context/LangRoutingContext.d.ts +0 -8
- package/dist/context/LangRoutingContext.js +0 -7
- package/dist/context/LovalingoContext.d.ts +0 -1
- package/dist/context/LovalingoContext.js +0 -1
- package/dist/hooks/provider/useBundleLoading.d.ts +0 -33
- package/dist/hooks/provider/useBundleLoading.js +0 -380
- package/dist/hooks/provider/useDomRules.d.ts +0 -15
- package/dist/hooks/provider/useDomRules.js +0 -38
- package/dist/hooks/provider/useLinkAutoPrefix.d.ts +0 -12
- package/dist/hooks/provider/useLinkAutoPrefix.js +0 -146
- package/dist/hooks/provider/useNavigationPrefetch.d.ts +0 -12
- package/dist/hooks/provider/useNavigationPrefetch.js +0 -82
- package/dist/hooks/provider/usePageviewTracking.d.ts +0 -10
- package/dist/hooks/provider/usePageviewTracking.js +0 -44
- package/dist/hooks/provider/usePrehide.d.ts +0 -5
- package/dist/hooks/provider/usePrehide.js +0 -72
- package/dist/hooks/provider/useSitemapLinkTag.d.ts +0 -7
- package/dist/hooks/provider/useSitemapLinkTag.js +0 -28
- package/dist/hooks/provider/useStringMissReporting.d.ts +0 -14
- package/dist/hooks/provider/useStringMissReporting.js +0 -155
- package/dist/hooks/useAixster.d.ts +0 -6
- package/dist/hooks/useAixster.js +0 -14
- package/dist/hooks/useAixsterEdit.d.ts +0 -5
- package/dist/hooks/useAixsterEdit.js +0 -13
- package/dist/hooks/useAixsterTranslate.d.ts +0 -4
- package/dist/hooks/useAixsterTranslate.js +0 -12
- package/dist/hooks/useLang.d.ts +0 -16
- package/dist/hooks/useLang.js +0 -23
- package/dist/hooks/useLangNavigate.d.ts +0 -24
- package/dist/hooks/useLangNavigate.js +0 -40
- package/dist/hooks/useLovalingo.d.ts +0 -1
- package/dist/hooks/useLovalingo.js +0 -1
- package/dist/hooks/useLovalingoEdit.d.ts +0 -1
- package/dist/hooks/useLovalingoEdit.js +0 -1
- package/dist/hooks/useLovalingoTranslate.d.ts +0 -1
- package/dist/hooks/useLovalingoTranslate.js +0 -1
- package/dist/types.d.ts +0 -76
- package/dist/types.js +0 -1
- package/dist/utils/api.d.ts +0 -42
- package/dist/utils/api.js +0 -395
- package/dist/utils/apiTypes.d.ts +0 -78
- package/dist/utils/apiTypes.js +0 -1
- package/dist/utils/apiUtils.d.ts +0 -4
- package/dist/utils/apiUtils.js +0 -54
- package/dist/utils/domRules.d.ts +0 -2
- package/dist/utils/domRules.js +0 -150
- package/dist/utils/hash.d.ts +0 -9
- package/dist/utils/hash.js +0 -27
- package/dist/utils/languageFlags.d.ts +0 -7
- package/dist/utils/languageFlags.js +0 -90
- package/dist/utils/logger.d.ts +0 -3
- package/dist/utils/logger.js +0 -40
- package/dist/utils/markerEngine.d.ts +0 -12
- package/dist/utils/markerEngine.js +0 -109
- package/dist/utils/markerEngineApply.d.ts +0 -3
- package/dist/utils/markerEngineApply.js +0 -136
- package/dist/utils/markerEngineConstants.d.ts +0 -10
- package/dist/utils/markerEngineConstants.js +0 -12
- package/dist/utils/markerEngineCritical.d.ts +0 -2
- package/dist/utils/markerEngineCritical.js +0 -98
- package/dist/utils/markerEngineDomUtils.d.ts +0 -8
- package/dist/utils/markerEngineDomUtils.js +0 -74
- package/dist/utils/markerEngineFilters.d.ts +0 -2
- package/dist/utils/markerEngineFilters.js +0 -26
- package/dist/utils/markerEngineMisses.d.ts +0 -5
- package/dist/utils/markerEngineMisses.js +0 -81
- package/dist/utils/markerEngineOriginals.d.ts +0 -5
- package/dist/utils/markerEngineOriginals.js +0 -29
- package/dist/utils/markerEngineScan.d.ts +0 -5
- package/dist/utils/markerEngineScan.js +0 -162
- package/dist/utils/markerEngineState.d.ts +0 -4
- package/dist/utils/markerEngineState.js +0 -14
- package/dist/utils/markerEngineStats.d.ts +0 -3
- package/dist/utils/markerEngineStats.js +0 -28
- package/dist/utils/markerEngineTranslations.d.ts +0 -3
- package/dist/utils/markerEngineTranslations.js +0 -49
- package/dist/utils/markerEngineTypes.d.ts +0 -62
- package/dist/utils/markerEngineTypes.js +0 -1
- package/dist/utils/markerEngineViewport.d.ts +0 -2
- package/dist/utils/markerEngineViewport.js +0 -27
- package/dist/utils/mergeEntitlements.d.ts +0 -2
- package/dist/utils/mergeEntitlements.js +0 -7
- package/dist/utils/nonLocalizedPaths.d.ts +0 -12
- package/dist/utils/nonLocalizedPaths.js +0 -136
- package/dist/utils/pathNormalizer.d.ts +0 -49
- package/dist/utils/pathNormalizer.js +0 -115
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -1
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
import { processPath } from "../../utils/pathNormalizer";
|
|
3
|
-
export function useNavigationPrefetch({ resolvedApiKey, apiBase, defaultLocale, locale, routing, allLocales, enhancedPathConfig, }) {
|
|
4
|
-
// Navigation prefetch: warm the HTTP cache for bootstrap + page bundle before the user clicks (reduces EN→FR flash on SPA route changes).
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
if (!resolvedApiKey)
|
|
7
|
-
return;
|
|
8
|
-
if (typeof window === "undefined" || typeof document === "undefined")
|
|
9
|
-
return;
|
|
10
|
-
const connection = navigator?.connection;
|
|
11
|
-
if (connection?.saveData)
|
|
12
|
-
return;
|
|
13
|
-
if (typeof connection?.effectiveType === "string" && /(^|-)2g$/.test(connection.effectiveType))
|
|
14
|
-
return;
|
|
15
|
-
const prefetched = new Set();
|
|
16
|
-
// Why: cap speculative requests to avoid flooding the network on pages with many links.
|
|
17
|
-
const maxPrefetch = 40;
|
|
18
|
-
const isAssetPath = (pathname) => {
|
|
19
|
-
if (pathname === "/robots.txt" || pathname === "/sitemap.xml")
|
|
20
|
-
return true;
|
|
21
|
-
if (pathname.startsWith("/.well-known/"))
|
|
22
|
-
return true;
|
|
23
|
-
return /\.(?:png|jpg|jpeg|gif|svg|webp|avif|ico|css|js|map|json|xml|txt|pdf|zip|gz|br|woff2?|ttf|eot)$/i.test(pathname);
|
|
24
|
-
};
|
|
25
|
-
const pickLocaleForUrl = (url) => {
|
|
26
|
-
if (routing === "path") {
|
|
27
|
-
const segment = url.pathname.split("/")[1] || "";
|
|
28
|
-
if (segment && allLocales.includes(segment))
|
|
29
|
-
return segment;
|
|
30
|
-
return locale;
|
|
31
|
-
}
|
|
32
|
-
const q = url.searchParams.get("t") || url.searchParams.get("locale");
|
|
33
|
-
if (q && allLocales.includes(q))
|
|
34
|
-
return q;
|
|
35
|
-
return locale;
|
|
36
|
-
};
|
|
37
|
-
const onIntent = (event) => {
|
|
38
|
-
if (prefetched.size >= maxPrefetch)
|
|
39
|
-
return;
|
|
40
|
-
const target = event.target;
|
|
41
|
-
const anchor = target?.closest?.("a[href]");
|
|
42
|
-
if (!anchor)
|
|
43
|
-
return;
|
|
44
|
-
const href = anchor.getAttribute("href") || "";
|
|
45
|
-
if (!href || /^(?:#|mailto:|tel:|sms:|javascript:)/i.test(href))
|
|
46
|
-
return;
|
|
47
|
-
let url;
|
|
48
|
-
try {
|
|
49
|
-
url = new URL(href, window.location.origin);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (url.origin !== window.location.origin)
|
|
55
|
-
return;
|
|
56
|
-
if (isAssetPath(url.pathname))
|
|
57
|
-
return;
|
|
58
|
-
const targetLocale = pickLocaleForUrl(url);
|
|
59
|
-
if (!targetLocale || targetLocale === defaultLocale)
|
|
60
|
-
return;
|
|
61
|
-
const normalizedPath = processPath(url.pathname, enhancedPathConfig);
|
|
62
|
-
const key = `${targetLocale}:${normalizedPath}`;
|
|
63
|
-
if (prefetched.has(key))
|
|
64
|
-
return;
|
|
65
|
-
prefetched.add(key);
|
|
66
|
-
const pathParam = `${url.pathname}${url.search}`;
|
|
67
|
-
const bootstrapUrl = `${apiBase}/functions/v1/bootstrap?key=${encodeURIComponent(resolvedApiKey)}&locale=${encodeURIComponent(targetLocale)}&path=${encodeURIComponent(pathParam)}`;
|
|
68
|
-
// Why: CF data plane serves page-scoped bundles only; keep prefetch aligned.
|
|
69
|
-
const bundleUrl = `${apiBase}/functions/v1/bundle?key=${encodeURIComponent(resolvedApiKey)}&locale=${encodeURIComponent(targetLocale)}&path=${encodeURIComponent(pathParam)}&scoped=1`;
|
|
70
|
-
void fetch(bootstrapUrl, { cache: "force-cache" }).catch(() => undefined);
|
|
71
|
-
void fetch(bundleUrl, { cache: "force-cache" }).catch(() => undefined);
|
|
72
|
-
};
|
|
73
|
-
document.addEventListener("pointerover", onIntent, { passive: true });
|
|
74
|
-
document.addEventListener("touchstart", onIntent, { passive: true });
|
|
75
|
-
document.addEventListener("focusin", onIntent);
|
|
76
|
-
return () => {
|
|
77
|
-
document.removeEventListener("pointerover", onIntent);
|
|
78
|
-
document.removeEventListener("touchstart", onIntent);
|
|
79
|
-
document.removeEventListener("focusin", onIntent);
|
|
80
|
-
};
|
|
81
|
-
}, [allLocales, apiBase, defaultLocale, enhancedPathConfig, locale, resolvedApiKey, routing]);
|
|
82
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type React from "react";
|
|
2
|
-
import type { LovalingoAPI } from "../../utils/api";
|
|
3
|
-
type UsePageviewTrackingOptions = {
|
|
4
|
-
apiRef: React.MutableRefObject<LovalingoAPI>;
|
|
5
|
-
resolvedApiKey: string;
|
|
6
|
-
};
|
|
7
|
-
export declare function usePageviewTracking({ apiRef, resolvedApiKey }: UsePageviewTrackingOptions): {
|
|
8
|
-
trackPageviewOnce: (path: string) => void;
|
|
9
|
-
};
|
|
10
|
-
export {};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
import { getCriticalFingerprint } from "../../utils/markerEngine";
|
|
3
|
-
export function usePageviewTracking({ apiRef, resolvedApiKey }) {
|
|
4
|
-
const lastPageviewRef = useRef("");
|
|
5
|
-
const lastPageviewFingerprintRef = useRef("");
|
|
6
|
-
const pageviewFingerprintTimeoutRef = useRef(null);
|
|
7
|
-
const pageviewFingerprintRetryTimeoutRef = useRef(null);
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
lastPageviewRef.current = "";
|
|
10
|
-
lastPageviewFingerprintRef.current = "";
|
|
11
|
-
}, [resolvedApiKey]);
|
|
12
|
-
const trackPageviewOnce = useCallback((path) => {
|
|
13
|
-
const next = (path || "").toString();
|
|
14
|
-
if (!next)
|
|
15
|
-
return;
|
|
16
|
-
if (lastPageviewRef.current === next)
|
|
17
|
-
return;
|
|
18
|
-
lastPageviewRef.current = next;
|
|
19
|
-
apiRef.current.trackPageview(next);
|
|
20
|
-
const trySendFingerprint = () => {
|
|
21
|
-
if (typeof window === "undefined")
|
|
22
|
-
return;
|
|
23
|
-
const markersReady = window.__lovalingoMarkersReady === true;
|
|
24
|
-
if (!markersReady)
|
|
25
|
-
return;
|
|
26
|
-
const fp = getCriticalFingerprint();
|
|
27
|
-
if (!fp || fp.critical_count <= 0)
|
|
28
|
-
return;
|
|
29
|
-
const signature = `${next}|${fp.critical_hash}|${fp.critical_count}`;
|
|
30
|
-
if (lastPageviewFingerprintRef.current === signature)
|
|
31
|
-
return;
|
|
32
|
-
lastPageviewFingerprintRef.current = signature;
|
|
33
|
-
apiRef.current.trackPageview(next, fp);
|
|
34
|
-
};
|
|
35
|
-
if (pageviewFingerprintTimeoutRef.current != null)
|
|
36
|
-
window.clearTimeout(pageviewFingerprintTimeoutRef.current);
|
|
37
|
-
if (pageviewFingerprintRetryTimeoutRef.current != null)
|
|
38
|
-
window.clearTimeout(pageviewFingerprintRetryTimeoutRef.current);
|
|
39
|
-
// Why: wait briefly for markers/content to settle before computing a critical fingerprint for change detection.
|
|
40
|
-
pageviewFingerprintTimeoutRef.current = window.setTimeout(trySendFingerprint, 800);
|
|
41
|
-
pageviewFingerprintRetryTimeoutRef.current = window.setTimeout(trySendFingerprint, 2000);
|
|
42
|
-
}, [apiRef]);
|
|
43
|
-
return { trackPageviewOnce };
|
|
44
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
// Why: keep the no-flash "prehide" budget tight to improve FCP while still preventing perma-hide.
|
|
3
|
-
export const PREHIDE_FAILSAFE_MS = 900;
|
|
4
|
-
export function usePrehide() {
|
|
5
|
-
const prehideStateRef = useRef({
|
|
6
|
-
active: false,
|
|
7
|
-
timeoutId: null,
|
|
8
|
-
startedAtMs: null,
|
|
9
|
-
prevHtmlVisibility: "",
|
|
10
|
-
prevBodyVisibility: "",
|
|
11
|
-
prevHtmlBg: "",
|
|
12
|
-
prevBodyBg: "",
|
|
13
|
-
});
|
|
14
|
-
const forceDisablePrehide = useCallback(() => {
|
|
15
|
-
if (typeof document === "undefined")
|
|
16
|
-
return;
|
|
17
|
-
const html = document.documentElement;
|
|
18
|
-
const body = document.body;
|
|
19
|
-
if (!html || !body)
|
|
20
|
-
return;
|
|
21
|
-
const state = prehideStateRef.current;
|
|
22
|
-
if (state.timeoutId != null) {
|
|
23
|
-
window.clearTimeout(state.timeoutId);
|
|
24
|
-
state.timeoutId = null;
|
|
25
|
-
}
|
|
26
|
-
if (!state.active)
|
|
27
|
-
return;
|
|
28
|
-
state.active = false;
|
|
29
|
-
state.startedAtMs = null;
|
|
30
|
-
html.style.visibility = state.prevHtmlVisibility;
|
|
31
|
-
body.style.visibility = state.prevBodyVisibility;
|
|
32
|
-
html.style.backgroundColor = state.prevHtmlBg;
|
|
33
|
-
body.style.backgroundColor = state.prevBodyBg;
|
|
34
|
-
}, []);
|
|
35
|
-
const enablePrehide = useCallback((bgColor) => {
|
|
36
|
-
if (typeof document === "undefined")
|
|
37
|
-
return;
|
|
38
|
-
const html = document.documentElement;
|
|
39
|
-
const body = document.body;
|
|
40
|
-
if (!html || !body)
|
|
41
|
-
return;
|
|
42
|
-
const state = prehideStateRef.current;
|
|
43
|
-
// Why: avoid "perma-hidden" pages when repeated navigation/errors keep prehide active; always hard-stop after a few seconds.
|
|
44
|
-
if (state.active && state.startedAtMs != null && Date.now() - state.startedAtMs > PREHIDE_FAILSAFE_MS * 3) {
|
|
45
|
-
forceDisablePrehide();
|
|
46
|
-
}
|
|
47
|
-
if (!state.active) {
|
|
48
|
-
state.active = true;
|
|
49
|
-
state.startedAtMs = Date.now();
|
|
50
|
-
state.prevHtmlVisibility = html.style.visibility || "";
|
|
51
|
-
state.prevBodyVisibility = body.style.visibility || "";
|
|
52
|
-
state.prevHtmlBg = html.style.backgroundColor || "";
|
|
53
|
-
state.prevBodyBg = body.style.backgroundColor || "";
|
|
54
|
-
}
|
|
55
|
-
html.style.visibility = "hidden";
|
|
56
|
-
body.style.visibility = "hidden";
|
|
57
|
-
if (bgColor) {
|
|
58
|
-
html.style.backgroundColor = bgColor;
|
|
59
|
-
body.style.backgroundColor = bgColor;
|
|
60
|
-
}
|
|
61
|
-
if (state.timeoutId != null) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
// Why: avoid a "perma-hide" when navigation events repeatedly re-trigger prehide and keep extending the timeout.
|
|
65
|
-
state.timeoutId = window.setTimeout(() => forceDisablePrehide(), PREHIDE_FAILSAFE_MS);
|
|
66
|
-
}, [forceDisablePrehide]);
|
|
67
|
-
const disablePrehide = forceDisablePrehide;
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
return () => disablePrehide();
|
|
70
|
-
}, [disablePrehide]);
|
|
71
|
-
return { enablePrehide, disablePrehide };
|
|
72
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
export function useSitemapLinkTag({ enabled, resolvedApiKey, isSeoActive }) {
|
|
3
|
-
// Auto-inject sitemap link tag
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
if (enabled && resolvedApiKey && isSeoActive()) {
|
|
6
|
-
// Prefer same-origin /sitemap.xml so crawlers discover the canonical sitemap URL.
|
|
7
|
-
// Reminder: /sitemap.xml should be published by the host app (recommended: build-time copy from Lovalingo CDN).
|
|
8
|
-
const sitemapUrl = `${window.location.origin}/sitemap.xml`;
|
|
9
|
-
// Check if link already exists to avoid duplicates
|
|
10
|
-
const existingLink = document.querySelector(`link[rel="sitemap"][href="${sitemapUrl}"]`);
|
|
11
|
-
if (existingLink)
|
|
12
|
-
return;
|
|
13
|
-
// Create and inject link tag
|
|
14
|
-
const link = document.createElement("link");
|
|
15
|
-
link.rel = "sitemap";
|
|
16
|
-
link.type = "application/xml";
|
|
17
|
-
link.href = sitemapUrl;
|
|
18
|
-
document.head.appendChild(link);
|
|
19
|
-
// Cleanup on unmount
|
|
20
|
-
return () => {
|
|
21
|
-
const linkToRemove = document.querySelector(`link[rel="sitemap"][href="${sitemapUrl}"]`);
|
|
22
|
-
if (linkToRemove) {
|
|
23
|
-
document.head.removeChild(linkToRemove);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
}, [enabled, resolvedApiKey, isSeoActive]);
|
|
28
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { MutableRefObject } from "react";
|
|
2
|
-
import type { LovalingoAPI } from "../../utils/api";
|
|
3
|
-
import { type NonLocalizedPathRule } from "../../utils/nonLocalizedPaths";
|
|
4
|
-
export declare function useStringMissReporting(args: {
|
|
5
|
-
apiRef: MutableRefObject<LovalingoAPI>;
|
|
6
|
-
resolvedApiKey: string;
|
|
7
|
-
locale: string;
|
|
8
|
-
defaultLocale: string;
|
|
9
|
-
routing: "path" | "query";
|
|
10
|
-
allLocales: string[];
|
|
11
|
-
nonLocalizedPaths: NonLocalizedPathRule[];
|
|
12
|
-
isLoading: boolean;
|
|
13
|
-
mode: "dom";
|
|
14
|
-
}): void;
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
-
import { addActiveTranslations, applyActiveTranslations, scanDomForMisses } from "../../utils/markerEngine";
|
|
3
|
-
import { logDebug } from "../../utils/logger";
|
|
4
|
-
import { isNonLocalizedPath, stripLocalePrefix } from "../../utils/nonLocalizedPaths";
|
|
5
|
-
// Why: throttle miss scans so we stay responsive on DOM-heavy pages.
|
|
6
|
-
const MISS_SCAN_THROTTLE_MS = 600;
|
|
7
|
-
// Why: allow large pages to report plenty of misses while keeping payloads bounded.
|
|
8
|
-
const MISS_MAX_PER_PAGE = 500;
|
|
9
|
-
function looksLike404Page() {
|
|
10
|
-
if (typeof document === "undefined")
|
|
11
|
-
return false;
|
|
12
|
-
// 1. Meta tag (Explicit opt-in for SPAs)
|
|
13
|
-
const meta = document.querySelector('meta[name="lovalingo:status"]');
|
|
14
|
-
if (meta && meta.getAttribute("content") === "404")
|
|
15
|
-
return true;
|
|
16
|
-
// 2. Title Heuristics
|
|
17
|
-
const title = (document.title || "").toLowerCase();
|
|
18
|
-
// Why: detect common 404 title patterns to avoid reporting misses on ghost pages.
|
|
19
|
-
if (/page not found|seite nicht gefunden|article not found|error 404|page missing|does not exist/.test(title))
|
|
20
|
-
return true;
|
|
21
|
-
// 3. H1 Heuristics
|
|
22
|
-
const h1 = document.querySelector("h1");
|
|
23
|
-
if (h1) {
|
|
24
|
-
const txt = (h1.innerText || "").toLowerCase();
|
|
25
|
-
if (/page not found|seite nicht gefunden|article not found|error 404/.test(txt))
|
|
26
|
-
return true;
|
|
27
|
-
if (txt.includes("404") && txt.length < 50)
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
export function useStringMissReporting(args) {
|
|
33
|
-
const pendingRef = useRef(new Set());
|
|
34
|
-
const seenRef = useRef(new Set());
|
|
35
|
-
const scheduledRef = useRef(null);
|
|
36
|
-
const inFlightRef = useRef(false);
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
pendingRef.current.clear();
|
|
39
|
-
seenRef.current.clear();
|
|
40
|
-
}, [args.locale]);
|
|
41
|
-
const shouldSkip = useCallback(() => {
|
|
42
|
-
if (typeof window === "undefined" || typeof document === "undefined")
|
|
43
|
-
return true;
|
|
44
|
-
if (looksLike404Page())
|
|
45
|
-
return true;
|
|
46
|
-
const disableLiveMisses = Boolean(window.__lovalingoDisableMisses);
|
|
47
|
-
if (disableLiveMisses)
|
|
48
|
-
return true;
|
|
49
|
-
if (!args.resolvedApiKey || args.mode !== "dom")
|
|
50
|
-
return true;
|
|
51
|
-
if (args.isLoading)
|
|
52
|
-
return true;
|
|
53
|
-
if (args.locale === args.defaultLocale)
|
|
54
|
-
return true;
|
|
55
|
-
if (args.routing === "path") {
|
|
56
|
-
const stripped = stripLocalePrefix(window.location.pathname, args.allLocales);
|
|
57
|
-
if (isNonLocalizedPath(stripped, args.nonLocalizedPaths))
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
61
|
-
}, [args.allLocales, args.defaultLocale, args.isLoading, args.locale, args.mode, args.nonLocalizedPaths, args.resolvedApiKey, args.routing]);
|
|
62
|
-
const runScan = useCallback(async () => {
|
|
63
|
-
scheduledRef.current = null;
|
|
64
|
-
if (shouldSkip())
|
|
65
|
-
return;
|
|
66
|
-
if (!document.body)
|
|
67
|
-
return;
|
|
68
|
-
if (inFlightRef.current)
|
|
69
|
-
return;
|
|
70
|
-
const ignore = new Set([...pendingRef.current, ...seenRef.current]);
|
|
71
|
-
const { misses } = scanDomForMisses({ max: MISS_MAX_PER_PAGE, ignore });
|
|
72
|
-
if (misses.length === 0)
|
|
73
|
-
return;
|
|
74
|
-
logDebug(`[Lovalingo] Live misses detected: ${misses.length}`);
|
|
75
|
-
misses.forEach((miss) => pendingRef.current.add(miss.source_text));
|
|
76
|
-
inFlightRef.current = true;
|
|
77
|
-
try {
|
|
78
|
-
const response = await args.apiRef.current.reportStringMisses(args.locale, misses, {
|
|
79
|
-
sourceLocale: args.defaultLocale,
|
|
80
|
-
locales: args.allLocales,
|
|
81
|
-
});
|
|
82
|
-
if (response?.ignored) {
|
|
83
|
-
for (const miss of misses) {
|
|
84
|
-
pendingRef.current.delete(miss.source_text);
|
|
85
|
-
seenRef.current.add(miss.source_text);
|
|
86
|
-
}
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const translations = Array.isArray(response?.translations) ? response.translations : [];
|
|
90
|
-
const pii = Array.isArray(response?.pii) ? response.pii : [];
|
|
91
|
-
logDebug(`[Lovalingo] Live misses resolved`, { translations: translations.length, pii: pii.length });
|
|
92
|
-
const resolved = new Set();
|
|
93
|
-
pii.forEach((text) => resolved.add(text));
|
|
94
|
-
translations.forEach((row) => {
|
|
95
|
-
if (row?.source_text)
|
|
96
|
-
resolved.add(row.source_text);
|
|
97
|
-
});
|
|
98
|
-
if (translations.length > 0) {
|
|
99
|
-
const additions = translations
|
|
100
|
-
.map((row) => ({
|
|
101
|
-
source_text: row.source_text,
|
|
102
|
-
translated_text: row.translated_text,
|
|
103
|
-
source_locale: args.defaultLocale,
|
|
104
|
-
target_locale: args.locale,
|
|
105
|
-
}))
|
|
106
|
-
.filter((row) => row.source_text && row.translated_text);
|
|
107
|
-
if (additions.length > 0) {
|
|
108
|
-
addActiveTranslations(additions);
|
|
109
|
-
applyActiveTranslations(document.body);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
for (const miss of misses) {
|
|
113
|
-
pendingRef.current.delete(miss.source_text);
|
|
114
|
-
if (resolved.has(miss.source_text)) {
|
|
115
|
-
seenRef.current.add(miss.source_text);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
for (const miss of misses) {
|
|
121
|
-
pendingRef.current.delete(miss.source_text);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
inFlightRef.current = false;
|
|
126
|
-
}
|
|
127
|
-
}, [args.apiRef, args.defaultLocale, args.locale, shouldSkip]);
|
|
128
|
-
const scheduleScan = useCallback(() => {
|
|
129
|
-
if (scheduledRef.current != null)
|
|
130
|
-
return;
|
|
131
|
-
scheduledRef.current = window.setTimeout(() => {
|
|
132
|
-
void runScan();
|
|
133
|
-
}, MISS_SCAN_THROTTLE_MS);
|
|
134
|
-
}, [runScan]);
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
if (shouldSkip())
|
|
137
|
-
return;
|
|
138
|
-
scheduleScan();
|
|
139
|
-
if (!document.body)
|
|
140
|
-
return;
|
|
141
|
-
const observer = new MutationObserver(() => scheduleScan());
|
|
142
|
-
observer.observe(document.body, {
|
|
143
|
-
childList: true,
|
|
144
|
-
subtree: true,
|
|
145
|
-
characterData: true,
|
|
146
|
-
});
|
|
147
|
-
return () => {
|
|
148
|
-
observer.disconnect();
|
|
149
|
-
if (scheduledRef.current != null) {
|
|
150
|
-
window.clearTimeout(scheduledRef.current);
|
|
151
|
-
scheduledRef.current = null;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
}, [scheduleScan, shouldSkip]);
|
|
155
|
-
}
|
package/dist/hooks/useAixster.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { useContext } from 'react';
|
|
2
|
-
import { LovalingoContext } from '../context/LovalingoContext';
|
|
3
|
-
export const useLovalingo = () => {
|
|
4
|
-
const context = useContext(LovalingoContext);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error('useLovalingo must be used within LovalingoProvider');
|
|
7
|
-
}
|
|
8
|
-
return {
|
|
9
|
-
locale: context.locale,
|
|
10
|
-
setLocale: context.setLocale,
|
|
11
|
-
isLoading: context.isLoading,
|
|
12
|
-
config: context.config,
|
|
13
|
-
};
|
|
14
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { useContext } from 'react';
|
|
2
|
-
import { LovalingoContext } from '../context/LovalingoContext';
|
|
3
|
-
export const useLovalingoEdit = () => {
|
|
4
|
-
const context = useContext(LovalingoContext);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error('useLovalingoEdit must be used within LovalingoProvider');
|
|
7
|
-
}
|
|
8
|
-
return {
|
|
9
|
-
editMode: context.editMode,
|
|
10
|
-
toggleEditMode: context.toggleEditMode,
|
|
11
|
-
excludeElement: context.excludeElement,
|
|
12
|
-
};
|
|
13
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { useContext } from 'react';
|
|
2
|
-
import { LovalingoContext } from '../context/LovalingoContext';
|
|
3
|
-
export const useLovalingoTranslate = () => {
|
|
4
|
-
const context = useContext(LovalingoContext);
|
|
5
|
-
if (!context) {
|
|
6
|
-
throw new Error('useLovalingoTranslate must be used within LovalingoProvider');
|
|
7
|
-
}
|
|
8
|
-
return {
|
|
9
|
-
translateElement: context.translateElement,
|
|
10
|
-
translateDOM: context.translateDOM,
|
|
11
|
-
};
|
|
12
|
-
};
|
package/dist/hooks/useLang.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useLang - Get the current language from the URL
|
|
3
|
-
*
|
|
4
|
-
* Works with LangRouter to extract language from /:lang/* routes
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* ```tsx
|
|
8
|
-
* function MyComponent() {
|
|
9
|
-
* const lang = useLang(); // 'en', 'fr', 'de', etc.
|
|
10
|
-
* return <div>Current language: {lang}</div>;
|
|
11
|
-
* }
|
|
12
|
-
* ```
|
|
13
|
-
*
|
|
14
|
-
* @returns Current language code (e.g., 'en', 'fr', 'de')
|
|
15
|
-
*/
|
|
16
|
-
export declare function useLang(): string;
|
package/dist/hooks/useLang.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { useParams } from 'react-router-dom';
|
|
2
|
-
import { useContext } from 'react';
|
|
3
|
-
import { LangContext } from '../context/LangContext';
|
|
4
|
-
/**
|
|
5
|
-
* useLang - Get the current language from the URL
|
|
6
|
-
*
|
|
7
|
-
* Works with LangRouter to extract language from /:lang/* routes
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* ```tsx
|
|
11
|
-
* function MyComponent() {
|
|
12
|
-
* const lang = useLang(); // 'en', 'fr', 'de', etc.
|
|
13
|
-
* return <div>Current language: {lang}</div>;
|
|
14
|
-
* }
|
|
15
|
-
* ```
|
|
16
|
-
*
|
|
17
|
-
* @returns Current language code (e.g., 'en', 'fr', 'de')
|
|
18
|
-
*/
|
|
19
|
-
export function useLang() {
|
|
20
|
-
const ctxLang = useContext(LangContext);
|
|
21
|
-
const { lang } = useParams();
|
|
22
|
-
return ctxLang ?? lang ?? 'en'; // Fallback to 'en' if not found
|
|
23
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useLangNavigate - Get a language-aware navigate function
|
|
3
|
-
*
|
|
4
|
-
* Automatically prepends the current language to navigation paths
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* ```tsx
|
|
8
|
-
* function MyComponent() {
|
|
9
|
-
* const navigate = useLangNavigate();
|
|
10
|
-
*
|
|
11
|
-
* const goToPricing = () => {
|
|
12
|
-
* navigate('pricing'); // Goes to /en/pricing or /fr/pricing automatically
|
|
13
|
-
* };
|
|
14
|
-
*
|
|
15
|
-
* return <button onClick={goToPricing}>Pricing</button>;
|
|
16
|
-
* }
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @returns Navigate function that auto-prepends language
|
|
20
|
-
*/
|
|
21
|
-
export declare function useLangNavigate(): (path: string, options?: {
|
|
22
|
-
replace?: boolean;
|
|
23
|
-
state?: unknown;
|
|
24
|
-
}) => void;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { useNavigate } from 'react-router-dom';
|
|
2
|
-
import { useCallback, useContext } from 'react';
|
|
3
|
-
import { useLang } from './useLang';
|
|
4
|
-
import { LangRoutingContext } from '../context/LangRoutingContext';
|
|
5
|
-
import { isNonLocalizedPath } from '../utils/nonLocalizedPaths';
|
|
6
|
-
/**
|
|
7
|
-
* useLangNavigate - Get a language-aware navigate function
|
|
8
|
-
*
|
|
9
|
-
* Automatically prepends the current language to navigation paths
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* ```tsx
|
|
13
|
-
* function MyComponent() {
|
|
14
|
-
* const navigate = useLangNavigate();
|
|
15
|
-
*
|
|
16
|
-
* const goToPricing = () => {
|
|
17
|
-
* navigate('pricing'); // Goes to /en/pricing or /fr/pricing automatically
|
|
18
|
-
* };
|
|
19
|
-
*
|
|
20
|
-
* return <button onClick={goToPricing}>Pricing</button>;
|
|
21
|
-
* }
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* @returns Navigate function that auto-prepends language
|
|
25
|
-
*/
|
|
26
|
-
export function useLangNavigate() {
|
|
27
|
-
const navigate = useNavigate();
|
|
28
|
-
const lang = useLang();
|
|
29
|
-
const routing = useContext(LangRoutingContext);
|
|
30
|
-
return useCallback((path, options) => {
|
|
31
|
-
const trimmed = (path || "").toString().trim();
|
|
32
|
-
if (!trimmed)
|
|
33
|
-
return;
|
|
34
|
-
const normalized = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
35
|
-
const fullPath = isNonLocalizedPath(normalized, routing.nonLocalizedPaths)
|
|
36
|
-
? normalized
|
|
37
|
-
: `/${lang}${normalized}`;
|
|
38
|
-
navigate(fullPath, options);
|
|
39
|
-
}, [lang, navigate, routing.nonLocalizedPaths]);
|
|
40
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useLovalingo } from "./useAixster";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useLovalingo } from "./useAixster";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useLovalingoEdit } from "./useAixsterEdit";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useLovalingoEdit } from "./useAixsterEdit";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useLovalingoTranslate } from "./useAixsterTranslate";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useLovalingoTranslate } from "./useAixsterTranslate";
|