@paroicms/server
Version:
The ParoiCMS server
214 lines • 7.8 kB
JavaScript
import { pathExists } from "@paroicms/internal-server-lib";
import { coreLanguages, languageLabelIn, } from "@paroicms/public-anywhere-lib";
import { jsonTypeValidator } from "@paroicms/public-server-lib";
import { readFile } from "node:fs/promises";
import { join } from "node:path";
export function createEmptySchemaLocales(languages, commonLanguageLabels) {
const locales = {};
for (const language of languages) {
locales[language] = {
languageLabel: commonLanguageLabels[language] ?? language,
nodeTypes: {},
};
}
return locales;
}
export function createEmptyLocalizedTranslations(languages) {
const locales = {};
for (const language of languages) {
locales[language] = {};
}
const onlyUnsupportedLanguages = !languages.some((language) => coreLanguages.includes(language));
if (onlyUnsupportedLanguages && !locales.en) {
locales.en = {};
}
return locales;
}
export function mergeL10nDepth2(from, to) {
for (const language of Object.keys(to)) {
const locales = from[language];
if (!locales)
continue;
for (const [key1, val1] of Object.entries(locales)) {
if (typeof val1 === "string") {
if (to[language][key1]) {
throw new Error(`duplicate '${key1}', for language '${language}'`);
}
to[language][key1] = val1;
continue;
}
if (!to[language][key1]) {
to[language][key1] = {};
}
else if (typeof to[language][key1] === "string") {
throw new Error(`translation type conflict '${key1}', for language '${language}'`);
}
for (const [key2, val2] of Object.entries(val1)) {
if (to[language][key1][key2]) {
throw new Error(`duplicate '${key1}.${key2}', for language '${language}'`);
}
to[language][key1][key2] = val2;
}
}
}
}
export function importPartOfL10n(options) {
const languages = Object.keys(options.to);
for (const language of languages) {
importPartOfL10nForLanguage(options, language);
}
const onlyUnsupportedLanguages = !languages.some((language) => coreLanguages.includes(language));
if (onlyUnsupportedLanguages && !options.to.en) {
options.to.en = {
languageLabel: languageLabelIn("en", "en"),
nodeTypes: {},
};
importPartOfL10nForLanguage(options, "en");
}
}
function importPartOfL10nForLanguage(options, language) {
const { from, fromPath, to, ifDuplicate } = options;
const toPath = options.toPath ?? fromPath;
const fromLocales = from[language];
if (!fromLocales)
return;
let fromCurrent = fromLocales;
for (const token of fromPath) {
const cur = fromCurrent[token];
if (!cur || typeof cur === "string")
return;
fromCurrent = cur;
}
if (!fromCurrent)
return;
const toParentPath = [...toPath];
const lastKey = toParentPath.pop();
if (!lastKey)
throw new Error("toPath should not be empty");
let toCurrent = to[language];
for (const token of toParentPath) {
let cur = toCurrent[token];
if (typeof cur === "string")
throw new Error(`should not be a string '${cur}'`);
if (!cur) {
toCurrent[token] = cur = {};
}
toCurrent = cur;
}
const copy = deepCopyLabelLocales(fromCurrent);
if (!toCurrent[lastKey]) {
toCurrent[lastKey] = copy;
}
else if (ifDuplicate === "overwrite") {
toCurrent[lastKey] = copy;
}
else if (ifDuplicate === "merge") {
Object.assign(toCurrent[lastKey], copy);
}
else {
throw new Error(`duplicate '${toPath.join(".")}', for language '${language}'`);
}
}
function deepCopyLabelLocales(obj) {
const result = {};
for (const key of Object.keys(obj)) {
const val = obj[key];
result[key] = typeof val === "string" ? val : deepCopyLabelLocales(val);
}
return result;
}
export async function appendToSchemaLocales({ dir, baseFileName, jsonTOnly, }, l10n) {
const languages = Object.keys(l10n);
for (const language of languages) {
const file = join(dir, `${baseFileName}.${language}.json`);
const jsonData = (await readJsonLocalesFile(file, { jsonTOnly }));
if (!jsonData)
continue;
for (const key1 of Object.keys(jsonData)) {
const val1 = jsonData[key1];
if (typeof val1 === "string") {
if (l10n[language][key1]) {
throw new Error(`translation for '${key1}' already exists, for language '${language}'`);
}
l10n[language][key1] = val1;
continue;
}
if (!l10n[language][key1]) {
l10n[language][key1] = {};
}
else if (typeof l10n[language][key1] === "string") {
throw new Error(`translation for '${key1}' already exists, for language '${language}'`);
}
for (const name of Object.keys(val1)) {
if (l10n[language][key1][name])
throw new Error(`translation for '${name}' already exists`);
l10n[language][key1][name] = val1[name];
}
}
}
}
async function readJsonLocalesFile(file, { jsonTOnly }) {
if (!(await pathExists(file)))
return;
const jsonContent = await readFile(file, "utf8");
const jsonData = JSON.parse(jsonContent);
const result = jsonTypeValidator.validate(jsonTOnly, jsonData);
if (!result.valid) {
throw new Error(`invalid site-schema '${file}': ${result.error ?? "(missing message)"}`);
}
return jsonData;
}
export async function completeSchemaLocales(dir, l10n) {
const languages = Object.keys(l10n);
const onlyUnsupportedLanguages = !languages.some((lang) => coreLanguages.includes(lang));
if (onlyUnsupportedLanguages && !l10n.en) {
l10n.en = {
languageLabel: languageLabelIn("en", "en"),
nodeTypes: {},
};
}
for (const language of Object.keys(l10n)) {
const file = join(dir, `site-schema.l10n.${language}.json`);
const jsonData = await readJsonLocalesFile(file, { jsonTOnly: "JtSiteSchemaTranslations" });
if (!jsonData)
continue;
if (typeof jsonData.siteTheme === "string") {
l10n[language].siteTheme = jsonData.siteTheme;
}
if (typeof jsonData.nodeTypes === "object") {
recursivelyMergeLocales(jsonData.nodeTypes, l10n[language].nodeTypes);
}
}
}
function recursivelyMergeLocales(from, to, debug = []) {
if (!from)
return;
for (const key of Object.keys(from)) {
const fromVal = from[key];
if (to[key]) {
const fromValType = typeof fromVal;
if (fromValType !== typeof to[key]) {
throw new Error(`inconsistant locales '${to[key]}' and '${fromVal}'`);
}
if (fromValType === "string") {
to[key] = fromVal;
}
else
recursivelyMergeLocales(fromVal, to[key], [
...debug,
key,
]);
}
else {
to[key] = fromVal;
}
}
}
export function makeSiteLanguageLabels(languages, commonLanguageLabels) {
const newLabels = {};
for (const language of languages) {
newLabels[language] = commonLanguageLabels[language] ?? language;
}
return newLabels;
}
//# sourceMappingURL=read-locales.js.map