RustyCMS: file-based headless CMS — API, Admin UI (content, types, assets), Docker/Caddy, image transform; only demo type and demo content in version control

Made-with: Cursor
This commit is contained in:
Peter Meier
2026-03-12 14:21:49 +01:00
parent aad93d145f
commit 7795a238e1
278 changed files with 15551 additions and 4072 deletions

263
admin-ui/messages/de.json Normal file
View File

@@ -0,0 +1,263 @@
{
"Sidebar": {
"dashboard": "Dashboard",
"types": "Typen",
"assets": "Assets",
"searchPlaceholder": "Sammlungen suchen…",
"searchAriaLabel": "Sammlungen suchen",
"closeMenu": "Menü schließen",
"loading": "Laden…",
"errorLoading": "Fehler beim Laden der Sammlungen",
"noResults": "Keine Ergebnisse für \"{query}\""
},
"ContentForm": {
"slugRequired": "Slug ist erforderlich.",
"slugInUse": "Slug bereits vergeben.",
"slugMustStartWith": "Der Slug muss mit \"{prefix}\" beginnen.",
"slugPrefix": "Präfix",
"slugSuffixPlaceholder": "z. B. meine-kampagne",
"slugSuffixAriaLabel": "Slug-Suffix (Präfix ist fest)",
"slugPlaceholder": "z. B. mein-beitrag",
"slugHint": "Kleinbuchstaben (a-z), Ziffern (0-9), Bindestriche. Leerzeichen werden zu Bindestrichen.",
"savedSuccessfully": "Erfolgreich gespeichert.",
"errorSaving": "Fehler beim Speichern",
"saving": "Speichern…",
"save": "Speichern",
"backToList": "Zurück zur Liste",
"pleaseSelect": "— Bitte auswählen —",
"removeEntry": "Entfernen",
"addEntry": "+ Eintrag hinzufügen",
"keyPlaceholder": "Schlüssel",
"valuePlaceholder": "Wert"
},
"SearchableSelect": {
"placeholder": "\u2014 Bitte ausw\u00e4hlen \u2014",
"clearLabel": "\u2014 Auswahl aufheben \u2014",
"filterPlaceholder": "Filtern\u2026",
"emptyLabel": "Keine Treffer"
},
"ReferenceField": {
"typeLabel": "Typ: {collection}",
"typesLabel": "Typen: {collections}",
"selectType": "\u2014 Typ w\u00e4hlen \u2014",
"newEntry": "Neuer Eintrag",
"noCollection": "Keine Referenz-Collection im Schema. Setze {collectionCode} oder {collectionsCode} im Typ, oder starte die API und lade die Seite neu."
},
"ReferenceArrayField": {
"typeLabel": "Typ: {collection}",
"typesLabel": "Typen: {collections}",
"componentType": "Komponententyp",
"selectType": "\u2014 Typ w\u00e4hlen \u2014",
"selectFromExisting": "\u2014 Aus vorhandenen w\u00e4hlen \u2014",
"filterPlaceholder": "Filtern\u2026",
"emptyLabel": "Keine Treffer",
"selectExistingAriaLabel": "Vorhandenen Eintrag zum Hinzuf\u00fcgen ausw\u00e4hlen",
"moveUp": "Nach oben",
"moveDown": "Nach unten",
"remove": "Entfernen",
"newComponent": "+ Neue {collection}-Komponente",
"createNewComponent": "+ Neue Komponente erstellen\u2026",
"openInNewTab": "In neuem Tab \u00f6ffnen; dann Seite neu laden.",
"noCollection": "Keine Referenz-Collection im Schema. Setze {collectionCode} oder {collectionsCode} im Typ, oder starte die API und lade die Seite neu."
},
"MarkdownEditor": {
"bold": "Fett",
"italic": "Kursiv",
"code": "Code",
"link": "Link",
"bulletList": "Aufz\u00e4hlungsliste",
"bulletListButton": "\u2022 Liste",
"placeholder": "Markdown eingeben\u2026 **fett**, *kursiv*, [Link](url), - Liste",
"preview": "Vorschau",
"emptyPreview": "Leer \u2014 Vorschau erscheint beim Tippen."
},
"PaginationLinks": {
"back": "Zur\u00fcck",
"next": "Weiter",
"pageInfo": "Seite {page} von {totalPages} ({total} Eintr\u00e4ge)"
},
"DataPreviewPanel": {
"hide": "Daten-Vorschau ausblenden",
"show": "Daten-Vorschau",
"loading": "Laden\u2026",
"errorLoading": "Fehler beim Laden"
},
"SchemaPanel": {
"hide": "Schema ausblenden",
"show": "Schema anzeigen"
},
"SchemaAndEditBar": {
"editSchema": "Schema bearbeiten"
},
"SchemaAndPreviewBar": {
"hideSchema": "Schema ausblenden",
"showSchema": "Schema anzeigen",
"editSchema": "Schema bearbeiten",
"hidePreview": "Daten-Vorschau ausblenden",
"showPreview": "Daten-Vorschau",
"loading": "Laden\u2026",
"errorLoading": "Fehler beim Laden"
},
"ReferenceOrInlineField": {
"reference": "Referenz",
"inline": "Eingebettet",
"inlineObject": "Eingebettetes Objekt (keine Referenz)",
"noInlineSchema": "Kein Inline-Schema. Seite neu laden oder API pr\u00fcfen (useFields / collection)."
},
"LocaleSwitcher": {
"label": "Sprache"
},
"ContentLocaleSwitcher": {
"label": "Inhaltssprache"
},
"Dashboard": {
"title": "Dashboard",
"subtitle": "W\u00e4hle eine Sammlung zur Inhaltsverwaltung.",
"noCollections": "Keine Sammlungen geladen. Pr\u00fcfe ob die RustyCMS-API unter {url} erreichbar ist."
},
"TypesPage": {
"title": "Typen",
"newType": "Neuer Typ",
"description": "Inhaltstypen (Sammlungen). Schema bearbeiten oder Typ l\u00f6schen. Beim L\u00f6schen wird nur die Typdefinitionsdatei entfernt; vorhandene Inhaltseintr\u00e4ge bleiben erhalten.",
"loading": "Laden\u2026",
"errorLoading": "Fehler beim Laden der Typen: {error}",
"noTypes": "Noch keine Typen vorhanden. Erstelle einen mit \"Neuer Typ\".",
"colName": "Name",
"colDescription": "Beschreibung",
"colCategory": "Kategorie",
"colActions": "Aktionen",
"confirmDelete": "\"{name}\" l\u00f6schen?",
"confirmDeleteFinal": "\"{name}\" wirklich l\u00f6schen? Dies kann nicht r\u00fcckg\u00e4ngig gemacht werden.",
"delete": "L\u00f6schen",
"yesDelete": "Ja, l\u00f6schen",
"deleting": "\u2026",
"cancel": "Abbrechen",
"edit": "Bearbeiten"
},
"NewTypePage": {
"title": "Neuen Typ anlegen",
"description": "Erstellt einen neuen Inhaltstyp (Sammlung). Die Schemadatei wird auf dem Server unter {path} gespeichert und per Hot-Reload geladen.",
"nameRequired": "Name ist erforderlich.",
"nameInvalid": "Name: nur Kleinbuchstaben, Ziffern und Unterstriche.",
"fieldRequired": "Mindestens ein Feld erforderlich.",
"fieldNamesUnique": "Feldnamen m\u00fcssen eindeutig sein.",
"errorCreating": "Fehler beim Erstellen des Typs.",
"nameLabel": "Name",
"namePlaceholder": "z.\u00a0B. produkt, blogbeitrag",
"nameHint": "Nur Kleinbuchstaben, Ziffern und Unterstriche.",
"descriptionLabel": "Beschreibung",
"categoryLabel": "Kategorie",
"categoryPlaceholder": "z.\u00a0B. inhalt",
"tagsLabel": "Tags (kommagetrennt)",
"tagsPlaceholder": "z.\u00a0B. inhalt, blog",
"strictLabel": "Strikt (unbekannte Felder ablehnen)",
"fieldsLabel": "Felder",
"addField": "Feld hinzuf\u00fcgen",
"fieldNamePlaceholder": "Feldname",
"required": "Pflichtfeld",
"removeField": "Feld entfernen",
"collectionPlaceholder": "Sammlung (z.\u00a0B. seite)",
"fieldDescriptionPlaceholder": "Feldbeschreibung (optional)",
"creating": "Erstellen\u2026",
"createType": "Typ erstellen",
"cancel": "Abbrechen"
},
"EditTypePage": {
"fieldRequired": "Mindestens ein Feld erforderlich.",
"fieldNamesUnique": "Feldnamen m\u00fcssen eindeutig sein.",
"errorSaving": "Fehler beim Speichern des Typs.",
"missingName": "Typname fehlt.",
"backToTypes": "Zur\u00fcck zu Typen",
"loading": "Laden\u2026",
"errorLoading": "Fehler beim Laden des Typs: {error}",
"title": "Typ bearbeiten: {name}",
"description": "Beschreibung, Kategorie, Tags und Felder \u00e4ndern. Die Schemadatei wird auf dem Server aktualisiert.",
"nameLabel": "Name",
"descriptionLabel": "Beschreibung",
"categoryLabel": "Kategorie",
"categoryPlaceholder": "z.\u00a0B. inhalt",
"tagsLabel": "Tags (kommagetrennt)",
"tagsPlaceholder": "z.\u00a0B. inhalt, blog",
"strictLabel": "Strikt (unbekannte Felder ablehnen)",
"fieldsLabel": "Felder",
"addField": "Feld hinzuf\u00fcgen",
"fieldNamePlaceholder": "Feldname",
"required": "Pflichtfeld",
"removeField": "Feld entfernen",
"collectionPlaceholder": "Sammlung (z.\u00a0B. seite)",
"fieldDescriptionPlaceholder": "Feldbeschreibung (optional)",
"saving": "Speichern\u2026",
"save": "Speichern",
"cancel": "Abbrechen"
},
"ErrorBoundary": {
"title": "Etwas ist schiefgelaufen",
"reload": "Seite neu laden"
},
"Breadcrumbs": {
"ariaLabel": "Breadcrumb",
"content": "Inhalte"
},
"ContentListPage": {
"title": "Einträge",
"newEntry": "Neuer Eintrag",
"colActions": "Aktionen",
"noEntries": "Keine Einträge.",
"noEntriesCreate": "Noch keine Einträge. Erstellen Sie den ersten.",
"edit": "Bearbeiten",
"searchPlaceholder": "Suchen…",
"loading": "Laden…",
"sortBy": "Sortieren nach {field}",
"sortAsc": "Aufsteigend",
"sortDesc": "Absteigend"
},
"ContentNewPage": {
"breadcrumbNew": "Neu",
"title": "Neuen Eintrag anlegen"
},
"ContentEditPage": {
"title": "Eintrag bearbeiten",
"apiLink": "API-Link (Daten-Vorschau):"
},
"AssetsPage": {
"titleAll": "Alle Assets",
"titleRoot": "Root",
"assetCount": "{count} Bild(er)",
"upload": "Hochladen",
"uploading": "Wird hochgeladen…",
"uploadedCount": "{count} Datei(en) hochgeladen.",
"dropZoneHintRoot": "Klicken oder hierher ziehen (Root)",
"dropZoneHintFolder": "Klicken oder hierher ziehen → \"{folder}\"",
"loading": "Laden…",
"errorLoading": "Fehler beim Laden der Assets",
"noAssets": "Noch keine Assets hier.",
"urlCopied": "URL kopiert.",
"copyUrl": "URL kopieren",
"confirmDelete": "\"{filename}\" löschen?",
"confirmDeleteDesc": "Dies kann nicht rückgängig gemacht werden.",
"yesDelete": "Ja, löschen",
"deleting": "…",
"cancel": "Abbrechen",
"deleted": "\"{filename}\" gelöscht.",
"folders": "Ordner",
"all": "Alle",
"root": "Root",
"newFolder": "Neuer Ordner",
"folderNamePlaceholder": "z. B. blog",
"folderCreated": "Ordner \"{name}\" erstellt.",
"folderDeleted": "Ordner \"{name}\" gelöscht.",
"confirmDeleteFolder": "Ordner \"{name}\" löschen?",
"confirmDeleteFolderDesc": "Nur leere Ordner können gelöscht werden.",
"renameTitle": "Bild umbenennen",
"renameFilenameLabel": "Dateiname",
"rename": "Umbenennen",
"renaming": "Wird umbenannt…",
"renamed": "\"{filename}\" umbenannt.",
"copyWithTransformTitle": "Kopie mit Transformation",
"copyWithTransformDesc": "Neues Asset aus diesem Bild mit Größe/Beschnitt/Format. Gleicher Ordner.",
"copyWithTransformNewName": "Neuer Dateiname",
"copyWithTransformCreate": "Kopie erstellen",
"copyWithTransformDone": "Transformierte Kopie erstellt.",
"creating": "Wird erstellt…"
}
}

263
admin-ui/messages/en.json Normal file
View File

@@ -0,0 +1,263 @@
{
"Sidebar": {
"dashboard": "Dashboard",
"types": "Types",
"assets": "Assets",
"searchPlaceholder": "Search collections…",
"searchAriaLabel": "Search collections",
"closeMenu": "Close menu",
"loading": "Loading…",
"errorLoading": "Error loading collections",
"noResults": "No results for \"{query}\""
},
"ContentForm": {
"slugRequired": "Slug is required.",
"slugInUse": "Slug already in use.",
"slugMustStartWith": "Slug must start with \"{prefix}\".",
"slugPrefix": "prefix",
"slugSuffixPlaceholder": "e.g. my-campaign",
"slugSuffixAriaLabel": "Slug suffix (prefix is fixed)",
"slugPlaceholder": "e.g. my-post",
"slugHint": "Lowercase letters (a-z), digits (0-9), hyphens. Spaces become hyphens.",
"savedSuccessfully": "Saved successfully.",
"errorSaving": "Error saving",
"saving": "Saving…",
"save": "Save",
"backToList": "Back to list",
"pleaseSelect": "— Please select —",
"removeEntry": "Remove",
"addEntry": "+ Add entry",
"keyPlaceholder": "Key",
"valuePlaceholder": "Value"
},
"SearchableSelect": {
"placeholder": "— Please select —",
"clearLabel": "— Clear selection —",
"filterPlaceholder": "Filter…",
"emptyLabel": "No matches"
},
"ReferenceField": {
"typeLabel": "Type: {collection}",
"typesLabel": "Types: {collections}",
"selectType": "— Select type —",
"newEntry": "New entry",
"noCollection": "No reference collection in schema. Set {collectionCode} or {collectionsCode} in the type, or start the API and reload the page."
},
"ReferenceArrayField": {
"typeLabel": "Type: {collection}",
"typesLabel": "Types: {collections}",
"componentType": "Component type",
"selectType": "— Select type —",
"selectFromExisting": "— Select from existing —",
"filterPlaceholder": "Filter…",
"emptyLabel": "No matches",
"selectExistingAriaLabel": "Select existing entry to add",
"moveUp": "Move up",
"moveDown": "Move down",
"remove": "Remove",
"newComponent": "+ New {collection} component",
"createNewComponent": "+ Create new component…",
"openInNewTab": "Open in new tab; then reload this page.",
"noCollection": "No reference collection in schema. Set {collectionCode} or {collectionsCode} in the type, or start the API and reload the page."
},
"MarkdownEditor": {
"bold": "Bold",
"italic": "Italic",
"code": "Code",
"link": "Link",
"bulletList": "Bullet list",
"bulletListButton": "• List",
"placeholder": "Enter markdown… **bold**, *italic*, [link](url), - list",
"preview": "Preview",
"emptyPreview": "Empty — preview appears as you type."
},
"PaginationLinks": {
"back": "Back",
"next": "Next",
"pageInfo": "Page {page} of {totalPages} ({total} entries)"
},
"DataPreviewPanel": {
"hide": "Hide data preview",
"show": "Data preview",
"loading": "Loading…",
"errorLoading": "Error loading"
},
"SchemaPanel": {
"hide": "Hide schema",
"show": "Show schema"
},
"SchemaAndEditBar": {
"editSchema": "Edit schema"
},
"SchemaAndPreviewBar": {
"hideSchema": "Hide schema",
"showSchema": "Show schema",
"editSchema": "Edit schema",
"hidePreview": "Hide data preview",
"showPreview": "Data preview",
"loading": "Loading…",
"errorLoading": "Error loading"
},
"ReferenceOrInlineField": {
"reference": "Reference",
"inline": "Inline",
"inlineObject": "Inline object (no reference)",
"noInlineSchema": "No inline schema. Reload or check API (useFields / collection)."
},
"LocaleSwitcher": {
"label": "Language"
},
"ContentLocaleSwitcher": {
"label": "Content language"
},
"Dashboard": {
"title": "Dashboard",
"subtitle": "Choose a collection to manage content.",
"noCollections": "No collections loaded. Check that the RustyCMS API is running at {url}."
},
"TypesPage": {
"title": "Types",
"newType": "New type",
"description": "Content types (collections). Edit the schema or delete a type. Deleting removes the type definition file; existing content entries are not removed.",
"loading": "Loading…",
"errorLoading": "Error loading types: {error}",
"noTypes": "No types yet. Create one with \"New type\".",
"colName": "Name",
"colDescription": "Description",
"colCategory": "Category",
"colActions": "Actions",
"confirmDelete": "Delete \"{name}\"?",
"confirmDeleteFinal": "Really delete \"{name}\"? This cannot be undone.",
"delete": "Delete",
"yesDelete": "Yes, delete",
"deleting": "…",
"cancel": "Cancel",
"edit": "Edit"
},
"NewTypePage": {
"title": "Add new type",
"description": "Creates a new content type (collection). The schema file is saved on the server at {path} and loaded via hot-reload.",
"nameRequired": "Name is required.",
"nameInvalid": "Name: only lowercase letters, digits and underscores.",
"fieldRequired": "At least one field required.",
"fieldNamesUnique": "Field names must be unique.",
"errorCreating": "Error creating type.",
"nameLabel": "Name",
"namePlaceholder": "e.g. product, blog_post",
"nameHint": "Lowercase letters, digits and underscores only.",
"descriptionLabel": "Description",
"categoryLabel": "Category",
"categoryPlaceholder": "e.g. content",
"tagsLabel": "Tags (comma-separated)",
"tagsPlaceholder": "e.g. content, blog",
"strictLabel": "Strict (reject unknown fields)",
"fieldsLabel": "Fields",
"addField": "Add field",
"fieldNamePlaceholder": "Field name",
"required": "Required",
"removeField": "Remove field",
"collectionPlaceholder": "Collection (e.g. page)",
"fieldDescriptionPlaceholder": "Field description (optional)",
"creating": "Creating…",
"createType": "Create type",
"cancel": "Cancel"
},
"EditTypePage": {
"fieldRequired": "At least one field required.",
"fieldNamesUnique": "Field names must be unique.",
"errorSaving": "Error saving type.",
"missingName": "Missing type name.",
"backToTypes": "Back to Types",
"loading": "Loading…",
"errorLoading": "Error loading type: {error}",
"title": "Edit type: {name}",
"description": "Change description, category, tags, and fields. The schema file is updated on the server.",
"nameLabel": "Name",
"descriptionLabel": "Description",
"categoryLabel": "Category",
"categoryPlaceholder": "e.g. content",
"tagsLabel": "Tags (comma-separated)",
"tagsPlaceholder": "e.g. content, blog",
"strictLabel": "Strict (reject unknown fields)",
"fieldsLabel": "Fields",
"addField": "Add field",
"fieldNamePlaceholder": "Field name",
"required": "Required",
"removeField": "Remove field",
"collectionPlaceholder": "Collection (e.g. page)",
"fieldDescriptionPlaceholder": "Field description (optional)",
"saving": "Saving…",
"save": "Save",
"cancel": "Cancel"
},
"ErrorBoundary": {
"title": "Something went wrong",
"reload": "Reload page"
},
"Breadcrumbs": {
"ariaLabel": "Breadcrumb",
"content": "Content"
},
"ContentListPage": {
"title": "Entries",
"newEntry": "New entry",
"colActions": "Actions",
"noEntries": "No entries.",
"noEntriesCreate": "No entries yet. Create the first one.",
"edit": "Edit",
"searchPlaceholder": "Search…",
"loading": "Loading…",
"sortBy": "Sort by {field}",
"sortAsc": "Ascending",
"sortDesc": "Descending"
},
"ContentNewPage": {
"breadcrumbNew": "New",
"title": "Create new entry"
},
"ContentEditPage": {
"title": "Edit entry",
"apiLink": "API link (data preview):"
},
"AssetsPage": {
"titleAll": "All assets",
"titleRoot": "Root",
"assetCount": "{count} image(s)",
"upload": "Upload",
"uploading": "Uploading…",
"uploadedCount": "Uploaded {count} file(s).",
"dropZoneHintRoot": "Click or drag & drop to upload to root",
"dropZoneHintFolder": "Click or drag & drop to upload to \"{folder}\"",
"loading": "Loading…",
"errorLoading": "Error loading assets",
"noAssets": "No assets here yet.",
"urlCopied": "URL copied.",
"copyUrl": "Copy URL",
"confirmDelete": "Delete \"{filename}\"?",
"confirmDeleteDesc": "This cannot be undone.",
"yesDelete": "Yes, delete",
"deleting": "…",
"cancel": "Cancel",
"deleted": "\"{filename}\" deleted.",
"folders": "Folders",
"all": "All",
"root": "Root",
"newFolder": "New folder",
"folderNamePlaceholder": "e.g. blog",
"folderCreated": "Folder \"{name}\" created.",
"folderDeleted": "Folder \"{name}\" deleted.",
"confirmDeleteFolder": "Delete folder \"{name}\"?",
"confirmDeleteFolderDesc": "Only empty folders can be deleted.",
"renameTitle": "Rename image",
"renameFilenameLabel": "Filename",
"rename": "Rename",
"renaming": "Renaming…",
"renamed": "\"{filename}\" renamed.",
"copyWithTransformTitle": "Copy with transformation",
"copyWithTransformDesc": "Create a new asset from this image with resize/crop/format. Same folder.",
"copyWithTransformNewName": "New filename",
"copyWithTransformCreate": "Create copy",
"copyWithTransformDone": "Transformed copy created.",
"creating": "Creating…"
}
}