1
0
forked from baron/baron-sso

각 프런트의 i18n 연결 및 locale 정리

This commit is contained in:
2026-05-11 11:42:24 +09:00
parent efbf970a18
commit b8a25135fc
12 changed files with 32 additions and 1084 deletions

View File

@@ -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<Locale, TomlObject> = {
ko: parseToml(koRaw),
en: parseToml(enRaw),
};
function formatTemplate(
template: string,
vars?: Record<string, string | number>,
): 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, string | number>,
): 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],
});

View File

@@ -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"

View File

@@ -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 = "테스트"

View File

@@ -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 = ""