Files
2026-10-25 22:55:29 +04:00

80 lines
3.0 KiB
TypeScript

import { createHash } from 'node:crypto';
import { readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { flatten, LOCALES, type Locale, type Messages } from '../src/index.ts';
export const ROOT = path.resolve(import.meta.dirname, '..');
export const TARGETS = LOCALES.filter((l) => l !== 'ru') as Exclude<Locale, 'ru'>[];
type Tree = { [k: string]: string | Tree };
export function readLocale(locale: Locale): Messages {
return flatten(JSON.parse(readFileSync(path.join(ROOT, 'locales', `${locale}.json`), 'utf8')) as Tree);
}
export function writeLocale(locale: Locale, flat: Messages, order: string[]) {
const tree: Tree = {};
for (const key of order) {
if (flat[key] === undefined) continue;
const parts = key.split('.');
let node = tree;
for (const p of parts.slice(0, -1)) node = (node[p] ??= {}) as Tree;
node[parts.at(-1)!] = flat[key]!;
}
writeFileSync(path.join(ROOT, 'locales', `${locale}.json`), JSON.stringify(tree, null, 2) + '\n');
}
/**
* review.json хранит для каждого языка { ключ: хеш русского текста на момент проверки }.
* Если русский текст изменился, хеш не совпадёт — ключ снова needs_review.
*/
export type Review = Record<Exclude<Locale, 'ru'>, Record<string, string>>;
export const readReview = (): Review => JSON.parse(readFileSync(path.join(ROOT, 'review.json'), 'utf8'));
export const writeReview = (r: Review) => writeFileSync(path.join(ROOT, 'review.json'), JSON.stringify(r, null, 2) + '\n');
export const sourceHash = (s: string) => createHash('sha256').update(s).digest('hex').slice(0, 12);
export type Status = 'approved' | 'needs_review' | 'missing';
export function statusOf(review: Review, locale: Exclude<Locale, 'ru'>, key: string, ru: string, value: string | undefined): Status {
if (value === undefined || value === '') return 'missing';
return review[locale][key] === sourceHash(ru) ? 'approved' : 'needs_review';
}
export const placeholders = (s: string) => [...s.matchAll(/\{\{\s*(\w+)\s*\}\}/g)].map((m) => m[1]).sort().join(',');
export function csvEscape(v: string) {
return /[",\n]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v;
}
export function parseCsv(text: string): string[][] {
const rows: string[][] = [];
let row: string[] = [];
let cell = '';
let quoted = false;
for (let i = 0; i < text.length; i++) {
const ch = text[i]!;
if (quoted) {
if (ch === '"' && text[i + 1] === '"') {
cell += '"';
i++;
} else if (ch === '"') quoted = false;
else cell += ch;
} else if (ch === '"') quoted = true;
else if (ch === ',') {
row.push(cell);
cell = '';
} else if (ch === '\n' || ch === '\r') {
if (ch === '\r' && text[i + 1] === '\n') i++;
row.push(cell);
rows.push(row);
row = [];
cell = '';
} else cell += ch;
}
if (cell || row.length) {
row.push(cell);
rows.push(row);
}
return rows.filter((r) => r.some((c) => c !== ''));
}