// Device preferences contain no identity and remain usable without storage access. export const languageChoices = ['auto', 'fr', 'en']; export const themeChoices = ['system', 'light', 'dark']; export function resolveLanguage(choice, languages = ['en']) { if (choice === 'fr' || choice === 'en') return choice; for (const language of languages) { const base = language.toLowerCase().split('-')[0]; if (base === 'fr' || base === 'en') return base; } return 'en'; } export function resolveTheme(choice, dark) { return choice === 'dark' || (choice !== 'light' && dark) ? 'dark' : 'light'; } export function createPreferences(browser) { const root = browser.document.documentElement; const media = browser.matchMedia('(prefers-color-scheme: dark)'); const read = key => { try { return browser.localStorage.getItem(key); } catch { return null; } }; const write = (key, value) => { try { browser.localStorage.setItem(key, value); } catch {} }; // Migrate the clock's existing device preference without changing app data. let language = read('dm-language') || 'auto'; let theme = read('dm-theme') || read('time-theme') || 'system'; if (!languageChoices.includes(language)) language = 'auto'; if (!themeChoices.includes(theme)) theme = 'system'; const preferences = { get language() { return language; }, get theme() { return theme; }, get locale() { return resolveLanguage(language, browser.navigator.languages?.length ? browser.navigator.languages : [browser.navigator.language || 'en']); }, setLanguage(value) { if (!languageChoices.includes(value)) return; language = value; write('dm-language', value); apply(); }, setTheme(value) { if (!themeChoices.includes(value)) return; theme = value; write('dm-theme', value); apply(); }, translate: value => value, }; function apply() { root.lang = preferences.locale; root.dataset.theme = resolveTheme(theme, media.matches); root.dataset.language = language; root.style.colorScheme = root.dataset.theme; browser.dispatchEvent(new browser.CustomEvent('dm-preferences-change', {detail: {language, locale: preferences.locale, theme, resolvedTheme: root.dataset.theme}})); } media.addEventListener('change', apply); browser.addEventListener('languagechange', apply); browser.addEventListener('storage', event => { if (event.key === 'dm-language' || event.key === null) language = languageChoices.includes(read('dm-language')) ? read('dm-language') : 'auto'; if (event.key === 'dm-theme' || event.key === null) theme = themeChoices.includes(read('dm-theme')) ? read('dm-theme') : 'system'; apply(); }); apply(); return preferences; }