UNPKG

@smallstack/svelte-ui

Version:

Tiny library for Svelte 5 and DaisyUI, published as multi entry ESM module and as web components.

101 lines (100 loc) 3.68 kB
import i18nNext from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import { getContext, setContext } from "svelte"; import { derived, get, writable } from "svelte/store"; export const createI18nStore = (i18nNextOptions) => { i18nNext.use(LanguageDetector).init({ fallbackLng: "en", detection: { order: ["localStorage", "navigator"], caches: ["localStorage"] }, interpolation: { escapeValue: false // not needed for svelte as it escapes by default }, defaultNS: "translation", ...i18nNextOptions }); return derived([currentLanguage], () => i18nNext); }; export function initI18n(i18nNextOptions) { setContext("i18n", createI18nStore(i18nNextOptions)); } export function addTranslations(locale, translations) { i18nNext.addResourceBundle(locale, "translation", translations, true, true); translationsUpdated.set(true); } export async function addTranslationsFromUrl(url) { // load translations async await fetch(url).then(async (response) => { const translations = await response.json(); for (const translation of translations.elements) { const keyValueMap = translation.translations.reduce((acc, curr) => { acc[curr.key] = curr.value; return acc; }, {}); i18nNext.addResourceBundle(translation.language, "translation", keyValueMap, true, true); } translationsUpdated.set(true); }); } export function getI18n() { const i18nFromContext = getContext("i18n"); if (!i18nFromContext) throw new Error("i18n store not found on context, did you forget to call initI18n?"); return i18nFromContext; } export const currentLanguage = writable("de"); i18nNext.on("languageChanged", (lng) => { const formattedLanguage = lng.split("-")[0]; currentLanguage.set(formattedLanguage); }); export const availableLanguages = writable(["de", "en"]); export const translationsUpdated = writable(undefined); export function translateInlineTranslation(translation, options = { useFallback: true }) { if (!translation) return undefined; if (typeof translation === "string") return translation; if (!options.language) options.language = get(currentLanguage); const t = translation[options.language]; if (t) return t; if (options.useFallback !== true) return ""; return Object.values(translation)[0]; } /** translates inline translations */ export const it = derived(currentLanguage, () => translateInlineTranslation); /** based on the input either inline translations or keys are getting translated */ export const t = derived(currentLanguage, (currentLanguage) => { return (key, options) => { if (typeof key === "string") return i18nNext.t(key, options); else if (isTranslatable(key)) return i18nNext.t(key.key, { ...options, ...key.replacers }); else return translateInlineTranslation(key, { language: currentLanguage }); }; }); export function isInlineTranslation(obj) { if (!(obj instanceof Object)) return false; for (const [key, value] of Object.entries(obj)) if (typeof key !== "string" || typeof value !== "string") return false; return true; } export function isTranslatable(obj) { if (!(obj instanceof Object)) return false; if (typeof obj.key !== "string") return false; if (obj.replacers && !(obj.replacers instanceof Object)) return false; return true; }