/* global React */ // Jullia — locale plumbing. English is the source of truth (window.JULLIA_I18N.en); // `t(path)` reads a dotted path from the active locale, falling back to English if // a key is missing. `localize(item)` overlays a data record's translated fields // (from its own `i18n[locale]` block) onto the English base — arrays that carry // non-text data alongside copy (like a gallery's image path) are merged by index // so translations only need to supply the words. const LocaleContext = React.createContext(null); function LocaleProvider({ children }) { const [locale, setLocaleState] = React.useState(() => { try { const saved = localStorage.getItem('jullia_locale'); if (saved && window.JULLIA_LOCALES && window.JULLIA_LOCALES.includes(saved)) return saved; } catch (e) { /* no-op — storage may be unavailable */ } return 'en'; }); const setLocale = (next) => { setLocaleState(next); try { localStorage.setItem('jullia_locale', next); } catch (e) { /* no-op */ } }; const dict = (window.JULLIA_I18N && window.JULLIA_I18N[locale]) || {}; const fallback = (window.JULLIA_I18N && window.JULLIA_I18N.en) || {}; const t = (path) => { const get = (obj) => path.split('.').reduce((o, k) => (o && o[k] !== undefined ? o[k] : undefined), obj); const v = get(dict); return v !== undefined ? v : get(fallback); }; const localize = (item) => { if (!item) return item; const overrides = item.i18n && item.i18n[locale]; if (!overrides) return item; const merged = { ...item, ...overrides }; if (overrides.gallery && item.gallery) { merged.gallery = item.gallery.map((g, i) => ({ ...g, ...(overrides.gallery[i] || {}) })); } return merged; }; const value = { locale, setLocale, t, localize }; return {children}; } function useLocale() { return React.useContext(LocaleContext); } Object.assign(window, { LocaleProvider, useLocale });