Improve documentation for translations API endpoints

I tinkered quite a bit with Kirby’s API and also the KQL plugin, often reading about the API internals and trying out different requests in Insomnia (an API tester).

Now I hit quite a dead end with the translations API (/api/translations), how it works and how it was meant to be used, and I believe these two pages about the two translation endpoints need some improvement.

I can create custom language variables in e.g. de.php and use them in Kirby templates. Also, I can call /translations/de to retrieve the translations, based on the documentation. Yet, this endpoint doesn’t return my variables defined in something like de.php, but all translation variables of the CMS/Panel. The same goes for the /translations endpoint, which returns, I assume, all available translations of the CMS/panel. Conclusion: Either doesn’t work the way I understand it from the documentation, or I’m missing something that the documentation isn’t telling me. Based on the documentation and the API results, I have no clue how to retrieve my custom language variables.

Then, there are several parameters that help me to define what kind of translations I want to have returned. Yet, those parameters are not really documented or linked to a documentation in the documentation page itself. For example, there is an author field, but I have actually no clue what this is and where it is coming from. Could I retrieve my very own translation variables by submitting the correct author? How am I supposed to know the author of a translation? Where is this defined?

Instead of simply answering those questions, I believe best would be to improve this part of the documentation, yet as I seem not to understand the endpoint, maybe someone would like to take that over.

If you feel like I’m totally wrong on that, feel free to relabel this post as a question.

I think there is no disagreement that the API docs can be improved certainly.

Nevertheless, as I am not sure when we will get to that, I’ll try to answer your questions here already.

The endpoint, e.g. translations/de should always get you all translation/i18n strings for that language - so not only our core strings for the Panel and error exceptions, but also your custom ones as well as all strings from all plugins. Are you sure that your strings aren’t included? In that case it would be interesting to see how you are registering them.

And what you are referring to as parameters (author etc.) aren’t parameters but fields in the API response. These special fields are specific translation keys in our translation files that contain some additional information about the translation instead of being real i18n strings, e.g. https://github.com/getkirby/kirby/blob/master/i18n/translations/de.json#L470-L473

For the translations/de endpoint (as an example) there are only two available parameters: pretty for getting pretty-printed response (for debugging) and select which lets you select the fields that the response should have (e.g. only direction and id). To be fair I think in case of this endpoint, the select parameter is less relevant than for other endpoints, but it is supported.

The endpoint, e.g. translations/de should always get you all translation/i18n strings for that language - so not only our core strings for the Panel and error exceptions, but also your custom ones as well as all strings from all plugins. Are you sure that your strings aren’t included? In that case it would be interesting to see how you are registering them.

I’m quite sure they are not there. But, I’m also aware that I might not understand this endpoint or might not register it correctly for API use. I followed the guides and at least in the Kirby templates, they seem to work.

That’s the way I register them (site/languages/de.php):


<?php

return [
    'code' => 'de',
    'default' => true,
    'direction' => 'ltr',
    'locale' => [
        'LC_ALL' => 'de'
    ],
    'name' => 'Deutsch',
    'translations' => [
        'labelname' => 'Name',
        'labelemail' => 'E-Mail',
        'labelresidence' => 'Wohnort',
        'labelquestion' => 'Frage stellen'
    ],
    'url' => ''
];

And when I send a request to api/translations/de (without any language header), I get this:

{
  "code": 200,
  "data": {
    "author": "Kirby Team",
    "data": {
      "add": "HinzufĂŒgen",
      "avatar": "Profilbild",
      "back": "ZurĂŒck",
      "cancel": "Abbrechen",
      "change": "Ändern",
      "close": "Schließen",
      "confirm": "OK",
      "collapse": "Zusammenklappen",
      "collapse.all": "Alle zusammenklappen",
      "copy": "Kopieren",
      "create": "Erstellen",
      "date": "Datum",
      "date.select": "Datum auswÀhlen",
      "day": "Tag",
      "days.fri": "Fr",
      "days.mon": "Mo",
      "days.sat": "Sa",
      "days.sun": "So",
      "days.thu": "Do",
      "days.tue": "Di",
      "days.wed": "Mi",
      "delete": "Löschen",
      "delete.all": "Alle löschen",
      "dimensions": "Maße",
      "disabled": "Gesperrt",
      "discard": "Verwerfen",
      "download": "Download",
      "duplicate": "Duplizieren",
      "edit": "Bearbeiten",
      "expand": "Aufklappen",
      "expand.all": "Alle aufklappen",
      "dialog.files.empty": "Keine verfĂŒgbaren Dateien",
      "dialog.pages.empty": "Keine verfĂŒgbaren Seiten",
      "dialog.users.empty": "Keine verfĂŒgbaren Accounts",
      "email": "E-Mail",
      "email.placeholder": "mail@beispiel.de",
      "error.access.code": "UngĂŒltiger Code",
      "error.access.login": "UngĂŒltige Zugangsdaten",
      "error.access.panel": "Du hast keinen Zugang zum Panel",
      "error.access.view": "Du hast keinen Zugriff auf diesen Teil des Panels",
      "error.avatar.create.fail": "Das Profilbild konnte nicht hochgeladen werden",
      "error.avatar.delete.fail": "Das Profilbild konnte nicht gelöscht werden",
      "error.avatar.dimensions.invalid": "Bitte lade ein Profilbild hoch, das nicht breiter oder höher als 3000 Pixel ist.",
      "error.avatar.mime.forbidden": "Das Profilbild muss vom Format JPEG oder PNG sein",
      "error.blueprint.notFound": "Das Blueprint \"{name}\" konnte nicht geladen werden.",
      "error.blocks.max.plural": "Bitte fĂŒge nicht mehr als {max} Blöcke hinzu",
      "error.blocks.max.singular": "Bitte fĂŒge nicht mehr als einen Block hinzu",
      "error.blocks.min.plural": "Bitte fĂŒge mindestens {min} Blöcke hinzu",
      "error.blocks.min.singular": "Bitte fĂŒge mindestens einen Block hinzu",
      "error.blocks.validation": "Fehler in Block {index}",
      "error.email.preset.notFound": "Die E-Mailvorlage \"{name}\" wurde nicht gefunden",
      "error.field.converter.invalid": "UngĂŒltiger Konverter:  \"{converter}\"",
      "error.file.changeName.empty": "Bitte gib einen Namen an",
      "error.file.changeName.permission": "Du darfst den Dateinamen von \"{filename}\" nicht Àndern",
      "error.file.duplicate": "Eine Datei mit dem Dateinamen \"{filename}\" besteht bereits",
      "error.file.extension.forbidden": "Verbotene Dateiendung \"{extension}\"",
      "error.file.extension.invalid": "Verbotene Dateiendung \"{extension}\"",
      "error.file.extension.missing": "Du kannst keine Dateien ohne Dateiendung hochladen",
      "error.file.maxheight": "Die Bildhöhe darf {height} Pixel nicht ĂŒberschreiten",
      "error.file.maxsize": "Die Datei ist zu groß",
      "error.file.maxwidth": "Die Bildbreite darf {height} Pixel nicht ĂŒberschreiten",
      "error.file.mime.differs": "Die Datei muss den Medientyp \"{mime}\" haben.",
      "error.file.mime.forbidden": "Der Medientyp \"{mime}\"  ist nicht erlaubt",
      "error.file.mime.invalid": "UngĂŒltiger Dateityp: {mime}",
      "error.file.mime.missing": "Der Medientyp fĂŒr \"{filename}\" konnte nicht erkannt werden",
      "error.file.minheight": "Die Bildhöhe muss mindestens {height} Pixel betragen",
      "error.file.minsize": "Die Datei ist zu klein",
      "error.file.minwidth": "Die Bildbreite muss mindestens {height} Pixel betragen",
      "error.file.name.missing": "Bitte gib einen Dateinamen an",
      "error.file.notFound": "Die Datei \"{filename}\" konnte nicht gefunden werden",
      "error.file.orientation": "Das Bildformat ist ungĂŒltig. Erwartetes Format: \"{orientation}\"",
      "error.file.type.forbidden": "Du kannst keinen {type}-Dateien hochladen",
      "error.file.type.invalid": "UngĂŒltiger Dateityp: {mime}",
      "error.file.undefined": "Die Datei konnte nicht gefunden werden",
      "error.form.incomplete": "Bitte behebe alle Fehler 
",
      "error.form.notSaved": "Das Formular konnte nicht gespeichert werden",
      "error.language.code": "Bitte gib einen gĂŒltigen Code fĂŒr die Sprache an",
      "error.language.duplicate": "Die Sprache besteht bereits",
      "error.language.name": "Bitte gib einen gĂŒltigen Namen fĂŒr die Sprache an",
      "error.layout.validation.block": "Fehler in Block {blockindex} in Layout {layoutIndex}",
      "error.layout.validation.settings": "Fehler in den Einstellungen von Layout {index}",
      "error.license.format": "Bitte gib einen gĂŒltigen LizenzschlĂŒssel ein",
      "error.license.email": "Bitte gib eine gĂŒltige E-Mailadresse an",
      "error.license.verification": "Die Lizenz konnte nicht verifiziert werden",
      "error.page.changeSlug.permission": "Du darfst die URL der Seite \"{slug}\" nicht Àndern",
      "error.page.changeStatus.incomplete": "Die Seite ist nicht vollstÀndig und kann daher nicht veröffentlicht werden",
      "error.page.changeStatus.permission": "Der Status der Seite kann nicht geÀndert werden",
      "error.page.changeStatus.toDraft.invalid": "Die Seite \"{slug}\" kann nicht in einen Entwurf umgewandelt werden",
      "error.page.changeTemplate.invalid": "Die Vorlage fĂŒr die Seite \"{slug}\" kann nicht geĂ€ndert werden",
      "error.page.changeTemplate.permission": "Du kannst die Vorlage fĂŒr die Seite \"{slug}\" nicht Ă€ndern",
      "error.page.changeTitle.empty": "Bitte gib einen Titel an",
      "error.page.changeTitle.permission": "Du kannst den Titel fĂŒr die Seite \"{slug}\" nicht Ă€ndern",
      "error.page.create.permission": "Du kannst die Seite \"{slug}\" nicht anlegen",
      "error.page.delete": "Die Seite \"{slug}\" kann nicht gelöscht werden",
      "error.page.delete.confirm": "Bitte gib zur BestÀtigung den Seitentitel ein",
      "error.page.delete.hasChildren": "Die Seite hat Unterseiten und kann nicht gelöscht werden",
      "error.page.delete.permission": "Du kannst die Seite \"{slug}\" nicht löschen",
      "error.page.draft.duplicate": "Ein Entwurf mit dem URL-KĂŒrzel \"{slug}\" besteht bereits",
      "error.page.duplicate": "Eine Seite mit dem URL-KĂŒrzel \"{slug}\" besteht bereits",
      "error.page.duplicate.permission": "Du kannst die Seite \"{slug}\" nicht duplizieren",
      "error.page.notFound": "Die Seite \"{slug}\" konnte nicht gefunden werden",
      "error.page.num.invalid": "Bitte gib eine gĂŒltige Sortierungszahl an. Negative Zahlen sind nicht erlaubt.",
      "error.page.slug.invalid": "Bitte gib ein gĂŒltiges URL-KĂŒrzel an",
      "error.page.slug.maxlength": "Die PfadlĂ€nge darf {length} Zeichen nicht ĂŒberschreiten",
      "error.page.sort.permission": "Die Seite \"{slug}\" kann nicht umsortiert werden",
      "error.page.status.invalid": "Bitte gib einen gĂŒltigen Seitenstatus an",
      "error.page.undefined": "Die Seite konnte nicht gefunden werden",
      "error.page.update.permission": "Du kannst die Seite \"{slug}\" nicht editieren",
      "error.section.files.max.plural": "Bitte fĂŒge nicht mehr als {max} Dateien zum Bereich \"{section}\" hinzu",
      "error.section.files.max.singular": "Bitte fĂŒge nicht mehr als eine Datei zum Bereich \"{section}\" hinzu",
      "error.section.files.min.plural": "Der Bereich \"{section}\" benötigt mindestens {min} Dateien",
      "error.section.files.min.singular": "Der Bereich \"{section}\" benötigt mindestens eine Datei",
      "error.section.pages.max.plural": "Bitte fĂŒge nicht mehr als {max} Seiten zum Bereich \"{section}\" hinzu",
      "error.section.pages.max.singular": "Bitte fĂŒge nicht mehr als eine Seite zum Bereich \"{section}\" hinzu",
      "error.section.pages.min.plural": "Der Bereich \"{section}\" benötigt mindestens {min} Seiten",
      "error.section.pages.min.singular": "Der Bereich \"{section}\" benötigt mindestens eine Seite",
      "error.section.notLoaded": "Der Bereich \"{name}\" konnte nicht geladen werden",
      "error.section.type.invalid": "Der Bereichstyp \"{type}\" ist nicht gĂŒltig",
      "error.site.changeTitle.empty": "Bitte gib einen Titel an",
      "error.site.changeTitle.permission": "Du kannst den Titel der Seite nicht Àndern",
      "error.site.update.permission": "Du darfst die Seite nicht bearbeiten",
      "error.template.default.notFound": "Die \"Default\"-Vorlage existiert nicht",
      "error.user.changeEmail.permission": "Du kannst die E-Mailadresse fĂŒr den Account \"{name}\" nicht Ă€ndern",
      "error.user.changeLanguage.permission": "Du kannst die Sprache fĂŒr den Account \"{name}\" nicht Ă€ndern",
      "error.user.changeName.permission": "Du kannst den Namen fĂŒr den Account \"{name}\" nicht Ă€ndern",
      "error.user.changePassword.permission": "Du kannst das Passwort fĂŒr den Account \"{name}\" nicht Ă€ndern",
      "error.user.changeRole.lastAdmin": "Die Rolle des letzten Accounts mit Administrationsrechten kann nicht geÀndert werden",
      "error.user.changeRole.permission": "Du kannst die Rolle fĂŒr den Benutzer \"{name}\" nicht Ă€ndern",
      "error.user.changeRole.toAdmin": "Du darfst die Admin-Rolle nicht an andere Accounts vergeben",
      "error.user.create.permission": "Du darfst diesen Account nicht anlegen",
      "error.user.delete": "Der Account \"{name}\" konnte nicht gelöscht werden",
      "error.user.delete.lastAdmin": "Du kannst den letzten Account mit Administrationsrechten nicht löschen",
      "error.user.delete.lastUser": "Der letzte Account kann nicht gelöscht werden",
      "error.user.delete.permission": "Du darfst den Account \"{name}\" nicht löschen",
      "error.user.duplicate": "Ein Account mit der E-Mailadresse \"{email}\" besteht bereits",
      "error.user.email.invalid": "Bitte gib eine gĂŒltige E-Mailadresse an",
      "error.user.language.invalid": "Bitte gib eine gĂŒltige Sprache an",
      "error.user.notFound": "Der Account \"{name}\" wurde nicht gefunden",
      "error.user.password.invalid": "Bitte gib ein gĂŒltiges Passwort ein. Passwörter mĂŒssen mindestens 8 Zeichen lang sein.",
      "error.user.password.notSame": "Die Passwörter stimmen nicht ĂŒberein",
      "error.user.password.undefined": "Der Account hat kein Passwort",
      "error.user.role.invalid": "Bitte gib eine gĂŒltige Rolle an",
      "error.user.update.permission": "Du darfst den den Account \"{name}\" nicht bearbeiten",
      "error.validation.accepted": "Bitte bestÀtige",
      "error.validation.alpha": "Bitte gib nur Zeichen zwischen A und Z ein",
      "error.validation.alphanum": "Bitte gib nur Zeichen zwischen A und Z und Zahlen zwischen 0 und 9 ein",
      "error.validation.between": "Bitte gib einen Wert zwischen \"{min}\" und \"{max}\" ein",
      "error.validation.boolean": "Bitte bestÀtige oder lehne ab",
      "error.validation.contains": "Bitte gib einen Wert ein, der \"{needle}\" enthÀlt",
      "error.validation.date": "Bitte gib ein gĂŒltiges Datum ein",
      "error.validation.date.after": "Bitte gib ein Datum nach dem {date} ein",
      "error.validation.date.before": "Bitte gib ein Datum vor dem {date} ein",
      "error.validation.date.between": "Bitte gib ein Datum zwischen dem {min} und dem {max} ein",
      "error.validation.denied": "Bitte lehne die Eingabe ab",
      "error.validation.different": "Der Wert darf nicht \"{other}\" sein",
      "error.validation.email": "Bitte gib eine gĂŒltige E-Mailadresse an",
      "error.validation.endswith": "Der Wert muss auf \"{end}\" enden",
      "error.validation.filename": "Bitte gib einen gĂŒltigen Dateinamen ein",
      "error.validation.in": "Bitte gib einen der folgenden Werte ein: ({in})",
      "error.validation.integer": "Bitte gib eine ganze Zahl ein",
      "error.validation.ip": "Bitte gib eine gĂŒltige IP Adresse ein",
      "error.validation.less": "Bitte gib einen Wert kleiner als {max} ein",
      "error.validation.match": "Der Wert entspricht nicht dem erwarteten Muster",
      "error.validation.max": "Bitte gib einen Wert ein, der nicht grĂ¶ĂŸer als {max} ist",
      "error.validation.maxlength": "Bitte gib einen kĂŒrzeren Text ein (max. {max} Zeichen)",
      "error.validation.maxwords": "Bitte nutze nicht mehr als {max} Wort(e)",
      "error.validation.min": "Bitte gib einen Wert ein, der nicht kleiner als {min} ist",
      "error.validation.minlength": "Bitte gib einen lÀngeren Text ein. (min. {min} Zeichen)",
      "error.validation.minwords": "Bitte nutze mindestens {min} Wort(e)",
      "error.validation.more": "Bitte gib einen grĂ¶ĂŸeren Wert als {min} ein",
      "error.validation.notcontains": "Bitte gib einen Wert ein, der nicht \"{needle}\" enthÀlt",
      "error.validation.notin": "Bitte gib keinen der folgenden Werte ein: ({notIn})",
      "error.validation.option": "Bitte wĂ€hle eine gĂŒltige Option aus",
      "error.validation.num": "Bitte gib eine gĂŒltige Zahl an",
      "error.validation.required": "Bitte gib etwas ein",
      "error.validation.same": "Bitte gib \"{other}\" ein",
      "error.validation.size": "Die GrĂ¶ĂŸe des Wertes muss \"{size}\" sein",
      "error.validation.startswith": "Der Wert muss mit \"{start}\" beginnen",
      "error.validation.time": "Bitte gib eine gĂŒltige Uhrzeit ein",
      "error.validation.time.after": "Bitte gib eine Zeit nach {time} ein",
      "error.validation.time.before": "Bitte gib eine Zeit vor {time} ein",
      "error.validation.time.between": "Bitte gib eine Zeit zwischen {min} und {max} ein",
      "error.validation.url": "Bitte gib eine gĂŒltige URL ein",
      "field.required": "Das Feld ist Pflicht",
      "field.blocks.changeType": "Blocktyp Àndern",
      "field.blocks.code.name": "Code",
      "field.blocks.code.language": "Sprache",
      "field.blocks.code.placeholder": "Code 
",
      "field.blocks.delete.confirm": "Willst du diesen Block wirklich löschen?",
      "field.blocks.delete.confirm.all": "Willst du wirklich alle Blöcke löschen?",
      "field.blocks.delete.confirm.selected": "Willst du wirklich die ausgewÀhlten Blöcke löschen?",
      "field.blocks.empty": "Keine Blöcke",
      "field.blocks.fieldsets.label": "Bitte wÀhle einen Blocktyp aus 
",
      "field.blocks.gallery.name": "Galerie",
      "field.blocks.gallery.images.empty": "Keine Bilder",
      "field.blocks.gallery.images.label": "Bilder",
      "field.blocks.heading.level": "Ebene",
      "field.blocks.heading.name": "Überschrift",
      "field.blocks.heading.text": "Text",
      "field.blocks.heading.placeholder": "Überschrift 
",
      "field.blocks.image.alt": "Alternativer Text",
      "field.blocks.image.caption": "Bildunterschrift",
      "field.blocks.image.crop": "Beschneiden",
      "field.blocks.image.link": "Link",
      "field.blocks.image.location": "Ort",
      "field.blocks.image.name": "Bild",
      "field.blocks.image.placeholder": "Bild auswÀhlen",
      "field.blocks.image.ratio": "SeitenverhÀltnis",
      "field.blocks.list.name": "Liste",
      "field.blocks.markdown.name": "Markdown",
      "field.blocks.markdown.label": "Text",
      "field.blocks.markdown.placeholder": "Markdown 
",
      "field.blocks.quote.name": "Zitat",
      "field.blocks.quote.text.label": "Text",
      "field.blocks.quote.text.placeholder": "Zitat 
",
      "field.blocks.quote.citation.label": "Quelle",
      "field.blocks.quote.citation.placeholder": "Quelle 
",
      "field.blocks.text.name": "Text",
      "field.blocks.text.placeholder": "Text 
",
      "field.blocks.video.caption": "Bildunterschrift",
      "field.blocks.video.name": "Video",
      "field.blocks.video.placeholder": "Video-URL eingeben",
      "field.blocks.video.url.label": "Video-URL",
      "field.blocks.video.url.placeholder": "https:\/\/youtube.com\/?v=",
      "field.files.empty": "Keine Dateien ausgewÀhlt",
      "field.layout.delete": "Layout löschen",
      "field.layout.delete.confirm": "Willst du dieses Layout wirklich löschen?",
      "field.layout.empty": "Keine Layouts",
      "field.layout.select": "Layout auswÀhlen",
      "field.pages.empty": "Keine Seiten ausgewÀhlt",
      "field.structure.delete.confirm": "Willst du diesen Eintrag wirklich löschen?",
      "field.structure.empty": "Es bestehen keine EintrÀge.",
      "field.users.empty": "Keine Accounts ausgewÀhlt",
      "file.blueprint": "Du kannst zusĂ€tzliche Felder und Bereiche fĂŒr diese Datei in <strong>\/site\/blueprints\/{template}.yml<\/strong> anlegen",
      "file.delete.confirm": "Willst du die Datei <strong>{filename}<\/strong> <br>wirklich löschen?",
      "file.sort": "Position Àndern",
      "files": "Dateien",
      "files.empty": "Keine Dateien",
      "hide": "Verbergen",
      "hour": "Stunde",
      "insert": "EinfĂŒgen",
      "insert.after": "Danach einfĂŒgen",
      "insert.before": "Davor einfĂŒgen",
      "install": "Installieren",
      "installation": "Installation",
      "installation.completed": "Das Panel wurde installiert",
      "installation.disabled": "Die Panel-Installation ist auf öffentlichen Servern automatisch deaktiviert. Bitte installiere das Panel auf einem lokalen Server oder aktiviere die Installation gezielt mit der <code>panel.install<\/code> Option. ",
      "installation.issues.accounts": "<code>\/site\/accounts<\/code> ist nicht beschreibbar",
      "installation.issues.content": "<code>\/content<\/code> existiert nicht oder ist nicht beschreibbar",
      "installation.issues.curl": "Die <code>CURL<\/code> Erweiterung wird benötigt",
      "installation.issues.headline": "Das Panel kann nicht installiert werden",
      "installation.issues.mbstring": "Die <code>MB String<\/code> Erweiterung wird benötigt",
      "installation.issues.media": "Der <code>\/media<\/code> Ordner ist nicht beschreibbar",
      "installation.issues.php": "Bitte verwende <code>PHP 7+<\/code>",
      "installation.issues.server": "Kirby benötigt <code>Apache<\/code>, <code>Nginx<\/code> or <code>Caddy<\/code>",
      "installation.issues.sessions": "<code>\/site\/sessions<\/code> ist nicht beschreibbar",
      "language": "Sprache",
      "language.code": "Code",
      "language.convert": "Als Standard auswÀhlen",
      "language.convert.confirm": "<p>Willst du <strong>{name}<\/strong> wirklich in die Standardsprache umwandeln? Dieser Schritt kann nicht rĂŒckgĂ€ngig gemacht werden.<\/p><p>Wenn <strong>{name}<\/strong> unĂŒbersetzte Felder hat, gibt es keine gĂŒltigen Standardwerte fĂŒr diese Felder und Inhalte könnten verloren gehen.<\/p>",
      "language.create": "Neue Sprache anlegen",
      "language.delete.confirm": "Willst du <strong>{name}<\/strong> inklusive aller Übersetzungen wirklich löschen? Dieser Schritt kann nicht rĂŒckgĂ€ngig gemacht werden!",
      "language.deleted": "Die Sprache wurde gelöscht",
      "language.direction": "Leserichtung",
      "language.direction.ltr": "Von links nach rechts",
      "language.direction.rtl": "Von rechts nach links",
      "language.locale": "PHP locale string",
      "language.locale.warning": "Du nutzt ein angepasstes Setup for PHP Locales. Bitte bearbeite dieses direkt in der entsprechenden Sprachdatei in \/site\/languages",
      "language.name": "Name",
      "language.updated": "Die Sprache wurde gespeichert",
      "languages": "Sprachen",
      "languages.default": "Standardsprache",
      "languages.empty": "Noch keine Sprachen",
      "languages.secondary": "SekundÀre Sprachen",
      "languages.secondary.empty": "Noch keine sekundÀren Sprachen",
      "license": "Lizenz",
      "license.buy": "Kaufe eine Lizenz",
      "license.register": "Registrieren",
      "license.register.help": "Den Lizenzcode findest du in der BestĂ€tigungsmail zu deinem Kauf. Bitte kopiere und fĂŒge ihn ein, um Kirby zu registrieren.",
      "license.register.label": "Bitte gib deinen Lizenzcode ein",
      "license.register.success": "Vielen Dank fĂŒr deine UnterstĂŒtzung",
      "license.unregistered": "Dies ist eine unregistrierte Kirby-Demo",
      "link": "Link",
      "link.text": "Linktext",
      "loading": "Laden",
      "lock.unsaved": "Ungespeicherte Änderungen",
      "lock.unsaved.empty": "Keine ungespeicherten Änderungen",
      "lock.isLocked": "Ungespeicherte Änderungen von <strong>{email}<\/strong>",
      "lock.file.isLocked": "Die Datei wird von {email} bearbeitet und kann nicht geÀndert werden.",
      "lock.page.isLocked": "Die Seite wird von {email} bearbeitet und kann nicht geÀndert werden.",
      "lock.unlock": "Entsperren",
      "lock.isUnlocked": "Deine ungespeicherten Änderungen wurden von einem anderen Account ĂŒberschrieben. Du kannst sie herunterladen, um sie manuell einzufĂŒgen. ",
      "login": "Anmelden",
      "login.code.label.login": "Anmeldecode",
      "login.code.label.password-reset": "Anmeldecode",
      "login.code.placeholder.email": "000 000",
      "login.code.text.email": "Wenn deine E-Mail-Adresse registriert ist, wurde der angeforderte Code per E-Mail versendet.",
      "login.email.login.body": "Hallo {user.nameOrEmail},\n\ndu hast gerade einen Anmeldecode fĂŒr das Kirby Panel angefordert.\nDer folgende Anmeldecode ist fĂŒr die nĂ€chsten {timeout} Minuten gĂŒltig:\n\n{code}\n\nWenn du keinen Anmeldecode angefordert hast, ignoriere bitte diese E-Mail oder kontaktiere bei Fragen deinen Administrator.\nBitte leite diese E-Mail aus SicherheitsgrĂŒnden NICHT weiter.",
      "login.email.login.subject": "Dein Anmeldecode",
      "login.email.password-reset.body": "Hallo {user.nameOrEmail},\n\ndu hast gerade einen Anmeldecode fĂŒr das Kirby Panel angefordert.\nDer folgende Anmeldecode ist fĂŒr die nĂ€chsten {timeout} Minuten gĂŒltig:\n\n{code}\n\nWenn du keinen Anmeldecode angefordert hast, ignoriere bitte diese E-Mail oder kontaktiere bei Fragen deinen Administrator.\nBitte leite diese E-Mail aus SicherheitsgrĂŒnden NICHT weiter.",
      "login.email.password-reset.subject": "Dein Anmeldecode",
      "login.remember": "Angemeldet bleiben",
      "login.reset": "Passwort zurĂŒcksetzen",
      "login.toggleText.code.email": "Anmelden ĂŒber E-Mail",
      "login.toggleText.code.email-password": "Anmelden mit Passwort",
      "login.toggleText.password-reset.email": "Passwort vergessen?",
      "login.toggleText.password-reset.email-password": "← ZurĂŒck zur Anmeldung",
      "logout": "Abmelden",
      "menu": "MenĂŒ",
      "meridiem": "AM\/PM",
      "mime": "Medientyp",
      "minutes": "Minuten",
      "month": "Monat",
      "months.april": "April",
      "months.august": "August",
      "months.december": "Dezember",
      "months.february": "Februar",
      "months.january": "Januar",
      "months.july": "Juli",
      "months.june": "Juni",
      "months.march": "MĂ€rz",
      "months.may": "Mai",
      "months.november": "November",
      "months.october": "Oktober",
      "months.september": "September",
      "more": "Mehr",
      "name": "Name",
      "next": "NĂ€chster Eintrag",
      "off": "aus",
      "on": "an",
      "open": "Öffnen",
      "options": "Optionen",
      "options.none": "Keine Optionen",
      "orientation": "Ausrichtung",
      "orientation.landscape": "Querformat",
      "orientation.portrait": "Hochformat",
      "orientation.square": "Quadratisch",
      "page.blueprint": "Du kannst zusĂ€tzliche Felder und Bereiche fĂŒr diese Seite in <strong>\/site\/blueprints\/{template}.yml<\/strong> anlegen",
      "page.changeSlug": "URL Àndern",
      "page.changeSlug.fromTitle": "Aus Titel erzeugen",
      "page.changeStatus": "Status Àndern",
      "page.changeStatus.position": "Bitte wÀhle eine Position aus",
      "page.changeStatus.select": "WĂ€hle einen neuen Status aus",
      "page.changeTemplate": "Vorlage Àndern",
      "page.delete.confirm": "Willst du die Seite <strong>{title}<\/strong> wirklich löschen?",
      "page.delete.confirm.subpages": "<strong>Diese Seite hat Unterseiten<\/strong>. <br>Alle Unterseiten werden ebenfalls gelöscht.",
      "page.delete.confirm.title": "Gib zur BestÀtigung den Seitentitel ein",
      "page.draft.create": "Entwurf anlegen",
      "page.duplicate.appendix": "Kopie",
      "page.duplicate.files": "Dateien kopieren",
      "page.duplicate.pages": "Seiten kopieren",
      "page.sort": "Position Àndern",
      "page.status": "Status",
      "page.status.draft": "Entwurf",
      "page.status.draft.description": "Die Seite ist im Entwurfsmodus und ist nur nach Anmeldung oder ĂŒber den geheimen Link sichtbar",
      "page.status.listed": "Öffentlich",
      "page.status.listed.description": "Die Seite ist öffentlich fĂŒr alle",
      "page.status.unlisted": "Ungelistet",
      "page.status.unlisted.description": "Die Seite kann nur ĂŒber die URL aufgerufen werden",
      "pages": "Seiten",
      "pages.empty": "Keine Seiten",
      "pages.status.draft": "EntwĂŒrfe",
      "pages.status.listed": "Veröffentlicht",
      "pages.status.unlisted": "Ungelistet",
      "pagination.page": "Seite",
      "password": "Passwort",
      "pixel": "Pixel",
      "prev": "Vorheriger Eintrag",
      "preview": "Vorschau",
      "remove": "Entfernen",
      "rename": "Umbenennen",
      "replace": "Ersetzen",
      "retry": "Wiederholen",
      "revert": "Verwerfen",
      "revert.confirm": "Willst du wirklich alle ungespeicherten Änderungen verwerfen? ",
      "role": "Rolle",
      "role.admin.description": "Admins haben alle Rechte",
      "role.admin.title": "Admin",
      "role.all": "Alle",
      "role.empty": "Keine Accounts mit dieser Rolle",
      "role.description.placeholder": "Keine Beschreibung",
      "role.nobody.description": "Dies ist die Platzhalterrolle ohne Rechte",
      "role.nobody.title": "Niemand",
      "save": "Speichern",
      "search": "Suchen",
      "search.min": "Gib mindestens {min}  Zeichen ein, um zu suchen",
      "search.all": "Alles zeigen",
      "search.results.none": "Keine Ergebnisse",
      "section.required": "Der Bereich ist Pflicht",
      "select": "AuswÀhlen",
      "settings": "Einstellungen",
      "show": "Anzeigen",
      "size": "GrĂ¶ĂŸe",
      "slug": "URL-Anhang",
      "sort": "Sortieren",
      "title": "Titel",
      "template": "Vorlage",
      "today": "Heute",
      "site.blueprint": "Du kannst zusĂ€tzliche Felder und Bereiche fĂŒr die Seite in <strong>\/site\/blueprints\/site.yml<\/strong> anlegen",
      "toolbar.button.code": "Code",
      "toolbar.button.bold": "Fetter Text",
      "toolbar.button.email": "E-Mail",
      "toolbar.button.headings": "Überschriften",
      "toolbar.button.heading.1": "Überschrift 1",
      "toolbar.button.heading.2": "Überschrift 2",
      "toolbar.button.heading.3": "Überschrift 3",
      "toolbar.button.italic": "Kursiver Text",
      "toolbar.button.file": "Datei",
      "toolbar.button.file.select": "Datei auswÀhlen",
      "toolbar.button.file.upload": "Datei hochladen",
      "toolbar.button.link": "Link",
      "toolbar.button.ol": "Geordnete Liste",
      "toolbar.button.ul": "Ungeordnete Liste",
      "translation.author": "Kirby Team",
      "translation.direction": "ltr",
      "translation.name": "Deutsch",
      "translation.locale": "de_DE",
      "upload": "Hochladen",
      "upload.error.cantMove": "Die Datei konnte nicht an ihren Zielort bewegt werden",
      "upload.error.cantWrite": "Die Datei konnte nicht auf der Festplatte gespeichert werden",
      "upload.error.default": "Die Datei konnte nicht hochgeladen werden",
      "upload.error.extension": "Der Dateiupload wurde durch eine Erweiterung verhindert",
      "upload.error.formSize": "Die Datei ist grĂ¶ĂŸer als die MAX_FILE_SIZE Einstellung im Formular",
      "upload.error.iniPostSize": "Die Datei ist grĂ¶ĂŸer als die post_max_size Einstellung in der php.ini",
      "upload.error.iniSize": "Die Datei ist grĂ¶ĂŸer als die upload_max_filesize Einstellung in der php.ini",
      "upload.error.noFile": "Es wurde keine Datei hochgeladen",
      "upload.error.noFiles": "Es wurden keine Dateien hochgeladen",
      "upload.error.partial": "Die Datei wurde nur teilweise hochgeladen",
      "upload.error.tmpDir": "Der temporĂ€re Ordner fĂŒr den Dateiupload existiert leider nicht",
      "upload.errors": "Fehler",
      "upload.progress": "Hochladen 
",
      "url": "Url",
      "url.placeholder": "https:\/\/beispiel.de",
      "user": "Account",
      "user.blueprint": "Du kannst zusĂ€tzliche Felder und Bereiche fĂŒr diese Rolle in <strong>\/site\/blueprints\/users\/{role}.yml<\/strong> anlegen",
      "user.changeEmail": "E-Mail Àndern",
      "user.changeLanguage": "Sprache Àndern",
      "user.changeName": "Account umbenennen",
      "user.changePassword": "Passwort Àndern",
      "user.changePassword.new": "Neues Passwort",
      "user.changePassword.new.confirm": "Wiederhole das Passwort 
",
      "user.changeRole": "Rolle Àndern",
      "user.changeRole.select": "Neue Rolle auswÀhlen",
      "user.create": "Neuen Account anlegen",
      "user.delete": "Account löschen",
      "user.delete.confirm": "Willst du den Account <br><strong>{email}<\/strong> wirklich löschen?",
      "users": "Accounts",
      "version": "Version",
      "view.account": "Dein Account",
      "view.installation": "Installation",
      "view.resetPassword": "Passwort zurĂŒcksetzen",
      "view.settings": "Einstellungen",
      "view.site": "Seite",
      "view.users": "Accounts",
      "welcome": "Willkommen",
      "year": "Jahr",
      "uniform-filled-potty": "Es wurde das Feld ausgefĂŒllt, das leer bleiben sollte. Falls Sie kein Spam-Bot sind, versuchen Sie es bitte erneut ohne das Feld auszufĂŒllen.",
      "uniform-calc-incorrect": "Bitte lösen Sie die Rechenaufgabe.",
      "uniform-email-subject": "Nachricht ĂŒber das Formular",
      "uniform-email-error": "Es ist ein Fehler beim Senden aufgetreten",
      "uniform-email-copy": "Kopie:",
      "uniform-calc-plus": "plus",
      "uniform-log-error": "Beim Schreiben in die Log-Datei ist ein Fehler aufgetreten.",
      "uniform-login-error": "Benutzername oder Passwort falsch.",
      "uniform-webhook-error": "Beim Aufruf des Webhook ist ein Fehler aufgetreten: ",
      "uniform-email-select-error": "UngĂŒltiger EmpfĂ€nger.",
      "uniform-upload-mkdir-fail": "Zielverzeichnis konnte nicht erstellt werden.",
      "uniform-upload-exists": "Die Datei existiert bereits.",
      "uniform-upload-failed": "Die Datei konnte nicht hochgeladen werden.",
      "trevor.heading": "Translations",
      "trevor.startinfo": "Select a variable on the left and start translating.",
      "trevor.addkey": "Enter the variable name",
      "trevor.payoff": "nobody trenslates voriables better."
    },
    "direction": "ltr",
    "id": "de",
    "name": "Deutsch"
  },
  "status": "ok",
  "type": "model"
}

Edited: Added the full response.

From looking at the source code, $kirby->translations() load the default translations plus registered translation extensions.

So that doesn’t look like translations defined in the language files are taken into account. You’d probably have to define your translations in a plugin for this to work as expected.

Don’t know if this can be considered a bug or a missing implementation.

1 Like

That does indeed sound like a bug possibly. Would you mind opening a bug issue for it pease? https://github.com/getkirby/kirby/issues

1 Like

Sure, will do. Thanks, everyone, for looking into it.

For reference: