feat(i18n): add multi-language support (ja/ko/vi/ru/zh-TW) with system auto-detection

- Extend AppLanguage to 7 locales: zh-CN, zh-TW, en, ja, ko, vi, ru
- Auto-detect system language via navigator.languages; fallback to English
- Add BCP47 locale mapping for date formatting and sorting
- Create 50 translation JSON files (5 languages × 10 namespaces, 1137 keys each)
- Update language selector with native language names
- Extend locale key parity test to cover all 7 languages (70 assertions)
- Add VNC/RDP desktop, serial terminal, AI agent, port forwarding, and SFTP features
- Add credential encryption, host metrics, session backup crypto, and zmodem bridge
- Update Rust backend: desktop services, host metrics, SSH/SFTP enhancements
This commit is contained in:
Logan.Li committed 2026-08-12 00:07:48 +08:00
1 parent fea6d8db04
commit b0d0e3a5f3
301 files changed
+28002 -2705

No files matched your search

+16 -11
View File
@@ -4,10 +4,12 @@
*/
import { useEffect, useState } from 'react'
import { Zap, ArrowRight, AlertCircle } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { LogIn, ArrowRight, AlertCircle } from 'lucide-react'
import { useTabStore } from '../../stores/tabStore'
import Modal from '../common/Modal'
import Button from '../common/Button'
import { translateErrorMessage } from '../../i18n/format'
interface QuickConnectDialogProps {
isOpen: boolean
@@ -62,6 +64,7 @@ export function parseSshTarget(raw: string): {
}
export default function QuickConnectDialog({ isOpen, onClose, initialTarget = '' }: QuickConnectDialogProps) {
const { t } = useTranslation(['sessions', 'common'])
const { addTab } = useTabStore()
const [target, setTarget] = useState('')
const [password, setPassword] = useState('')
@@ -79,7 +82,7 @@ export default function QuickConnectDialog({ isOpen, onClose, initialTarget = ''
const handleSubmit = async () => {
const parsed = parseSshTarget(target)
if (!parsed) {
setError('请输入主机,例如 root@192.168.1.10 或 opsbase.cn:5555')
setError(t('sessions:quickConnect.errorInvalidTarget'))
return
}
setIsConnecting(true)
@@ -101,7 +104,8 @@ export default function QuickConnectDialog({ isOpen, onClose, initialTarget = ''
setTarget('')
setPassword('')
} catch (err: unknown) {
setError((err instanceof Error ? err.message : String(err)) || '连接失败')
const raw = (err instanceof Error ? err.message : String(err)) || t('sessions:quickConnect.errorConnectFailed')
setError(translateErrorMessage(raw))
} finally {
setIsConnecting(false)
}
@@ -111,8 +115,8 @@ export default function QuickConnectDialog({ isOpen, onClose, initialTarget = ''
<Modal
isOpen={isOpen}
onClose={onClose}
title="快速连接"
icon={<Zap className="h-5 w-5" />}
title={t('sessions:quickConnect.title')}
icon={<LogIn className="h-5 w-5" />}
size="md"
showFooter={false}
>
@@ -124,24 +128,25 @@ export default function QuickConnectDialog({ isOpen, onClose, initialTarget = ''
className="space-y-4"
>
<div>
<label className="ui-label">目标</label>
<label className="ui-label">{t('sessions:quickConnect.target')}</label>
<input
type="text"
required
autoFocus
value={target}
onChange={(e) => setTarget(e.target.value)}
placeholder="root@192.168.1.10:22"
placeholder={t('sessions:quickConnect.targetPlaceholder')}
className="ui-input font-mono"
autoComplete="off"
spellCheck={false}
/>
<p className="mt-1.5 text-[11px] leading-relaxed text-txt-faint">支持 host、user@host、user@host:port</p>
<p className="mt-1.5 text-[11px] leading-relaxed text-txt-faint">{t('sessions:quickConnect.targetHint')}</p>
</div>
<div>
<label className="ui-label">
密码 <span className="font-normal text-txt-faint">可选 · 用密钥可留空</span>
{t('sessions:quickConnect.password')}{' '}
<span className="font-normal text-txt-faint">{t('sessions:quickConnect.passwordOptional')}</span>
</label>
<input
type="password"
@@ -162,7 +167,7 @@ export default function QuickConnectDialog({ isOpen, onClose, initialTarget = ''
<div className="border-dark-700/50 flex justify-end gap-2.5 border-t pt-2">
<Button type="button" variant="ghost" size="sm" onClick={onClose} disabled={isConnecting}>
取消
{t('common:action.cancel')}
</Button>
<Button
type="submit"
@@ -171,7 +176,7 @@ export default function QuickConnectDialog({ isOpen, onClose, initialTarget = ''
loading={isConnecting}
icon={<ArrowRight className="h-3.5 w-3.5" />}
>
连接
{t('sessions:quickConnect.connect')}
</Button>
</div>
</form>