diff --git a/web/src/components/LanguageSwitcher.tsx b/web/src/components/LanguageSwitcher.tsx index dc477021ee83f..cf07b350d65f0 100644 --- a/web/src/components/LanguageSwitcher.tsx +++ b/web/src/components/LanguageSwitcher.tsx @@ -3,32 +3,37 @@ import { Typography } from "@/components/NouiTypography"; import { useI18n } from "@/i18n/context"; /** - * Compact language toggle β€” shows a clickable flag that switches between - * English and Chinese. Persists choice to localStorage. + * Compact language toggle β€” cycles between English, Chinese, and Korean. + * Persists choice to localStorage. */ export function LanguageSwitcher() { const { locale, setLocale, t } = useI18n(); - const toggle = () => setLocale(locale === "en" ? "zh" : "en"); + const cycle = () => { + if (locale === "en") setLocale("zh"); + else if (locale === "zh") setLocale("ko"); + else setLocale("en"); + }; + + const flag = locale === "en" ? "πŸ‡¬πŸ‡§" : locale === "zh" ? "πŸ‡¨πŸ‡³" : "πŸ‡°πŸ‡·"; + const label = locale === "en" ? "EN" : locale === "zh" ? "δΈ­ζ–‡" : "ν•œκ΅­μ–΄"; return ( diff --git a/web/src/i18n/context.tsx b/web/src/i18n/context.tsx index 6fc6f6e56a05d..7c242a546539f 100644 --- a/web/src/i18n/context.tsx +++ b/web/src/i18n/context.tsx @@ -2,14 +2,15 @@ import { createContext, useContext, useState, useCallback, type ReactNode } from import type { Locale, Translations } from "./types"; import { en } from "./en"; import { zh } from "./zh"; +import { ko } from "./ko"; -const TRANSLATIONS: Record = { en, zh }; +const TRANSLATIONS: Record = { en, zh, ko }; const STORAGE_KEY = "hermes-locale"; function getInitialLocale(): Locale { try { const stored = localStorage.getItem(STORAGE_KEY); - if (stored === "en" || stored === "zh") return stored; + if (stored === "en" || stored === "zh" || stored === "ko") return stored; } catch { // SSR or privacy mode } diff --git a/web/src/i18n/ko.ts b/web/src/i18n/ko.ts new file mode 100644 index 0000000000000..f1696840ed286 --- /dev/null +++ b/web/src/i18n/ko.ts @@ -0,0 +1,429 @@ +import type { Translations } from "./types"; + +export const ko: Translations = { + common: { + save: "μ €μž₯", + saving: "μ €μž₯ 쀑...", + cancel: "μ·¨μ†Œ", + close: "λ‹«κΈ°", + confirm: "확인", + delete: "μ‚­μ œ", + refresh: "μƒˆλ‘œκ³ μΉ¨", + retry: "λ‹€μ‹œ μ‹œλ„", + search: "검색...", + loading: "λΆˆλŸ¬μ˜€λŠ” 쀑...", + create: "생성", + creating: "생성 쀑...", + set: "μ„€μ •", + replace: "ꡐ체", + clear: "μ΄ˆκΈ°ν™”", + live: "μ‹€μ‹œκ°„", + off: "꺼짐", + enabled: "ν™œμ„±ν™”λ¨", + disabled: "λΉ„ν™œμ„±ν™”λ¨", + active: "ν™œμ„±", + inactive: "λΉ„ν™œμ„±", + unknown: "μ•Œ 수 μ—†μŒ", + untitled: "제λͺ© μ—†μŒ", + none: "μ—†μŒ", + form: "폼", + noResults: "κ²°κ³Ό μ—†μŒ", + of: "/", + page: "νŽ˜μ΄μ§€", + msgs: "λ©”μ‹œμ§€", + tools: "도ꡬ", + match: "일치", + other: "기타", + configured: "섀정됨", + removed: "제거됨", + failedToToggle: "μ „ν™˜ μ‹€νŒ¨", + failedToRemove: "제거 μ‹€νŒ¨", + failedToReveal: "ν‘œμ‹œ μ‹€νŒ¨", + collapse: "μ ‘κΈ°", + expand: "펼치기", + general: "일반", + messaging: "λ©”μ‹œμ§€", + pluginLoadFailed: + "ν”ŒλŸ¬κ·ΈμΈ 슀크립트λ₯Ό 뢈러올 수 μ—†μŠ΅λ‹ˆλ‹€. Network νƒ­(dashboard-plugins/…)κ³Ό μ„œλ²„μ˜ ν”ŒλŸ¬κ·ΈμΈ 경둜λ₯Ό ν™•μΈν•˜μ„Έμš”.", + pluginNotRegistered: + "ν”ŒλŸ¬κ·ΈμΈ μŠ€ν¬λ¦½νŠΈκ°€ register()λ₯Ό ν˜ΈμΆœν•˜μ§€ μ•Šμ•˜κ±°λ‚˜ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λΈŒλΌμš°μ € μ½˜μ†”μ„ ν™•μΈν•˜μ„Έμš”.", + }, + + app: { + brand: "Hermes Agent", + brandShort: "HA", + closeNavigation: "λ‚΄λΉ„κ²Œμ΄μ…˜ λ‹«κΈ°", + closeModelTools: "λͺ¨λΈ 및 도ꡬ λ‹«κΈ°", + footer: { + org: "Nous Research", + }, + activeSessionsLabel: "ν™œμ„± μ„Έμ…˜:", + gatewayStatusLabel: "κ²Œμ΄νŠΈμ›¨μ΄ μƒνƒœ:", + gatewayStrip: { + failed: "μ‹œμž‘ μ‹€νŒ¨", + off: "꺼짐", + running: "μ‹€ν–‰ 쀑", + starting: "μ‹œμž‘ 쀑", + stopped: "쀑지됨", + }, + nav: { + analytics: "뢄석", + chat: "μ±„νŒ…", + config: "μ„€μ •", + cron: "μŠ€μΌ€μ€„", + documentation: "λ¬Έμ„œ", + keys: "API ν‚€", + logs: "둜그", + models: "λͺ¨λΈ", + profiles: "ν”„λ‘œν•„ : λ©€ν‹° μ—μ΄μ „νŠΈ", + plugins: "ν”ŒλŸ¬κ·ΈμΈ", + sessions: "μ„Έμ…˜", + skills: "μŠ€ν‚¬", + }, + modelToolsSheetSubtitle: "& 도ꡬ", + modelToolsSheetTitle: "λͺ¨λΈ", + navigation: "λ‚΄λΉ„κ²Œμ΄μ…˜", + openDocumentation: "μƒˆ νƒ­μ—μ„œ λ¬Έμ„œ μ—΄κΈ°", + openNavigation: "λ‚΄λΉ„κ²Œμ΄μ…˜ μ—΄κΈ°", + pluginNavSection: "ν”ŒλŸ¬κ·ΈμΈ", + sessionsActiveCount: "{count}개 ν™œμ„±", + statusOverview: "μƒνƒœ κ°œμš”", + system: "μ‹œμŠ€ν…œ", + webUi: "Web UI", + }, + + status: { + actionFailed: "μ•‘μ…˜ μ‹€νŒ¨", + actionFinished: "μ™„λ£Œ", + actions: "μ•‘μ…˜", + agent: "μ—μ΄μ „νŠΈ", + activeSessions: "ν™œμ„± μ„Έμ…˜", + connected: "연결됨", + connectedPlatforms: "μ—°κ²°λœ ν”Œλž«νΌ", + disconnected: "μ—°κ²° λŠκΉ€", + error: "였λ₯˜", + failed: "μ‹€νŒ¨", + gateway: "κ²Œμ΄νŠΈμ›¨μ΄", + gatewayFailedToStart: "κ²Œμ΄νŠΈμ›¨μ΄ μ‹œμž‘ μ‹€νŒ¨", + lastUpdate: "λ§ˆμ§€λ§‰ μ—…λ°μ΄νŠΈ", + noneRunning: "μ—†μŒ", + notRunning: "μ‹€ν–‰ 쀑 μ•„λ‹˜", + pid: "PID", + platformDisconnected: "μ—°κ²° λŠκΉ€", + platformError: "였λ₯˜", + recentSessions: "졜근 μ„Έμ…˜", + restartGateway: "κ²Œμ΄νŠΈμ›¨μ΄ μž¬μ‹œμž‘", + restartingGateway: "κ²Œμ΄νŠΈμ›¨μ΄ μž¬μ‹œμž‘ 쀑…", + running: "μ‹€ν–‰ 쀑", + runningRemote: "원격 μ‹€ν–‰ 쀑", + startFailed: "μ‹œμž‘ μ‹€νŒ¨", + starting: "μ‹œμž‘ 쀑", + startedInBackground: "λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ‹œμž‘λ¨ β€” μ§„ν–‰ 상황은 둜그λ₯Ό ν™•μΈν•˜μ„Έμš”", + stopped: "쀑지됨", + updateHermes: "Hermes μ—…λ°μ΄νŠΈ", + updatingHermes: "Hermes μ—…λ°μ΄νŠΈ 쀑…", + waitingForOutput: "좜λ ₯ λŒ€κΈ° 쀑…", + }, + + sessions: { + title: "μ„Έμ…˜", + searchPlaceholder: "λ©”μ‹œμ§€ λ‚΄μš© 검색...", + noSessions: "μ„Έμ…˜μ΄ μ—†μŠ΅λ‹ˆλ‹€", + noMatch: "검색 κ²°κ³Όκ°€ μ—†μŠ΅λ‹ˆλ‹€", + startConversation: "λŒ€ν™”λ₯Ό μ‹œμž‘ν•˜λ©΄ 여기에 ν‘œμ‹œλ©λ‹ˆλ‹€", + noMessages: "λ©”μ‹œμ§€ μ—†μŒ", + untitledSession: "제λͺ© μ—†λŠ” μ„Έμ…˜", + deleteSession: "μ„Έμ…˜ μ‚­μ œ", + confirmDeleteTitle: "μ„Έμ…˜μ„ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?", + confirmDeleteMessage: + "이 λŒ€ν™”μ™€ λͺ¨λ“  λ©”μ‹œμ§€κ°€ 영ꡬ적으둜 μ‚­μ œλ©λ‹ˆλ‹€. 이 μž‘μ—…μ€ 되돌릴 수 μ—†μŠ΅λ‹ˆλ‹€.", + sessionDeleted: "μ„Έμ…˜μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€", + failedToDelete: "μ„Έμ…˜ μ‚­μ œ μ‹€νŒ¨", + resumeInChat: "μ±„νŒ…μ—μ„œ 이어보기", + previousPage: "이전 νŽ˜μ΄μ§€", + nextPage: "λ‹€μŒ νŽ˜μ΄μ§€", + roles: { + user: "μ‚¬μš©μž", + assistant: "μ–΄μ‹œμŠ€ν„΄νŠΈ", + system: "μ‹œμŠ€ν…œ", + tool: "도ꡬ", + }, + }, + + analytics: { + period: "κΈ°κ°„:", + totalTokens: "총 토큰", + totalSessions: "총 μ„Έμ…˜", + apiCalls: "API 호좜", + dailyTokenUsage: "일별 토큰 μ‚¬μš©λŸ‰", + dailyBreakdown: "일별 μ„ΈλΆ€ λ‚΄μ—­", + perModelBreakdown: "λͺ¨λΈλ³„ μ„ΈλΆ€ λ‚΄μ—­", + topSkills: "μƒμœ„ μŠ€ν‚¬", + skill: "μŠ€ν‚¬", + loads: "μ—μ΄μ „νŠΈ λ‘œλ“œ", + edits: "μ—μ΄μ „νŠΈ 관리", + lastUsed: "λ§ˆμ§€λ§‰ μ‚¬μš©", + input: "μž…λ ₯", + output: "좜λ ₯", + total: "합계", + noUsageData: "ν•΄λ‹Ή κΈ°κ°„μ˜ μ‚¬μš© 데이터가 μ—†μŠ΅λ‹ˆλ‹€", + startSession: "μ„Έμ…˜μ„ μ‹œμž‘ν•˜λ©΄ 뢄석 데이터가 ν‘œμ‹œλ©λ‹ˆλ‹€", + date: "λ‚ μ§œ", + model: "λͺ¨λΈ", + tokens: "토큰", + perDayAvg: "/일 평균", + acrossModels: "{count}개 λͺ¨λΈ μ‚¬μš©", + inOut: "μž…λ ₯ {input} / 좜λ ₯ {output}", + }, + + models: { + modelsUsed: "μ‚¬μš©ν•œ λͺ¨λΈ", + estimatedCost: "μ˜ˆμƒ λΉ„μš©", + tokens: "토큰", + sessions: "μ„Έμ…˜", + avgPerSession: "μ„Έμ…˜ 평균", + apiCalls: "API 호좜", + toolCalls: "도ꡬ 호좜", + noModelsData: "ν•΄λ‹Ή κΈ°κ°„μ˜ λͺ¨λΈ μ‚¬μš© 데이터가 μ—†μŠ΅λ‹ˆλ‹€", + startSession: "μ„Έμ…˜μ„ μ‹œμž‘ν•˜λ©΄ λͺ¨λΈ 데이터가 ν‘œμ‹œλ©λ‹ˆλ‹€", + }, + + logs: { + title: "둜그", + autoRefresh: "μžλ™ μƒˆλ‘œκ³ μΉ¨", + file: "파일", + level: "레벨", + component: "μ»΄ν¬λ„ŒνŠΈ", + lines: "쀄", + noLogLines: "λ‘œκ·Έκ°€ μ—†μŠ΅λ‹ˆλ‹€", + }, + + cron: { + confirmDeleteMessage: + "이 μž‘μ—…μ„ μŠ€μΌ€μ€„μ—μ„œ μ œκ±°ν•©λ‹ˆλ‹€. 이 μž‘μ—…μ€ 되돌릴 수 μ—†μŠ΅λ‹ˆλ‹€.", + confirmDeleteTitle: "μŠ€μΌ€μ€„ μž‘μ—…μ„ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?", + newJob: "μƒˆ 크둠 μž‘μ—…", + nameOptional: "이름 (선택)", + namePlaceholder: "예: 일일 μš”μ•½", + prompt: "ν”„λ‘¬ν”„νŠΈ", + promptPlaceholder: "각 μ‹€ν–‰ μ‹œ μ—μ΄μ „νŠΈκ°€ ν•  μž‘μ—…μ„ μž…λ ₯ν•˜μ„Έμš”", + schedule: "μŠ€μΌ€μ€„ (크둠 ν‘œν˜„μ‹)", + schedulePlaceholder: "0 9 * * *", + deliverTo: "전달 λŒ€μƒ", + scheduledJobs: "μŠ€μΌ€μ€„λœ μž‘μ—…", + noJobs: "크둠 μž‘μ—…μ΄ μ—†μŠ΅λ‹ˆλ‹€. μœ„μ—μ„œ μƒμ„±ν•˜μ„Έμš”.", + last: "λ§ˆμ§€λ§‰", + next: "λ‹€μŒ", + pause: "μΌμ‹œμ •μ§€", + resume: "재개", + triggerNow: "μ¦‰μ‹œ μ‹€ν–‰", + delivery: { + local: "둜컬", + telegram: "ν…”λ ˆκ·Έλž¨", + discord: "λ””μŠ€μ½”λ“œ", + slack: "μŠ¬λž™", + email: "이메일", + }, + }, + + profiles: { + newProfile: "μƒˆ ν”„λ‘œν•„", + name: "이름", + namePlaceholder: "예: coder, writer λ“±", + nameRequired: "이름은 ν•„μˆ˜μž…λ‹ˆλ‹€", + nameRule: + "μ†Œλ¬Έμž, 숫자, _ 및 -만 μ‚¬μš© κ°€λŠ₯ν•©λ‹ˆλ‹€. 문자 λ˜λŠ” 숫자둜 μ‹œμž‘ν•΄μ•Ό ν•˜λ©° μ΅œλŒ€ 64μžμž…λ‹ˆλ‹€.", + invalidName: "잘λͺ»λœ ν”„λ‘œν•„ 이름", + cloneFromDefault: "κΈ°λ³Έ ν”„λ‘œν•„μ—μ„œ μ„€μ • 볡제", + allProfiles: "ν”„λ‘œν•„", + noProfiles: "ν”„λ‘œν•„μ΄ μ—†μŠ΅λ‹ˆλ‹€.", + defaultBadge: "κΈ°λ³Έ", + hasEnv: "env", + model: "λͺ¨λΈ", + skills: "μŠ€ν‚¬", + rename: "이름 λ³€κ²½", + editSoul: "SOUL.md νŽΈμ§‘", + soulSection: "SOUL.md (페λ₯΄μ†Œλ‚˜ / μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ)", + soulPlaceholder: "# 이 μ—μ΄μ „νŠΈμ˜ λ™μž‘ 방식…", + saveSoul: "SOUL μ €μž₯", + soulSaved: "SOUL.md μ €μž₯됨", + openInTerminal: "CLI λͺ…λ Ή 볡사", + commandCopied: "ν΄λ¦½λ³΄λ“œμ— 볡사됨", + copyFailed: "볡사 μ‹€νŒ¨", + confirmDeleteTitle: "ν”„λ‘œν•„μ„ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?", + confirmDeleteMessage: + "ν”„λ‘œν•„ '{name}'의 μ„€μ •, ν‚€, λ©”λͺ¨λ¦¬, μ„Έμ…˜, μŠ€ν‚¬, 크둠 μž‘μ—…μ΄ 영ꡬ적으둜 μ‚­μ œλ©λ‹ˆλ‹€. 되돌릴 수 μ—†μŠ΅λ‹ˆλ‹€.", + created: "생성됨", + deleted: "μ‚­μ œλ¨", + renamed: "이름 변경됨", + }, + + pluginsPage: { + contextEngineLabel: "μ»¨ν…μŠ€νŠΈ μ—”μ§„", + dashboardSlots: "λŒ€μ‹œλ³΄λ“œ 슬둯", + disableRuntime: "λΉ„ν™œμ„±ν™”", + enableAfterInstall: "μ„€μΉ˜ ν›„ ν™œμ„±ν™”", + enableRuntime: "ν™œμ„±ν™”", + forceReinstall: "κ°•μ œ μž¬μ„€μΉ˜ (κΈ°μ‘΄ 폴더 μ‚­μ œ ν›„)", + headline: + "Hermes ν”ŒλŸ¬κ·ΈμΈμ„ 검색, μ„€μΉ˜, ν™œμ„±ν™” 및 μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€ (`hermes plugins` 동일).", + identifierLabel: "Git URL λ˜λŠ” owner/repo", + inactive: "λΉ„ν™œμ„±", + installBtn: "Gitμ—μ„œ μ„€μΉ˜", + installHeading: "GitHub / Git URLμ—μ„œ μ„€μΉ˜", + installHint: "owner/repo λ‹¨μΆ•ν˜• λ˜λŠ” https:// λ˜λŠ” git@ 클둠 URL을 μ‚¬μš©ν•˜μ„Έμš”.", + memoryProviderLabel: "λ©”λͺ¨λ¦¬ ν”„λ‘œλ°”μ΄λ”", + missingEnvWarn: "ν”ŒλŸ¬κ·ΈμΈ μ‹€ν–‰ μ „ Keysμ—μ„œ μ„€μ •ν•˜μ„Έμš”:", + noDashboardTab: "λŒ€μ‹œλ³΄λ“œ νƒ­ μ—†μŒ", + openTab: "μ—΄κΈ°", + orphanHeading: "λŒ€μ‹œλ³΄λ“œ μ „μš© ν™•μž₯ (μ—μ΄μ „νŠΈ plugin.yaml μ—†μŒ)", + pluginListHeading: "μ„€μΉ˜λœ ν”ŒλŸ¬κ·ΈμΈ", + providerDefaults: "κΈ°λ³Έ λ‚΄μž₯", + providersHeading: "λŸ°νƒ€μž„ ν”„λ‘œλ°”μ΄λ” ν”ŒλŸ¬κ·ΈμΈ", + providersHint: + "memory.provider (λΉ„μ–΄μžˆμœΌλ©΄ λ‚΄μž₯)와 context.engine을 config.yaml에 μ”λ‹ˆλ‹€. λ‹€μŒ μ„Έμ…˜λΆ€ν„° μ μš©λ©λ‹ˆλ‹€.", + refreshDashboard: "λŒ€μ‹œλ³΄λ“œ ν™•μž₯ μž¬μŠ€μΊ”", + removeConfirm: "~/.hermes/plugins/μ—μ„œ 이 ν”ŒλŸ¬κ·ΈμΈμ„ μ œκ±°ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?", + removeHint: "~/.hermes/plugins/ μ•„λž˜ μ‚¬μš©μž μ„€μΉ˜ ν”ŒλŸ¬κ·ΈμΈλ§Œ μ œκ±°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.", + rescanHeading: "SPA ν”ŒλŸ¬κ·ΈμΈ λ ˆμ§€μŠ€νŠΈλ¦¬", + rescanHint: "λ””μŠ€ν¬μ— 파일 μΆ”κ°€ ν›„ μž¬μŠ€μΊ”ν•˜λ©΄ μ‚¬μ΄λ“œλ°”μ— μƒˆ λ§€λ‹ˆνŽ˜μŠ€νŠΈκ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.", + runtimeHeading: "κ²Œμ΄νŠΈμ›¨μ΄ λŸ°νƒ€μž„ (YAML ν”ŒλŸ¬κ·ΈμΈ)", + saveProviders: "ν”„λ‘œλ°”μ΄λ” μ„€μ • μ €μž₯", + savedProviders: "ν”„λ‘œλ°”μ΄λ” 섀정이 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€.", + sourceBadge: "μ†ŒμŠ€", + authRequired: "인증 ν•„μš”", + authRequiredHint: "μΈμ¦ν•˜λ €λ©΄ λ‹€μŒ λͺ…령을 μ‹€ν–‰ν•˜μ„Έμš”:", + updateGit: "Git pull", + versionBadge: "버전", + showInSidebar: "μ‚¬μ΄λ“œλ°”μ— ν‘œμ‹œ", + hideFromSidebar: "μ‚¬μ΄λ“œλ°”μ—μ„œ 숨기기", + }, + + skills: { + title: "μŠ€ν‚¬", + searchPlaceholder: "μŠ€ν‚¬ 및 νˆ΄μ…‹ 검색...", + enabledOf: "{enabled}/{total} ν™œμ„±ν™”", + all: "전체", + categories: "μΉ΄ν…Œκ³ λ¦¬", + filters: "ν•„ν„°", + noSkills: "μŠ€ν‚¬μ΄ μ—†μŠ΅λ‹ˆλ‹€. μŠ€ν‚¬μ€ ~/.hermes/skills/μ—μ„œ λΆˆλŸ¬μ˜΅λ‹ˆλ‹€", + noSkillsMatch: "검색 λ˜λŠ” 필터에 λ§žλŠ” μŠ€ν‚¬μ΄ μ—†μŠ΅λ‹ˆλ‹€.", + skillCount: "{count}개 μŠ€ν‚¬", + resultCount: "{count}개 κ²°κ³Ό", + noDescription: "μ„€λͺ…이 μ—†μŠ΅λ‹ˆλ‹€.", + toolsets: "νˆ΄μ…‹", + toolsetLabel: "{name} νˆ΄μ…‹", + noToolsetsMatch: "검색에 λ§žλŠ” νˆ΄μ…‹μ΄ μ—†μŠ΅λ‹ˆλ‹€.", + setupNeeded: "μ„€μ • ν•„μš”", + disabledForCli: "CLIμ—μ„œ λΉ„ν™œμ„±ν™”λ¨", + more: "+{count}개 더", + }, + + config: { + configPath: "~/.hermes/config.yaml", + filters: "ν•„ν„°", + sections: "μ„Ήμ…˜", + exportConfig: "섀정을 JSON으둜 내보내기", + importConfig: "JSONμ—μ„œ μ„€μ • κ°€μ Έμ˜€κΈ°", + resetDefaults: "κΈ°λ³Έκ°’μœΌλ‘œ μ΄ˆκΈ°ν™”", + resetScopeTooltip: "{scope} κΈ°λ³Έκ°’μœΌλ‘œ μ΄ˆκΈ°ν™”", + confirmResetScope: "{scope} 섀정을 κΈ°λ³Έκ°’μœΌλ‘œ μ΄ˆκΈ°ν™”ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? 폼만 μ—…λ°μ΄νŠΈλ˜λ©° μ €μž₯을 λˆŒλŸ¬μ•Ό config.yaml에 λ°˜μ˜λ©λ‹ˆλ‹€.", + resetScopeToast: "{scope} κΈ°λ³Έκ°’μœΌλ‘œ μ΄ˆκΈ°ν™”λ¨ β€” 확인 ν›„ μ €μž₯ν•˜μ„Έμš”", + rawYaml: "YAML μ„€μ • 원문", + searchResults: "검색 κ²°κ³Ό", + fields: "ν•„λ“œ", + noFieldsMatch: '"{query}"에 λ§žλŠ” ν•„λ“œκ°€ μ—†μŠ΅λ‹ˆλ‹€', + configSaved: "섀정이 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€", + yamlConfigSaved: "YAML 섀정이 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€", + failedToSave: "μ €μž₯ μ‹€νŒ¨", + failedToSaveYaml: "YAML μ €μž₯ μ‹€νŒ¨", + failedToLoadRaw: "μ„€μ • 원문 뢈러였기 μ‹€νŒ¨", + configImported: "μ„€μ • κ°€μ Έμ˜΄ β€” 확인 ν›„ μ €μž₯ν•˜μ„Έμš”", + invalidJson: "잘λͺ»λœ JSON 파일", + categories: { + general: "일반", + agent: "μ—μ΄μ „νŠΈ", + terminal: "터미널", + display: "ν™”λ©΄", + delegation: "μœ„μž„", + memory: "λ©”λͺ¨λ¦¬", + compression: "μ••μΆ•", + security: "λ³΄μ•ˆ", + browser: "λΈŒλΌμš°μ €", + voice: "μŒμ„±", + tts: "ν…μŠ€νŠΈ μŒμ„± λ³€ν™˜", + stt: "μŒμ„± ν…μŠ€νŠΈ λ³€ν™˜", + logging: "λ‘œκΉ…", + discord: "λ””μŠ€μ½”λ“œ", + auxiliary: "보쑰", + }, + }, + + env: { + changesNote: "변경사항은 μ¦‰μ‹œ μ €μž₯λ©λ‹ˆλ‹€. ν™œμ„± μ„Έμ…˜μ€ μƒˆ ν‚€λ₯Ό μžλ™μœΌλ‘œ λ°˜μ˜ν•©λ‹ˆλ‹€.", + confirmClearMessage: + "이 λ³€μˆ˜μ˜ μ €μž₯된 값이 .env νŒŒμΌμ—μ„œ μ œκ±°λ©λ‹ˆλ‹€. UIμ—μ„œλŠ” 되돌릴 수 μ—†μŠ΅λ‹ˆλ‹€.", + confirmClearTitle: "이 ν‚€λ₯Ό μ΄ˆκΈ°ν™”ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?", + description: "API ν‚€ 및 μ‹œν¬λ¦Ώ 관리:", + hideAdvanced: "κ³ κΈ‰ 숨기기", + showAdvanced: "κ³ κΈ‰ ν‘œμ‹œ", + llmProviders: "LLM ν”„λ‘œλ°”μ΄λ”", + providersConfigured: "{total}개 쀑 {configured}개 섀정됨", + getKey: "ν‚€ λ°œκΈ‰", + notConfigured: "{count}개 λ―Έμ„€μ •", + notSet: "μ„€μ •λ˜μ§€ μ•ŠμŒ", + keysCount: "{count}개 ν‚€", + enterValue: "κ°’ μž…λ ₯...", + replaceCurrentValue: "ν˜„μž¬ κ°’ ꡐ체 ({preview})", + showValue: "μ‹€μ œ κ°’ ν‘œμ‹œ", + hideValue: "κ°’ 숨기기", + }, + + oauth: { + title: "ν”„λ‘œλ°”μ΄λ” 둜그인 (OAuth)", + providerLogins: "ν”„λ‘œλ°”μ΄λ” 둜그인 (OAuth)", + description: "{total}개 쀑 {connected}개 OAuth ν”„λ‘œλ°”μ΄λ” 연결됨. λ‘œκ·ΈμΈμ€ CLIλ₯Ό 톡해 μ§„ν–‰λ©λ‹ˆλ‹€. λͺ…λ Ή 볡사 ν›„ 터미널에 λΆ™μ—¬λ„£μœΌμ„Έμš”.", + connected: "연결됨", + expired: "만료됨", + notConnected: "μ—°κ²°λ˜μ§€ μ•ŠμŒ. ν„°λ―Έλ„μ—μ„œ {command}λ₯Ό μ‹€ν–‰ν•˜μ„Έμš”.", + runInTerminal: "ν„°λ―Έλ„μ—μ„œ μ‹€ν–‰", + noProviders: "OAuth 지원 ν”„λ‘œλ°”μ΄λ”κ°€ μ—†μŠ΅λ‹ˆλ‹€.", + login: "둜그인", + disconnect: "μ—°κ²° 끊기", + managedExternally: "μ™ΈλΆ€μ—μ„œ 관리됨", + copied: "볡사됨 βœ“", + cli: "CLI", + copyCliCommand: "CLI λͺ…λ Ή 볡사 (μ™ΈλΆ€/λŒ€μ²΄μš©)", + connect: "μ—°κ²°", + sessionExpires: "{time} ν›„ 만료", + initiatingLogin: "둜그인 μ§„ν–‰ 쀑…", + exchangingCode: "ν† ν°μœΌλ‘œ μ½”λ“œ κ΅ν™˜ 쀑…", + connectedClosing: "연결됨! λ‹«λŠ” 쀑…", + loginFailed: "둜그인 μ‹€νŒ¨.", + sessionExpired: "μ„Έμ…˜μ΄ λ§Œλ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ‹€μ‹œ μ‹œλ„λ₯Ό ν΄λ¦­ν•˜μ„Έμš”.", + reOpenAuth: "인증 νŽ˜μ΄μ§€ λ‹€μ‹œ μ—΄κΈ°", + reOpenVerification: "인증 확인 νŽ˜μ΄μ§€ λ‹€μ‹œ μ—΄κΈ°", + submitCode: "μ½”λ“œ 제좜", + pasteCode: "인증 μ½”λ“œ λΆ™μ—¬λ„£κΈ° (#state 접미사 포함 κ°€λŠ₯)", + waitingAuth: "λΈŒλΌμš°μ €μ—μ„œ 인증 λŒ€κΈ° 쀑…", + enterCodePrompt: "μƒˆ 탭이 μ—΄λ ΈμŠ΅λ‹ˆλ‹€. μš”μ²­ μ‹œ 이 μ½”λ“œλ₯Ό μž…λ ₯ν•˜μ„Έμš”:", + pkceStep1: "claude.ai둜 μƒˆ 탭이 μ—΄λ ΈμŠ΅λ‹ˆλ‹€. 둜그인 ν›„ μŠΉμΈμ„ ν΄λ¦­ν•˜μ„Έμš”.", + pkceStep2: "승인 ν›„ ν‘œμ‹œλœ 인증 μ½”λ“œλ₯Ό λ³΅μ‚¬ν•˜μ„Έμš”.", + pkceStep3: "μ•„λž˜μ— λΆ™μ—¬λ„£κ³  μ œμΆœν•˜μ„Έμš”.", + flowLabels: { + pkce: "λΈŒλΌμš°μ € 둜그인 (PKCE)", + device_code: "λ””λ°”μ΄μŠ€ μ½”λ“œ", + external: "μ™ΈλΆ€ CLI", + }, + expiresIn: "{time} ν›„ 만료", + }, + + language: { + switchTo: "ν•œκ΅­μ–΄λ‘œ μ „ν™˜", + }, + + theme: { + title: "ν…Œλ§ˆ", + switchTheme: "ν…Œλ§ˆ λ³€κ²½", + }, +}; diff --git a/web/src/i18n/types.ts b/web/src/i18n/types.ts index d93260d26d704..a595b11fe18d4 100644 --- a/web/src/i18n/types.ts +++ b/web/src/i18n/types.ts @@ -1,4 +1,4 @@ -export type Locale = "en" | "zh"; +export type Locale = "en" | "zh" | "ko"; export interface Translations { // ── Common ──