Newly added language does not load plugin translations

Hello all,

I have a really strange behavior of my current kirby setup. I have a properly working multilanguage setup. The languages en and de are showing in the frontend as expected. The translations from the content, the language translations and plugin translations do what they should.

We need to add a new language es to the page. So we added a new language-file es.php to the kirby languages folder. Properly working :white_check_mark: We started to translate the content. Also properly working :white_check_mark: We started to add es.php files to the languages folder of some plugins. The translations are not picked up by kirby and we only see the default language :no_entry_sign:

Strange is, that the “old” languages from the plugins still work. Does anyone have an idea how to further investigate or better to solve this issue?

This part I don’t quite understand. Are these files then included in the index.php of those plugins? Are these plugins you own plugins or third party plugins?

Yes, we include these language files in the index.php of the plugin as follows:

Kirby::plugin('sample/plugin', [
    'blueprints' => [
    'snippets' => [
    'translations' => [
        'en' => require_once __DIR__ . '/languages/en.php',
        'de' => require_once __DIR__ . '/languages/de.php',
        'es' => require_once __DIR__ . '/languages/es.php'

The plugins are our owns.

I think you need to rename the key to es_ES

Screenshot 2023-05-12 at 15.56.18

Did you solve it and if, how. I’m in the same situation.

Ok, I got it, I think.

Kirby works with alpha 2 language codes most of the time[1], unless it’s not (I might have missed that in the docs) and as @texnixe pointed out, Spanish is available via es_419 and es_ES.

To me, it wasn’t entirely clear how the backend languages were related to the frontend languages.

The answer lies in AppTranslations.php public function translation

Where $inject holds all my es languages vars from my plugins, but since there is no es.json it gets nixed and the fallback language takes over.

return Translation::load($locale, $this->root('i18n:translations') . '/' . $locale . '.json', $inject);

So in order to use Spanish (or Portuguese), you have to set up the /site/languages/es.php like this:

return [
    'code' => 'es_ES', // not only 'es'!!!
    'default' => false,
    'direction' => 'ltr',
    'locale' => [
        'LC_ALL' => 'es_ES'
    'name' => 'Español',
	'translations' => Yaml::decode(F::read(kirby()->root('languages').'/es.yml')),
    'url' => option('')

and plugin files reference languages like this:

'translations' => [
        'en' => require_once __DIR__ . '/languages/en.php',
        'de' => require_once __DIR__ . '/languages/de.php',
        'es_ES' => require_once __DIR__ . '/languages/es.php'

Did I get this right?