Complete Commonlib rc.6 integration and setup workflows

This commit is contained in:
vorotamoroz
2026-07-21 15:00:28 +00:00
parent e005f9eb13
commit 1ef8c88139
150 changed files with 31041 additions and 260 deletions
+12
View File
@@ -0,0 +1,12 @@
import { writeFileSync } from "fs";
import { allMessages } from "../src/common/messages/combinedMessages.dev.ts";
import path from "path";
const __dirname = import.meta.dirname;
const currentPath = __dirname;
const outDir = path.resolve(currentPath, "../src/common/messages/combinedMessages.prod.ts");
console.log(`Writing to ${outDir}`);
writeFileSync(
outDir,
`export const allMessages: Readonly<Record<string, Readonly<Record<string, string>>>> = ${JSON.stringify(allMessages, null, 4)};`
);
+58
View File
@@ -0,0 +1,58 @@
import { readFile } from "fs/promises";
import { join, resolve } from "path";
import { glob } from "tinyglobby";
import { parse } from "yaml";
import { objectToDotted } from "./messagelib.ts";
const __dirname = import.meta.dirname;
const targetDir = resolve(join(__dirname, "../src/common/messagesYAML/"));
const files = (await glob(`*.yaml`, { expandDirectories: false, absolute: true, cwd: targetDir })).sort();
function flattenMessages(src: Record<string, unknown>) {
return Object.fromEntries(
Object.entries(objectToDotted(src))
.map(([key, value]) => [key.endsWith("._value") ? key.slice(0, -7) : key, value] as const)
.filter(([, value]) => typeof value === "string")
.sort(([a], [b]) => a.localeCompare(b))
) as Record<string, string>;
}
const localeData = new Map<string, Record<string, string>>();
for (const file of files) {
const segments = file.split(/[/\\]/);
const locale = segments[segments.length - 1]!.replace(/\.yaml$/, "");
const content = await readFile(file, "utf-8");
localeData.set(locale, flattenMessages(parse(content) ?? {}));
}
const baseLocale = "en";
const base = localeData.get(baseLocale);
if (!base) {
throw new Error("en.yaml not found");
}
const baseKeys = Object.keys(base);
const report = Object.fromEntries(
[...localeData.entries()].map(([locale, data]) => {
const keys = new Set(Object.keys(data));
const missing = baseKeys.filter((key) => !keys.has(key));
const identicalToEnglish = baseKeys.filter(
(key) => keys.has(key) && locale !== baseLocale && data[key] === base[key]
);
const translated = baseKeys.length - missing.length;
return [
locale,
{
totalBaseKeys: baseKeys.length,
translatedKeys: translated,
missingKeys: missing.length,
identicalToEnglishCount: identicalToEnglish.length,
coverage: `${translated}/${baseKeys.length}`,
missing,
identicalToEnglish,
},
];
})
);
console.log(JSON.stringify(report, null, 2));
+55
View File
@@ -0,0 +1,55 @@
import { writeFileSync } from "fs";
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../src/common/rosetta";
import { allMessages } from "../src/common/messages/combinedMessages.dev.ts";
import path from "path";
const thisFileDir = __dirname;
const outDir = path.join(thisFileDir, "i18n");
const out = {} as Record<string, { [key: string]: string | undefined }>;
for (const [key, value] of Object.entries(allMessages)) {
for (const lang of [...SUPPORTED_I18N_LANGS, "def"]) {
if (!out[lang]) out[lang] = {};
if (lang in value) {
out[lang][key] = value[lang as I18N_LANGS];
} else {
if (lang === "def") {
out[lang][key] = key;
} else {
out[lang][key] = undefined;
}
}
}
}
for (const [lang, value] of Object.entries(out)) {
const filename = `${lang}.ts`;
const escapeString = (prefix: string, key: string, str: string) => {
if (str.indexOf("\n") !== -1) {
const encoded = JSON.stringify(str);
const lineWrapped = encoded.split("\\n").join("\\\n" + prefix);
return `${prefix}${JSON.stringify(key)}: ${lineWrapped},`;
}
return `${prefix}${JSON.stringify(key)}: ${JSON.stringify(str)},`;
};
// const z ="a" "b" "c";
const _stringify = (value: Record<string, string | undefined>) => {
let res = "{\n";
for (const key of Object.keys(value)) {
const v = value[key];
if (v) {
res += escapeString("", key, v) + "\n";
} else {
res += escapeString("// ", key, out["def"]?.[key] ?? "") + "\n";
}
}
return res + "\n}";
};
void writeFileSync(
path.join(outDir, filename),
`export const PartialMessages ={\n "${lang}":${_stringify(value)}\n} as const;`
);
}
+30
View File
@@ -0,0 +1,30 @@
import { writeFileSync } from "fs";
import { allMessages } from "../src/common/messages/combinedMessages.prod.ts";
const __dirname = import.meta.dirname;
import path from "path";
const thisFileDir = __dirname;
const outDir = path.resolve(thisFileDir, "../src/common/messagesJson");
const out = {} as Record<string, { [key: string]: string | undefined }>;
for (const [key, value] of Object.entries(allMessages)) {
//@ts-ignore
for (const [lang, langValue] of Object.entries(allMessages[key])) {
if (!out[lang]) out[lang] = {};
if (lang in value) {
out[lang][key] = langValue as string;
} else {
if (lang === "def") {
out[lang][key] = key;
} else {
out[lang][key] = undefined;
}
}
}
}
for (const [lang, value] of Object.entries(out)) {
const filename = `${lang}.json`;
void writeFileSync(path.join(outDir, filename), JSON.stringify(value, null, 4));
}
+27
View File
@@ -0,0 +1,27 @@
// Convert Application convenient Message Resources (JSON) to Human-Editable format (YAML)
import { readFile, writeFile } from "fs/promises";
import { join, resolve } from "path";
import { stringify } from "yaml";
import { glob } from "tinyglobby";
import { dottedToObject } from "./messagelib";
const __dirname = import.meta.dirname;
const targetDir = resolve(join(__dirname, "../src/common/messagesJson/"));
console.log(`Target directory: ${targetDir}`);
const files = await glob(`*.json`, { expandDirectories: false, absolute: true, cwd: targetDir });
for (const file of files) {
const filePath = resolve(file);
console.log(`Processing file: ${filePath}`);
const content = await readFile(filePath, "utf-8");
const jsonDataSrc = JSON.parse(content);
const jsonDataD2 = Object.fromEntries(
Object.entries(jsonDataSrc).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
);
const jsonData = dottedToObject(jsonDataD2);
const yamlData = stringify(jsonData, { indent: 2 });
const yamlFilePath = filePath.replace(/\.json$/, ".yaml").replace("Json", "YAML");
await writeFile(yamlFilePath, yamlData, "utf-8");
console.log(`Converted ${filePath} to ${yamlFilePath}`);
}
// console.dir(files, { depth: 0 });
+38
View File
@@ -0,0 +1,38 @@
export function objectToDotted(obj: any, prefix = ""): Record<string, any> {
return Object.entries(obj).reduce(
(acc, [key, value]) => {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
Object.assign(acc, objectToDotted(value, newKey));
} else {
acc[newKey] = value;
}
return acc;
},
{} as Record<string, any>
);
}
export function dottedToObject(obj: Record<string, any>): Record<string, any> {
return Object.entries(obj).reduce(
(acc, [key, value]) => {
if (key.includes(" ")) {
// Return as is.
return { ...acc, [key]: value }; // Skip keys with spaces
}
const keys = key.split(".");
keys.reduce((nestedAcc, currKey, index) => {
if (currKey in nestedAcc && typeof nestedAcc[currKey] !== "object") {
nestedAcc[currKey] = { _value: nestedAcc[currKey] }; // Convert to object if not already
}
if (index === keys.length - 1) {
nestedAcc[currKey] = value;
} else {
nestedAcc[currKey] = nestedAcc[currKey] || {};
}
return nestedAcc[currKey];
}, acc);
return acc;
},
{} as Record<string, any>
);
}
+29
View File
@@ -0,0 +1,29 @@
// Convert Human-Editable format (YAML) to Application convenient Message Resources (JSON)
import { readFile, writeFile } from "fs/promises";
import { join, resolve } from "path";
import { parse } from "yaml";
import { glob } from "tinyglobby";
import { objectToDotted } from "./messagelib";
const __dirname = import.meta.dirname;
const targetDir = resolve(join(__dirname, "../src/common/messagesYAML/"));
console.log(`Target directory: ${targetDir}`);
const files = await glob(`*.yaml`, { expandDirectories: false, absolute: true, cwd: targetDir });
for (const file of files) {
const filePath = resolve(file);
const content = await readFile(filePath, "utf-8");
const jsonDataSrc = parse(content);
const jsonDataD2 = objectToDotted(jsonDataSrc);
const jsonData = Object.fromEntries(
Object.entries(jsonDataD2)
.map(([key, value]) => [key.endsWith("._value") ? key.slice(0, -7) : key, value])
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
);
const yamlData = JSON.stringify(jsonData, null, 4) + "\n";
const yamlFilePath = filePath.replace(/\.yaml$/, ".json").replace("YAML", "Json");
await writeFile(yamlFilePath, yamlData, "utf-8");
console.log(`Converted ${filePath} to ${yamlFilePath}`);
}
// console.dir(files, { depth: 0 });