import { useEffect, useState } from "react"; import { Localisation } from "../../plugins/localisation"; import type { TLocalisationResourceAddedEvent } from "../../plugins/localisation/types"; type TUseLocalisationPluginReturn = { isLoading: boolean; isReady: boolean; isError: boolean; plugin: Localisation | null; }; /** * Хук для использования плагина локализации. Возвращает экземпляр вызова и состояние загрузки. * Если передан `code`, хук будет ожидать доступность указанного ключа локали, * даже если она подгружается асинхронно после инициализации плагина. * * @param code{string} Код локали, доступность которого необходимо проверить * @returns {TUseLocalisationPluginReturn} Возвращает объект с экземпляром плагина локализации (`plugin`), * флагом готовности (`isReady`), флагом загрузки (`isLoading`) и флагом ошибки (`isError`). * @example * import { useLocalisationPlugin } from "@delement/ui/utils/react"; * * const App = () => { * const { plugin, isReady, isLoading } = useLocalisationPlugin("forms.invalidInput"); * return
{isReady ? plugin.getLocale("forms.invalidInput") : "loading"}
; * }; */ export const useLocalisationPlugin = (code?: string): TUseLocalisationPluginReturn => { // экземпляр вызова плагина const [ plugin, setPlugin ] = useState(() => Localisation.instance); // Функция для определения начального состояния готовности const getInitialReadyState = () => { if (!Localisation.instance) { return false; } if (!code) { return Localisation.isReady; } return Localisation.isReady && Localisation.instance.isLocaleExist(code); }; // состояние готовности const [ isReady, setIsReady ] = useState(() => getInitialReadyState()); // состояние загрузки const [ isLoading, setIsLoading ] = useState(() => !getInitialReadyState()); // состояние ошибки const [ isError, setIsError ] = useState(false); useEffect(() => { let isMounted = true; let unsubscribeResourceAdded: (() => void) | null = null; let unsubscribeStoreAdded: (() => void) | null = null; /** * Отписывается от всех слушателей событий добавления ресурсов */ const unsubscribeAll = () => { if (unsubscribeResourceAdded) { unsubscribeResourceAdded(); unsubscribeResourceAdded = null; } if (unsubscribeStoreAdded) { unsubscribeStoreAdded(); unsubscribeStoreAdded = null; } }; /** * Проверяет доступность локали и обновляет состояния. * Если локаль найдена — отписывается от всех слушателей. */ const checkLocaleAvailability = (instance: Localisation) => { if (!isMounted) { return; } if (!code) { // Код не передан — плагин готов setIsReady(true); setIsLoading(false); setIsError(false); return; } if (instance.isLocaleExist(code)) { // Локаль доступна — всё готово, отписываемся от слушателей setIsReady(true); setIsLoading(false); setIsError(false); unsubscribeAll(); } else { // Локаль ещё не загружена — продолжаем ждать setIsReady(false); setIsLoading(true); setIsError(false); } }; /** * Обработчик события добавления ресурсов. * Содержит точную информацию о добавленных ключах. */ const handleResourceAdded = (data: TLocalisationResourceAddedEvent) => { if (!isMounted || !plugin || !code) { return; } if (typeof data.isHasCode === "function" && data.isHasCode(code)) { checkLocaleAvailability(plugin); } }; /** * Обработчик СТАНДАРТНОГО события i18next store "added". * Используется как fallback, если ресурсы добавляются не через наши методы. */ const handleStoreAdded = (_lng: string, _ns: string) => { if (!isMounted || !plugin) { return; } // Перепроверяем доступность локали при любом изменении store checkLocaleAvailability(plugin); }; /** * Обработчик готовности плагина */ const handlePluginReady = (instance: Localisation) => { if (!isMounted) { return; } setPlugin(instance); // Проверяем доступность ДО подписки if (instance.isLocaleExist(code)) { // Локаль уже существует — сразу отмечаем как готово setIsReady(true); setIsLoading(false); setIsError(false); return; // Без подписки } // Локали нет — подписываемся и ждём if (code) { setIsReady(false); setIsLoading(true); setIsError(false); unsubscribeResourceAdded = Localisation.onResourceAdded(handleResourceAdded); instance.lib.store.on("added", handleStoreAdded); unsubscribeStoreAdded = () => { instance.lib.store.off("added", handleStoreAdded); }; } else { // Без кода — просто готово setIsReady(true); setIsLoading(false); setIsError(false); } }; // Если плагин уже готов if (Localisation.instance && Localisation.isReady) { handlePluginReady(Localisation.instance); } else { // Иначе ждём инициализации setIsReady(false); setIsLoading(true); setIsError(false); Localisation.addReadyCallback(handlePluginReady); Localisation.create() .catch((err: unknown) => { if (!isMounted) { return; } setIsReady(false); setIsError(true); setIsLoading(false); setPlugin(null); console.error(`[useLocalisationPlugin] Localisation plugin is crushed with error`, err); }); } return () => { isMounted = false; unsubscribeAll(); }; }, [ code, plugin ]); return { isLoading, isReady, isError, plugin, }; };