createStringsResource: support merged imports
This commit is contained in:
parent
4f8b31ca31
commit
8a9c788fe1
6 changed files with 58 additions and 32 deletions
|
@ -35,7 +35,7 @@ export function autoMatchLangTag() {
|
||||||
return match(Array.from(navigator.languages), SUPPORTED_LANGS, DEFAULT_LANG);
|
return match(Array.from(navigator.languages), SUPPORTED_LANGS, DEFAULT_LANG);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DateFnLocaleCx = createContext<Accessor<Locale>>(() => enGB);
|
const DateFnLocaleCx = /* __@PURE__ */createContext<Accessor<Locale>>(() => enGB);
|
||||||
|
|
||||||
const cachedDateFnLocale: Record<string, Locale> = {
|
const cachedDateFnLocale: Record<string, Locale> = {
|
||||||
enGB,
|
enGB,
|
||||||
|
@ -149,11 +149,20 @@ export function useLanguage() {
|
||||||
return () => settings().language || autoMatchLangTag();
|
return () => settings().language || autoMatchLangTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImportFn<T> = (name: string) => Promise<{default: T}>
|
||||||
|
|
||||||
|
type ImportedModule<F> = F extends ImportFn<infer T> ? T: never
|
||||||
|
|
||||||
|
type MergedImportedModule<T> =
|
||||||
|
T extends [] ? {} :
|
||||||
|
T extends [infer I] ? ImportedModule<I> :
|
||||||
|
T extends [infer I, ...infer J] ? ImportedModule<I> & MergedImportedModule<J> : never
|
||||||
|
|
||||||
export function createStringResource<
|
export function createStringResource<
|
||||||
M extends Record<string, string | Template<any>>,
|
T extends ImportFn<Record<string, string | Template<any> | undefined>>[],
|
||||||
>(importFn: (code: string) => Promise<{ default: M }>) {
|
>(...importFns: T) {
|
||||||
const language = useLanguage();
|
const language = useLanguage();
|
||||||
const cache: Record<string, M | undefined> = {};
|
const cache: Record<string, MergedImportedModule<T>> = {};
|
||||||
|
|
||||||
return createResource(
|
return createResource(
|
||||||
() => [language()] as const,
|
() => [language()] as const,
|
||||||
|
@ -162,9 +171,13 @@ export function createStringResource<
|
||||||
return cache[nlang];
|
return cache[nlang];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { default: dict } = await importFn(`${nlang}`);
|
const results = await Promise.all(importFns.map(x => x(nlang).then(v => v.default)))
|
||||||
|
|
||||||
return dict;
|
const merged: MergedImportedModule<T> = Object.assign({}, ...results)
|
||||||
|
|
||||||
|
cache[nlang] = merged;
|
||||||
|
|
||||||
|
return merged;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
} from "@suid/material";
|
} from "@suid/material";
|
||||||
import {
|
import {
|
||||||
Close as CloseIcon,
|
Close as CloseIcon,
|
||||||
|
Logout,
|
||||||
Public as PublicIcon,
|
Public as PublicIcon,
|
||||||
Refresh as RefreshIcon,
|
Refresh as RefreshIcon,
|
||||||
Translate as TranslateIcon,
|
Translate as TranslateIcon,
|
||||||
|
@ -38,11 +39,19 @@ import {
|
||||||
SUPPORTED_REGIONS,
|
SUPPORTED_REGIONS,
|
||||||
useDateFnLocale,
|
useDateFnLocale,
|
||||||
} from "../platform/i18n.jsx";
|
} from "../platform/i18n.jsx";
|
||||||
import { resolveTemplate, translator } from "@solid-primitives/i18n";
|
import { resolveTemplate, translator, type Template } from "@solid-primitives/i18n";
|
||||||
|
|
||||||
|
type Strings = {
|
||||||
|
["lang.auto"]: Template<{detected: string}>
|
||||||
|
} & Record<string, string | undefined>
|
||||||
|
|
||||||
const Settings: ParentComponent = () => {
|
const Settings: ParentComponent = () => {
|
||||||
const [strings] = createStringResource(
|
const [strings] = createStringResource(
|
||||||
(code) => import(`./i18n/${code}.json`),
|
(code) =>
|
||||||
|
import(`./i18n/${code}.json`) as Promise<{
|
||||||
|
default: Strings;
|
||||||
|
}>,
|
||||||
|
() => import(`./i18n/lang-names.json`)
|
||||||
);
|
);
|
||||||
const t = translator(strings, resolveTemplate);
|
const t = translator(strings, resolveTemplate);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -87,31 +96,34 @@ const Settings: ParentComponent = () => {
|
||||||
<li>
|
<li>
|
||||||
<ul>
|
<ul>
|
||||||
<ListSubheader>{t("Accounts")}</ListSubheader>
|
<ListSubheader>{t("Accounts")}</ListSubheader>
|
||||||
<ListItem>
|
<ListItemButton disabled>
|
||||||
<ListItemText>{t("All Notifications")}</ListItemText>
|
<ListItemText>{t("All Notifications")}</ListItemText>
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<Switch value={false} />
|
<Switch value={false} disabled/>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItemButton>
|
||||||
<Divider />
|
<Divider />
|
||||||
<ListItem>
|
<ListItemButton disabled>
|
||||||
<ListItemText>{t("Sign in...")}</ListItemText>
|
<ListItemText>{t("Sign in...")}</ListItemText>
|
||||||
</ListItem>
|
</ListItemButton>
|
||||||
<Divider />
|
<Divider />
|
||||||
</ul>
|
</ul>
|
||||||
<For each={profiles()}>
|
<For each={profiles()}>
|
||||||
{({ account: acct, inf }) => (
|
{({ account: acct, inf }) => (
|
||||||
<ul data-site={acct.site} data-username={inf?.username}>
|
<ul data-site={acct.site} data-username={inf?.username}>
|
||||||
<ListSubheader>{`@${inf?.username ?? "..."}@${new URL(acct.site).host}`}</ListSubheader>
|
<ListSubheader>{`@${inf?.username ?? "..."}@${new URL(acct.site).host}`}</ListSubheader>
|
||||||
<ListItem>
|
<ListItemButton disabled>
|
||||||
<ListItemText>Notifications</ListItemText>
|
<ListItemText>{t("Notifications")}</ListItemText>
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<Switch value={false} />
|
<Switch value={false} disabled/>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItemButton>
|
||||||
<Divider />
|
<Divider />
|
||||||
<ListItemButton onClick={[doSignOut, acct]}>
|
<ListItemButton onClick={[doSignOut, acct]}>
|
||||||
<ListItemText>Sign out</ListItemText>
|
<ListItemIcon>
|
||||||
|
<Logout/>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>{t("Sign out")}</ListItemText>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<Divider />
|
<Divider />
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -120,11 +132,7 @@ const Settings: ParentComponent = () => {
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ListSubheader>{t("Reading")}</ListSubheader>
|
<ListSubheader>{t("Reading")}</ListSubheader>
|
||||||
<ListItem>
|
<ListItemButton
|
||||||
<ListItemText secondary="Regular">Fonts</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<Divider />
|
|
||||||
<ListItem
|
|
||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
$settings.setKey(
|
$settings.setKey(
|
||||||
"prefetchTootsDisabled",
|
"prefetchTootsDisabled",
|
||||||
|
@ -138,7 +146,7 @@ const Settings: ParentComponent = () => {
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<Switch checked={!settings$().prefetchTootsDisabled} />
|
<Switch checked={!settings$().prefetchTootsDisabled} />
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
</ListItem>
|
</ListItemButton>
|
||||||
<Divider />
|
<Divider />
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -162,7 +170,7 @@ const Settings: ParentComponent = () => {
|
||||||
>
|
>
|
||||||
<option value={"xauto"}>
|
<option value={"xauto"}>
|
||||||
{t("lang.auto", {
|
{t("lang.auto", {
|
||||||
detected: t("lang." + autoMatchLangTag()),
|
detected: t("lang." + autoMatchLangTag()) ?? autoMatchLangTag(),
|
||||||
})}
|
})}
|
||||||
</option>
|
</option>
|
||||||
<For each={SUPPORTED_LANGS}>
|
<For each={SUPPORTED_LANGS}>
|
||||||
|
@ -191,7 +199,7 @@ const Settings: ParentComponent = () => {
|
||||||
>
|
>
|
||||||
<option value={"xauto"}>
|
<option value={"xauto"}>
|
||||||
{t("region.auto", {
|
{t("region.auto", {
|
||||||
detected: t("region." + autoMatchRegion()),
|
detected: t("region." + autoMatchRegion()) ?? autoMatchRegion(),
|
||||||
})}
|
})}
|
||||||
</option>
|
</option>
|
||||||
<For each={SUPPORTED_REGIONS}>
|
<For each={SUPPORTED_REGIONS}>
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Region": "Region",
|
"Region": "Region",
|
||||||
"lang.auto": "Auto({{detected}})",
|
"lang.auto": "Auto({{detected}})",
|
||||||
"lang.zh-Hans": "中文(简体)",
|
|
||||||
"lang.en": "English",
|
|
||||||
"region.auto": "Auto({{detected}})",
|
"region.auto": "Auto({{detected}})",
|
||||||
"region.en_GB": "Great Britan (English)",
|
"region.en_GB": "Great Britan (English)",
|
||||||
"region.en_US": "United States (English)",
|
"region.en_US": "United States (English)",
|
||||||
"region.zh_CN": "China Mainland (Chinese)",
|
"region.zh_CN": "China Mainland (Chinese)",
|
||||||
"datefmt": "yyyy/MM/dd"
|
"datefmt": "yyyy/MM/dd",
|
||||||
|
"Sign out": "Sign out",
|
||||||
|
"Notifications": "Notifications"
|
||||||
}
|
}
|
4
src/settings/i18n/lang-names.json
Normal file
4
src/settings/i18n/lang-names.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"lang.zh-Hans": "中文(简体)",
|
||||||
|
"lang.en": "English"
|
||||||
|
}
|
|
@ -15,11 +15,11 @@
|
||||||
"Language": "语言",
|
"Language": "语言",
|
||||||
"Region": "区域",
|
"Region": "区域",
|
||||||
"lang.auto": "自动({{detected}})",
|
"lang.auto": "自动({{detected}})",
|
||||||
"lang.zh-Hans": "中文(简体)",
|
|
||||||
"lang.en": "English",
|
|
||||||
"region.auto": "自动({{detected}})",
|
"region.auto": "自动({{detected}})",
|
||||||
"region.en_GB": "英国和苏格兰(英语)",
|
"region.en_GB": "英国和苏格兰(英语)",
|
||||||
"region.en_US": "美国(英语)",
|
"region.en_US": "美国(英语)",
|
||||||
"region.zh_CN": "中国大陆(中文)",
|
"region.zh_CN": "中国大陆(中文)",
|
||||||
"datefmt": "yyyy年MM月dd日"
|
"datefmt": "yyyy年MM月dd日",
|
||||||
|
"Sign out": "登出此账户",
|
||||||
|
"Notifications": "通知"
|
||||||
}
|
}
|
|
@ -11,5 +11,6 @@
|
||||||
"types": ["vite/client", "vite-plugin-pwa/solid"],
|
"types": ["vite/client", "vite-plugin-pwa/solid"],
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue