From b8a25135fc23bd2bc82b68b59903a000e5e1bb5d Mon Sep 17 00:00:00 2001 From: kyy Date: Mon, 11 May 2026 11:42:24 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=81=20=ED=94=84=EB=9F=B0=ED=8A=B8?= =?UTF-8?q?=EC=9D=98=20i18n=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20locale=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adminfront/src/lib/i18n.ts | 155 +++------------------------ adminfront/src/locales/en.toml | 66 ------------ adminfront/src/locales/ko.toml | 66 ------------ adminfront/src/locales/template.toml | 66 ------------ devfront/src/lib/i18n.ts | 151 ++------------------------ devfront/src/locales/en.toml | 78 -------------- devfront/src/locales/ko.toml | 78 -------------- devfront/src/locales/template.toml | 78 -------------- orgfront/src/lib/i18n.ts | 150 ++------------------------ orgfront/src/locales/en.toml | 76 ------------- orgfront/src/locales/ko.toml | 76 ------------- orgfront/src/locales/template.toml | 76 ------------- 12 files changed, 32 insertions(+), 1084 deletions(-) diff --git a/adminfront/src/lib/i18n.ts b/adminfront/src/lib/i18n.ts index 53540cb7..d96190f7 100644 --- a/adminfront/src/lib/i18n.ts +++ b/adminfront/src/lib/i18n.ts @@ -1,148 +1,21 @@ -const LOCALE_STORAGE_KEY = "locale"; -const DEFAULT_LOCALE = "ko"; -const SUPPORTED_LOCALES = ["ko", "en"] as const; - -type Locale = (typeof SUPPORTED_LOCALES)[number]; - -type TomlValue = string | TomlObject; - -interface TomlObject { - [key: string]: TomlValue; -} - -function isSupportedLocale(value: string): value is Locale { - return (SUPPORTED_LOCALES as readonly string[]).includes(value); -} - -function parseToml(raw: string): TomlObject { - const lines = raw.split(/\r?\n/); - const root: TomlObject = {}; - let currentPath: string[] = []; - - for (const rawLine of lines) { - const line = rawLine.trim(); - if (!line || line.startsWith("#")) { - continue; - } - - if (line.startsWith("[") && line.endsWith("]")) { - const sectionName = line.slice(1, -1).trim(); - currentPath = sectionName - ? sectionName - .split(".") - .map((part) => part.trim()) - .filter(Boolean) - : []; - continue; - } - - const eqIndex = line.indexOf("="); - if (eqIndex === -1) { - continue; - } - - const key = line.slice(0, eqIndex).trim(); - const valueRaw = line.slice(eqIndex + 1).trim(); - if (!key) { - continue; - } - - let value = valueRaw; - if ( - (value.startsWith('"') && value.endsWith('"')) || - (value.startsWith("'") && value.endsWith("'")) - ) { - value = value.slice(1, -1); - } - - let cursor: TomlObject = root; - for (const section of currentPath) { - if (!cursor[section] || typeof cursor[section] === "string") { - cursor[section] = {}; - } - cursor = cursor[section] as TomlObject; - } - - cursor[key] = value; - } - - return root; -} - -function getValue(target: TomlObject, key: string): string | undefined { - const parts = key.split("."); - let cursor: TomlValue = target; - for (const part of parts) { - if (typeof cursor !== "object" || cursor === null) { - return undefined; - } - cursor = (cursor as TomlObject)[part]; - if (cursor === undefined) { - return undefined; - } - } - return typeof cursor === "string" ? cursor : undefined; -} - -function detectLocale(): Locale { - if (typeof window === "undefined") { - return DEFAULT_LOCALE; - } - - const stored = window.localStorage.getItem(LOCALE_STORAGE_KEY); - if (stored && isSupportedLocale(stored)) { - return stored; - } - - const pathLocale = window.location.pathname.split("/")[1]; - if (pathLocale && isSupportedLocale(pathLocale)) { - return pathLocale; - } - - const browserLang = window.navigator.language.toLowerCase(); - if (browserLang.startsWith("ko")) { - return "ko"; - } - - return DEFAULT_LOCALE; -} +import { createTomlTranslator } from "../../../common/core/i18n"; +// eslint-disable-next-line import/no-unresolved +import commonEnRaw from "../../../common/locales/en.toml?raw"; +// eslint-disable-next-line import/no-unresolved +import commonKoRaw from "../../../common/locales/ko.toml?raw"; // eslint-disable-next-line import/no-unresolved import enRaw from "../locales/en.toml?raw"; // Vite ?raw import는 런타임 상수로 번들됩니다. // eslint-disable-next-line import/no-unresolved import koRaw from "../locales/ko.toml?raw"; -const translations: Record = { - ko: parseToml(koRaw), - en: parseToml(enRaw), -}; - -function formatTemplate( - template: string, - vars?: Record, -): string { - if (!vars) { - return template; - } - return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => { - const value = vars[key]; - if (value === undefined || value === null) { - return match; - } - return String(value); - }); -} - -export function t( - key: string, - fallback?: string, - vars?: Record, -): string { - const locale = detectLocale(); - const value = getValue(translations[locale], key); - if (value && value.length > 0) { - return formatTemplate(value, vars); - } - return formatTemplate(fallback ?? key, vars); -} +export const t = createTomlTranslator( + { + ko: [commonKoRaw, koRaw], + en: [commonEnRaw, enRaw], + }, + { + normalizeEscapedNewlines: true, + }, +); diff --git a/adminfront/src/locales/en.toml b/adminfront/src/locales/en.toml index 22645e1d..7ef622d7 100644 --- a/adminfront/src/locales/en.toml +++ b/adminfront/src/locales/en.toml @@ -327,15 +327,6 @@ no_custom = "No custom fields defined for this tenant." [msg.admin.users.list.registry] count = "Count" -[msg.common] -error = "Error" -loading = "Loading..." -no_description = "No Description." -parsing = "Parsing data..." -requesting = "Requesting..." -saving = "Saving..." -unknown_error = "unknown error" - [msg.dev] logout_confirm = "Are you sure you want to log out?" @@ -1281,63 +1272,6 @@ name = "Name" role = "Role" -[ui.common] -add = "Add" -all = "All" -admin_only = "Admin Only" -assign = "Assign" -back = "Back" -cancel = "Cancel" -change_file = "Change File" -clear_search = "Clear Search" -close = "Close" -collapse = "Collapse" -confirm = "Confirm" -copy = "Copy" -create = "Create" -delete = "Delete" -details = "Details" -edit = "Edit" -export = "Export" -fail = "Fail" -go_home = "Go Home" -view = "View" -hyphen = "-" -manage = "Manage" -na = "N/A" -never = "Never" -next = "Next" -none = "None" -page_of = "Page {{page}} of {{total}}" -prev = "Prev" -previous = "Previous" -qr = "QR" -reset = "Reset" -read_only = "Read Only" -refresh = "Refresh" -remove = "Remove" -resend = "Resend" -retry = "Retry" -save = "Save" -search = "Search" -select = "Select" -select_file = "Select File" -select_placeholder = "Select Placeholder" -show_more = "Show More" -language = "Language" -language_ko = "Korean" -language_en = "English" -success = "Success" -theme_dark = "Dark" -theme_light = "Light" -theme_toggle = "Theme Toggle" -unknown = "Unknown" - -[ui.common.badge] -admin_only = "Admin only" -command_only = "Command only" -system = "System" - [ui.common.role] admin = "Admin" rp_admin = "RP Admin" diff --git a/adminfront/src/locales/ko.toml b/adminfront/src/locales/ko.toml index b3c9ab3c..dcbee70c 100644 --- a/adminfront/src/locales/ko.toml +++ b/adminfront/src/locales/ko.toml @@ -329,15 +329,6 @@ no_custom = "이 테넌트에 정의된 커스텀 필드가 없습니다." [msg.admin.users.list.registry] count = "총 {{count}}명의 사용자가 등록되어 있습니다." -[msg.common] -error = "오류가 발생했습니다." -loading = "로딩 중..." -no_description = "설명이 없습니다." -parsing = "데이터 파싱 중..." -requesting = "요청 중..." -saving = "저장 중..." -unknown_error = "알 수 없는 오류" - [msg.dev] logout_confirm = "로그아웃 하시겠습니까?" @@ -1283,63 +1274,6 @@ name = "이름" role = "역할" -[ui.common] -add = "추가" -all = "전체" -admin_only = "관리자 전용" -assign = "할당" -back = "돌아가기" -cancel = "취소" -change_file = "파일 변경" -clear_search = "검색 초기화" -close = "닫기" -collapse = "접기" -confirm = "확인" -copy = "복사" -create = "생성" -delete = "삭제" -details = "상세정보" -edit = "편집" -export = "내보내기" -fail = "실패" -go_home = "홈으로" -view = "보기" -hyphen = "-" -manage = "관리" -na = "N/A" -never = "Never" -next = "다음" -none = "없음" -page_of = "Page {{page}} of {{total}}" -prev = "이전" -previous = "이전" -qr = "QR" -reset = "초기화" -read_only = "읽기 전용" -refresh = "새로고침" -remove = "제외" -resend = "재발송" -retry = "다시 시도" -save = "저장" -search = "검색" -select = "선택" -select_file = "파일 선택" -select_placeholder = "선택하세요" -show_more = "+ 더보기" -language = "언어" -language_ko = "한국어" -language_en = "English" -success = "성공" -theme_dark = "Dark" -theme_light = "Light" -theme_toggle = "테마 전환" -unknown = "Unknown" - -[ui.common.badge] -admin_only = "Admin only" -command_only = "Command only" -system = "System" - [ui.common.role] admin = "Admin" rp_admin = "RP Admin" diff --git a/adminfront/src/locales/template.toml b/adminfront/src/locales/template.toml index e85b29ba..55394070 100644 --- a/adminfront/src/locales/template.toml +++ b/adminfront/src/locales/template.toml @@ -337,15 +337,6 @@ no_custom = "" [msg.admin.users.list.registry] count = "" -[msg.common] -error = "" -loading = "" -no_description = "" -parsing = "" -requesting = "" -saving = "" -unknown_error = "" - [msg.dev] logout_confirm = "" @@ -1261,63 +1252,6 @@ name = "" role = "" -[ui.common] -add = "" -all = "" -admin_only = "" -assign = "" -back = "" -cancel = "" -change_file = "" -clear_search = "" -close = "" -collapse = "" -confirm = "" -copy = "" -create = "" -delete = "" -details = "" -edit = "" -export = "" -fail = "" -go_home = "" -view = "" -hyphen = "" -manage = "" -na = "" -never = "" -next = "" -none = "" -page_of = "" -prev = "" -previous = "" -qr = "" -reset = "" -read_only = "" -refresh = "" -remove = "" -resend = "" -retry = "" -save = "" -search = "" -select = "" -select_file = "" -select_placeholder = "" -show_more = "" -language = "" -language_ko = "" -language_en = "" -success = "" -theme_dark = "" -theme_light = "" -theme_toggle = "" -unknown = "" - -[ui.common.badge] -admin_only = "" -command_only = "" -system = "" - [ui.common.role] admin = "" rp_admin = "" diff --git a/devfront/src/lib/i18n.ts b/devfront/src/lib/i18n.ts index ee5265e1..91b55a98 100644 --- a/devfront/src/lib/i18n.ts +++ b/devfront/src/lib/i18n.ts @@ -1,149 +1,16 @@ -const LOCALE_STORAGE_KEY = "locale"; -const DEFAULT_LOCALE = "ko"; -const SUPPORTED_LOCALES = ["ko", "en"] as const; - -type Locale = (typeof SUPPORTED_LOCALES)[number]; - -type TomlValue = string | TomlObject; - -interface TomlObject { - [key: string]: TomlValue; -} - -function isSupportedLocale(value: string): value is Locale { - return (SUPPORTED_LOCALES as readonly string[]).includes(value); -} - -function parseToml(raw: string): TomlObject { - const lines = raw.split(/\r?\n/); - const root: TomlObject = {}; - let currentPath: string[] = []; - - for (const rawLine of lines) { - const line = rawLine.trim(); - if (!line || line.startsWith("#")) { - continue; - } - - if (line.startsWith("[") && line.endsWith("]")) { - const sectionName = line.slice(1, -1).trim(); - currentPath = sectionName - ? sectionName - .split(".") - .map((part) => part.trim()) - .filter(Boolean) - : []; - continue; - } - - const eqIndex = line.indexOf("="); - if (eqIndex === -1) { - continue; - } - - const key = line.slice(0, eqIndex).trim(); - const valueRaw = line.slice(eqIndex + 1).trim(); - if (!key) { - continue; - } - - let value = valueRaw; - if ( - (value.startsWith('"') && value.endsWith('"')) || - (value.startsWith("'") && value.endsWith("'")) - ) { - value = value.slice(1, -1); - } - - let cursor: TomlObject = root; - for (const section of currentPath) { - if (!cursor[section] || typeof cursor[section] === "string") { - cursor[section] = {}; - } - cursor = cursor[section] as TomlObject; - } - - cursor[key] = value; - } - - return root; -} - -function getValue(target: TomlObject, key: string): string | undefined { - const parts = key.split("."); - let cursor: TomlValue = target; - for (const part of parts) { - if (typeof cursor !== "object" || cursor === null) { - return undefined; - } - cursor = (cursor as TomlObject)[part]; - if (cursor === undefined) { - return undefined; - } - } - return typeof cursor === "string" ? cursor : undefined; -} - -function detectLocale(): Locale { - if (typeof window === "undefined") { - return DEFAULT_LOCALE; - } - - const stored = window.localStorage.getItem(LOCALE_STORAGE_KEY); - if (stored && isSupportedLocale(stored)) { - return stored; - } - - const pathLocale = window.location.pathname.split("/")[1]; - if (pathLocale && isSupportedLocale(pathLocale)) { - return pathLocale; - } - - const browserLang = window.navigator.language.toLowerCase(); - if (browserLang.startsWith("ko")) { - return "ko"; - } - - return DEFAULT_LOCALE; -} +import { createTomlTranslator } from "../../../common/core/i18n"; +// eslint-disable-next-line import/no-unresolved +import commonEnRaw from "../../../common/locales/en.toml?raw"; +// eslint-disable-next-line import/no-unresolved +import commonKoRaw from "../../../common/locales/ko.toml?raw"; // eslint-disable-next-line import/no-unresolved import enRaw from "../locales/en.toml?raw"; // Vite ?raw import는 런타임 상수로 번들됩니다. // eslint-disable-next-line import/no-unresolved import koRaw from "../locales/ko.toml?raw"; -const translations: Record = { - ko: parseToml(koRaw), - en: parseToml(enRaw), -}; - -function formatTemplate( - template: string, - vars?: Record, -): string { - const normalizedTemplate = template.replace(/\\n/g, "\n"); - if (!vars) { - return normalizedTemplate; - } - return normalizedTemplate.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => { - const value = vars[key]; - if (value === undefined || value === null) { - return match; - } - return String(value); - }); -} - -export function t( - key: string, - fallback?: string, - vars?: Record, -): string { - const locale = detectLocale(); - const value = getValue(translations[locale], key); - if (value && value.length > 0) { - return formatTemplate(value, vars); - } - return formatTemplate(fallback ?? key, vars); -} +export const t = createTomlTranslator({ + ko: [commonKoRaw, koRaw], + en: [commonEnRaw, enRaw], +}); diff --git a/devfront/src/locales/en.toml b/devfront/src/locales/en.toml index ed6ce6a2..f81818bc 100644 --- a/devfront/src/locales/en.toml +++ b/devfront/src/locales/en.toml @@ -300,15 +300,6 @@ no_custom = "No custom fields defined for this tenant." [msg.admin.users.list.registry] count = "Count" -[msg.common] -error = "Error" -loading = "Loading..." -no_description = "No Description." -parsing = "Parsing data..." -requesting = "Requesting..." -saving = "Saving..." -unknown_error = "unknown error" - [msg.dev] logout_confirm = "Are you sure you want to log out?" @@ -1244,75 +1235,6 @@ name = "Name" role = "Role" -[ui.common] -add = "Add" -all = "All" -admin_only = "Admin Only" -assign = "Assign" -back = "Back" -back_to_login = "Back to login" -cancel = "Cancel" -change_file = "Change File" -clear_search = "Clear Search" -close = "Close" -collapse = "Collapse" -confirm = "Confirm" -copy = "Copy" -create = "Create" -delete = "Delete" -details = "Details" -disabled = "Disabled" -edit = "Edit" -enabled = "Enabled" -export = "Export" -fail = "Fail" -go_home = "Go Home" -view = "View" -hyphen = "-" -manage = "Manage" -na = "N/A" -never = "Never" -next = "Next" -none = "None" -page_of = "Page {{page}} of {{total}}" -prev = "Prev" -previous = "Previous" -qr = "QR" -reset = "Reset" -read_only = "Read Only" -refresh = "Refresh" -remove = "Remove" -resend = "Resend" -retry = "Retry" -save = "Save" -search = "Search" -select = "Select" -select_file = "Select File" -select_placeholder = "Select Placeholder" -show_more = "Show More" -language = "Language" -language_ko = "Korean" -language_en = "English" -success = "Success" -theme_dark = "Dark" -theme_light = "Light" -theme_toggle = "Theme Toggle" -unknown = "Unknown" - -[ui.common.badge] -admin_only = "Admin only" -command_only = "Command only" -system = "System" - -[ui.common.status] -active = "Active" -blocked = "Blocked" -failure = "Failure" -inactive = "Inactive" -ok = "Ok" -pending = "Pending" -success = "Success" - [test] key = "Test" diff --git a/devfront/src/locales/ko.toml b/devfront/src/locales/ko.toml index c23efa55..c00aca4d 100644 --- a/devfront/src/locales/ko.toml +++ b/devfront/src/locales/ko.toml @@ -300,15 +300,6 @@ no_custom = "이 테넌트에 정의된 커스텀 필드가 없습니다." [msg.admin.users.list.registry] count = "총 {{count}}명의 사용자가 등록되어 있습니다." -[msg.common] -error = "오류가 발생했습니다." -loading = "로딩 중..." -no_description = "설명이 없습니다." -parsing = "데이터 파싱 중..." -requesting = "요청 중..." -saving = "저장 중..." -unknown_error = "알 수 없는 오류" - [msg.dev] logout_confirm = "로그아웃 하시겠습니까?" @@ -1244,75 +1235,6 @@ name = "이름" role = "역할" -[ui.common] -add = "추가" -all = "전체" -admin_only = "관리자 전용" -assign = "할당" -back = "돌아가기" -back_to_login = "로그인으로 돌아가기" -cancel = "취소" -change_file = "파일 변경" -clear_search = "검색 초기화" -close = "닫기" -collapse = "접기" -confirm = "확인" -copy = "복사" -create = "생성" -delete = "삭제" -details = "상세정보" -disabled = "사용 안 함" -edit = "편집" -enabled = "사용" -export = "내보내기" -fail = "실패" -go_home = "홈으로" -view = "보기" -hyphen = "-" -manage = "관리" -na = "N/A" -never = "Never" -next = "다음" -none = "없음" -page_of = "Page {{page}} of {{total}}" -prev = "이전" -previous = "이전" -qr = "QR" -reset = "초기화" -read_only = "읽기 전용" -refresh = "새로고침" -remove = "제외" -resend = "재발송" -retry = "다시 시도" -save = "저장" -search = "검색" -select = "선택" -select_file = "파일 선택" -select_placeholder = "선택하세요" -show_more = "+ 더보기" -language = "언어" -language_ko = "한국어" -language_en = "English" -success = "성공" -theme_dark = "Dark" -theme_light = "Light" -theme_toggle = "테마 전환" -unknown = "Unknown" - -[ui.common.badge] -admin_only = "Admin only" -command_only = "Command only" -system = "System" - -[ui.common.status] -active = "활성" -blocked = "차단됨" -failure = "실패" -inactive = "비활성" -ok = "정상" -pending = "준비 중" -success = "성공" - [test] key = "테스트" diff --git a/devfront/src/locales/template.toml b/devfront/src/locales/template.toml index 32e7cae4..7cacce98 100644 --- a/devfront/src/locales/template.toml +++ b/devfront/src/locales/template.toml @@ -314,15 +314,6 @@ no_custom = "" [msg.admin.users.list.registry] count = "" -[msg.common] -error = "" -loading = "" -no_description = "" -parsing = "" -requesting = "" -saving = "" -unknown_error = "" - [msg.dev] logout_confirm = "" @@ -1297,75 +1288,6 @@ name = "" role = "" -[ui.common] -add = "" -all = "" -admin_only = "" -assign = "" -back = "" -back_to_login = "" -cancel = "" -change_file = "" -clear_search = "" -close = "" -collapse = "" -confirm = "" -copy = "" -create = "" -delete = "" -details = "" -disabled = "" -edit = "" -enabled = "" -export = "" -fail = "" -go_home = "" -view = "" -hyphen = "" -manage = "" -na = "" -never = "" -next = "" -none = "" -page_of = "" -prev = "" -previous = "" -qr = "" -reset = "" -read_only = "" -refresh = "" -remove = "" -resend = "" -retry = "" -save = "" -search = "" -select = "" -select_file = "" -select_placeholder = "" -show_more = "" -language = "" -language_ko = "" -language_en = "" -success = "" -theme_dark = "" -theme_light = "" -theme_toggle = "" -unknown = "" - -[ui.common.badge] -admin_only = "" -command_only = "" -system = "" - -[ui.common.status] -active = "" -blocked = "" -failure = "" -inactive = "" -ok = "" -pending = "" -success = "" - [test] key = "" diff --git a/orgfront/src/lib/i18n.ts b/orgfront/src/lib/i18n.ts index 53540cb7..91b55a98 100644 --- a/orgfront/src/lib/i18n.ts +++ b/orgfront/src/lib/i18n.ts @@ -1,148 +1,16 @@ -const LOCALE_STORAGE_KEY = "locale"; -const DEFAULT_LOCALE = "ko"; -const SUPPORTED_LOCALES = ["ko", "en"] as const; - -type Locale = (typeof SUPPORTED_LOCALES)[number]; - -type TomlValue = string | TomlObject; - -interface TomlObject { - [key: string]: TomlValue; -} - -function isSupportedLocale(value: string): value is Locale { - return (SUPPORTED_LOCALES as readonly string[]).includes(value); -} - -function parseToml(raw: string): TomlObject { - const lines = raw.split(/\r?\n/); - const root: TomlObject = {}; - let currentPath: string[] = []; - - for (const rawLine of lines) { - const line = rawLine.trim(); - if (!line || line.startsWith("#")) { - continue; - } - - if (line.startsWith("[") && line.endsWith("]")) { - const sectionName = line.slice(1, -1).trim(); - currentPath = sectionName - ? sectionName - .split(".") - .map((part) => part.trim()) - .filter(Boolean) - : []; - continue; - } - - const eqIndex = line.indexOf("="); - if (eqIndex === -1) { - continue; - } - - const key = line.slice(0, eqIndex).trim(); - const valueRaw = line.slice(eqIndex + 1).trim(); - if (!key) { - continue; - } - - let value = valueRaw; - if ( - (value.startsWith('"') && value.endsWith('"')) || - (value.startsWith("'") && value.endsWith("'")) - ) { - value = value.slice(1, -1); - } - - let cursor: TomlObject = root; - for (const section of currentPath) { - if (!cursor[section] || typeof cursor[section] === "string") { - cursor[section] = {}; - } - cursor = cursor[section] as TomlObject; - } - - cursor[key] = value; - } - - return root; -} - -function getValue(target: TomlObject, key: string): string | undefined { - const parts = key.split("."); - let cursor: TomlValue = target; - for (const part of parts) { - if (typeof cursor !== "object" || cursor === null) { - return undefined; - } - cursor = (cursor as TomlObject)[part]; - if (cursor === undefined) { - return undefined; - } - } - return typeof cursor === "string" ? cursor : undefined; -} - -function detectLocale(): Locale { - if (typeof window === "undefined") { - return DEFAULT_LOCALE; - } - - const stored = window.localStorage.getItem(LOCALE_STORAGE_KEY); - if (stored && isSupportedLocale(stored)) { - return stored; - } - - const pathLocale = window.location.pathname.split("/")[1]; - if (pathLocale && isSupportedLocale(pathLocale)) { - return pathLocale; - } - - const browserLang = window.navigator.language.toLowerCase(); - if (browserLang.startsWith("ko")) { - return "ko"; - } - - return DEFAULT_LOCALE; -} +import { createTomlTranslator } from "../../../common/core/i18n"; +// eslint-disable-next-line import/no-unresolved +import commonEnRaw from "../../../common/locales/en.toml?raw"; +// eslint-disable-next-line import/no-unresolved +import commonKoRaw from "../../../common/locales/ko.toml?raw"; // eslint-disable-next-line import/no-unresolved import enRaw from "../locales/en.toml?raw"; // Vite ?raw import는 런타임 상수로 번들됩니다. // eslint-disable-next-line import/no-unresolved import koRaw from "../locales/ko.toml?raw"; -const translations: Record = { - ko: parseToml(koRaw), - en: parseToml(enRaw), -}; - -function formatTemplate( - template: string, - vars?: Record, -): string { - if (!vars) { - return template; - } - return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => { - const value = vars[key]; - if (value === undefined || value === null) { - return match; - } - return String(value); - }); -} - -export function t( - key: string, - fallback?: string, - vars?: Record, -): string { - const locale = detectLocale(); - const value = getValue(translations[locale], key); - if (value && value.length > 0) { - return formatTemplate(value, vars); - } - return formatTemplate(fallback ?? key, vars); -} +export const t = createTomlTranslator({ + ko: [commonKoRaw, koRaw], + en: [commonEnRaw, enRaw], +}); diff --git a/orgfront/src/locales/en.toml b/orgfront/src/locales/en.toml index 1794ae04..0749b5af 100644 --- a/orgfront/src/locales/en.toml +++ b/orgfront/src/locales/en.toml @@ -302,15 +302,6 @@ no_custom = "No custom fields defined for this tenant." [msg.admin.users.list.registry] count = "Count" -[msg.common] -error = "Error" -loading = "Loading..." -no_description = "No Description." -parsing = "Parsing data..." -requesting = "Requesting..." -saving = "Saving..." -unknown_error = "unknown error" - [msg.dev] logout_confirm = "Are you sure you want to log out?" @@ -1176,73 +1167,6 @@ name = "Name" role = "Role" -[ui.common] -add = "Add" -all = "All" -admin_only = "Admin Only" -assign = "Assign" -back = "Back" -back_to_login = "Back to login" -cancel = "Cancel" -change_file = "Change File" -clear_search = "Clear Search" -close = "Close" -collapse = "Collapse" -confirm = "Confirm" -copy = "Copy" -create = "Create" -delete = "Delete" -details = "Details" -edit = "Edit" -export = "Export" -fail = "Fail" -go_home = "Go Home" -view = "View" -hyphen = "-" -manage = "Manage" -na = "N/A" -never = "Never" -next = "Next" -none = "None" -page_of = "Page {{page}} of {{total}}" -prev = "Prev" -previous = "Previous" -qr = "QR" -reset = "Reset" -read_only = "Read Only" -refresh = "Refresh" -remove = "Remove" -resend = "Resend" -retry = "Retry" -save = "Save" -search = "Search" -select = "Select" -select_file = "Select File" -select_placeholder = "Select Placeholder" -show_more = "Show More" -language = "Language" -language_ko = "Korean" -language_en = "English" -success = "Success" -theme_dark = "Dark" -theme_light = "Light" -theme_toggle = "Theme Toggle" -unknown = "Unknown" - -[ui.common.badge] -admin_only = "Admin only" -command_only = "Command only" -system = "System" - -[ui.common.status] -active = "Active" -blocked = "Blocked" -failure = "Failure" -inactive = "Inactive" -ok = "Ok" -pending = "Pending" -success = "Success" - [test] key = "Test" diff --git a/orgfront/src/locales/ko.toml b/orgfront/src/locales/ko.toml index 2b9503b0..d43657ef 100644 --- a/orgfront/src/locales/ko.toml +++ b/orgfront/src/locales/ko.toml @@ -302,15 +302,6 @@ no_custom = "이 테넌트에 정의된 커스텀 필드가 없습니다." [msg.admin.users.list.registry] count = "총 {{count}}명의 사용자가 등록되어 있습니다." -[msg.common] -error = "오류가 발생했습니다." -loading = "로딩 중..." -no_description = "설명이 없습니다." -parsing = "데이터 파싱 중..." -requesting = "요청 중..." -saving = "저장 중..." -unknown_error = "알 수 없는 오류" - [msg.dev] logout_confirm = "로그아웃 하시겠습니까?" @@ -1178,73 +1169,6 @@ name = "이름" role = "역할" -[ui.common] -add = "추가" -all = "전체" -admin_only = "관리자 전용" -assign = "할당" -back = "돌아가기" -back_to_login = "로그인으로 돌아가기" -cancel = "취소" -change_file = "파일 변경" -clear_search = "검색 초기화" -close = "닫기" -collapse = "접기" -confirm = "확인" -copy = "복사" -create = "생성" -delete = "삭제" -details = "상세정보" -edit = "편집" -export = "내보내기" -fail = "실패" -go_home = "홈으로" -view = "보기" -hyphen = "-" -manage = "관리" -na = "N/A" -never = "Never" -next = "다음" -none = "없음" -page_of = "Page {{page}} of {{total}}" -prev = "이전" -previous = "이전" -qr = "QR" -reset = "초기화" -read_only = "읽기 전용" -refresh = "새로고침" -remove = "제외" -resend = "재발송" -retry = "다시 시도" -save = "저장" -search = "검색" -select = "선택" -select_file = "파일 선택" -select_placeholder = "선택하세요" -show_more = "+ 더보기" -language = "언어" -language_ko = "한국어" -language_en = "English" -success = "성공" -theme_dark = "Dark" -theme_light = "Light" -theme_toggle = "테마 전환" -unknown = "Unknown" - -[ui.common.badge] -admin_only = "Admin only" -command_only = "Command only" -system = "System" - -[ui.common.status] -active = "활성" -blocked = "차단됨" -failure = "실패" -inactive = "비활성" -ok = "정상" -pending = "준비 중" -success = "성공" - [test] key = "테스트" diff --git a/orgfront/src/locales/template.toml b/orgfront/src/locales/template.toml index 8b3a86fd..f2fa8ccf 100644 --- a/orgfront/src/locales/template.toml +++ b/orgfront/src/locales/template.toml @@ -302,15 +302,6 @@ no_custom = "" [msg.admin.users.list.registry] count = "" -[msg.common] -error = "" -loading = "" -no_description = "" -parsing = "" -requesting = "" -saving = "" -unknown_error = "" - [msg.dev] logout_confirm = "" @@ -1177,73 +1168,6 @@ name = "" role = "" -[ui.common] -add = "" -all = "" -admin_only = "" -assign = "" -back = "" -back_to_login = "" -cancel = "" -change_file = "" -clear_search = "" -close = "" -collapse = "" -confirm = "" -copy = "" -create = "" -delete = "" -details = "" -edit = "" -export = "" -fail = "" -go_home = "" -view = "" -hyphen = "" -manage = "" -na = "" -never = "" -next = "" -none = "" -page_of = "" -prev = "" -previous = "" -qr = "" -reset = "" -read_only = "" -refresh = "" -remove = "" -resend = "" -retry = "" -save = "" -search = "" -select = "" -select_file = "" -select_placeholder = "" -show_more = "" -language = "" -language_ko = "" -language_en = "" -success = "" -theme_dark = "" -theme_light = "" -theme_toggle = "" -unknown = "" - -[ui.common.badge] -admin_only = "" -command_only = "" -system = "" - -[ui.common.status] -active = "" -blocked = "" -failure = "" -inactive = "" -ok = "" -pending = "" -success = "" - [test] key = ""