-
-
Notifications
You must be signed in to change notification settings - Fork 521
Expand file tree
/
Copy pathi18n.jsx
More file actions
121 lines (104 loc) Β· 3.11 KB
/
i18n.jsx
File metadata and controls
121 lines (104 loc) Β· 3.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { createContext } from 'preact';
import { useContext, useEffect, useState } from 'preact/hooks';
import { useLocation } from 'preact-iso';
import { localStorageGet, localStorageSet } from './localstorage';
import { useResource } from './use-resource.js';
import config from '../config.json';
import englishTranslations from '../locales/en.json';
const translationURLs = import.meta.glob('../locales/!(en)*.json', {
query: '?url&no-inline',
eager: true,
import: 'default'
});
/**
* @typedef LanguageContext
* @property {string} lang
* @property {(string) => void} setLang
* @property {typeof englishTranslations} translations
* @property {typeof englishTranslations} fallback
*/
/**
* @type {import('preact').Context<LanguageContext>}
*/
const LanguageContext = createContext(/** @type {LanguageContext} */ ({}));
/**
* Get the default language based on the preferred preference of the browser
* @param {Record<string, string>} available All available languages
* @returns {string | undefined}
*/
function getNavigatorLanguage(available) {
if (typeof navigator === 'undefined') return;
let langs = [navigator.language].concat(navigator.languages);
for (let i = 0; i < langs.length; i++) {
if (langs[i]) {
let lang = String(langs[i]).toLowerCase();
if (available[lang]) return lang;
// Respect order of `navigator.languages` by returning if the fallback language `English` is found
if (lang === 'en') return;
}
}
}
export function LanguageProvider({ children }) {
const { query } = useLocation();
// We only prerender in English
const [lang, setLang] = useState('en');
const translations = useResource(() => {
if (lang == 'en') return englishTranslations;
let url = '';
for (const translationURL in translationURLs) {
if (translationURL.includes(`/${lang}.json`)) {
url = /** @type {string} */ (translationURLs[translationURL]);
break;
}
}
if (!url) throw new Error(`No translation found for language: ${lang}`);
return fetch(url, {
credentials: 'include',
mode: 'no-cors'
}).then(r => r.json());
}, [lang]);
useEffect(() => {
const userLang =
query.lang ||
localStorageGet('lang') ||
getNavigatorLanguage(config.locales) ||
'en';
setLang(userLang);
document.documentElement.lang = userLang;
}, []);
const setAndUpdateHtmlAttr = lang => {
localStorageSet('lang', lang);
setLang(lang);
document.documentElement.lang = lang;
};
return (
<LanguageContext.Provider
value={{
lang,
setLang: setAndUpdateHtmlAttr,
translations,
fallback: englishTranslations
}}
>
{children}
</LanguageContext.Provider>
);
}
export function useLanguageContext() {
return useContext(LanguageContext);
}
/**
* Maps a key to its translated string based upon the current language.
*/
export function useTranslate() {
const { translations, fallback } = useContext(LanguageContext);
/**
* @template {keyof typeof translations} T
* @template {keyof typeof translations[T]} K
* @param {T} namespace
* @param {K} key
* @return {typeof translations[T][K]}
*/
return (namespace, key) =>
translations[namespace][key] || fallback[namespace][key];
}