mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-11 00:26:40 -04:00

the #shared alias used by Nuxt cannot be easily disabled and to prevent breackage with jiti, we make use of it
103 lines
4.5 KiB
TypeScript
103 lines
4.5 KiB
TypeScript
import fs from 'node:fs/promises';
|
|
|
|
import { deepListKeys } from '#shared/helpers.ts';
|
|
import { listMissingTranslations } from '#shared/missingTranslations.ts';
|
|
import { DictNode, parse } from '~/helpers/merge/sumlAst.ts';
|
|
import type { Node } from '~/helpers/merge/sumlAst.ts';
|
|
import type { Config } from '~~/locale/config.ts';
|
|
import type { Translations } from '~~/locale/translations.ts';
|
|
import { loadSuml } from '~~/server/loader.ts';
|
|
import { rootDir } from '~~/server/paths.ts';
|
|
|
|
export const loadDocument = async (name: string) => {
|
|
return parse(await fs.readFile(`${rootDir}/${name}`, 'utf-8'));
|
|
};
|
|
|
|
export const saveDocument = async (name: string, document: Node) => {
|
|
await fs.writeFile(`${rootDir}/${name}`, `${document.toString()}\n`);
|
|
};
|
|
|
|
export const mergeTranslationProposals = async (locale: string, proposalsFile: string) => {
|
|
const baseTranslationsDocument = await loadDocument('locale/_base/translations.suml');
|
|
const localeTranslationsDocument = await loadDocument(`locale/${locale}/translations.suml`);
|
|
const proposalsDocument = await loadDocument(proposalsFile);
|
|
|
|
if (!(proposalsDocument instanceof DictNode) ||
|
|
!(baseTranslationsDocument instanceof DictNode) ||
|
|
!(localeTranslationsDocument instanceof DictNode)) {
|
|
throw new Error('input nodes must be DictNode');
|
|
}
|
|
|
|
merge(proposalsDocument, baseTranslationsDocument, localeTranslationsDocument);
|
|
await saveDocument(`locale/${locale}/translations.suml`, localeTranslationsDocument);
|
|
};
|
|
|
|
export const merge = (source: DictNode, base: DictNode, target: DictNode) => {
|
|
const baseKeys = base.items.map((entry) => entry.key);
|
|
for (const sourceEntry of source.items) {
|
|
const baseEntry = base.items.find((baseEntry) => baseEntry.key === sourceEntry.key);
|
|
const targetEntry = target.items.find((targetEntry) => targetEntry.key === sourceEntry.key);
|
|
if (targetEntry === undefined) {
|
|
let insertIndex = 0;
|
|
if (baseEntry === undefined) {
|
|
insertIndex = target.items.length;
|
|
} else {
|
|
const baseIndex = baseKeys.indexOf(baseEntry.key);
|
|
const previousKeys = baseKeys.slice(0, baseIndex).toReversed();
|
|
for (const previousKey of previousKeys) {
|
|
const targetIndex = target.items.findIndex((targetEntry) => targetEntry.key === previousKey);
|
|
if (targetIndex !== -1) {
|
|
insertIndex = targetIndex + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
target.items.splice(insertIndex, 0, sourceEntry);
|
|
} else if (targetEntry.value instanceof DictNode) {
|
|
if (!(sourceEntry.value instanceof DictNode)) {
|
|
throw new Error('source entry must be an DictNode when merging');
|
|
}
|
|
const childBase = baseEntry?.value instanceof DictNode ? baseEntry.value : new DictNode([], []);
|
|
merge(sourceEntry.value, childBase, targetEntry.value);
|
|
} else {
|
|
targetEntry.value = sourceEntry.value;
|
|
}
|
|
}
|
|
};
|
|
|
|
export const createTranslationFiles = async (locale: string) => {
|
|
const baseTranslations = await loadSuml<Translations>('locale/_base/translations.suml');
|
|
const config = await loadSuml<Config>('locale/_base/config.suml');
|
|
|
|
const configDocument = await loadDocument('locale/_base/config.suml');
|
|
const translationsDocument = await loadDocument('locale/_base/translations.suml');
|
|
|
|
const requiredTranslationKeys = listMissingTranslations({}, baseTranslations, config);
|
|
for (const key of deepListKeys(baseTranslations)) {
|
|
if (!requiredTranslationKeys.includes(key)) {
|
|
remove(translationsDocument, key.split('.'));
|
|
}
|
|
}
|
|
|
|
await fs.mkdir(`${rootDir}/locale/${locale}`, { recursive: true });
|
|
await saveDocument(`locale/${locale}/config.suml`, configDocument);
|
|
await saveDocument(`locale/${locale}/translations.suml`, translationsDocument);
|
|
};
|
|
|
|
export const remove = (node: Node, path: string[]) => {
|
|
if (!(node instanceof DictNode)) {
|
|
throw new Error('node must be DictNode');
|
|
}
|
|
|
|
if (path.length === 1) {
|
|
node.items = node.items.filter((item) => item.key !== path[0]);
|
|
return;
|
|
}
|
|
const childNode = node.items.find((item) => item.key === path[0])?.value;
|
|
if (!childNode) {
|
|
return;
|
|
}
|
|
remove(childNode, path.slice(1));
|
|
node.items = node.items.filter((item) => !(item.value instanceof DictNode) || item.value.items.length > 0);
|
|
};
|