feat(i18n): add more i18n resources

This commit is contained in:
kunish 2023-09-02 00:15:19 +08:00
parent 508e19a247
commit b3172da302
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
7 changed files with 198 additions and 157 deletions

View File

@ -72,32 +72,32 @@ export const Header = () => {
const navs = () => [ const navs = () => [
{ {
href: '/overview', href: '/overview',
name: t('navs.overview'), name: t('overview'),
icon: <IconHome />, icon: <IconHome />,
}, },
{ {
href: '/proxies', href: '/proxies',
name: t('navs.proxies'), name: t('proxies'),
icon: <IconGlobe />, icon: <IconGlobe />,
}, },
{ {
href: '/rules', href: '/rules',
name: t('navs.rules'), name: t('rules'),
icon: <IconRuler />, icon: <IconRuler />,
}, },
{ {
href: '/conns', href: '/conns',
name: t('navs.connections'), name: t('connections'),
icon: <IconNetwork />, icon: <IconNetwork />,
}, },
{ {
href: '/logs', href: '/logs',
name: t('navs.logs'), name: t('logs'),
icon: <IconFileStack />, icon: <IconFileStack />,
}, },
{ {
href: '/config', href: '/config',
name: t('navs.config'), name: t('config'),
icon: <IconSettings />, icon: <IconSettings />,
}, },
] ]

View File

@ -4,48 +4,70 @@ import { ParentComponent, createEffect, createSignal } from 'solid-js'
const dict = { const dict = {
'en-US': { 'en-US': {
navs: {
overview: 'Overview', overview: 'Overview',
proxies: 'Proxies', proxies: 'Proxies',
rules: 'Rules', rules: 'Rules',
connections: 'Connections', connections: 'Connections',
logs: 'Logs', logs: 'Logs',
config: 'Config', config: 'Config',
},
stats: {
upload: 'Upload', upload: 'Upload',
download: 'Download', download: 'Download',
uploadTotal: 'Upload Total', uploadTotal: 'Upload Total',
downloadTotal: 'Download Total', downloadTotal: 'Download Total',
activeConnections: 'Active Connections', activeConnections: 'Active Connections',
memoryUsage: 'Memory Usage', memoryUsage: 'Memory Usage',
}, traffic: 'Traffic',
proxies: { memory: 'Memory',
proxies: 'Proxies', down: 'Down',
proxyProvider: 'Proxy Provider', up: 'Up',
}, proxyProviders: 'Proxy Providers',
ruleProviders: 'Rule Providers',
search: 'Search',
type: 'Type',
name: 'Name',
process: 'Process',
host: 'Host',
chains: 'Chains',
dlSpeed: 'DL Speed',
ulSpeed: 'UL Speed',
dl: 'DL',
ul: 'UL',
source: 'Source',
destination: 'Destination',
close: 'Close',
}, },
'zh-Hans': { 'zh-Hans': {
navs: {
overview: '概览', overview: '概览',
proxies: '代理', proxies: '代理',
rules: '规则', rules: '规则',
connections: '连接', connections: '连接',
logs: '日志', logs: '日志',
config: '配置', config: '配置',
},
stats: {
upload: '上传', upload: '上传',
download: '下载', download: '下载',
uploadTotal: '上传总量', uploadTotal: '上传总量',
downloadTotal: '下载总量', downloadTotal: '下载总量',
activeConnections: '活动连接', activeConnections: '活动连接',
memoryUsage: '内存使用情况', memoryUsage: '内存使用情况',
}, traffic: '流量',
proxies: { memory: '内存',
proxies: '代理节点组', down: '下载',
proxyProvider: '代理提供商', up: '上传',
}, proxyProviders: '代理提供者',
ruleProviders: '规则提供者',
search: '搜索',
type: '类型',
name: '名字',
process: '进程',
host: '主机',
chains: '链路',
dlSpeed: '下载速度',
ulSpeed: '上传速度',
dl: '下载量',
ul: '上传量',
source: '源地址',
destination: '目标地址',
close: '关闭',
}, },
} }

View File

@ -1,4 +1,5 @@
import { createEventSignal } from '@solid-primitives/event-listener' import { createEventSignal } from '@solid-primitives/event-listener'
import { useI18n } from '@solid-primitives/i18n'
import { makePersisted } from '@solid-primitives/storage' import { makePersisted } from '@solid-primitives/storage'
import { createReconnectingWS } from '@solid-primitives/websocket' import { createReconnectingWS } from '@solid-primitives/websocket'
import { import {
@ -39,6 +40,7 @@ const initColumnVisibility = {
} }
export default () => { export default () => {
const [t] = useI18n()
const [columnVisibility, setColumnVisibility] = makePersisted( const [columnVisibility, setColumnVisibility] = makePersisted(
createSignal<ColumnVisibility>(initColumnVisibility), createSignal<ColumnVisibility>(initColumnVisibility),
{ {
@ -239,12 +241,14 @@ export default () => {
<div class="flex w-full"> <div class="flex w-full">
<input <input
class="input input-primary mr-4 w-40 flex-1" class="input input-primary mr-4 w-40 flex-1"
placeholder="Search" placeholder={t('search')}
onInput={(e) => setSearch(e.target.value)} onInput={(e) => setSearch(e.target.value)}
/> />
<label for="connection-modal" class="btn btn-circle"> <label for="connection-modal" class="btn btn-circle">
<IconSettings /> <IconSettings />
</label> </label>
<ConnectionsModal <ConnectionsModal
order={columnOrder()} order={columnOrder()}
visible={columnVisibility()} visible={columnVisibility()}

View File

@ -1,4 +1,5 @@
import { createEventSignal } from '@solid-primitives/event-listener' import { createEventSignal } from '@solid-primitives/event-listener'
import { useI18n } from '@solid-primitives/i18n'
import { createReconnectingWS } from '@solid-primitives/websocket' import { createReconnectingWS } from '@solid-primitives/websocket'
import { import {
ColumnDef, ColumnDef,
@ -13,6 +14,7 @@ import { Log } from '~/types'
type LogWithSeq = Log & { seq: number } type LogWithSeq = Log & { seq: number }
export default () => { export default () => {
const [t] = useI18n()
let seq = 0 let seq = 0
const [search, setSearch] = createSignal('') const [search, setSearch] = createSignal('')
const [logs, setLogs] = createSignal<LogWithSeq[]>([]) const [logs, setLogs] = createSignal<LogWithSeq[]>([])
@ -68,7 +70,7 @@ export default () => {
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<input <input
class="input input-primary" class="input input-primary"
placeholder="Search" placeholder={t('search')}
onInput={(e) => setSearch(e.target.value)} onInput={(e) => setSearch(e.target.value)}
/> />

View File

@ -18,37 +18,6 @@ import type { Connection } from '~/types'
const CHART_MAX_XAXIS = 10 const CHART_MAX_XAXIS = 10
const defaultChartOptions: ApexOptions = {
chart: {
toolbar: { show: false },
zoom: { enabled: false },
animations: { easing: 'linear' },
},
noData: { text: 'Loading...' },
legend: {
fontSize: '14px',
labels: { colors: 'gray' },
itemMargin: { horizontal: 64 },
},
dataLabels: { enabled: false },
grid: { yaxis: { lines: { show: false } } },
stroke: { curve: 'smooth' },
tooltip: { enabled: false },
xaxis: {
range: CHART_MAX_XAXIS,
labels: { show: false },
axisTicks: { show: false },
},
yaxis: {
labels: {
style: { colors: 'gray' },
formatter(val) {
return byteSize(val).toString()
},
},
},
}
const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => ( const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
<div class="stat flex-1"> <div class="stat flex-1">
<div class="stat-title text-secondary-content">{props.label}</div> <div class="stat-title text-secondary-content">{props.label}</div>
@ -60,6 +29,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 }[]>(
[], [],
) )
@ -99,18 +69,53 @@ export default () => {
} }
}) })
const defaultChartOptions: ApexOptions = {
chart: {
toolbar: { show: false },
zoom: { enabled: false },
animations: { easing: 'linear' },
},
noData: { text: 'Loading...' },
legend: {
fontSize: '14px',
labels: { colors: 'gray' },
itemMargin: { horizontal: 64 },
},
dataLabels: { enabled: false },
grid: { yaxis: { lines: { show: false } } },
stroke: { curve: 'smooth' },
tooltip: { enabled: false },
xaxis: {
range: CHART_MAX_XAXIS,
labels: { show: false },
axisTicks: { show: false },
},
yaxis: {
labels: {
style: { colors: 'gray' },
formatter(val) {
return byteSize(val).toString()
},
},
},
}
const trafficChartOptions = createMemo<ApexOptions>(() => ({ const trafficChartOptions = createMemo<ApexOptions>(() => ({
title: { text: 'Traffic', align: 'center', style: { color: 'gray' } }, title: {
text: t('traffic'),
align: 'center',
style: { color: 'gray' },
},
...defaultChartOptions, ...defaultChartOptions,
})) }))
const trafficChartSeries = createMemo(() => [ const trafficChartSeries = createMemo(() => [
{ {
name: 'Down', name: t('down'),
data: traffics().map((t) => t.down), data: traffics().map((t) => t.down),
}, },
{ {
name: 'Up', name: t('up'),
data: traffics().map((t) => t.up), data: traffics().map((t) => t.up),
}, },
]) ])
@ -138,16 +143,15 @@ export default () => {
}) })
const memoryChartOptions = createMemo<ApexOptions>(() => ({ const memoryChartOptions = createMemo<ApexOptions>(() => ({
title: { text: 'Memory', align: 'center', style: { color: 'gray' } }, title: {
text: t('memory'),
align: 'center',
style: { color: 'gray' },
},
...defaultChartOptions, ...defaultChartOptions,
})) }))
const memoryChartSeries = createMemo(() => [ const memoryChartSeries = createMemo(() => [{ data: memories() }])
{
name: 'memory',
data: memories(),
},
])
const connectionsWS = createReconnectingWS( const connectionsWS = createReconnectingWS(
`${wsEndpointURL()}/connections?token=${secret()}`, `${wsEndpointURL()}/connections?token=${secret()}`,
@ -172,27 +176,27 @@ export default () => {
return ( return (
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="stats stats-vertical w-full bg-primary shadow lg:stats-horizontal lg:flex"> <div class="stats stats-vertical w-full bg-primary shadow lg:stats-horizontal lg:flex">
<TrafficWidget label={t('stats.upload')}> <TrafficWidget label={t('upload')}>
{byteSize(traffic()?.up || 0).toString()}/s {byteSize(traffic()?.up || 0).toString()}/s
</TrafficWidget> </TrafficWidget>
<TrafficWidget label={t('stats.download')}> <TrafficWidget label={t('download')}>
{byteSize(traffic()?.down || 0).toString()}/s {byteSize(traffic()?.down || 0).toString()}/s
</TrafficWidget> </TrafficWidget>
<TrafficWidget label={t('stats.uploadTotal')}> <TrafficWidget label={t('uploadTotal')}>
{byteSize(connection()?.uploadTotal || 0).toString()} {byteSize(connection()?.uploadTotal || 0).toString()}
</TrafficWidget> </TrafficWidget>
<TrafficWidget label={t('stats.downloadTotal')}> <TrafficWidget label={t('downloadTotal')}>
{byteSize(connection()?.downloadTotal || 0).toString()} {byteSize(connection()?.downloadTotal || 0).toString()}
</TrafficWidget> </TrafficWidget>
<TrafficWidget label={t('stats.activeConnections')}> <TrafficWidget label={t('activeConnections')}>
{connection()?.connections.length || 0} {connection()?.connections.length || 0}
</TrafficWidget> </TrafficWidget>
<TrafficWidget label={t('stats.memoryUsage')}> <TrafficWidget label={t('memoryUsage')}>
{byteSize(memory() || 0).toString()} {byteSize(memory() || 0).toString()}
</TrafficWidget> </TrafficWidget>
</div> </div>

View File

@ -62,9 +62,9 @@ export default () => {
} }
return ( return (
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-2">
<div> <div>
<h1 class="pb-4 text-lg font-semibold">{t('proxies.proxies')}</h1> <h1 class="pb-4 text-lg font-semibold">{t('proxies')}</h1>
<ForTwoLine <ForTwoLine
subChild={proxies().map((proxy) => { subChild={proxies().map((proxy) => {
@ -119,7 +119,8 @@ export default () => {
</div> </div>
<Show when={proxyProviders().length > 0}> <Show when={proxyProviders().length > 0}>
<h1 class="pb-4 text-lg font-semibold">{t('proxies.proxyProvider')}</h1> <div>
<h1 class="pb-4 text-lg font-semibold">{t('proxyProviders')}</h1>
<ForTwoLine <ForTwoLine
subChild={proxyProviders().map((proxyProvider) => { subChild={proxyProviders().map((proxyProvider) => {
@ -138,7 +139,9 @@ export default () => {
</button> </button>
<button <button
class="btn btn-circle btn-sm" class="btn btn-circle btn-sm"
onClick={(e) => onHealthCheckClick(e, proxyProvider.name)} onClick={(e) =>
onHealthCheckClick(e, proxyProvider.name)
}
> >
<IconBrandSpeedtest /> <IconBrandSpeedtest />
</button> </button>
@ -151,7 +154,9 @@ export default () => {
{proxyProvider.vehicleType} :: Updated{' '} {proxyProvider.vehicleType} :: Updated{' '}
{formatTimeFromNow(proxyProvider.updatedAt)} {formatTimeFromNow(proxyProvider.updatedAt)}
</div> </div>
<Show when={!collapsedMap()[`provider-${proxyProvider.name}`]}> <Show
when={!collapsedMap()[`provider-${proxyProvider.name}`]}
>
<ProxyNodeDots <ProxyNodeDots
proxyNameList={ proxyNameList={
proxyProvider.proxies.map((i) => i.name) ?? [] proxyProvider.proxies.map((i) => i.name) ?? []
@ -160,6 +165,7 @@ export default () => {
</Show> </Show>
</> </>
) )
const content = ( const content = (
<ProxyCardGroups <ProxyCardGroups
proxies={proxyProvider.proxies.map((i) => i.name)} proxies={proxyProvider.proxies.map((i) => i.name)}
@ -181,6 +187,7 @@ export default () => {
) )
})} })}
/> />
</div>
</Show> </Show>
</div> </div>
) )

View File

@ -1,9 +1,11 @@
import { useI18n } from '@solid-primitives/i18n'
import InfiniteScroll from 'solid-infinite-scroll' import InfiniteScroll from 'solid-infinite-scroll'
import { For, createMemo, createSignal, onMount } from 'solid-js' import { For, createMemo, createSignal, onMount } from 'solid-js'
import { useRequest } from '~/signals' import { useRequest } from '~/signals'
import type { Rule, RuleProvider } from '~/types' import type { Rule, RuleProvider } from '~/types'
export default () => { export default () => {
const [t] = useI18n()
const request = useRequest() const request = useRequest()
const [maxRuleRender, setMaxRuleRender] = createSignal(100) const [maxRuleRender, setMaxRuleRender] = createSignal(100)
const [rules, setRules] = createSignal<Rule[]>([]) const [rules, setRules] = createSignal<Rule[]>([])
@ -27,7 +29,7 @@ export default () => {
return ( return (
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div> <div>
<h1 class="pb-4 text-lg font-semibold">Rules</h1> <h1 class="pb-4 text-lg font-semibold">{t('rules')}</h1>
<div class="grid grid-cols-1 gap-2 sm:grid-cols-1"> <div class="grid grid-cols-1 gap-2 sm:grid-cols-1">
<InfiniteScroll <InfiniteScroll
@ -48,7 +50,7 @@ export default () => {
</div> </div>
<div> <div>
<h1 class="pb-4 text-lg font-semibold">Rules Providers</h1> <h1 class="pb-4 text-lg font-semibold">{t('ruleProviders')}</h1>
<div class="grid grid-cols-1 gap-2 sm:grid-cols-1"> <div class="grid grid-cols-1 gap-2 sm:grid-cols-1">
<For each={rulesProviders()}> <For each={rulesProviders()}>