From b4fa751345311ffe53072db2f4c5103b37d5f880 Mon Sep 17 00:00:00 2001 From: thislight Date: Thu, 26 Sep 2024 17:23:40 +0800 Subject: [PATCH] first prototype of i18n system --- bun.lockb | Bin 238907 -> 239741 bytes package.json | 2 + src/App.tsx | 21 +++- src/platform/i18n.tsx | 170 +++++++++++++++++++++++++++++++++ src/settings/Settings.tsx | 119 +++++++++++++++++++---- src/settings/i18n/en.json | 25 +++++ src/settings/i18n/zh-Hans.json | 25 +++++ src/settings/stores.ts | 2 + src/timelines/RegularToot.tsx | 4 +- 9 files changed, 348 insertions(+), 20 deletions(-) create mode 100644 src/platform/i18n.tsx create mode 100644 src/settings/i18n/en.json create mode 100644 src/settings/i18n/zh-Hans.json diff --git a/bun.lockb b/bun.lockb index 024e9977b43bda1ed14ddda5e184503ea59508b0..8230f5ff44fe878835dc364611480f661f380f35 100755 GIT binary patch delta 42224 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ℑfQCml7XzvXdP2z-ASf`V85jS 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 VF+RM#)!a8@#KZ%6&*bIX{vVlK`(Xe8 delta 41999 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 rSuL_HFNm?{ import("./accounts/SignIn.js")); const AccountMastodonOAuth2Callback = lazy( @@ -47,6 +49,7 @@ const App: Component = () => { const theme = useRootTheme(); const accts = useStore($accounts); const clientStore = createSignal([]); + const lang = useLanguage(); createRenderEffect(() => { const [, setClients] = clientStore; @@ -55,6 +58,16 @@ const App: Component = () => { ); }); + createRenderEffect(() => { + const root = document.querySelector(":root")!; + root.setAttribute("lang", lang()); + }); + + onCleanup(() => { + const root = document.querySelector(":root")!; + root.removeAttribute("lang"); + }); + const UnexpectedError = lazy(() => import("./UnexpectedError.js")); return ( @@ -65,9 +78,11 @@ const App: Component = () => { }} > - - - + + + + + ); diff --git a/src/platform/i18n.tsx b/src/platform/i18n.tsx new file mode 100644 index 0000000..7627ea1 --- /dev/null +++ b/src/platform/i18n.tsx @@ -0,0 +1,170 @@ +import { + ParentComponent, + createContext, + createMemo, + createRenderEffect, + createResource, + useContext, +} from "solid-js"; +import { match } from "@formatjs/intl-localematcher"; +import { Accessor, createEffect, createSignal } from "solid-js"; +import { $settings } from "../settings/stores"; +import { enGB } from "date-fns/locale/en-GB"; +import { useStore } from "@nanostores/solid"; +import type { Locale } from "date-fns"; +import type { Template } from "@solid-primitives/i18n"; + +async function synchronised( + name: string, + callback: () => Promise | void, +): Promise { + await navigator.locks.request(name, callback); +} + +export const SUPPORTED_LANGS = ["en", "zh-Hans"]; + +export const SUPPORTED_REGIONS = ["en_US", "en_GB", "zh_CN"]; + +const DEFAULT_LANG = "en"; + +/** + * Decide the using language for the user. + * @returns the selected language tag + */ +export function autoMatchLangTag() { + return match(Array.from(navigator.languages), SUPPORTED_LANGS, DEFAULT_LANG); +} + +const DateFnLocaleCx = createContext>(() => enGB); + +const cachedDateFnLocale: Record = { + enGB, +}; + +export function autoMatchRegion() { + const regions = navigator.languages + .map((x) => { + const parts = x.split("_"); + if (parts.length > 1) { + return parts[1]; + } + return undefined; + }) + .filter((x): x is string => !!x); + for (const r of regions) { + for (const available of SUPPORTED_REGIONS) { + if (available.toLowerCase().endsWith(r.toLowerCase())) { + return available; + } + } + } + return "en_GB"; +} + +export function useRegion() { + const appSettings = useStore($settings); + + return createMemo(() => { + const settings = appSettings(); + if (typeof settings.region !== "undefined") { + return settings.region; + } else { + return autoMatchRegion(); + } + }); +} + +async function importDateFnLocale(tag: string): Promise { + switch (tag.toLowerCase()) { + case "en_us": + return (await import("date-fns/locale/en-US")).enUS; + case "en_gb": + return (await import("date-fns/locale/en-GB")).enGB; + case "zh_cn": + return (await import("date-fns/locale/zh-CN")).zhCN; + default: + throw new TypeError(`unsupported tag "${tag}"`); + } +} + +/** + * Provides runtime values and fetch dependencies for date-fns locale + */ +export const DateFnScope: ParentComponent = (props) => { + const [dateFnLocale, setDateFnLocale] = createSignal(enGB); + const region = useRegion(); + + createEffect(() => { + const dateFnLocaleName = region(); + + if (cachedDateFnLocale[dateFnLocaleName]) { + setDateFnLocale(cachedDateFnLocale[dateFnLocaleName]); + } else { + synchronised("i18n-wrapper-load-date-fns-locale", async () => { + if (cachedDateFnLocale[dateFnLocaleName]) { + setDateFnLocale(cachedDateFnLocale[dateFnLocaleName]); + return; + } + const target = `date-fns/locale/${dateFnLocaleName}`; + try { + const mod = await importDateFnLocale(dateFnLocaleName); + cachedDateFnLocale[dateFnLocaleName] = mod; + setDateFnLocale(mod); + } catch (reason) { + console.error( + { + act: "load-date-fns-locale", + stat: "failed", + reason, + target, + }, + "failed to load date-fns locale", + ); + } + }); + } + }); + + return ( + + {props.children} + + ); +}; + +/** + * Get the {@link Locale} object for date-fns. + * + * This function must be using in {@link DateFnScope} + * + * @returns Accessor for Locale + */ +export function useDateFnLocale(): Accessor { + const cx = useContext(DateFnLocaleCx); + return cx; +} + +export function useLanguage() { + const settings = useStore($settings); + return () => settings().language || autoMatchLangTag(); +} + +export function createStringResource< + M extends Record>, +>(importFn: (code: string) => Promise<{ default: M }>) { + const language = useLanguage(); + const cache: Record = {}; + + return createResource( + () => [language()] as const, + async ([nlang]) => { + if (cache[nlang]) { + return cache[nlang]; + } + + const { default: dict } = await importFn(`${nlang}`); + + return dict; + }, + ); +} diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 9eebcac..65d94b9 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -7,32 +7,50 @@ import { List, ListItem, ListItemButton, + ListItemIcon, ListItemSecondaryAction, ListItemText, ListSubheader, + NativeSelect, Switch, Toolbar, } from "@suid/material"; import { Close as CloseIcon, + Public as PublicIcon, Refresh as RefreshIcon, + Translate as TranslateIcon, } from "@suid/icons-material"; import { useNavigate } from "@solidjs/router"; import { Title } from "../material/typography.jsx"; import { css } from "solid-styled"; import { useSignedInProfiles } from "../masto/acct.js"; import { signOut, type Account } from "../accounts/stores.js"; -import { intlFormat } from "date-fns"; +import { format, intlFormat } from "date-fns"; import { useStore } from "@nanostores/solid"; import { $settings } from "./stores.js"; import { useRegisterSW } from "virtual:pwa-register/solid"; +import { + autoMatchLangTag, + autoMatchRegion, + createStringResource, + SUPPORTED_LANGS, + SUPPORTED_REGIONS, + useDateFnLocale, +} from "../platform/i18n.jsx"; +import { resolveTemplate, translator } from "@solid-primitives/i18n"; const Settings: ParentComponent = () => { + const [strings] = createStringResource( + (code) => import(`./i18n/${code}.json`), + ); + const t = translator(strings, resolveTemplate); const navigate = useNavigate(); const settings$ = useStore($settings); const { needRefresh: [needRefresh], } = useRegisterSW(); + const dateFnLocale = useDateFnLocale(); const [profiles] = useSignedInProfiles(); @@ -60,7 +78,7 @@ const Settings: ParentComponent = () => { - Settings + {t("Settings")} } @@ -68,16 +86,16 @@ const Settings: ParentComponent = () => {
    • - Accounts + {t("Accounts")} - All Notifications + {t("All Notifications")} - Sign in... + {t("Sign in...")}
    @@ -101,7 +119,7 @@ const Settings: ParentComponent = () => {
  • - Reading + {t("Reading")} Fonts @@ -114,8 +132,8 @@ const Settings: ParentComponent = () => { ) } > - - Prefetch Toots + + {t("Prefetch Toots")} @@ -124,24 +142,93 @@ const Settings: ParentComponent = () => {
  • - This Application + {t("This Application")} - - About Tutu + + + + {t("Language")} + + { + $settings.setKey( + "language", + e.currentTarget.value === "xauto" + ? undefined + : e.currentTarget.value, + ); + }} + > + + + {(code) => } + + + + + + + + + + {t("Region")} + + { + $settings.setKey( + "region", + e.currentTarget.value === "xauto" + ? undefined + : e.currentTarget.value, + ); + }} + > + + + {(code) => ( + + )} + + + + + + + + {t("About Tutu")} - {needRefresh() - ? "An update is ready, restart the Tutu to apply" - : "No updates"} + {needRefresh() ? t("updates.ready") : t("updates.no")} - window.location.reload()}> + window.location.reload()} + > diff --git a/src/settings/i18n/en.json b/src/settings/i18n/en.json new file mode 100644 index 0000000..07fb85d --- /dev/null +++ b/src/settings/i18n/en.json @@ -0,0 +1,25 @@ +{ + "Settings": "Settings", + "Accounts": "Accounts", + "All Notifications": "All Notifications", + "Sign in...": "Sign in...", + "Reading": "Reading", + "Prefetch Toots": "Prefetch Toots", + "Prefetch Toots.2nd": "Tutu will download the toots before you need.", + "This Application": "This Application", + "About Tutu": "About Tutu", + "About Tutu.2nd": "Comfortable tooting experience.", + "updates.ready": "An update is ready, restart the Tutu to apply", + "updates.no": "No updates", + "version": "Using v{{packageVersion}} (built on {{builtAt}}, {{buildMode}})", + "Language": "Language", + "Region": "Region", + "lang.auto": "Auto({{detected}})", + "lang.zh-Hans": "中文(简体)", + "lang.en": "English", + "region.auto": "Auto({{detected}})", + "region.en_GB": "Great Britan (English)", + "region.en_US": "United States (English)", + "region.zh_CN": "China Mainland (Chinese)", + "datefmt": "yyyy/MM/dd" +} \ No newline at end of file diff --git a/src/settings/i18n/zh-Hans.json b/src/settings/i18n/zh-Hans.json new file mode 100644 index 0000000..df9941e --- /dev/null +++ b/src/settings/i18n/zh-Hans.json @@ -0,0 +1,25 @@ +{ + "Settings": "设置", + "Accounts": "所有账号", + "All Notifications": "所有通知", + "Sign in...": "登录新账户...", + "Reading": "阅读", + "Prefetch Toots": "提前下载嘟文", + "Prefetch Toots.2nd": "图图会在你可能需要的时候提前下载嘟文。", + "This Application": "本应用", + "About Tutu": "关于图图", + "About Tutu.2nd": "舒服地刷嘟。", + "updates.ready": "更新已准备好,下次开启会启动新版本", + "updates.no": "已是最新版本", + "version": "正在使用 v{{packageVersion}} ({{builtAt}}构建, {{buildMode}})", + "Language": "语言", + "Region": "区域", + "lang.auto": "自动({{detected}})", + "lang.zh-Hans": "中文(简体)", + "lang.en": "English", + "region.auto": "自动({{detected}})", + "region.en_GB": "英国和苏格兰(英语)", + "region.en_US": "美国(英语)", + "region.zh_CN": "中国大陆(中文)", + "datefmt": "yyyy年MM月dd日" +} \ No newline at end of file diff --git a/src/settings/stores.ts b/src/settings/stores.ts index 25f6484..151f449 100644 --- a/src/settings/stores.ts +++ b/src/settings/stores.ts @@ -3,6 +3,8 @@ import { persistentMap } from "@nanostores/persistent"; type Settings = { onGoingOAuth2Process?: string; prefetchTootsDisabled?: boolean; + language?: string; + region?: string; }; export const $settings = persistentMap( diff --git a/src/timelines/RegularToot.tsx b/src/timelines/RegularToot.tsx index 97178c3..30376de 100644 --- a/src/timelines/RegularToot.tsx +++ b/src/timelines/RegularToot.tsx @@ -36,6 +36,7 @@ import Button from "../material/Button.js"; import MediaAttachmentGrid from "./MediaAttachmentGrid.js"; import { FastAverageColor } from "fast-average-color"; import Color from "colorjs.io"; +import { useDateFnLocale } from "../platform/i18n"; type TootContentViewProps = { source?: string; @@ -170,6 +171,7 @@ function TootActionGroup( function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) { const toot = () => props.status; + const dateFnLocale = useDateFnLocale() return (
    @@ -187,7 +189,7 @@ function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) { }} /> @{toot().account.username}@{new URL(toot().account.url).hostname}