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

+335
View File
@@ -0,0 +1,335 @@
{
"action": {
"confirm": "확인",
"cancel": "취소",
"close": "닫기",
"delete": "삭제",
"save": "저장",
"create": "생성",
"copy": "복사",
"copied": "복사됨",
"refresh": "새로고침",
"gotIt": "확인",
"edit": "편집",
"open": "열기",
"clone": "복제",
"reconnect": "재연결",
"disconnect": "연결 끊기",
"submit": "확인"
},
"status": {
"loading": "불러오는 중...",
"processing": "처리 중...",
"connecting": "연결 중...",
"connected": "연결됨",
"disconnected": "연결 끊김",
"background": "백그라운드",
"suspended": "일시 중단",
"reconnecting": "재연결 중",
"error": "연결 오류",
"reading": "불러오는 중...",
"saving": "저장 중..."
},
"error": {
"title": "문제가 발생했습니다",
"body": "앱에서 예기치 않은 문제가 발생했습니다",
"recoverable": "이 오류는 복구 가능합니다. 페이지를 새로고침해 보세요.",
"refreshPage": "페이지 새로고침",
"restartApp": "앱 재시작",
"supportHint": "문제가 지속되면 로그를 확인하거나 지원팀에 문의하세요"
},
"toast": {
"closeAria": "알림 닫기"
},
"metrics": {
"timezone": "시간대 {{value}}",
"memory": "메모리 {{percent}}%{{detail}}",
"sessionTransfer": "세션 트래픽 {{value}}"
},
"palette": {
"title": "명령 팔레트",
"aria": "명령 팔레트",
"placeholder": "명령, 세션 또는 스니펫 검색...",
"empty": "일치하는 결과가 없습니다",
"emptyHint": "다른 키워드를 시도해 보세요",
"resultCount": "{{count}}개 결과",
"badge": {
"command": "명령",
"session": "세션",
"snippet": "스니펫"
},
"hint": {
"nav": "탐색",
"run": "실행",
"close": "닫기"
},
"cmd": {
"newSession": "새 세션",
"newSessionDesc": "새 SSH 세션 프로필 생성",
"quickConnect": "빠른 연결",
"quickConnectDesc": "호스트 정보로 직접 연결",
"localTerminal": "로컬 터미널",
"localTerminalDesc": "로컬 셸 열기",
"settings": "설정 열기",
"settingsDesc": "앱 환경설정",
"toggleSidebar": "사이드바 전환",
"toggleSidebarDesc": "터미널에 집중하도록 사이드바 숨기기",
"newTab": "새 탭",
"closeTab": "현재 탭 닫기",
"closeAllTabs": "모든 탭 닫기",
"portForward": "새 포트 포워딩",
"upgradePro": "OpsXShell Pro 업그레이드",
"editSession": "세션 편집: {{name}}"
}
},
"tab": {
"welcome": "환영합니다",
"new": "새 탭",
"settings": "설정",
"portForward": "포트 포워딩",
"terminal": "터미널",
"batch": "일괄 실행",
"newTerminal": "새 터미널",
"kind": {
"terminal": "터미널",
"sftp": "SFTP",
"portforward": "포워딩",
"serial": "시리얼",
"rdp": "RDP",
"vnc": "VNC",
"batch": "일괄",
"settings": "설정"
},
"menu": {
"closeOthers": "다른 탭 닫기",
"closeRight": "오른쪽 탭 닫기",
"closeLeft": "왼쪽 탭 닫기",
"closeAll": "모두 닫기",
"clone": "탭 복제"
}
},
"sidebar": {
"brandSub": "SSH / SFTP 터미널",
"collapse": "사이드바 접기 (Ctrl/Cmd+B)",
"collapseAria": "사이드바 접기",
"expand": "사이드바 펼치기",
"expandAria": "사이드바 펼치기",
"toggle": "사이드바 전환",
"searchPlaceholder": "검색...",
"localShell": {
"openDefault": "기본 로컬 셸 열기",
"choose": "셸 선택",
"title": "로컬 셸",
"subtitle": "이 컴퓨터에서 터미널 열기",
"default": "기본"
}
},
"connectionPanel": {
"title": "연결",
"empty": "연결 없음",
"emptyDesc": "활성 연결이 없습니다. 서버에 연결하면 여기에 표시됩니다.",
"stat": {
"total": "전체",
"active": "활성",
"background": "백그라운드",
"error": "연결 끊김/오류"
},
"status": {
"connected": "연결됨",
"connecting": "연결 중",
"background": "백그라운드",
"suspended": "일시 중단",
"error": "오류",
"disconnected": "연결 끊김",
"reconnecting": "재연결 중"
},
"refCount": "참조 {{count}}개",
"reconnect": "재연결",
"reconnecting": "재연결 중...",
"disconnect": "연결 끊기"
},
"pro": {
"upgrade": "Pro 업그레이드",
"feature": "Pro 기능",
"unlock": "{{feature}} 잠금 해제",
"comingSoon": "출시 예정",
"activateSuccess": "활성화됨",
"activateFailed": "활성화 실패",
"licenseActivated": "Pro 라이선스가 활성화되었습니다",
"paymentPending": "결제 후 라이선스를 여기에 붙여넣거나 나중에 다시 활성화해 보세요",
"invalidLicense": "활성화 실패: 잘못된 라이선스",
"goUpgrade": "업그레이드",
"haveLicense": "라이선스가 있나요? 활성화",
"title": "OpsXShell Pro · Pro 운영 기능 잠금 해제",
"subtitleComingSoon": "{{feature}} 출시 예정 · 조기 액세스는 Pro 업그레이드",
"subtitleUnlock": "잠금 해제 중: {{feature}}",
"subtitleDefault": "포워딩, 프록시, 시리얼, AI, 일괄 실행, 브로드캐스트",
"activated": "Pro 활성화됨",
"expiresAt": " · {{date}}까지 유효",
"lifetime": " · 평생 라이선스",
"oneTimeFee": "1회 구매 · 이 기기에서 평생 사용 · 구독 없음",
"pasteLicense": "라이선스 붙여넣기",
"activateButton": "활성화",
"clearLicense": "로컬 라이선스 삭제",
"paymentNote": "결제는 Waffo가 처리합니다. 이 앱은 카드 정보를 수집하지 않습니다.",
"testDeviceId": "테스트 환경 · 기기 ID: {{deviceId}}",
"later": "나중에 하기",
"viewPro": "Pro 보기",
"wallComingSoonSuffix": " · 출시 예정",
"wallUnlock": "{{feature}} 잠금 해제",
"wallMessageComingSoon": "이 Pro 기능은 아직 개발 중입니다. 조기 액세스를 위해 업그레이드하세요.",
"wallMessageDefault": "이 기능은 OpsXShell Pro가 필요합니다. 이 기기에서 사용하려면 업그레이드하세요.",
"checkoutUnavailable": "결제를 사용할 수 없습니다 ({{status}})",
"freeTier": "무료",
"proTier": "Pro",
"freeItems": {
"ssh": "SSH 터미널",
"sftp": "SFTP 파일 관리자",
"local": "로컬 터미널",
"crud": "세션 CRUD",
"import": "단일 / ssh_config 가져오기"
},
"proItems": {
"forward": "포트 포워딩",
"proxy": "세션 프록시 / SOCKS",
"serial": "시리얼 터미널",
"ai": "터미널 AI 어시스턴트",
"batch": "일괄 실행 / 브로드캐스트 동기화",
"broadcast": "브로드캐스트 입력 동기화",
"importExport": "호스트 일괄 가져오기/내보내기",
"desktop": "RDP / VNC 원격 데스크톱"
}
},
"launcher": {
"title": "빠른 실행",
"description": "세션, 로컬 셸 및 기기 콘솔",
"cards": {
"quickConnect": {
"title": "빠른 연결",
"desc": "user@host:port"
},
"saveSession": {
"title": "세션 저장",
"desc": "자격 증명, 그룹, 프록시"
},
"serial": {
"title": "시리얼 터미널",
"desc": "보레이트 및 포트 설정"
}
},
"sectionLocalShell": "로컬 셸",
"sectionSavedSessions": "저장된 세션",
"systemDefault": "시스템 기본값"
},
"statusBar": {
"socksLabel": "포워딩",
"socksIdle": "포트 포워딩 열기 · 우클릭으로 SOCKS 복사",
"socksRunning": "SOCKS5 실행 중 · 클릭하여 관리 · 우클릭으로 복사",
"forwardOpenIdle": "포트 포워딩 열기 · 우클릭으로 socks5://127.0.0.1:{{port}} 복사",
"forwardOpenRunning": "SOCKS5 실행 중 (:{{port}}) · 클릭하여 관리 · 우클릭으로 복사",
"forwardStarted": "포워딩 시작됨",
"forwardStartedDynamic": "SOCKS5 시작됨: socks5://127.0.0.1:{{port}}",
"socksStartFailed": "시작 실패: {{message}}",
"socksStopFailed": "중지 실패: {{message}}",
"socksCopied": "socks5://127.0.0.1:{{port}} 복사됨"
},
"portForward": {
"title": "포트 포워딩",
"connectedAs": "연결됨: {{target}}",
"notConnected": "연결되지 않음 — 먼저 SSH를 연결하세요",
"socksDefaultHint": "로컬 SOCKS5: 127.0.0.1:1080",
"socksRunning": "SOCKS5 실행 중",
"socksStart": "SOCKS5 시작",
"addRule": "추가",
"emptyTitle": "포트 포워딩 없음",
"emptyDesc": "SOCKS5를 시작하거나 로컬/원격 규칙을 추가하세요",
"emptyAction": "127.0.0.1:1080 시작",
"typeLocal": "로컬",
"typeRemote": "원격",
"typeDynamic": "동적",
"running": "실행 중",
"socksProxy": "SOCKS5: 127.0.0.1:{{port}}",
"remoteToLocal": "원격 {{remote}} → 로컬 {{local}}",
"stats": "{{connections}}개 연결 · ↑{{up}} · ↓{{down}}",
"copyProxy": "프록시 복사",
"copyRule": "규칙 복제",
"editRule": "편집",
"stopListener": "중지",
"start": "시작",
"delete": "삭제",
"editTitle": "포워딩 편집",
"addTitle": "포워딩 추가",
"typeLocalDesc": "로컬 → 원격",
"typeRemoteDesc": "원격 → 로컬",
"typeDynamicDesc": "SOCKS5",
"selectType": "유형",
"drawerSubtitle": "현재 SSH 세션을 통한 터널",
"ruleFields": "세부 정보",
"dynamicHint": "127.0.0.1:1080의 로컬 SOCKS5. 트래픽은 이 SSH 서버의 공인 IP를 통해 나갑니다.",
"dynamicSingleHint": "SSH 호스트당 하나의 동적 규칙, 고정 포트 1080.",
"dynamicPortHint": "If the port is busy, pick another local port (e.g. 1081).",
"ruleList": "규칙",
"ruleListEmpty": "아직 로컬/원격 규칙이 없습니다",
"addSection": "새로 추가",
"errorDynamicExists": "동적 규칙이 이미 존재합니다 (1080). 대신 시작하거나 중지하세요.",
"diagram": {
"title": "작동 방식",
"localApp": "로컬 앱",
"sshHost": "SSH 호스트",
"remoteService": "원격 서비스",
"remoteClient": "원격 클라이언트",
"localService": "로컬 서비스",
"localClient": "로컬",
"browser": "로컬 클라이언트",
"socksProxy": "로컬 SOCKS",
"anyTarget": "모든 대상",
"localSide": "로컬",
"remoteSide": "원격",
"firewall": "방화벽",
"viaApp": "OpsXShell 경유",
"viaSsh": "SSH 경유",
"toTarget": "대상으로",
"toListen": "수신",
"targetDb": "DB",
"targetWeb": "웹",
"targetApi": "API",
"remotePeerA": "원격 A",
"remotePeerB": "원격 B",
"remotePeerC": "원격 C",
"cloudA": "클라우드 A",
"cloudB": "클라우드 B",
"internet": "인터넷",
"localCaption": "로컬: 원격 포트를 로컬 포트인 것처럼 접근합니다.",
"remoteCaption": "원격: 원격 접근을 로컬 서비스로 포워딩합니다.",
"dynamicCaption": "동적: 이 컴퓨터가 현재 SSH 서버의 공인 IP를 통해 나가는 SOCKS5 프록시가 됩니다."
},
"ruleName": "이름",
"ruleNamePlaceholder": "예: MySQL",
"localListenPort": "로컬 수신 포트",
"localTargetPort": "로컬 대상 포트",
"remoteTargetPort": "원격 대상 포트",
"remoteListenPort": "원격 수신 포트",
"portPlaceholder": "예: 3306",
"remoteHost": "원격 호스트",
"remoteHostPlaceholder": "localhost 또는 IP",
"socksRuleName": "SOCKS5 프록시",
"defaultLocalName": "로컬 포워딩",
"defaultRemoteName": "원격 포워딩",
"sessionRestoredTitle": "세션 복원됨",
"sessionRestoredDesc": "마지막 세션의 포트 포워딩 탭이 복원되었습니다.\n다시 사용하려면 SSH를 재연결하세요.",
"stopEditTitle": "편집 전 중지",
"stopEditMessage": "이 포워딩이 실행 중입니다. 편집하기 전에 중지하시겠습니까?",
"stopEditConfirm": "중지하고 편집",
"deleteTitle": "규칙 삭제",
"deleteRunning": "실행 중인 포워딩이 먼저 중지됩니다.",
"deleteConfirm": "이 규칙을 삭제하시겠습니까?",
"errorNamePort": "이름과 유효한 포트(1–65535)를 입력하세요",
"errorTargetPort": "유효한 대상 포트(1–65535)를 입력하세요",
"errorRemoteHost": "원격 호스트를 입력하세요",
"errorNotConnected": "연결되지 않음",
"errorStartFailed": "시작 실패",
"errorStopFailed": "중지 실패",
"errorSocksRunning": "SOCKS5가 이미 실행 중입니다",
"copySuffix": "복사"
}
}