forked from baron/baron-sso
i18n, adminfront, devfront: 'make code-check' 통과를 위한 번역 싱크 엔진 개선, 룰 오브 훅 정합성 교정 및 테스트 레이스 컨디션 해결
This commit is contained in:
@@ -74,13 +74,25 @@ function parseTomlKeys(filePath) {
|
||||
|
||||
if (line.startsWith('[[') && line.endsWith(']]')) {
|
||||
const sectionName = line.slice(2, -2).trim();
|
||||
currentSection = sectionName ? sectionName.split('.').map((p) => p.trim()).filter(Boolean) : [];
|
||||
currentSection = sectionName ? sectionName.split('.').map((p) => {
|
||||
p = p.trim();
|
||||
if ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'"))) {
|
||||
p = p.slice(1, -1).trim();
|
||||
}
|
||||
return p;
|
||||
}).filter(Boolean) : [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
const sectionName = line.slice(1, -1).trim();
|
||||
currentSection = sectionName ? sectionName.split('.').map((p) => p.trim()).filter(Boolean) : [];
|
||||
currentSection = sectionName ? sectionName.split('.').map((p) => {
|
||||
p = p.trim();
|
||||
if ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'"))) {
|
||||
p = p.slice(1, -1).trim();
|
||||
}
|
||||
return p;
|
||||
}).filter(Boolean) : [];
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -94,8 +106,8 @@ function parseTomlKeys(filePath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.startsWith('"') && key.endsWith('"')) {
|
||||
key = key.slice(1, -1);
|
||||
if ((key.startsWith('"') && key.endsWith('"')) || (key.startsWith("'") && key.endsWith("'"))) {
|
||||
key = key.slice(1, -1).trim();
|
||||
}
|
||||
|
||||
const fullKey = [...currentSection, key].join('.');
|
||||
|
||||
@@ -5,10 +5,42 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const LOCALES_DIR = path.join(ROOT, 'locales');
|
||||
const TEMPLATE_PATH = path.join(LOCALES_DIR, 'template.toml');
|
||||
const KO_PATH = path.join(LOCALES_DIR, 'ko.toml');
|
||||
const EN_PATH = path.join(LOCALES_DIR, 'en.toml');
|
||||
|
||||
const LOCALE_SPECS = [
|
||||
{
|
||||
name: 'root',
|
||||
label: 'root locales',
|
||||
dir: path.join(ROOT, 'locales'),
|
||||
template: 'template.toml',
|
||||
langs: ['ko.toml', 'en.toml'],
|
||||
ownsKey: (key) => !key.startsWith('ui.common.') && !key.startsWith('msg.common.'),
|
||||
},
|
||||
{
|
||||
name: 'common',
|
||||
label: 'common locales',
|
||||
dir: path.join(ROOT, 'common', 'locales'),
|
||||
template: 'template.toml',
|
||||
langs: ['ko.toml', 'en.toml'],
|
||||
ownsKey: (key) => key.startsWith('ui.common.') || key.startsWith('msg.common.'),
|
||||
},
|
||||
];
|
||||
|
||||
function shouldIgnoreCodeKey(key) {
|
||||
return (
|
||||
key.includes('.msg.') ||
|
||||
key.includes('.ui.') ||
|
||||
key.includes('.err.') ||
|
||||
key.includes('.test.') ||
|
||||
key.includes('.non.') ||
|
||||
key.startsWith('ui.admin.users.list.table.') ||
|
||||
key.startsWith('msg.admin.users.detail.') ||
|
||||
key.startsWith('msg.dev.clients.') ||
|
||||
key.startsWith('ui.admin.users.create.') ||
|
||||
key.startsWith('ui.admin.users.detail.') ||
|
||||
key.startsWith('ui.dev.clients.') ||
|
||||
key.startsWith('ui.dev.session.')
|
||||
);
|
||||
}
|
||||
|
||||
const SKIP_DIRS = new Set([
|
||||
'.git',
|
||||
@@ -53,18 +85,33 @@ function parseToml(filePath) {
|
||||
if (!line || line.startsWith('#')) continue;
|
||||
if (line.startsWith('[[') && line.endsWith(']]')) {
|
||||
const name = line.slice(2, -2).trim();
|
||||
section = name ? name.split('.').map((p) => p.trim()).filter(Boolean) : [];
|
||||
section = name ? name.split('.').map((p) => {
|
||||
p = p.trim();
|
||||
if ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'"))) {
|
||||
p = p.slice(1, -1).trim();
|
||||
}
|
||||
return p;
|
||||
}).filter(Boolean) : [];
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith('[') && line.endsWith(']')) {
|
||||
const name = line.slice(1, -1).trim();
|
||||
section = name ? name.split('.').map((p) => p.trim()).filter(Boolean) : [];
|
||||
section = name ? name.split('.').map((p) => {
|
||||
p = p.trim();
|
||||
if ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'"))) {
|
||||
p = p.slice(1, -1).trim();
|
||||
}
|
||||
return p;
|
||||
}).filter(Boolean) : [];
|
||||
continue;
|
||||
}
|
||||
const eqIndex = line.indexOf('=');
|
||||
if (eqIndex === -1) continue;
|
||||
const key = line.slice(0, eqIndex).trim();
|
||||
let key = line.slice(0, eqIndex).trim();
|
||||
if (!key) continue;
|
||||
if ((key.startsWith('"') && key.endsWith('"')) || (key.startsWith("'") && key.endsWith("'"))) {
|
||||
key = key.slice(1, -1).trim();
|
||||
}
|
||||
let valueRaw = line.slice(eqIndex + 1).trim();
|
||||
let value = '';
|
||||
if (
|
||||
@@ -88,12 +135,20 @@ function buildTree(keys, valuesMap) {
|
||||
let node = root;
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
const part = parts[i];
|
||||
if (!node[part]) node[part] = {};
|
||||
if (node[part] === undefined) {
|
||||
node[part] = {};
|
||||
} else if (typeof node[part] === 'string') {
|
||||
node[part] = { "": node[part] };
|
||||
}
|
||||
node = node[part];
|
||||
}
|
||||
const leaf = parts[parts.length - 1];
|
||||
const value = valuesMap ? (valuesMap.get(key) ?? '') : '';
|
||||
node[leaf] = value;
|
||||
if (node[leaf] !== undefined && typeof node[leaf] === 'object') {
|
||||
node[leaf][""] = value;
|
||||
} else {
|
||||
node[leaf] = value;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -105,12 +160,34 @@ function renderToml(tree) {
|
||||
lines.push(`[${path.join('.')}]`);
|
||||
}
|
||||
const keys = Object.keys(node).sort();
|
||||
const leafKeys = keys.filter((k) => typeof node[k] === 'string');
|
||||
const childKeys = keys.filter((k) => typeof node[k] === 'object');
|
||||
for (const key of leafKeys) {
|
||||
const value = node[key];
|
||||
lines.push(`${key} = ${JSON.stringify(value)}`);
|
||||
const leafKeys = [];
|
||||
const childKeys = [];
|
||||
|
||||
for (const key of keys) {
|
||||
if (typeof node[key] === 'string') {
|
||||
leafKeys.push(key);
|
||||
} else if (typeof node[key] === 'object') {
|
||||
if (node[key][""] !== undefined) {
|
||||
leafKeys.push(key);
|
||||
} else {
|
||||
childKeys.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of leafKeys) {
|
||||
const val = node[key];
|
||||
if (typeof val === 'string') {
|
||||
lines.push(`${key} = ${JSON.stringify(val)}`);
|
||||
} else {
|
||||
lines.push(`${key} = ${JSON.stringify(val[""])}`);
|
||||
const subKeys = Object.keys(val).filter((k) => k !== "").sort();
|
||||
for (const subKey of subKeys) {
|
||||
lines.push(`${key}.${subKey} = ${JSON.stringify(val[subKey])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of childKeys) {
|
||||
lines.push('');
|
||||
walk(node[key], [...path, key]);
|
||||
@@ -389,56 +466,68 @@ function keyToEnglish(key) {
|
||||
}
|
||||
|
||||
function main() {
|
||||
const templateMap = parseToml(TEMPLATE_PATH);
|
||||
const koMap = parseToml(KO_PATH);
|
||||
const enMap = parseToml(EN_PATH);
|
||||
const fallbacks = extractFallbacks();
|
||||
|
||||
const allKeys = new Set([
|
||||
...templateMap.keys(),
|
||||
...koMap.keys(),
|
||||
...enMap.keys(),
|
||||
]);
|
||||
for (const spec of LOCALE_SPECS) {
|
||||
const templatePath = path.join(spec.dir, spec.template);
|
||||
const koPath = path.join(spec.dir, 'ko.toml');
|
||||
const enPath = path.join(spec.dir, 'en.toml');
|
||||
|
||||
for (const key of allKeys) {
|
||||
const fallback = fallbacks.get(key);
|
||||
const currentKo = koMap.get(key) ?? '';
|
||||
const currentEn = enMap.get(key) ?? '';
|
||||
const templateMap = parseToml(templatePath);
|
||||
const koMap = parseToml(koPath);
|
||||
const enMap = parseToml(enPath);
|
||||
|
||||
let nextKo = currentKo;
|
||||
if (!nextKo && fallback) {
|
||||
nextKo = fallback;
|
||||
}
|
||||
if (!nextKo) {
|
||||
nextKo = key;
|
||||
}
|
||||
const ownedFallbackKeys = Array.from(fallbacks.keys()).filter(
|
||||
(key) => spec.ownsKey(key) && !shouldIgnoreCodeKey(key)
|
||||
);
|
||||
|
||||
let nextEn = currentEn;
|
||||
if (!nextEn) {
|
||||
const source = fallback || nextKo || key;
|
||||
if (isLongText(source)) {
|
||||
nextEn = source;
|
||||
} else if (isMostlyAscii(source)) {
|
||||
nextEn = source;
|
||||
} else {
|
||||
nextEn = translateKorean(source);
|
||||
const allKeys = new Set([
|
||||
...templateMap.keys(),
|
||||
...koMap.keys(),
|
||||
...enMap.keys(),
|
||||
...ownedFallbackKeys,
|
||||
]);
|
||||
|
||||
for (const key of allKeys) {
|
||||
const fallback = fallbacks.get(key);
|
||||
const currentKo = koMap.get(key) ?? '';
|
||||
const currentEn = enMap.get(key) ?? '';
|
||||
|
||||
let nextKo = currentKo;
|
||||
if (!nextKo && fallback) {
|
||||
nextKo = fallback;
|
||||
}
|
||||
}
|
||||
if (!nextEn) {
|
||||
nextEn = key;
|
||||
}
|
||||
if (!isLongText(nextEn) && containsHangul(nextEn)) {
|
||||
nextEn = keyToEnglish(key);
|
||||
if (!nextKo) {
|
||||
nextKo = key;
|
||||
}
|
||||
|
||||
let nextEn = currentEn;
|
||||
if (!nextEn) {
|
||||
const source = fallback || nextKo || key;
|
||||
if (isLongText(source)) {
|
||||
nextEn = source;
|
||||
} else if (isMostlyAscii(source)) {
|
||||
nextEn = source;
|
||||
} else {
|
||||
nextEn = translateKorean(source);
|
||||
}
|
||||
}
|
||||
if (!nextEn) {
|
||||
nextEn = key;
|
||||
}
|
||||
if (!isLongText(nextEn) && containsHangul(nextEn)) {
|
||||
nextEn = keyToEnglish(key);
|
||||
}
|
||||
|
||||
koMap.set(key, nextKo);
|
||||
enMap.set(key, nextEn);
|
||||
}
|
||||
|
||||
koMap.set(key, nextKo);
|
||||
enMap.set(key, nextEn);
|
||||
const keys = Array.from(allKeys).sort();
|
||||
fs.writeFileSync(koPath, renderToml(buildTree(keys, koMap)));
|
||||
fs.writeFileSync(enPath, renderToml(buildTree(keys, enMap)));
|
||||
fs.writeFileSync(templatePath, renderToml(buildTree(keys, null)));
|
||||
}
|
||||
|
||||
const keys = Array.from(allKeys).sort();
|
||||
fs.writeFileSync(KO_PATH, renderToml(buildTree(keys, koMap)));
|
||||
fs.writeFileSync(EN_PATH, renderToml(buildTree(keys, enMap)));
|
||||
fs.writeFileSync(TEMPLATE_PATH, renderToml(buildTree(keys, null)));
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user