refactor(i18n): add i18n context provider

This commit is contained in:
kunish 2023-09-23 01:38:36 +08:00
parent 48ca8d6dfa
commit e4a324625f
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
18 changed files with 54 additions and 43 deletions

View File

@ -19,6 +19,7 @@
"@felte/validator-zod": "^1.0.17", "@felte/validator-zod": "^1.0.17",
"@fontsource/fira-sans": "^5.0.12", "@fontsource/fira-sans": "^5.0.12",
"@solid-primitives/clipboard": "^1.5.7", "@solid-primitives/clipboard": "^1.5.7",
"@solid-primitives/context": "^0.2.1",
"@solid-primitives/event-listener": "^2.3.0", "@solid-primitives/event-listener": "^2.3.0",
"@solid-primitives/i18n": "^2.0.0", "@solid-primitives/i18n": "^2.0.0",
"@solid-primitives/keyed": "^1.2.0", "@solid-primitives/keyed": "^1.2.0",

View File

@ -20,6 +20,9 @@ dependencies:
'@solid-primitives/clipboard': '@solid-primitives/clipboard':
specifier: ^1.5.7 specifier: ^1.5.7
version: 1.5.7(solid-js@1.7.12) version: 1.5.7(solid-js@1.7.12)
'@solid-primitives/context':
specifier: ^0.2.1
version: 0.2.1(solid-js@1.7.12)
'@solid-primitives/event-listener': '@solid-primitives/event-listener':
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.0(solid-js@1.7.12) version: 2.3.0(solid-js@1.7.12)
@ -2032,6 +2035,14 @@ packages:
solid-js: 1.7.12 solid-js: 1.7.12
dev: false dev: false
/@solid-primitives/context@0.2.1(solid-js@1.7.12):
resolution: {integrity: sha512-XIIwCOWpRKDersgkR9LNFXaJHIV8QlCFo/tq5bV0cAOZklcwOFcqi2bN+uWgEIQSWGjWXU2kc1H1/TzgYzVDlg==}
peerDependencies:
solid-js: ^1.6.12
dependencies:
solid-js: 1.7.12
dev: false
/@solid-primitives/event-listener@2.3.0(solid-js@1.7.12): /@solid-primitives/event-listener@2.3.0(solid-js@1.7.12):
resolution: {integrity: sha512-0DS7DQZvCExWSpurVZC9/wjI8RmkhuOtWOy6Pp1Woq9ElMT9/bfjNpkwXsOwisLpcTqh9eUs17kp7jtpWcC20w==} resolution: {integrity: sha512-0DS7DQZvCExWSpurVZC9/wjI8RmkhuOtWOy6Pp1Woq9ElMT9/bfjNpkwXsOwisLpcTqh9eUs17kp7jtpWcC20w==}
peerDependencies: peerDependencies:

View File

@ -45,7 +45,7 @@ const TagClientSourceIPWithNameForm: Component = () => {
sourceIP: z.string().nonempty(), sourceIP: z.string().nonempty(),
}) })
const { t } = useI18n() const [t] = useI18n()
const { form, reset } = createForm<z.infer<typeof schema>>({ const { form, reset } = createForm<z.infer<typeof schema>>({
extend: validator({ schema }), extend: validator({ schema }),
@ -113,7 +113,7 @@ export const ConnectionsSettingsModal = (props: {
onVisibleChange: (value: ConnectionsTableColumnVisibility) => void onVisibleChange: (value: ConnectionsTableColumnVisibility) => void
}) => { }) => {
const modalID = MODAL.CONNECTIONS_SETTINGS const modalID = MODAL.CONNECTIONS_SETTINGS
const { t } = useI18n() const [t] = useI18n()
const [activeKey, setActiveKey] = const [activeKey, setActiveKey] =
createSignal<CONNECTIONS_TABLE_ACCESSOR_KEY | null>(null) createSignal<CONNECTIONS_TABLE_ACCESSOR_KEY | null>(null)

View File

@ -13,7 +13,7 @@ import {
import { For, ParentComponent, Show, createSignal } from 'solid-js' import { For, ParentComponent, Show, createSignal } from 'solid-js'
import { Button, LogoText } from '~/components' import { Button, LogoText } from '~/components'
import { LANG, ROUTES, themes } from '~/constants' import { LANG, ROUTES, themes } from '~/constants'
import { useI18n } from '~/i18n' import { setLocale, useI18n } from '~/i18n'
import { setCurTheme } from '~/signals' import { setCurTheme } from '~/signals'
const Nav: ParentComponent<{ href: string; tooltip: string }> = ({ const Nav: ParentComponent<{ href: string; tooltip: string }> = ({
@ -62,7 +62,7 @@ const ThemeSwitcher = () => (
) )
export const Header = () => { export const Header = () => {
const { t, locale } = useI18n() const [t] = useI18n()
const navs = () => [ const navs = () => [
{ {
href: ROUTES.Overview, href: ROUTES.Overview,
@ -154,11 +154,9 @@ export const Header = () => {
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button <Button
class="btn-circle btn-secondary btn-sm" class="btn-circle btn-secondary btn-sm"
onClick={() => { onClick={() =>
const curLocale = locale() setLocale((locale) => (locale === LANG.EN ? LANG.ZH : LANG.EN))
}
locale(curLocale === LANG.EN ? LANG.ZH : LANG.EN)
}}
icon={<IconLanguage />} icon={<IconLanguage />}
/> />

View File

@ -4,7 +4,7 @@ import { useI18n } from '~/i18n'
import { latencyQualityMap, useProxies } from '~/signals' import { latencyQualityMap, useProxies } from '~/signals'
export const Latency = (props: { name?: string }) => { export const Latency = (props: { name?: string }) => {
const { t } = useI18n() const [t] = useI18n()
const { latencyMap } = useProxies() const { latencyMap } = useProxies()
const [textClassName, setTextClassName] = createSignal('') const [textClassName, setTextClassName] = createSignal('')
const latency = createMemo(() => { const latency = createMemo(() => {

View File

@ -19,7 +19,7 @@ import {
export const LogsSettingsModal = () => { export const LogsSettingsModal = () => {
const modalID = MODAL.LOGS_SETTINGS const modalID = MODAL.LOGS_SETTINGS
const { t } = useI18n() const [t] = useI18n()
return ( return (
<dialog id={modalID} class="modal modal-bottom sm:modal-middle"> <dialog id={modalID} class="modal modal-bottom sm:modal-middle">

View File

@ -20,7 +20,7 @@ import {
export const ProxiesSettingsModal = () => { export const ProxiesSettingsModal = () => {
const modalID = MODAL.PROXIES_SETTINGS const modalID = MODAL.PROXIES_SETTINGS
const { t } = useI18n() const [t] = useI18n()
return ( return (
<dialog id={modalID} class="modal modal-bottom sm:modal-middle"> <dialog id={modalID} class="modal modal-bottom sm:modal-middle">

View File

@ -14,13 +14,13 @@ const getSubscriptionsInfo = (subscriptionInfo: ISubscriptionInfo) => {
const percentage = toFinite((((Download + Upload) / Total) * 100).toFixed(2)) const percentage = toFinite((((Download + Upload) / Total) * 100).toFixed(2))
const expirePrefix = () => { const expirePrefix = () => {
const { t } = useI18n() const [t] = useI18n()
return t('expire') return t('expire')
} }
const expireStr = () => { const expireStr = () => {
const { t } = useI18n() const [t] = useI18n()
if (Expire === 0) { if (Expire === 0) {
return t('noExpire') return t('noExpire')

View File

@ -1,10 +1,11 @@
import { createContextProvider } from '@solid-primitives/context'
import * as i18n from '@solid-primitives/i18n' import * as i18n from '@solid-primitives/i18n'
import { makePersisted } from '@solid-primitives/storage' import { makePersisted } from '@solid-primitives/storage'
import { createMemo, createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { LANG } from '~/constants' import { LANG } from '~/constants'
import dict from './dict' import dict, { Dict } from './dict'
export const [curLocale, setCurLocale] = makePersisted( export const [locale, setLocale] = makePersisted(
createSignal<LANG>( createSignal<LANG>(
Reflect.has(dict, navigator.language) Reflect.has(dict, navigator.language)
? (navigator.language as LANG) ? (navigator.language as LANG)
@ -16,12 +17,9 @@ export const [curLocale, setCurLocale] = makePersisted(
}, },
) )
const locale = (localeName?: LANG) => export const [I18nProvider, useMaybeI18n] = createContextProvider<
localeName ? setCurLocale(localeName) : curLocale() [i18n.Translator<Dict>],
{ locale: LANG }
>((props) => [i18n.translator(() => i18n.flatten(dict[props.locale]))])
export const useI18n = () => { export const useI18n = () => useMaybeI18n()!
const curDict = createMemo(() => i18n.flatten(dict[curLocale()]))!
const t = createMemo(() => i18n.translator(() => curDict()))
return { t: t(), locale }
}

View File

@ -7,14 +7,17 @@ import 'dayjs/locale/zh-cn'
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime'
import { render } from 'solid-js/web' import { render } from 'solid-js/web'
import { App } from '~/App' import { App } from '~/App'
import { I18nProvider, locale } from '~/i18n'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
render( render(
() => ( () => (
<Router source={hashIntegration()}> <I18nProvider locale={locale()}>
<App /> <Router source={hashIntegration()}>
</Router> <App />
</Router>
</I18nProvider>
), ),
document.getElementById('root')!, document.getElementById('root')!,
) )

View File

@ -48,7 +48,7 @@ const dnsQueryFormSchema = z.object({
}) })
const DNSQueryForm = () => { const DNSQueryForm = () => {
const { t } = useI18n() const [t] = useI18n()
const request = useRequest() const request = useRequest()
const { form, isSubmitting } = createForm<z.infer<typeof dnsQueryFormSchema>>( const { form, isSubmitting } = createForm<z.infer<typeof dnsQueryFormSchema>>(
@ -111,7 +111,7 @@ const configFormSchema = z.object({
}) })
const ConfigForm = () => { const ConfigForm = () => {
const { t } = useI18n() const [t] = useI18n()
const navigate = useNavigate() const navigate = useNavigate()
const portList = [ const portList = [
@ -352,7 +352,7 @@ const ConfigForm = () => {
} }
const ConfigForXd = () => { const ConfigForXd = () => {
const { t } = useI18n() const [t] = useI18n()
return ( return (
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
@ -439,7 +439,7 @@ const Versions = () => {
} }
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
return ( return (
<div class="mx-auto flex max-w-screen-md flex-col gap-4"> <div class="mx-auto flex max-w-screen-md flex-col gap-4">

View File

@ -78,7 +78,7 @@ const fuzzyFilter: FilterFn<Connection> = (row, columnId, value, addMeta) => {
} }
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections) const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections)
const { activeConnections, closedConnections, paused, setPaused } = const { activeConnections, closedConnections, paused, setPaused } =
@ -88,7 +88,7 @@ export default () => {
const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>() const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>()
const columns = createMemo<ColumnDef<Connection>[]>(() => [ const columns: ColumnDef<Connection>[] = [
{ {
header: () => t('details'), header: () => t('details'),
enableGrouping: false, enableGrouping: false,
@ -243,7 +243,7 @@ export default () => {
original.metadata.destinationIP || original.metadata.destinationIP ||
original.metadata.host, original.metadata.host,
}, },
]) ]
const [grouping, setGrouping] = createSignal<GroupingState>([]) const [grouping, setGrouping] = createSignal<GroupingState>([])
const [sorting, setSorting] = makePersisted( const [sorting, setSorting] = makePersisted(
@ -281,7 +281,7 @@ export default () => {
}, },
sortDescFirst: true, sortDescFirst: true,
enableHiding: true, enableHiding: true,
columns: columns(), columns,
onGlobalFilterChange: setGlobalFilter, onGlobalFilterChange: setGlobalFilter,
globalFilterFn: fuzzyFilter, globalFilterFn: fuzzyFilter,
onGroupingChange: setGrouping, onGroupingChange: setGrouping,

View File

@ -40,7 +40,7 @@ const fuzzyFilter: FilterFn<LogWithSeq> = (row, columnId, value, addMeta) => {
} }
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
let seq = 1 let seq = 1
const [logs, setLogs] = createSignal<LogWithSeq[]>([]) const [logs, setLogs] = createSignal<LogWithSeq[]>([])

View File

@ -26,7 +26,7 @@ const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
) )
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
const [traffics, setTraffics] = createSignal<{ down: number; up: number }[]>( const [traffics, setTraffics] = createSignal<{ down: number; up: number }[]>(
[], [],

View File

@ -34,7 +34,7 @@ enum ActiveTab {
} }
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
const { const {
proxies, proxies,
selectProxyInGroup, selectProxyInGroup,

View File

@ -12,7 +12,7 @@ enum ActiveTab {
} }
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
const { const {
rules, rules,
ruleProviders, ruleProviders,

View File

@ -23,7 +23,7 @@ const schema = z.object({
}) })
export default () => { export default () => {
const { t } = useI18n() const [t] = useI18n()
const navigate = useNavigate() const navigate = useNavigate()
const onSetupSuccess = (id: string) => { const onSetupSuccess = (id: string) => {

View File

@ -1,5 +1,5 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { curLocale } from '~/i18n' import { locale } from '~/i18n'
export const formatTimeFromNow = (time: number | string) => export const formatTimeFromNow = (time: number | string) =>
dayjs(time).locale(curLocale()).fromNow() dayjs(time).locale(locale()).fromNow()