Skip to content

Commit e6ab683

Browse files
authored
feat(i18n): 한국어(ko) 로컬라이제이션 추가 (#1081)
- 백엔드: LocaleKO 상수, IsSupported에 ko 추가, catalog_ko.go 185개 문자열 - 웹 UI: 38개 네임스페이스 JSON 파일 (locales/ko/) - constants.ts SUPPORTED_LANGUAGES에 ko 추가 - index.ts에 ko 리소스 등록 및 getInitialLanguage에 ko 감지 - en/topbar.json languages에 한국어 옵션 추가 - webui handler: index.html에 no-cache 헤더 추가하여 배포 후 캐시 문제 방지
1 parent ce4535f commit e6ab683

44 files changed

Lines changed: 5599 additions & 4 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/i18n/catalog_ko.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package i18n
2+
3+
func init() {
4+
register(LocaleKO, map[string]string{
5+
// Common validation
6+
MsgRequired: "%s은(는) 필수입니다",
7+
MsgInvalidID: "잘못된 %s ID",
8+
MsgNotFound: "%s을(를) 찾을 수 없습니다: %s",
9+
MsgAlreadyExists: "%s이(가) 이미 존재합니다: %s",
10+
MsgInvalidRequest: "잘못된 요청: %s",
11+
MsgInvalidJSON: "잘못된 JSON",
12+
MsgUnauthorized: "인증되지 않음",
13+
MsgPermissionDenied: "권한이 거부되었습니다: %s",
14+
MsgInternalError: "내부 오류: %s",
15+
MsgInvalidSlug: "%s은(는) 유효한 슬러그여야 합니다 (소문자, 숫자, 하이픈만 허용)",
16+
MsgFailedToList: "%s 목록을 가져오지 못했습니다",
17+
MsgFailedToCreate: "%s 생성에 실패했습니다: %s",
18+
MsgFailedToUpdate: "%s 업데이트에 실패했습니다: %s",
19+
MsgFailedToDelete: "%s 삭제에 실패했습니다: %s",
20+
MsgFailedToSave: "%s 저장에 실패했습니다: %s",
21+
MsgInvalidUpdates: "잘못된 업데이트",
22+
23+
// Agent
24+
MsgAgentNotFound: "에이전트를 찾을 수 없습니다: %s",
25+
MsgCannotDeleteDefault: "기본 에이전트는 삭제할 수 없습니다",
26+
MsgUserCtxRequired: "사용자 컨텍스트가 필요합니다",
27+
28+
// Chat
29+
MsgRateLimitExceeded: "요청 한도 초과 — 잠시 후 다시 시도해주세요",
30+
MsgNoUserMessage: "사용자 메시지를 찾을 수 없습니다",
31+
MsgUserIDRequired: "user_id가 필요합니다",
32+
MsgMsgRequired: "메시지가 필요합니다",
33+
34+
// Channel instances
35+
MsgInvalidChannelType: "잘못된 channel_type",
36+
MsgInstanceNotFound: "인스턴스를 찾을 수 없습니다",
37+
38+
// Cron
39+
MsgJobNotFound: "작업을 찾을 수 없습니다",
40+
MsgInvalidCronExpr: "잘못된 크론 표현식: %s",
41+
42+
// Config
43+
MsgConfigHashMismatch: "설정이 변경되었습니다 (해시 불일치)",
44+
45+
// Exec approval
46+
MsgExecApprovalDisabled: "실행 승인이 활성화되지 않았습니다",
47+
48+
// Pairing
49+
MsgSenderChannelRequired: "senderId와 channel이 필요합니다",
50+
MsgCodeRequired: "코드가 필요합니다",
51+
MsgSenderIDRequired: "sender_id가 필요합니다",
52+
53+
// HTTP API
54+
MsgInvalidAuth: "잘못된 인증",
55+
MsgMsgsRequired: "messages가 필요합니다",
56+
MsgUserIDHeader: "X-GoClaw-User-Id 헤더가 필요합니다",
57+
MsgFileTooLarge: "파일이 너무 크거나 잘못된 멀티파트 양식입니다",
58+
MsgMissingFileField: "'file' 필드가 누락되었습니다",
59+
MsgInvalidFilename: "잘못된 파일명",
60+
MsgChannelKeyReq: "channel과 key가 필요합니다",
61+
MsgMethodNotAllowed: "허용되지 않는 메서드",
62+
MsgStreamingNotSupported: "스트리밍이 지원되지 않습니다",
63+
MsgOwnerOnly: "소유자만 %s할 수 있습니다",
64+
MsgNoAccess: "이 %s에 대한 접근 권한이 없습니다",
65+
MsgAlreadySummoning: "에이전트가 이미 소환 중입니다",
66+
MsgSummoningUnavailable: "소환을 사용할 수 없습니다",
67+
MsgNoDescription: "에이전트에 재소환할 설명이 없습니다",
68+
MsgInvalidPath: "잘못된 경로",
69+
70+
// Scheduler
71+
MsgQueueFull: "세션 대기열이 가득 찼습니다",
72+
MsgShuttingDown: "게이트웨이가 종료 중입니다. 잠시 후 다시 시도해주세요",
73+
74+
// Provider
75+
MsgProviderReqFailed: "%s: 요청에 실패했습니다: %s",
76+
77+
// Unknown method
78+
MsgUnknownMethod: "알 수 없는 메서드: %s",
79+
80+
// Not implemented
81+
MsgNotImplemented: "%s은(는) 아직 구현되지 않았습니다",
82+
83+
// Agent links
84+
MsgLinksNotConfigured: "에이전트 링크가 설정되지 않았습니다",
85+
MsgInvalidDirection: "방향은 outbound, inbound, 또는 bidirectional이어야 합니다",
86+
MsgSourceTargetSame: "소스와 대상은 서로 다른 에이전트여야 합니다",
87+
MsgCannotDelegateOpen: "오픈 에이전트에게는 위임할 수 없습니다 — 사전 정의된 에이전트만 위임 대상이 될 수 있습니다",
88+
MsgNoUpdatesProvided: "업데이트가 제공되지 않았습니다",
89+
MsgInvalidLinkStatus: "상태는 active 또는 disabled여야 합니다",
90+
91+
// Teams
92+
MsgTeamsNotConfigured: "팀이 설정되지 않았습니다",
93+
MsgAgentIsTeamLead: "에이전트가 이미 팀 리더입니다",
94+
MsgCannotRemoveTeamLead: "팀 리더는 제거할 수 없습니다",
95+
96+
// Channels
97+
MsgCannotDeleteDefaultInst: "기본 채널 인스턴스는 삭제할 수 없습니다",
98+
MsgCannotRemoveLastWriter: "마지막 파일 작성자는 제거할 수 없습니다",
99+
100+
// Skills
101+
MsgSkillsUpdateNotSupported: "파일 기반 스킬에는 skills.update가 지원되지 않습니다",
102+
MsgCannotResolveSkillID: "파일 기반 스킬의 스킬 ID를 확인할 수 없습니다",
103+
104+
// Logs
105+
MsgInvalidLogAction: "action은 'start' 또는 'stop'이어야 합니다",
106+
107+
// Config
108+
MsgRawConfigRequired: "원시 설정이 필요합니다",
109+
MsgRawPatchRequired: "원시 패치가 필요합니다",
110+
MsgConfigMasterScopeOnly: "config.* 메서드는 마스터 범위 전용입니다. 테넌트별 재정의는 테넌트 도구 설정 엔드포인트를 사용하세요",
111+
112+
// Storage / File
113+
MsgCannotDeleteSkillsDir: "스킬 디렉토리는 삭제할 수 없습니다",
114+
MsgFailedToReadFile: "파일 읽기에 실패했습니다",
115+
MsgFileNotFound: "파일을 찾을 수 없습니다",
116+
MsgInvalidVersion: "잘못된 버전",
117+
MsgVersionNotFound: "버전을 찾을 수 없습니다",
118+
MsgFailedToDeleteFile: "삭제에 실패했습니다",
119+
120+
// OAuth
121+
MsgNoPendingOAuth: "진행 중인 OAuth 흐름이 없습니다",
122+
MsgFailedToSaveToken: "토큰 저장에 실패했습니다",
123+
124+
// Intent Classify
125+
MsgStatusWorking: "🔄 요청을 처리 중입니다... 잠시 기다려주세요.",
126+
MsgStatusDetailed: "🔄 현재 요청을 처리 중입니다...\n%s (반복 %d)\n실행 시간: %s\n\n잠시 기다려주세요 — 완료되면 응답하겠습니다.",
127+
MsgStatusPhaseThinking: "단계: 생각 중...",
128+
MsgStatusPhaseToolExec: "단계: %s 실행 중",
129+
MsgStatusPhaseTools: "단계: 도구 실행 중...",
130+
MsgStatusPhaseCompact: "단계: 컨텍스트 압축 중...",
131+
MsgStatusPhaseDefault: "단계: 처리 중...",
132+
MsgCancelledReply: "✋ 취소되었습니다. 다음에 무엇을 하시겠습니까?",
133+
MsgInjectedAck: "알겠습니다. 작업 중인 내용에 반영하겠습니다.",
134+
135+
// Knowledge Graph
136+
MsgEntityIDRequired: "entity_id가 필요합니다",
137+
MsgEntityFieldsRequired: "external_id, name, entity_type이 필요합니다",
138+
MsgTextRequired: "텍스트가 필요합니다",
139+
MsgProviderModelRequired: "provider와 model이 필요합니다",
140+
MsgInvalidProviderOrModel: "잘못된 provider 또는 model",
141+
142+
// Builtin tool descriptions
143+
MsgToolReadFile: "경로를 기준으로 에이전트 워크스페이스에서 파일 내용을 읽습니다",
144+
MsgToolWriteFile: "필요한 디렉토리를 생성하면서 워크스페이스의 파일에 내용을 씁니다",
145+
MsgToolListFiles: "워크스페이스 내 지정된 경로의 파일과 디렉토리를 나열합니다",
146+
MsgToolEdit: "전체 파일을 다시 쓰지 않고 기존 파일에 타겟 검색-교체 편집을 적용합니다",
147+
MsgToolExec: "워크스페이스에서 셸 명령을 실행하고 stdout/stderr를 반환합니다",
148+
MsgToolWebSearch: "검색 엔진(Brave 또는 DuckDuckGo)을 사용하여 웹에서 정보를 검색합니다",
149+
MsgToolWebFetch: "웹 페이지 또는 API 엔드포인트를 가져와 텍스트 내용을 추출합니다",
150+
MsgToolMemorySearch: "의미론적 유사성을 사용하여 에이전트의 장기 기억을 검색합니다",
151+
MsgToolMemoryGet: "파일 경로로 특정 기억 문서를 검색합니다",
152+
MsgToolKGSearch: "에이전트의 지식 그래프에서 엔티티, 관계, 관찰을 검색합니다",
153+
MsgToolReadImage: "비전 지원 LLM 제공자를 사용하여 이미지를 분석합니다",
154+
MsgToolReadDocument: "문서 지원 LLM 제공자를 사용하여 문서(PDF, Word, Excel, PowerPoint, CSV 등)를 분석합니다",
155+
MsgToolCreateImage: "이미지 생성 제공자를 사용하여 텍스트 프롬프트에서 이미지를 생성합니다",
156+
MsgToolReadAudio: "오디오 지원 LLM 제공자를 사용하여 오디오 파일(음성, 음악, 소리)을 분석합니다",
157+
MsgToolReadVideo: "비디오 지원 LLM 제공자를 사용하여 비디오 파일을 분석합니다",
158+
MsgToolCreateVideo: "AI를 사용하여 텍스트 설명에서 비디오를 생성합니다",
159+
MsgToolCreateAudio: "AI를 사용하여 텍스트 설명에서 음악이나 음향 효과를 생성합니다",
160+
MsgToolTTS: "텍스트를 자연스러운 음성 오디오로 변환합니다",
161+
MsgToolBrowser: "브라우저 상호작용 자동화: 페이지 탐색, 요소 클릭, 폼 작성, 스크린샷 촬영",
162+
MsgToolSessionsList: "모든 채널의 활성 채팅 세션을 나열합니다",
163+
MsgToolSessionStatus: "특정 채팅 세션의 현재 상태와 메타데이터를 가져옵니다",
164+
MsgToolSessionsHistory: "특정 채팅 세션의 메시지 기록을 검색합니다",
165+
MsgToolSessionsSend: "에이전트를 대신하여 활성 채팅 세션에 메시지를 보냅니다",
166+
MsgToolMessage: "연결된 채널(Telegram, Discord 등)에서 사용자에게 능동적으로 메시지를 보냅니다",
167+
MsgToolCron: "크론 표현식, 특정 시간 또는 간격을 사용하여 반복 작업을 예약하거나 관리합니다",
168+
MsgToolSpawn: "백그라운드 작업을 위해 하위 에이전트를 생성하거나 연결된 에이전트에 작업을 위임합니다",
169+
MsgToolSkillSearch: "키워드나 설명으로 사용 가능한 스킬을 검색하여 관련 기능을 찾습니다",
170+
MsgToolUseSkill: "전문화된 기능을 사용하기 위해 스킬을 활성화합니다 (추적 마커)",
171+
MsgToolSkillManage: "대화 경험에서 스킬을 생성, 수정 또는 삭제합니다",
172+
MsgToolPublishSkill: "스킬 디렉토리를 시스템 데이터베이스에 등록하여 검색 가능하게 만듭니다",
173+
MsgToolTeamTasks: "팀 작업 보드에서 작업을 보고, 생성하고, 업데이트하고, 완료합니다",
174+
175+
MsgSkillNudgePostscript: "이 작업은 여러 단계를 포함했습니다. 이 과정을 재사용 가능한 스킬로 저장할까요? **\"스킬로 저장\"** 또는 **\"건너뛰기\"**로 답장하세요.",
176+
MsgSkillNudge70Pct: "[System] 반복 예산의 70%에 도달했습니다. 이 세션의 패턴 중 좋은 스킬이 될 수 있는 것이 있는지 고려해보세요.",
177+
MsgSkillNudge90Pct: "[System] 반복 예산의 90%에 도달했습니다. 이 세션에 재사용 가능한 패턴이 포함되어 있다면 완료하기 전에 스킬로 저장하는 것을 고려해보세요.",
178+
179+
MsgInvalidRole: "잘못된 역할: 허용되는 값은 owner, admin, operator, member, viewer입니다",
180+
181+
MsgContactIDsRequired: "contact_ids가 필요합니다",
182+
MsgMergeTargetRequired: "tenant_user_id 또는 create_user 중 정확히 하나가 필요합니다",
183+
MsgTenantUserNotFound: "테넌트 사용자를 찾을 수 없습니다",
184+
MsgTenantMismatch: "테넌트 사용자가 이 테넌트에 속하지 않습니다",
185+
MsgTenantScopeRequired: "이 작업에는 테넌트 범위가 필요합니다",
186+
})
187+
}

internal/i18n/i18n.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const (
1111
LocaleEN = "en"
1212
LocaleVI = "vi"
1313
LocaleZH = "zh"
14+
LocaleKO = "ko"
1415

1516
DefaultLocale = LocaleEN
1617
)
@@ -58,7 +59,7 @@ func lookup(locale, key string) string {
5859
// IsSupported returns true if the locale is a known language.
5960
func IsSupported(locale string) bool {
6061
switch locale {
61-
case LocaleEN, LocaleVI, LocaleZH:
62+
case LocaleEN, LocaleVI, LocaleZH, LocaleKO:
6263
return true
6364
}
6465
return false

internal/webui/handler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ func (h *spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
4646
// Static assets: set long cache for /assets/* (Vite hashed filenames).
4747
if strings.HasPrefix(r.URL.Path, "/assets/") {
4848
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
49+
} else {
50+
// index.html and other root files must never be cached so the
51+
// browser always gets the latest asset manifest after a deploy.
52+
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
4953
}
5054
h.fileServer.ServeHTTP(w, r)
5155
return

ui/web/src/i18n/index.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,46 @@ import viHooks from "./locales/vi/hooks.json";
8787
import viWebhooks from "./locales/vi/webhooks.json";
8888
import viWorkstations from "./locales/vi/workstations.json";
8989

90+
// --- KO namespaces ---
91+
import koCommon from "./locales/ko/common.json";
92+
import koSidebar from "./locales/ko/sidebar.json";
93+
import koTopbar from "./locales/ko/topbar.json";
94+
import koLogin from "./locales/ko/login.json";
95+
import koOverview from "./locales/ko/overview.json";
96+
import koChat from "./locales/ko/chat.json";
97+
import koAgents from "./locales/ko/agents.json";
98+
import koTeams from "./locales/ko/teams.json";
99+
import koSessions from "./locales/ko/sessions.json";
100+
import koSkills from "./locales/ko/skills.json";
101+
import koCron from "./locales/ko/cron.json";
102+
import koConfig from "./locales/ko/config.json";
103+
import koChannels from "./locales/ko/channels.json";
104+
import koProviders from "./locales/ko/providers.json";
105+
import koTraces from "./locales/ko/traces.json";
106+
import koEvents from "./locales/ko/events.json";
107+
import koUsage from "./locales/ko/usage.json";
108+
import koApprovals from "./locales/ko/approvals.json";
109+
import koNodes from "./locales/ko/nodes.json";
110+
import koLogs from "./locales/ko/logs.json";
111+
import koTools from "./locales/ko/tools.json";
112+
import koMcp from "./locales/ko/mcp.json";
113+
import koTts from "./locales/ko/tts.json";
114+
import koSetup from "./locales/ko/setup.json";
115+
import koMemory from "./locales/ko/memory.json";
116+
import koVault from "./locales/ko/vault.json";
117+
import koStorage from "./locales/ko/storage.json";
118+
import koPendingMessages from "./locales/ko/pending-messages.json";
119+
import koContacts from "./locales/ko/contacts.json";
120+
import koActivity from "./locales/ko/activity.json";
121+
import koApiKeys from "./locales/ko/api-keys.json";
122+
import koCliCredentials from "./locales/ko/cli-credentials.json";
123+
import koPackages from "./locales/ko/packages.json";
124+
import koTenants from "./locales/ko/tenants.json";
125+
import koSystemSettings from "./locales/ko/system-settings.json";
126+
import koImportExport from "./locales/ko/import-export.json";
127+
import koV3Capabilities from "./locales/ko/v3-capabilities.json";
128+
import koBackup from "./locales/ko/backup.json";
129+
90130
// --- ZH namespaces ---
91131
import zhCommon from "./locales/zh/common.json";
92132
import zhSidebar from "./locales/zh/sidebar.json";
@@ -134,10 +174,11 @@ const STORAGE_KEY = "goclaw:language";
134174

135175
function getInitialLanguage(): string {
136176
const stored = localStorage.getItem(STORAGE_KEY);
137-
if (stored === "en" || stored === "vi" || stored === "zh") return stored;
177+
if (stored === "en" || stored === "vi" || stored === "zh" || stored === "ko") return stored;
138178
const lang = navigator.language.toLowerCase();
139179
if (lang.startsWith("vi")) return "vi";
140180
if (lang.startsWith("zh")) return "zh";
181+
if (lang.startsWith("ko")) return "ko";
141182
return "en";
142183
}
143184

@@ -220,6 +261,24 @@ i18n.use(initReactI18next).init({
220261
webhooks: zhWebhooks,
221262
workstations: zhWorkstations,
222263
},
264+
ko: {
265+
common: koCommon, sidebar: koSidebar, topbar: koTopbar, login: koLogin,
266+
overview: koOverview, chat: koChat, agents: koAgents, teams: koTeams,
267+
sessions: koSessions, skills: koSkills, cron: koCron, config: koConfig,
268+
channels: koChannels, providers: koProviders, traces: koTraces,
269+
events: koEvents, usage: koUsage,
270+
approvals: koApprovals, nodes: koNodes, logs: koLogs, tools: koTools,
271+
mcp: koMcp, tts: koTts, setup: koSetup, memory: koMemory, vault: koVault, storage: koStorage,
272+
"pending-messages": koPendingMessages,
273+
contacts: koContacts, activity: koActivity, "api-keys": koApiKeys,
274+
"cli-credentials": koCliCredentials,
275+
packages: koPackages,
276+
tenants: koTenants,
277+
"system-settings": koSystemSettings,
278+
"import-export": koImportExport,
279+
"v3-capabilities": koV3Capabilities,
280+
backup: koBackup,
281+
},
223282
},
224283
ns: [...ns],
225284
defaultNS: "common",

ui/web/src/i18n/locales/en/topbar.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"languages": {
3030
"en": "English",
3131
"vi": "Tiếng Việt",
32-
"zh": "中文"
32+
"zh": "中文",
33+
"ko": "한국어"
3334
}
3435
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"title": "활동 로그",
3+
"description": "관리자 및 설정 변경의 감사 추적.",
4+
"filters": {
5+
"action": "작업",
6+
"allActions": "모든 작업",
7+
"agentCreated": "에이전트 생성됨",
8+
"agentUpdated": "에이전트 업데이트됨",
9+
"agentDeleted": "에이전트 삭제됨",
10+
"entityType": "엔티티 유형",
11+
"allEntities": "모든 엔티티",
12+
"agent": "에이전트"
13+
},
14+
"table": {
15+
"action": "작업",
16+
"actor": "실행자",
17+
"entity": "엔티티",
18+
"entityId": "엔티티 ID",
19+
"ip": "IP 주소",
20+
"time": "시간"
21+
},
22+
"empty": {
23+
"title": "활동 로그 없음",
24+
"description": "관리자 작업이 수행되면 여기에 활동 로그가 나타납니다."
25+
}
26+
}

0 commit comments

Comments
 (0)