fast-react-i18n
Version:
Minimalist i18n library for React and Next.js
145 lines (140 loc) • 4.03 kB
JavaScript
// src/I18nProvider.tsx
import {
createContext,
useContext,
useEffect,
useRef,
useState
} from "react";
// src/loadTranslations.ts
var loadTranslations = async (path, locale) => {
try {
const res = await fetch(`${path}/${locale}.json`);
if (!res.ok) throw new Error(`No se pudo cargar: ${path}/${locale}.json`);
return await res.json();
} catch {
console.warn(`[i18n] No se pudo cargar: ${path}/${locale}.json`);
return {};
}
};
// src/utils/load.ts
async function load(path, locale, fallbackLocale) {
try {
return await loadTranslations(path, locale);
} catch (error) {
console.warn(`[i18n] No se pudo cargar ${locale}, intentando fallback...`);
if (fallbackLocale && fallbackLocale !== locale) {
try {
return await loadTranslations(path, fallbackLocale);
} catch (fallbackError) {
console.error(
`[i18n] Tampoco se pudo cargar el fallback: ${fallbackLocale}`,
fallbackError
);
}
}
return {};
}
}
// src/I18nProvider.tsx
import { jsx } from "react/jsx-runtime";
var I18nContext = createContext({
locale: "en",
translations: {},
setLocale: () => {
},
setTranslations: () => {
}
});
var useI18nContext = () => useContext(I18nContext);
var I18nProvider = ({
initialLocale,
fallbackLocale,
translationsPath,
persist = false,
children
}) => {
const [locale, setLocale] = useState(initialLocale || "auto");
const [translations, setTranslations] = useState({});
const cacheRef = useRef(/* @__PURE__ */ new Map());
useEffect(() => {
const resolvedLocale = locale === "auto" ? typeof navigator !== "undefined" ? navigator.language.split("-")[0] : "en" : locale;
if (locale === "auto") {
setLocale(resolvedLocale);
}
async function fetchTranslations() {
if (persist) {
const cached = localStorage.getItem(`i18n_${resolvedLocale}`);
if (cached) {
setTranslations(JSON.parse(cached));
return;
}
}
const cachedInMemory = cacheRef.current.get(resolvedLocale);
if (cachedInMemory) {
setTranslations(cachedInMemory);
return;
}
const res = await load(translationsPath, resolvedLocale, fallbackLocale);
setTranslations(res);
cacheRef.current.set(resolvedLocale, res);
if (persist) {
localStorage.setItem(`i18n_${resolvedLocale}`, JSON.stringify(res));
}
}
fetchTranslations();
}, [locale, translationsPath, fallbackLocale, persist]);
return /* @__PURE__ */ jsx(
I18nContext.Provider,
{
value: { locale, translations, setLocale, setTranslations },
children
}
);
};
// src/utils/functions.ts
var clearTranslationsCache = () => {
Object.keys(localStorage).forEach((key) => {
if (key.startsWith("i18n_")) {
localStorage.removeItem(key);
}
});
};
var useTranslation = () => {
const { translations } = useI18nContext();
return { translations };
};
var detectUserLocale = () => {
if (typeof window === "undefined") return "en";
const urlParams = new URLSearchParams(window.location.search);
const langQuery = urlParams.get("lang");
if (langQuery) return langQuery.split("-")[0];
const match = document.cookie.match(/(^| )lang=([^;]+)/);
if (match) return match[2].split("-")[0];
if (navigator.language) return navigator.language.split("-")[0];
return "en";
};
var useForceReloadTranslations = () => {
const { locale, setTranslations } = useI18nContext();
return async (path, fallbackLocale) => {
const data = await load(path, locale, fallbackLocale);
setTranslations(data);
};
};
var loadAvailableLocales = async (path) => {
try {
const res = await fetch(`${path}/locales.json`);
if (!res.ok) throw new Error("Failed to load locales");
return await res.json();
} catch {
return [];
}
};
export {
I18nProvider,
clearTranslationsCache,
detectUserLocale,
loadAvailableLocales,
useForceReloadTranslations,
useTranslation
};