mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
feat(i18n): add Korean (#1126)
Adds Korean as a UI language: Settings › Appearance & Behavior › System Settings › Language and Region lists 한국어, with Korean catalogs for message keys and source text. Only Chinese must translate every UI text. Other locales may lack entries and show the English text for them; their catalogs are still checked for stale entries, keys English lacks and placeholders that differ from the English text. Co-authored-by: fhrmrk <81476595+fhrmrk@users.noreply.github.com>
This commit is contained in:
@@ -39,6 +39,7 @@ Visual style: dark, low-contrast greys, one cool accent
|
||||
icons from `src/ui/icons.tsx`, never emoji. Real `<button>`/`<input>`; `aria-label` on icon buttons.
|
||||
- User-visible text goes through the text catalog: add every new string to both
|
||||
`src/platform/locales/text/en.json` and `zh.json` (`textCatalog.test.ts` enforces it).
|
||||
Other locales may lack entries; their UI shows the English text until a translator adds it.
|
||||
- Keep comments sparse and purposeful; match surrounding style.
|
||||
- For GUI verification, prefer Tauri MCP; use Computer Use only when Tauri MCP is
|
||||
unavailable or cannot cover the required checks.
|
||||
|
||||
@@ -291,6 +291,7 @@ describe("validateSettings", () => {
|
||||
it("keeps valid values, including dark-hc", () => {
|
||||
const v = validateSettings({ appearance: { theme: "dark-hc", accent: "#C9A080", locale: "zh" } });
|
||||
expect(v.appearance).toMatchObject({ theme: "dark-hc", accent: "#c9a080", locale: "zh" });
|
||||
expect(validateSettings({ appearance: { locale: "ko" } }).appearance.locale).toBe("ko");
|
||||
});
|
||||
|
||||
it("reads and writes dotted keys immutably", () => {
|
||||
|
||||
@@ -35,7 +35,7 @@ import { isThemeChoiceId, type ThemeChoiceId } from "@/platform/themes";
|
||||
|
||||
/** A built-in theme, a plugin's (`plugin:<id>/<theme>`) or the legacy `"system"`. */
|
||||
export type ThemeId = ThemeChoiceId | "system";
|
||||
export type LocaleId = "en" | "zh";
|
||||
export type LocaleId = "en" | "zh" | "ko";
|
||||
|
||||
/** A user keymap (Keymap › Duplicate…): its own overrides on top of a bundled preset. */
|
||||
export interface CustomKeymapSettings {
|
||||
@@ -175,7 +175,7 @@ export const LIMITS = {
|
||||
} as const;
|
||||
|
||||
const theme = (v: unknown, d: ThemeId): ThemeId => (v === "system" || isThemeChoiceId(v) ? v : d);
|
||||
const LOCALES: readonly LocaleId[] = ["en", "zh"];
|
||||
const LOCALES: readonly LocaleId[] = ["en", "zh", "ko"];
|
||||
export const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
||||
|
||||
type Obj = Record<string, unknown>;
|
||||
|
||||
@@ -717,7 +717,7 @@ export const BEHAVIOR_PAGES: PageSpec[] = [
|
||||
id: "general.language",
|
||||
title: "Language and Region",
|
||||
order: 0,
|
||||
keywords: ["locale", "chinese", "中文", "translation"],
|
||||
keywords: ["locale", "chinese", "中文", "korean", "한국어", "translation"],
|
||||
sections: [
|
||||
{
|
||||
options: [
|
||||
@@ -729,6 +729,7 @@ export const BEHAVIOR_PAGES: PageSpec[] = [
|
||||
options: [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文 (Chinese, Simplified)" },
|
||||
{ value: "ko", label: "한국어 (Korean)" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -12,7 +12,10 @@ import { isTauri, onIdeEvent } from "@/platform/ipc";
|
||||
import { applyKeymapSettings } from "@/platform/keymap";
|
||||
import { loadSettings, useProject } from "@/platform/project";
|
||||
import { applyAppearanceOptions, installAltDragGuard, installIdleSave } from "./appearanceRuntime";
|
||||
import { validateSettings } from "./model/schema";
|
||||
import { type LocaleId, validateSettings } from "./model/schema";
|
||||
|
||||
/** `<html lang>` of each UI locale. */
|
||||
const HTML_LANG: Record<LocaleId, string> = { en: "en", zh: "zh-CN", ko: "ko" };
|
||||
|
||||
export function applyRuntimeSettings(raw: unknown, root: HTMLElement = document.documentElement): void {
|
||||
const s = validateSettings(raw);
|
||||
@@ -20,7 +23,7 @@ export function applyRuntimeSettings(raw: unknown, root: HTMLElement = document.
|
||||
|
||||
applyAppearanceOptions(s, root);
|
||||
|
||||
root.lang = s.appearance.locale === "zh" ? "zh-CN" : "en";
|
||||
root.lang = HTML_LANG[s.appearance.locale];
|
||||
}
|
||||
|
||||
/** Subscribes to settings changes; returns the unsubscribe function. */
|
||||
|
||||
+25
-12
@@ -13,6 +13,8 @@ import {
|
||||
useI18n,
|
||||
} from "./i18n";
|
||||
import en from "./locales/en.json";
|
||||
import ko from "./locales/ko.json";
|
||||
import koText from "./locales/text/ko.json";
|
||||
import zh from "./locales/zh.json";
|
||||
import zhText from "./locales/text/zh.json";
|
||||
|
||||
@@ -70,6 +72,7 @@ describe("t / locales", () => {
|
||||
it("resolves locale tags", () => {
|
||||
expect(resolveLocale("zh-CN")).toBe("zh");
|
||||
expect(resolveLocale("zh_Hans")).toBe("zh");
|
||||
expect(resolveLocale("ko-KR")).toBe("ko");
|
||||
expect(resolveLocale("EN-us")).toBe("en");
|
||||
expect(resolveLocale("fr")).toBe("en");
|
||||
expect(resolveLocale(undefined)).toBe(resolveLocale(navigator.language));
|
||||
@@ -93,6 +96,13 @@ describe("t / locales", () => {
|
||||
expect(t("app.keymap.count", { count: 1 })).toBe("1 个快捷键");
|
||||
});
|
||||
|
||||
it("switches to Korean", async () => {
|
||||
await setLocale("ko-KR");
|
||||
expect(useI18n.getState().locale).toBe("ko");
|
||||
expect(t("common.cancel")).toBe("취소");
|
||||
expect(tx("Cancel")).toBe("취소");
|
||||
});
|
||||
|
||||
it("falls back to English for unsupported locales", async () => {
|
||||
await setLocale("de");
|
||||
expect(useI18n.getState().locale).toBe("en");
|
||||
@@ -162,36 +172,39 @@ describe("t / locales", () => {
|
||||
});
|
||||
|
||||
describe("catalogs", () => {
|
||||
const catalogs: Record<string, Record<string, string>> = { en, zh };
|
||||
const catalogs: Record<string, Record<string, string>> = { en, zh, ko };
|
||||
const textCatalogs: Record<string, Record<string, string>> = { zh: zhText, ko: koText };
|
||||
const translations = Object.entries(catalogs).filter(([locale]) => locale !== "en");
|
||||
|
||||
it("translate source text into non-empty, different text", () => {
|
||||
for (const [source, text] of Object.entries(zhText as Record<string, string>)) {
|
||||
it.each(Object.entries(textCatalogs))("translate source text into non-empty, different text (%s)", (_, texts) => {
|
||||
for (const [source, text] of Object.entries(texts)) {
|
||||
expect(text.trim(), source).not.toBe("");
|
||||
expect(text, source).not.toBe(source);
|
||||
}
|
||||
});
|
||||
|
||||
it("keep the placeholders of source-text templates", () => {
|
||||
it.each(Object.entries(textCatalogs))("keep the placeholders of source-text templates (%s)", (_, texts) => {
|
||||
const names = (m: string) => [...m.matchAll(/\{(\w+)\}/g)].map((x) => x[1]).sort();
|
||||
for (const [source, text] of Object.entries(zhText as Record<string, string>))
|
||||
expect(names(text), source).toEqual(names(source));
|
||||
for (const [source, text] of Object.entries(texts)) expect(names(text), source).toEqual(names(source));
|
||||
});
|
||||
|
||||
it("have the same keys", () => {
|
||||
it("have the same keys (zh)", () => {
|
||||
expect(Object.keys(zh).sort()).toEqual(Object.keys(en).sort());
|
||||
});
|
||||
|
||||
it.each(translations)("have no keys English lacks (%s)", (_, catalog) => {
|
||||
expect(Object.keys(catalog).filter((key) => !Object.hasOwn(en, key))).toEqual([]);
|
||||
});
|
||||
|
||||
it("contain only well-formed messages", () => {
|
||||
for (const [locale, catalog] of Object.entries(catalogs))
|
||||
for (const [key, message] of Object.entries(catalog))
|
||||
expect(isValidMessage(message), `${locale}:${key}`).toBe(true);
|
||||
});
|
||||
|
||||
it("use the same parameters across locales", () => {
|
||||
it.each(translations)("use the same parameters across locales (%s)", (_, catalog) => {
|
||||
const params = (m: string) => [...m.matchAll(/\{\s*([a-z]\w*)\s*[,}]/g)].map((x) => x[1]).sort();
|
||||
for (const key of Object.keys(en))
|
||||
expect(params((zh as Record<string, string>)[key] ?? ""), key).toEqual(
|
||||
params((en as Record<string, string>)[key]!),
|
||||
);
|
||||
for (const [key, message] of Object.entries(catalog))
|
||||
expect(params(message), key).toEqual(params((en as Record<string, string>)[key] ?? ""));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ export type MessageParams = Record<string, string | number | boolean | null | un
|
||||
export type Translate = (key: string, params?: MessageParams) => string;
|
||||
|
||||
export const DEFAULT_LOCALE = "en";
|
||||
export const SUPPORTED_LOCALES = ["en", "zh"] as const;
|
||||
export const SUPPORTED_LOCALES = ["en", "zh", "ko"] as const;
|
||||
export type Locale = (typeof SUPPORTED_LOCALES)[number];
|
||||
|
||||
const loaders = import.meta.glob<Catalog>(["./locales/*.json", "!./locales/en.json"], { import: "default" });
|
||||
@@ -54,7 +54,7 @@ interface I18nState {
|
||||
|
||||
export const useI18n = create<I18nState>(() => ({ locale: DEFAULT_LOCALE, messages: en as Catalog, texts: {} }));
|
||||
|
||||
/** Maps settings values / BCP 47 tags to a supported locale (`zh-CN` → `zh`, `system` → browser). */
|
||||
/** Maps settings values / BCP 47 tags to a supported locale (`zh-CN` → `zh`, `ko-KR` → `ko`, `system` → browser). */
|
||||
export function resolveLocale(input: string | null | undefined): Locale {
|
||||
let tag = (input ?? "").trim();
|
||||
if (!tag || tag === "system" || tag === "auto")
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"app.about.build": "빌드",
|
||||
"app.about.configDir": "구성",
|
||||
"app.about.copyInfo": "정보 복사",
|
||||
"app.about.copyright": "Copyright © 2026 OpenIDEA 기여자",
|
||||
"app.about.disclaimer": "JetBrains와 관련이 없습니다.",
|
||||
"app.about.license": "Apache License 2.0에 따라 라이선스가 부여됩니다",
|
||||
"app.about.platform": "플랫폼",
|
||||
"app.about.runtime": "런타임",
|
||||
"app.about.showConfig": "구성 폴더 열기",
|
||||
"app.about.tagline": "IntelliJ IDEA에서 영감을 받은 오픈 소스 IDE",
|
||||
"app.about.thirdParty": "IntelliJ Platform의 아이콘(Apache-2.0)이 포함되어 있습니다.",
|
||||
"app.about.title": "OpenIDEA 정보",
|
||||
"app.about.version": "버전",
|
||||
"app.action.about": "OpenIDEA 정보",
|
||||
"app.action.checkForUpdates": "업데이트 확인…",
|
||||
"app.action.copy": "복사",
|
||||
"app.action.cut": "잘라내기",
|
||||
"app.action.keymapReference": "키맵 참조",
|
||||
"app.action.lightEditOpenProject": "포함 폴더를 프로젝트로 열기",
|
||||
"app.action.newWindow": "새 창",
|
||||
"app.action.nextWindow": "다음 프로젝트 창",
|
||||
"app.action.openFile": "파일 열기…",
|
||||
"app.action.openInNewWindow": "새 창에서 열기…",
|
||||
"app.action.openRecentProject": "최근 프로젝트 열기",
|
||||
"app.action.paste": "붙여넣기",
|
||||
"app.action.previousWindow": "이전 프로젝트 창",
|
||||
"app.action.quit": "{os, select, macos {OpenIDEA 종료} other {종료}}",
|
||||
"app.action.redo": "다시 실행",
|
||||
"app.action.selectAll": "모두 선택",
|
||||
"app.action.showLog": "{os, select, macos {Finder에서 로그 표시} windows {탐색기에서 로그 표시} other {파일에서 로그 표시}}",
|
||||
"app.action.toggleFullScreen": "전체 화면 전환",
|
||||
"app.action.undo": "실행 취소",
|
||||
"app.category.edit": "편집",
|
||||
"app.category.file": "파일",
|
||||
"app.category.help": "도움말",
|
||||
"app.category.view": "보기",
|
||||
"app.category.window": "창",
|
||||
"app.exit.body": "OpenIDEA를 종료하시겠습니까?",
|
||||
"app.exit.confirm": "종료",
|
||||
"app.exit.dontAsk": "다시 묻지 않음",
|
||||
"app.exit.title": "종료 확인",
|
||||
"app.keymap.count": "{count, plural, other {단축키 #개}}",
|
||||
"app.keymap.empty": "일치하는 단축키가 없습니다",
|
||||
"app.keymap.filter": "액션 또는 단축키로 필터링",
|
||||
"app.keymap.other": "기타",
|
||||
"app.keymap.preset": "키맵: {preset}",
|
||||
"app.keymap.title": "키맵 참조",
|
||||
"app.lightEdit.openProject": "프로젝트로 열기",
|
||||
"app.lightEdit.openProjectTooltip": "{folder}을(를) 프로젝트로 열기",
|
||||
"app.log.failed": "로그를 표시할 수 없습니다",
|
||||
"app.project.opening": "{name} 여는 중…",
|
||||
"app.title.default": "OpenIDEA",
|
||||
"app.unsaved.body": "{project}에 저장되지 않은 변경 사항이 있는 {count, plural, other {파일이 #개}} 있습니다. 닫기 전에 저장하시겠습니까?",
|
||||
"app.unsaved.discard": "저장 안 함",
|
||||
"app.unsaved.save": "모두 저장",
|
||||
"app.unsaved.saveFailed": "모든 파일을 저장할 수 없습니다",
|
||||
"app.unsaved.title": "저장되지 않은 변경 사항",
|
||||
"app.update.available": "OpenIDEA {version}을(를) 사용할 수 있습니다",
|
||||
"app.update.cancel": "취소",
|
||||
"app.update.cancelled": "업데이트가 취소되었습니다",
|
||||
"app.update.checking": "업데이트 확인 중…",
|
||||
"app.update.disabled": "자동 업데이트가 구성되지 않았습니다",
|
||||
"app.update.disabledBody": "업데이트 확인을 활성화하려면 업데이트 엔드포인트(OPENIDEA_UPDATE_ENDPOINT 또는 plugins.updater.endpoints)를 설정하세요.",
|
||||
"app.update.downloading": "OpenIDEA {version} 다운로드 중",
|
||||
"app.update.failed": "업데이트 확인에 실패했습니다",
|
||||
"app.update.install": "설치 후 다시 시작",
|
||||
"app.update.installFailed": "업데이트를 설치할 수 없습니다",
|
||||
"app.update.installed": "OpenIDEA {version}이(가) 설치되었습니다",
|
||||
"app.update.installedBody": "OpenIDEA를 다시 시작하면 적용됩니다.",
|
||||
"app.update.installingMessage": "설치 중…",
|
||||
"app.update.installUncancellable": "업데이트를 설치하는 중입니다. 이 단계는 취소할 수 없습니다",
|
||||
"app.update.none": "OpenIDEA {version}은(는) 최신 버전입니다",
|
||||
"app.update.progress": "{done}MB / {total}MB",
|
||||
"app.update.progressUnknown": "{done}MB",
|
||||
"app.update.restart": "다시 시작",
|
||||
"app.update.retry": "다시 시도",
|
||||
"app.window.desktopOnly": "여러 창을 사용하려면 데스크톱 앱이 필요합니다",
|
||||
"app.window.openFailed": "{name}을(를) 열 수 없습니다",
|
||||
"app.window.pickFileTitle": "파일 열기",
|
||||
"app.window.pickTitle": "새 창에서 열기",
|
||||
"app.window.askTitle": "프로젝트 열기",
|
||||
"app.window.askBody": "프로젝트를 새 창에서 열거나, 현재 프로젝트를 대체하거나, 이미 열려 있는 프로젝트에 연결할 수 있습니다.\n\"{name}\"을(를) 어떻게 여시겠습니까?",
|
||||
"app.window.askThis": "현재 창",
|
||||
"app.window.askAttach": "연결",
|
||||
"app.window.askNew": "새 창",
|
||||
"app.window.askRemember": "다시 묻지 않음",
|
||||
"common.cancel": "취소",
|
||||
"common.close": "닫기",
|
||||
"common.copied": "복사됨",
|
||||
"common.copy": "복사",
|
||||
"menu.app": "OpenIDEA",
|
||||
"menu.app.about": "OpenIDEA 정보",
|
||||
"menu.app.checkForUpdates": "업데이트 확인…",
|
||||
"menu.app.quit": "{os, select, macos {OpenIDEA 종료} other {종료}}",
|
||||
"menu.appearance": "모양",
|
||||
"menu.code": "코드",
|
||||
"menu.code.commentBlock": "블록 주석으로 주석 처리",
|
||||
"menu.code.commentLine": "줄 주석으로 주석 처리",
|
||||
"menu.code.completion": "기본",
|
||||
"menu.code.deleteLine": "줄 삭제",
|
||||
"menu.code.duplicateLine": "줄 또는 선택 영역 복제",
|
||||
"menu.code.extendSelection": "선택 영역 확장",
|
||||
"menu.code.foldAll": "모두 접기",
|
||||
"menu.code.foldToggle": "펼치기/접기",
|
||||
"menu.code.generate": "생성…",
|
||||
"menu.code.pasteAsPlainText": "일반 텍스트로 붙여넣기",
|
||||
"menu.editor.copyReference": "참조 복사",
|
||||
"menu.code.cyclicExpandWord": "단어 순환 확장",
|
||||
"menu.code.cyclicExpandWordBackward": "단어 순환 확장(역방향)",
|
||||
"menu.code.expressionTypeInfo": "표현식 타입 정보",
|
||||
"menu.code.overrideMethods": "메서드 재정의…",
|
||||
"menu.code.implementMethods": "메서드 구현…",
|
||||
"menu.code.insertLiveTemplate": "라이브 템플릿 삽입…",
|
||||
"menu.code.moveLineDown": "줄 아래로 이동",
|
||||
"menu.code.moveLineUp": "줄 위로 이동",
|
||||
"menu.code.optimizeImports": "import 최적화",
|
||||
"menu.code.parameterInfo": "매개변수 정보",
|
||||
"menu.code.quickDocumentation": "빠른 문서",
|
||||
"menu.code.reformat": "코드 서식 다시 지정",
|
||||
"menu.code.showIntentions": "컨텍스트 액션 표시",
|
||||
"menu.code.shrinkSelection": "선택 영역 축소",
|
||||
"menu.code.surroundWith": "다음으로 감싸기…",
|
||||
"menu.code.unwrap": "감싸기 해제/제거…",
|
||||
"menu.code.unfoldAll": "모두 펼치기",
|
||||
"menu.debug.evaluate": "표현식 평가…",
|
||||
"menu.debug.pause": "프로그램 일시 중지",
|
||||
"menu.debug.resume": "프로그램 재개",
|
||||
"menu.debug.runToCursor": "커서까지 실행",
|
||||
"menu.debug.stepInto": "스텝 인",
|
||||
"menu.debug.stepOut": "스텝 아웃",
|
||||
"menu.debug.stepOver": "스텝 오버",
|
||||
"menu.debug.toggleBreakpoint": "줄 중단점 전환",
|
||||
"menu.debug.viewBreakpoints": "중단점 보기…",
|
||||
"menu.debug.attachToProcess": "프로세스에 연결…",
|
||||
"menu.debug.forceStepOver": "강제 스텝 오버",
|
||||
"menu.debug.forceStepInto": "강제 스텝 인",
|
||||
"menu.debug.smartStepInto": "스마트 스텝 인",
|
||||
"menu.debug.forceRunToCursor": "커서까지 강제 실행",
|
||||
"menu.debug.showExecutionPoint": "실행 지점 표시",
|
||||
"menu.debug.resetFrame": "프레임 리셋",
|
||||
"menu.debug.toggleTemporaryBreakpoint": "임시 줄 중단점 전환",
|
||||
"menu.debug.toggleMethodBreakpoint": "메서드 중단점 전환",
|
||||
"menu.debug.toggleFieldWatchpoint": "필드 감시점 전환",
|
||||
"menu.edit": "편집",
|
||||
"menu.edit.copy": "복사",
|
||||
"menu.edit.cut": "잘라내기",
|
||||
"menu.edit.paste": "붙여넣기",
|
||||
"menu.code.pasteFromHistory": "기록에서 붙여넣기…",
|
||||
"menu.edit.redo": "다시 실행",
|
||||
"menu.edit.selectAll": "모두 선택",
|
||||
"menu.edit.undo": "실행 취소",
|
||||
"menu.editor.copyPath": "경로 복사",
|
||||
"menu.code.sortLines": "줄 정렬",
|
||||
"menu.code.reverseLines": "줄 순서 반전",
|
||||
"menu.editor.toggleColumnSelection": "열 선택 모드",
|
||||
"menu.editor.splitDown": "아래로 분할",
|
||||
"menu.editor.splitRight": "오른쪽으로 분할",
|
||||
"menu.editor.toggleZen": "{distractionFree, select, on {집중 모드 종료} other {집중 모드 시작}}",
|
||||
"menu.editor.unsplit": "분할 해제",
|
||||
"menu.file": "파일",
|
||||
"menu.fileProperties": "파일 속성",
|
||||
"menu.newProjectsSetup": "새 프로젝트 설정",
|
||||
"menu.file.newProjectsSettings": "새 프로젝트용 설정…",
|
||||
"menu.file.toggleReadOnly": "파일을 읽기 전용/쓰기 가능으로 설정",
|
||||
"menu.file.closeProject": "프로젝트 닫기",
|
||||
"menu.remote.reloadEnvironment": "환경 다시 로드",
|
||||
"menu.file.new": "새로 만들기…",
|
||||
"menu.file.newScratchFile": "새 스크래치 파일…",
|
||||
"menu.file.openInNewWindow": "새 창에서 열기…",
|
||||
"menu.file.openProject": "열기…",
|
||||
"menu.file.save": "모두 저장",
|
||||
"menu.file.settings": "설정…",
|
||||
"menu.find": "찾기",
|
||||
"menu.find.inFile": "찾기…",
|
||||
"menu.find.inPath": "파일에서 찾기…",
|
||||
"menu.find.replaceInFile": "바꾸기…",
|
||||
"menu.find.replaceInPath": "파일에서 바꾸기…",
|
||||
"menu.find.showUsages": "사용 위치 표시",
|
||||
"menu.find.usages": "사용 위치 찾기",
|
||||
"menu.completion": "코드 완성",
|
||||
"menu.folding": "접기",
|
||||
"menu.code.foldSelection": "선택 영역 접기/영역 제거",
|
||||
"menu.git": "Git",
|
||||
"menu.help": "도움말",
|
||||
"menu.mainMenu": "메인 메뉴",
|
||||
"window.caption.minimize": "최소화",
|
||||
"window.caption.maximize": "최대화",
|
||||
"window.caption.restore": "이전 크기로 복원",
|
||||
"window.caption.close": "닫기",
|
||||
"menu.help.keymapReference": "키맵 참조",
|
||||
"menu.help.showLog": "{os, select, macos {Finder에서 로그 표시} windows {탐색기에서 로그 표시} other {파일에서 로그 표시}}",
|
||||
"menu.native.bringAllToFront": "모두 앞으로 가져오기",
|
||||
"menu.native.copy": "복사",
|
||||
"menu.native.cut": "잘라내기",
|
||||
"menu.native.hide": "OpenIDEA 숨기기",
|
||||
"menu.native.hideOthers": "기타 숨기기",
|
||||
"menu.native.minimize": "최소화",
|
||||
"menu.native.paste": "붙여넣기",
|
||||
"menu.native.redo": "다시 실행",
|
||||
"menu.native.selectAll": "모두 선택",
|
||||
"menu.native.services": "서비스",
|
||||
"menu.native.showAll": "모두 표시",
|
||||
"menu.native.undo": "실행 취소",
|
||||
"menu.native.zoom": "확대/축소",
|
||||
"menu.navigate": "탐색",
|
||||
"menu.navigate.back": "뒤로",
|
||||
"menu.navigate.lastEditLocation": "마지막 편집 위치",
|
||||
"menu.navigate.nextEditLocation": "다음 편집 위치",
|
||||
"menu.navigate.callHierarchy": "호출 계층 구조",
|
||||
"menu.navigate.declaration": "선언 또는 사용 위치",
|
||||
"menu.navigate.fileStructure": "파일 구조",
|
||||
"menu.navigate.findAction": "액션 찾기…",
|
||||
"menu.navigate.forward": "앞으로",
|
||||
"menu.navigate.gotoClass": "클래스…",
|
||||
"menu.navigate.gotoFile": "파일…",
|
||||
"menu.navigate.gotoLine": "줄/열…",
|
||||
"menu.navigate.gotoSymbol": "심볼…",
|
||||
"menu.navigate.implementation": "구현",
|
||||
"menu.navigate.nextError": "다음 강조 표시된 오류",
|
||||
"menu.navigate.prevError": "이전 강조 표시된 오류",
|
||||
"menu.navigate.recentFiles": "최근 파일",
|
||||
"menu.navigate.recentLocations": "최근 위치",
|
||||
"menu.navigate.searchEverywhere": "어디서나 검색",
|
||||
"menu.navigate.selectIn": "다음에서 선택…",
|
||||
"menu.navigate.showBookmarks": "북마크 표시",
|
||||
"menu.navigate.toggleBookmark": "북마크 전환",
|
||||
"menu.navigate.typeDeclaration": "타입 선언",
|
||||
"menu.navigate.typeHierarchy": "타입 계층 구조",
|
||||
"menu.navigate.superMethod": "슈퍼 메서드",
|
||||
"menu.navigate.gotoTest": "테스트",
|
||||
"menu.navigate.nextMethod": "다음 메서드",
|
||||
"menu.navigate.previousMethod": "이전 메서드",
|
||||
"menu.openRecent": "최근 항목 열기",
|
||||
"menu.recent.none": "최근 프로젝트 없음",
|
||||
"menu.refactor": "리팩터링",
|
||||
"menu.refactor.changeSignature": "시그니처 변경…",
|
||||
"menu.refactor.extractConstant": "상수 도입…",
|
||||
"menu.refactor.extractField": "필드 도입…",
|
||||
"menu.refactor.extractMethod": "메서드 추출…",
|
||||
"menu.refactor.extractParameter": "매개변수 도입…",
|
||||
"menu.refactor.extractVariable": "변수 도입…",
|
||||
"menu.refactor.inline": "인라인…",
|
||||
"menu.refactor.move": "이동…",
|
||||
"menu.refactor.rename": "이름 변경…",
|
||||
"menu.refactor.safeDelete": "안전 삭제…",
|
||||
"menu.refactor.thisMenu": "이 항목 리팩터링…",
|
||||
"menu.run": "실행",
|
||||
"menu.run.chooseConfig": "실행…",
|
||||
"menu.run.debug": "디버그",
|
||||
"menu.run.debugContext": "컨텍스트 구성 디버그",
|
||||
"menu.run.coverage": "커버리지와 함께 실행",
|
||||
"menu.run.profile": "프로파일",
|
||||
"menu.coverage.hide": "커버리지 숨기기",
|
||||
"menu.debuggingActions": "디버깅 액션",
|
||||
"menu.debug.reloadChangedClasses": "변경된 클래스 다시 로드",
|
||||
"menu.toolwindow.coverage": "커버리지",
|
||||
"menu.toolwindow.profiler": "프로파일러",
|
||||
"menu.run.editConfigs": "구성 편집…",
|
||||
"menu.run.run": "실행",
|
||||
"menu.run.runContext": "컨텍스트 구성 실행",
|
||||
"menu.run.stop": "중지",
|
||||
"menu.toolWindows": "도구 창",
|
||||
"menu.toolwindow.bookmarks": "북마크",
|
||||
"menu.toolwindow.commit": "커밋",
|
||||
"menu.toolwindow.debug": "디버그",
|
||||
"menu.toolwindow.find": "찾기",
|
||||
"menu.toolwindow.git": "Git",
|
||||
"menu.toolwindow.hideAll": "모든 도구 창 숨기기",
|
||||
"menu.toolwindow.problems": "문제",
|
||||
"menu.toolwindow.project": "프로젝트",
|
||||
"menu.toolwindow.run": "실행",
|
||||
"menu.toolwindow.structure": "구조",
|
||||
"menu.toolwindow.terminal": "터미널",
|
||||
"terminal.close.title": "'{name}' 프로세스가 실행 중입니다",
|
||||
"terminal.close.message": "'{name}' 프로세스를 종료하시겠습니까?",
|
||||
"terminal.close.terminate": "종료",
|
||||
"menu.vcs.annotate": "Git Blame으로 주석 달기",
|
||||
"menu.vcs.branches": "브랜치…",
|
||||
"menu.vcs.commit": "커밋…",
|
||||
"menu.vcs.fetch": "페치",
|
||||
"menu.vcs.localHistory": "로컬 기록",
|
||||
"menu.vcs.newBranch": "새 브랜치…",
|
||||
"menu.vcs.push": "푸시…",
|
||||
"menu.vcs.rollback": "롤백…",
|
||||
"menu.vcs.showDiff": "차이 표시",
|
||||
"menu.vcs.showLog": "Git 로그 표시",
|
||||
"menu.vcs.stash": "변경 사항 스태시…",
|
||||
"menu.vcs.unstash": "변경 사항 스태시 적용…",
|
||||
"menu.vcs.update": "프로젝트 업데이트…",
|
||||
"menu.vcs.pull": "풀…",
|
||||
"menu.vcs.merge": "병합…",
|
||||
"menu.vcs.rebase": "리베이스…",
|
||||
"menu.vcs.newWorktree": "새 워크트리…",
|
||||
"menu.vcs.worktrees": "워크트리…",
|
||||
"menu.vcs.manageRemotes": "원격 관리…",
|
||||
"menu.vcs.clone": "복제…",
|
||||
"menu.vcs.operations": "VCS 작업 팝업…",
|
||||
"menu.view": "보기",
|
||||
"menu.view.notifications": "알림",
|
||||
"menu.view.toggleTheme": "밝은/어두운 테마 전환",
|
||||
"menu.welcome.recentProjects": "프로젝트 관리…",
|
||||
"menu.window": "창",
|
||||
"menu.tools": "도구",
|
||||
"menu.http.newRequest": "새 HTTP 요청",
|
||||
"menu.http.showToolWindow": "HTTP 클라이언트",
|
||||
"menu.db.showToolWindow": "데이터베이스",
|
||||
"menu.db.newDataSource": "새 데이터 소스…",
|
||||
"menu.db.openConsole": "쿼리 콘솔 열기",
|
||||
"menu.vim.toggle": "Vim",
|
||||
"menu.window.new": "새 창",
|
||||
"menu.window.next": "다음 프로젝트 창",
|
||||
"menu.window.previous": "이전 프로젝트 창",
|
||||
"menu.window.toggleFullScreen": "전체 화면 전환",
|
||||
"os.linux": "Linux",
|
||||
"os.macos": "macOS",
|
||||
"os.web": "브라우저",
|
||||
"os.windows": "Windows",
|
||||
"menu.gitPatch": "패치",
|
||||
"menu.vcs.newTag": "새 태그…",
|
||||
"menu.vcs.resetHead": "HEAD 리셋…",
|
||||
"menu.vcs.shelve": "변경 사항 셸브…",
|
||||
"menu.vcs.showShelf": "셸프 표시",
|
||||
"menu.vcs.createPatch": "패치 생성…",
|
||||
"menu.vcs.applyPatch": "패치 적용…",
|
||||
"menu.vcs.applyPatchFromClipboard": "클립보드에서 패치 적용…",
|
||||
"menu.vcs.resolveConflicts": "충돌 해결…",
|
||||
"menu.vcs.rebase.continue": "리베이스 계속",
|
||||
"menu.vcs.rebase.abort": "리베이스 중단",
|
||||
"menu.vcs.rebase.skip": "커밋 건너뛰기",
|
||||
"menu.vcs.merge.abort": "병합 중단",
|
||||
"menu.vcs.cherryPick.continue": "체리픽 계속",
|
||||
"menu.vcs.cherryPick.abort": "체리픽 중단",
|
||||
"menu.vcs.revert.continue": "되돌리기 계속",
|
||||
"menu.vcs.revert.abort": "되돌리기 중단",
|
||||
"menu.gitUncommittedChanges": "커밋되지 않은 변경 사항",
|
||||
"menu.gitCurrentFile": "현재 파일",
|
||||
"menu.vcs.addToVcs": "VCS에 추가",
|
||||
"menu.vcs.compareWithBranch": "브랜치 또는 리비전과 비교…",
|
||||
"menu.vcs.showHistory": "기록 표시",
|
||||
"menu.build": "빌드",
|
||||
"menu.build.buildProject": "프로젝트 빌드",
|
||||
"menu.build.buildModule": "모듈 빌드",
|
||||
"menu.build.rebuildProject": "프로젝트 다시 빌드",
|
||||
"menu.build.buildArtifacts": "아티팩트 빌드",
|
||||
"menu.toolwindow.build": "빌드",
|
||||
"menu.find.structural": "구조적으로 검색…",
|
||||
"menu.find.replaceStructural": "구조적으로 바꾸기…",
|
||||
"menu.analyze.inspectCode": "코드 검사…",
|
||||
"menu.analyzeCode": "코드 분석",
|
||||
"menu.analyze.locateDuplicates": "중복 찾기…",
|
||||
"menu.analyze.stackTrace": "스택 추적 또는 스레드 덤프 분석…",
|
||||
"menu.toolwindow.services": "서비스",
|
||||
"menu.gitHub": "GitHub",
|
||||
"menu.github.pullRequests": "풀 리퀘스트 보기",
|
||||
"menu.github.createPullRequest": "풀 리퀘스트 생성…",
|
||||
"menu.github.checkoutPullRequest": "풀 리퀘스트 체크아웃…",
|
||||
"menu.github.openInBrowser": "GitHub에서 열기",
|
||||
"menu.file.newProject": "새 프로젝트…",
|
||||
"menu.file.renameProject": "프로젝트 이름 변경…",
|
||||
"menu.file.reloadAllFromDisk": "디스크에서 모두 다시 로드",
|
||||
"menu.file.invalidateCaches": "캐시 무효화…",
|
||||
"menu.manageIdeSettings": "IDE 설정 관리",
|
||||
"menu.file.exportSettings": "설정 내보내기…",
|
||||
"menu.file.importSettings": "설정 가져오기…",
|
||||
"menu.file.togglePowerSave": "절전 모드",
|
||||
"menu.macros": "매크로",
|
||||
"menu.macro.startRecording": "매크로 기록 시작",
|
||||
"menu.macro.stopRecording": "매크로 기록 중지",
|
||||
"menu.macro.playBack": "마지막 매크로 재생",
|
||||
"menu.macro.playSaved": "저장된 매크로 재생…",
|
||||
"menu.macro.edit": "매크로 편집…",
|
||||
"menu.bookmarks": "북마크",
|
||||
"menu.navigate.toggleBookmarkWithMnemonic": "니모닉으로 북마크 전환",
|
||||
"menu.navigate.nextBookmark": "다음 북마크",
|
||||
"menu.navigate.prevBookmark": "이전 북마크",
|
||||
"menu.view.toggleCompactMode": "컴팩트 모드",
|
||||
"menu.view.zoomIn": "IDE 확대",
|
||||
"menu.view.zoomOut": "IDE 축소",
|
||||
"menu.view.resetZoom": "IDE 확대/축소 초기화",
|
||||
"menu.toolbar.customize": "메인 도구 모음 사용자 지정…",
|
||||
"menu.code.quickDefinition": "빠른 정의",
|
||||
"menu.code.quickTypeDefinition": "빠른 타입 정의",
|
||||
"menu.localHistory.recentChanges": "최근 변경 사항",
|
||||
"menu.diff.compareWithClipboard": "클립보드와 비교",
|
||||
"menu.view.quickSwitchScheme": "구성표 빠른 전환…",
|
||||
"menu.editor.resetFontSize": "모든 편집기의 글꼴 크기 초기화",
|
||||
"menu.code.cleanup": "코드 정리…",
|
||||
"menu.runAnything.show": "무엇이든 실행…",
|
||||
"menu.localHistory.putLabel": "레이블 지정…",
|
||||
"menu.toolWindowLayouts": "도구 창 레이아웃",
|
||||
"menu.layout.save": "현재 레이아웃을 다른 이름으로 저장…",
|
||||
"menu.layout.restoreDefault": "기본 레이아웃 복원",
|
||||
"menu.layout.rename": "레이아웃 이름 변경…",
|
||||
"menu.layout.delete": "레이아웃 삭제…",
|
||||
"menu.activeToolWindow": "활성 도구 창",
|
||||
"menu.toolwindow.hideActive": "활성 도구 창 숨기기",
|
||||
"menu.toolwindow.maximize": "도구 창 최대화",
|
||||
"menu.toolwindow.closeContent": "활성 탭 닫기",
|
||||
"menu.toolwindow.jumpToLast": "마지막 도구 창으로 이동",
|
||||
"menu.toolwindow.focusEditor": "편집기에 포커스",
|
||||
"menu.editorTabs": "편집기 탭",
|
||||
"menu.editor.nextTab": "다음 탭 선택",
|
||||
"menu.editor.prevTab": "이전 탭 선택",
|
||||
"menu.editor.pinTab": "탭 고정",
|
||||
"menu.editor.closeTab": "탭 닫기",
|
||||
"menu.editor.closeOtherTabs": "다른 탭 닫기",
|
||||
"menu.editor.closeAllTabs": "모든 탭 닫기",
|
||||
"menu.editor.reopenClosedTab": "닫은 탭 다시 열기",
|
||||
"menu.editor.moveToOppositeGroup": "반대쪽 그룹으로 이동",
|
||||
"menu.editor.openInOppositeGroup": "반대쪽 그룹에서 열기",
|
||||
"menu.editor.changeSplitOrientation": "분할 방향 변경",
|
||||
"menu.editor.unsplitAll": "모든 분할 해제",
|
||||
"menu.navigate.showFilePath": "파일 경로",
|
||||
"menu.navigate.customRegion": "사용자 지정 접기…",
|
||||
"menu.code.showErrorDescription": "오류 설명",
|
||||
"menu.code.surroundWithLiveTemplate": "라이브 템플릿으로 감싸기…",
|
||||
"menu.code.autoIndentLines": "줄 자동 들여쓰기",
|
||||
"menu.code.moveStatementUp": "문 위로 이동",
|
||||
"menu.code.moveStatementDown": "문 아래로 이동",
|
||||
"menu.code.moveElementLeft": "요소 왼쪽으로 이동",
|
||||
"menu.code.moveElementRight": "요소 오른쪽으로 이동",
|
||||
"menu.code.collapseBlock": "코드 블록 접기",
|
||||
"menu.analyze.inspectFile": "현재 파일 검사",
|
||||
"menu.project.copyElement": "복사…",
|
||||
"menu.run.chooseDebugConfig": "디버그…",
|
||||
"menu.debug.quickEvaluate": "빠른 표현식 평가",
|
||||
"menu.resizeToolWindow": "크기 조정",
|
||||
"menu.toolwindow.resizeLeft": "왼쪽으로 늘이기",
|
||||
"menu.toolwindow.resizeRight": "오른쪽으로 늘이기",
|
||||
"menu.toolwindow.resizeUp": "위쪽으로 늘이기",
|
||||
"menu.toolwindow.resizeDown": "아래쪽으로 늘이기",
|
||||
"menu.editor.nextSplitter": "다음 분할기로 이동",
|
||||
"menu.editor.prevSplitter": "이전 분할기로 이동",
|
||||
"menu.expandToLevel": "수준까지 펼치기",
|
||||
"menu.expandAllToLevel": "모두 수준까지 펼치기",
|
||||
"menu.code.expandToLevel1": "1",
|
||||
"menu.code.expandAllToLevel1": "1",
|
||||
"menu.code.expandToLevel2": "2",
|
||||
"menu.code.expandAllToLevel2": "2",
|
||||
"menu.code.expandToLevel3": "3",
|
||||
"menu.code.expandAllToLevel3": "3",
|
||||
"menu.code.expandToLevel4": "4",
|
||||
"menu.code.expandAllToLevel4": "4",
|
||||
"menu.code.expandToLevel5": "5",
|
||||
"menu.code.expandAllToLevel5": "5",
|
||||
"menu.editor.contextInfo": "컨텍스트 정보",
|
||||
"run.services.configurationCount": "구성 {count}개",
|
||||
"search.bookmarks.count": "{count, plural, other {북마크 #개}}",
|
||||
"settings.zoom.hint": "{zoomIn} 또는 {zoomOut}(으)로 변경합니다. {reset}(으)로 100%로 설정합니다"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -180,6 +180,7 @@ const UNTRANSLATED = new Set<string>([
|
||||
// Language names, written in their own language
|
||||
"English",
|
||||
"简体中文 (Chinese, Simplified)",
|
||||
"한국어 (Korean)",
|
||||
// Terms, keywords and acronyms kept in English
|
||||
"DDL",
|
||||
"GOPATH",
|
||||
@@ -245,6 +246,9 @@ const UNTRANSLATED = new Set<string>([
|
||||
"text",
|
||||
]);
|
||||
|
||||
/** Locales that must translate every UI text; the others show English for text they lack. */
|
||||
const COMPLETE_LOCALES = new Set(["zh"]);
|
||||
|
||||
/**
|
||||
* Template literals that reach an exact lookup on purpose: the text around the
|
||||
* values is code or a format that reads the same in every locale.
|
||||
@@ -576,7 +580,7 @@ describe.each(Object.keys(catalogs))("source-text catalog %s", (locale) => {
|
||||
const found = ({ text, match }: Use) =>
|
||||
has(text) || (match === "pattern" && matchesTemplate(text)) || (match === "shape" && shapes.has(shape(text)));
|
||||
|
||||
it("translates every literal UI text", () => {
|
||||
it.runIf(COMPLETE_LOCALES.has(locale))("translates every literal UI text", () => {
|
||||
const missing = new Map<string, string>();
|
||||
for (const use of scan.translated)
|
||||
if (!found(use) && needsTranslation(use.text) && !missing.has(use.text)) missing.set(use.text, use.at);
|
||||
|
||||
Reference in New Issue
Block a user