Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0189ASmkMee4F5aJ3jnKeKQv
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import { useEffect, useMemo, useState } from "react";
|
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import { I18nProvider } from "@lingui/react";
|
|
import type { Messages } from "@lingui/core";
|
|
import { playerQueries } from "@/features/players/queries";
|
|
import {
|
|
createI18n,
|
|
ensureMessages,
|
|
hasCachedMessages,
|
|
i18nMessagesQueryKey,
|
|
primeMessages,
|
|
resolveLocale,
|
|
} from "@/lib/i18n";
|
|
|
|
/**
|
|
* Must mount ABOVE AuthProvider: the localized server-query hooks (useMe
|
|
* included) call useLingui, so this provider cannot depend on auth context.
|
|
* It reads the same auth query via raw useQuery instead — passively (useMe
|
|
* is the active fetcher; SSR data is ensured in __root beforeLoad).
|
|
*/
|
|
export const LinguiProvider = ({ children }: { children: React.ReactNode }) => {
|
|
const { queryKey, queryFn } = playerQueries.auth();
|
|
const { data } = useQuery({
|
|
queryKey,
|
|
queryFn: async () => {
|
|
const result = await queryFn();
|
|
if (!result.success) throw new Error(result.error.userMessage);
|
|
return result.data;
|
|
},
|
|
staleTime: 30 * 1000,
|
|
refetchOnMount: false,
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
|
|
const queryClient = useQueryClient();
|
|
const locale = resolveLocale(data?.metadata?.locale);
|
|
const [catalogVersion, setCatalogVersion] = useState(0);
|
|
|
|
const i18n = useMemo(
|
|
() => {
|
|
if (!hasCachedMessages(locale)) {
|
|
const seeded = queryClient.getQueryData<Messages>(
|
|
i18nMessagesQueryKey(locale)
|
|
);
|
|
if (seeded) primeMessages(locale, seeded);
|
|
}
|
|
return createI18n(locale);
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[locale, catalogVersion, queryClient]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (hasCachedMessages(locale)) return;
|
|
let cancelled = false;
|
|
let retryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
const load = () => {
|
|
ensureMessages(locale).then(
|
|
() => {
|
|
if (!cancelled) setCatalogVersion((v) => v + 1);
|
|
},
|
|
() => {
|
|
if (!cancelled) retryTimer = setTimeout(load, 3000);
|
|
}
|
|
);
|
|
};
|
|
load();
|
|
return () => {
|
|
cancelled = true;
|
|
if (retryTimer) clearTimeout(retryTimer);
|
|
};
|
|
}, [locale]);
|
|
|
|
useEffect(() => {
|
|
document.documentElement.lang = locale;
|
|
}, [locale]);
|
|
|
|
return <I18nProvider i18n={i18n}>{children}</I18nProvider>;
|
|
};
|