set up i18n
This commit is contained in:
parent
490cf0c677
commit
27aac495b9
10 changed files with 183 additions and 3 deletions
9
src/features/i18n/constants.ts
Normal file
9
src/features/i18n/constants.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { Locale } from "./context";
|
||||
|
||||
import Flag_en_GB from 'flag-icons/flags/4x3/gb.svg';
|
||||
import Flag_nl_NL from 'flag-icons/flags/4x3/nl.svg';
|
||||
|
||||
export const locales: Record<Locale, { label: string, flag: any }> = {
|
||||
'en-GB': { label: 'English', flag: Flag_en_GB },
|
||||
'nl-NL': { label: 'Nederlands', flag: Flag_nl_NL },
|
||||
} as const;
|
60
src/features/i18n/context.tsx
Normal file
60
src/features/i18n/context.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Accessor, createContext, createMemo, createSignal, ParentComponent, Setter, useContext } from 'solid-js';
|
||||
import { translator, flatten, Translator, Flatten } from "@solid-primitives/i18n";
|
||||
import en from '~/i18n/en-GB.json';
|
||||
import nl from '~/i18n/nl-NL.json';
|
||||
import { makePersisted } from '@solid-primitives/storage';
|
||||
|
||||
type RawDictionary = typeof en;
|
||||
type Dictionary = Flatten<RawDictionary>;
|
||||
export type Locale = 'en-GB' | 'nl-NL';
|
||||
|
||||
const dictionaries = {
|
||||
'en-GB': en,
|
||||
'nl-NL': nl,
|
||||
} as const;
|
||||
|
||||
interface I18nContextType {
|
||||
readonly t: Translator<Dictionary>;
|
||||
readonly locale: Accessor<Locale>;
|
||||
readonly setLocale: Setter<Locale>;
|
||||
readonly dictionaries: Accessor<Record<Locale, RawDictionary>>;
|
||||
readonly availableLocales: Accessor<Locale[]>;
|
||||
}
|
||||
|
||||
const I18nContext = createContext<I18nContextType>();
|
||||
|
||||
export const I18nProvider: ParentComponent = (props) => {
|
||||
const [locale, setLocale, initLocale] = makePersisted(createSignal<Locale>('en-GB'), { name: 'locale' });
|
||||
const dictionary = createMemo(() => flatten(dictionaries[locale()]));
|
||||
const t = translator(dictionary);
|
||||
|
||||
const ctx: I18nContextType = {
|
||||
t,
|
||||
locale,
|
||||
setLocale,
|
||||
dictionaries: createMemo(() => dictionaries),
|
||||
availableLocales: createMemo(() => Object.keys(dictionaries) as Locale[]),
|
||||
};
|
||||
|
||||
return <I18nContext.Provider value={ctx}>{props.children}</I18nContext.Provider>
|
||||
};
|
||||
|
||||
export const useI18n = () => {
|
||||
const context = useContext(I18nContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error(`'useI18n' is called outside the scope of an <I18nProvider />`);
|
||||
}
|
||||
|
||||
return { t: context.t, locale: context.locale };
|
||||
};
|
||||
|
||||
export const internal_useI18n = () => {
|
||||
const context = useContext(I18nContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error(`'useI18n' is called outside the scope of an <I18nProvider />`);
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
2
src/features/i18n/index.tsx
Normal file
2
src/features/i18n/index.tsx
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { I18nProvider, useI18n } from './context';
|
||||
export { LocalePicker } from './picker';
|
7
src/features/i18n/picker.module.css
Normal file
7
src/features/i18n/picker.module.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
.box {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.flag {
|
||||
inline-size: 1em;
|
||||
}
|
22
src/features/i18n/picker.tsx
Normal file
22
src/features/i18n/picker.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { Component } from "solid-js";
|
||||
import { internal_useI18n } from "./context";
|
||||
import { locales } from "./constants";
|
||||
import { ComboBox } from "~/components/combobox";
|
||||
import { Dynamic } from "solid-js/web";
|
||||
import css from './picker.module.css';
|
||||
|
||||
interface LocalePickerProps { }
|
||||
|
||||
export const LocalePicker: Component<LocalePickerProps> = (props) => {
|
||||
const { locale, setLocale } = internal_useI18n();
|
||||
|
||||
return <ComboBox
|
||||
id="locale-picker"
|
||||
class={css.box}
|
||||
value={locale()}
|
||||
setValue={setLocale}
|
||||
values={locales}
|
||||
>
|
||||
{(locale, { flag, label }) => <Dynamic component={flag} lang={locale} aria-label={label} class={css.flag} />}
|
||||
</ComboBox>
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue