- Add @indiekit/endpoint-posts (beta.41) as a second patch target - Translate new filter.* keys (type, status, sort, search, clear) - Translate purge.* keys (permanently delete flow) - Translate form.pinned, form.ai-text-level, form.ai-code-level, form.ai-tools, form.ai-description - Fix mp-syndicate-to label: "Syndikat zu" → "Syndizieren nach" - Fix posts.posts.title: "Veröffentlichte Beiträge" → "Beiträge" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
201 lines
5.0 KiB
JavaScript
201 lines
5.0 KiB
JavaScript
import { access, readFile, writeFile } from "node:fs/promises";
|
||
import path from "node:path";
|
||
|
||
const endpointCandidates = [
|
||
"node_modules/@rmdes/indiekit-endpoint-posts",
|
||
"node_modules/@indiekit/endpoint-posts",
|
||
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-posts",
|
||
"node_modules/@indiekit/indiekit/node_modules/@indiekit/endpoint-posts",
|
||
];
|
||
|
||
const sourceLocale = "en";
|
||
const targetLocales = ["de"];
|
||
|
||
const localeOverrides = {
|
||
de: {
|
||
posts: {
|
||
delete: {
|
||
cancel: "Nein – zurück zum Beitrag",
|
||
},
|
||
purge: {
|
||
action: "Endgültig löschen",
|
||
title: "Diesen Beitrag endgültig löschen?",
|
||
warning:
|
||
"Der Beitrag wird aus der Datenbank entfernt. Diese Aktion kann nicht rückgängig gemacht werden.",
|
||
submit: "Ich bin sicher – endgültig löschen",
|
||
cancel: "Nein – zurück zur Beitragsübersicht",
|
||
success: "Beitrag endgültig gelöscht",
|
||
},
|
||
post: {
|
||
syndicate: "Beitrag syndizieren",
|
||
},
|
||
posts: {
|
||
title: "Beiträge",
|
||
},
|
||
status: {
|
||
unlisted: "Nicht gelistet",
|
||
},
|
||
filter: {
|
||
type: "Typ",
|
||
status: "Status",
|
||
all: "Alle",
|
||
status_published: "Veröffentlicht",
|
||
status_draft: "Entwürfe",
|
||
status_deleted: "Gelöscht",
|
||
status_all: "Alle",
|
||
searchPlaceholder: "Beiträge suchen…",
|
||
searchButton: "Suchen",
|
||
clear: "Zurücksetzen",
|
||
newest: "Neueste",
|
||
oldest: "Älteste",
|
||
results: "Beiträge",
|
||
},
|
||
form: {
|
||
summary: {
|
||
label: "Zusammenfassung",
|
||
},
|
||
"mp-syndicate-to": {
|
||
label: "Syndizieren nach",
|
||
},
|
||
pinned: {
|
||
label: "Hervorgehobener Beitrag",
|
||
no: "Nein",
|
||
yes: "Ja – im Bereich Hervorgehoben anpinnen",
|
||
},
|
||
"ai-text-level": {
|
||
label: "KI-Textebene",
|
||
0: "0 – Keine",
|
||
1: "1 – Redaktionelle Unterstützung",
|
||
2: "2 – Ko-Erstellung",
|
||
3: "3 – KI-generiert (menschlich geprüft)",
|
||
},
|
||
"ai-code-level": {
|
||
label: "KI-Codeebene",
|
||
0: "0 – Menschlich geschrieben",
|
||
1: "1 – KI-unterstützt",
|
||
2: "2 – Überwiegend KI-generiert",
|
||
},
|
||
"ai-tools": {
|
||
label: "KI-Werkzeuge",
|
||
placeholder: "z. B. Claude, ChatGPT, Copilot",
|
||
},
|
||
"ai-description": {
|
||
label: "KI-Nutzungshinweis",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
};
|
||
|
||
function isObject(value) {
|
||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||
}
|
||
|
||
function mergeMissing(target, fallback) {
|
||
if (target === undefined) {
|
||
return fallback;
|
||
}
|
||
|
||
if (!isObject(target) || !isObject(fallback)) {
|
||
return target;
|
||
}
|
||
|
||
const merged = { ...target };
|
||
|
||
for (const [key, fallbackValue] of Object.entries(fallback)) {
|
||
merged[key] = mergeMissing(merged[key], fallbackValue);
|
||
}
|
||
|
||
return merged;
|
||
}
|
||
|
||
function applyOverrides(target, overrides) {
|
||
if (!isObject(target) || !isObject(overrides)) {
|
||
return target;
|
||
}
|
||
|
||
const merged = { ...target };
|
||
|
||
for (const [key, value] of Object.entries(overrides)) {
|
||
if (isObject(value)) {
|
||
const existing = isObject(merged[key]) ? merged[key] : {};
|
||
merged[key] = applyOverrides(existing, value);
|
||
continue;
|
||
}
|
||
|
||
merged[key] = value;
|
||
}
|
||
|
||
return merged;
|
||
}
|
||
|
||
async function exists(filePath) {
|
||
try {
|
||
await access(filePath);
|
||
return true;
|
||
} catch {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
let checkedEndpoints = 0;
|
||
let checkedLocales = 0;
|
||
let patchedLocales = 0;
|
||
|
||
for (const endpointPath of endpointCandidates) {
|
||
if (!(await exists(endpointPath))) {
|
||
continue;
|
||
}
|
||
|
||
checkedEndpoints += 1;
|
||
|
||
const sourcePath = path.join(endpointPath, "locales", `${sourceLocale}.json`);
|
||
|
||
if (!(await exists(sourcePath))) {
|
||
continue;
|
||
}
|
||
|
||
let sourceLocaleJson;
|
||
try {
|
||
sourceLocaleJson = JSON.parse(await readFile(sourcePath, "utf8"));
|
||
} catch {
|
||
continue;
|
||
}
|
||
|
||
checkedLocales += 1;
|
||
|
||
for (const locale of targetLocales) {
|
||
const localePath = path.join(endpointPath, "locales", `${locale}.json`);
|
||
checkedLocales += 1;
|
||
|
||
let localeJson = {};
|
||
if (await exists(localePath)) {
|
||
try {
|
||
localeJson = JSON.parse(await readFile(localePath, "utf8"));
|
||
} catch {
|
||
localeJson = {};
|
||
}
|
||
}
|
||
|
||
const merged = mergeMissing(localeJson, sourceLocaleJson);
|
||
const patched = applyOverrides(merged, localeOverrides[locale] || {});
|
||
|
||
if (JSON.stringify(patched) === JSON.stringify(localeJson)) {
|
||
continue;
|
||
}
|
||
|
||
await writeFile(localePath, `${JSON.stringify(patched, null, 2)}\n`, "utf8");
|
||
patchedLocales += 1;
|
||
}
|
||
}
|
||
|
||
if (checkedEndpoints === 0) {
|
||
console.log("[postinstall] No posts endpoint directories found");
|
||
} else if (patchedLocales === 0) {
|
||
console.log("[postinstall] posts locales already patched");
|
||
} else {
|
||
console.log(
|
||
`[postinstall] Patched posts locales in ${patchedLocales}/${checkedLocales} file(s)`,
|
||
);
|
||
}
|