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:
1 parent
fea6d8db04
commit
b0d0e3a5f3
301 files changed
+28002
-2705
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user