UNPKG

@paroicms/server

Version:
214 lines 7.8 kB
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