Files
indiekit-server/scripts/patch-endpoint-posts-locales.mjs
Sven 6c5b003bcf fix(i18n): extend posts locale patch to cover beta.41 and filter/purge/AI keys
- 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>
2026-03-15 23:28:56 +01:00

201 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)`,
);
}