Assistance Needed with Localization Issue: Translations Not Applied as Expected (CakePHP 5.1.1)

Hello CakePHP community,

I’m encountering a challenging issue with translations and localization in CakePHP 5.1.1, and I’d greatly appreciate any guidance. Normally, clearing the cache resolves any minor issues I’ve had with translations appearing, but this time, the problem seems different. Here’s what I’m currently doing:

  1. In my App.php, I have:
    'App' => [
        ...
        'defaultLocale' => env('APP_DEFAULT_LOCALE', 'fr_FR'),
        ...
    ]
    
  2. I generate .pot files using bin/cake i18n extract, placing them in /resources/locales/fr_FR/.
  3. I rename the .pot files to .po, and then translate the necessary messages.

Issue

Despite this setup, none of my translations are being applied—except for one, but inconsistently. In my form inputs, all required fields display the English message “This field cannot be left empty” except for one, which shows the French translation “Veuillez compléter ce champ.” However, even when a translation is displayed correctly, it doesn’t match the exact text provided in cake.po.

When I inspect the cake.po file, I find the following:

#: ./vendor/cakephp/cakephp/src/Validation/Validator.php:3036
#: ./vendor/cakephp/cakephp/src/View/Form/ArrayContext.php:232
msgid "This field cannot be left empty"
msgstr "Ce champ ne peut être vide."

This suggests that the translation should be applied globally, yet it isn’t reflected as expected in the application.

I’ve also tried forcing the locale to French in the initialize() method of AppController.php using I18n::setLocale('fr_FR'); but the issue persists.

Any insights or recommendations from the community would be highly valued! Thank you all in advance for any help you can offer.

Best regards,

Can you give an example of a line of your code where you’re expecting “This field cannot be left empty” to be translated by the cake.po file?

Here is an extract of the view

<?php

/**
 * @var \App\View\AppView $this
 * @var mixed $companies
 * @var mixed $directions
 * @var string $breadCrumb
 * @var mixed $contracttypes
 * @var mixed $crumbs
 * @var mixed $hiringreasons
 * @var mixed $idSelectedContracttype
 * @var mixed $idSelectedHiringreason
 * @var mixed $idSelectedProfessionalCategory
 * @var mixed $idSelectedWorktime
 * @var mixed $professionalCategories
 * @var mixed $worktimes
 * @var mixed $idSelectedProfessionalcategory
 * @var mixed $professionalcategories
 */

use App\Model\Entity\Applicationform;
?>

...  some code here

    <div class="row">
        <div class="col">
            <?php
            echo  $this->element(
                'Applicationforms/selectHiringReason',
                [
                    'hiringreasons' => $hiringreasons,
                    'idSelectedHiringreason' => isset($idSelectedHiringreason) ?
                        $idSelectedHiringreason : null,
                ]
            );
            ?>
        </div>
        <div class="col">
            <?php
            echo  $this->Form->control(
                Applicationform::FIELD_REASONFORREPLACEMENT,
                [
                    'title' => 'Raison succinte du remplacement.',
                    'tooltip' => 'raison succinte du remplacement.',
                    'required' => true,
                ]
            );
            // $applicationsform->reasonforreplacement;
            ?>
        </div>
        <div class="col">
            <?php
            echo  $this->element('Applicationforms/dateBeginAt',);
            ?>
        </div>
        <div class="col">
            <?php
            echo  $this->element('Applicationforms/dateEndAt',);
            ?>
        </div>
    </div>

... some code here 

Hello

Unless I’ve missed something significant—always a possibility!—I’ve ensured the following setup:

Both the cake.po and default.po files exist in the resources/locales/fr_FR directory.

The cake.po file indeed contains the exact string “This field cannot be left empty”.

The .po file structure follows the correct syntax, as shown here:

msgid "This field cannot be left empty"
msgstr "Ce champ ne peut être vide"

I tested that code in AppController

    /**
     * Initialization hook method.
     *
     * Use this method to add common initialization code like loading components.
     *
     * e.g. `$this->loadComponent('FormProtection');`
     *
     * @return void
     */
    public function initialize(): void {
        parent::initialize();

        debug(I18n::getLocale());
        I18n::setLocale('fr_FR');
        debug(__('This field cannot be left empty'));
        debug(__d('cake', 'This field cannot be left empty'));
        debug(__d('default', 'This field cannot be left empty'));
        die();
        $this->loadComponent('Flash');
        /* Removed in CakePHP 5 ! $this->loadComponent('RequestHandler'); */

        /* Ajoutez cette ligne pour vérifier le résultat de l'authentification et verrouiller votre site */
        $this->loadComponent('Authentication.Authentication');

        /*
         * Enable the following component for recommended CakePHP form protection settings.
         * see https://book.cakephp.org/4/en/controllers/components/form-protection.html
         */
        //$this->loadComponent('FormProtection');
    }

and got that in my browser

 APP/Controller/AppController.php (line 79)
'fr_FR'

APP/Controller/AppController.php (line 81)
'This field cannot be left empty'

APP/Controller/AppController.php (line 82)
'This field cannot be left empty'

APP/Controller/AppController.php (line 83)
'This field cannot be left empty'

Yet, as seen in the debug output, the translation isn’t being applied as expected, and I continue seeing the English message “This field cannot be left empty.”

Would you have any other suggestions for further troubleshooting?

Thank you in advance for your help!

Best regards,

I don’t know much about the internals of any of this. I do see a few places where what you’re doing is different from what I do. I have no idea whether they affect things.

I don’t rename my pot files, I leave them in place and use PoEdit to manage the po files. At least two differences from this are that the pot file then still exists (don’t know if that’s important or not), and there’s a line reading "Language: fr_FR\n" in the header of my po file (again, don’t know if that’s important).

Also, you said you cleared the cache, but didn’t say how. I expect you did it correctly, but seems useful to double check that tmp/cache/persistent is empty after you do it.

ok,

here is the header of my cake.pot file
I see the line CAKEPHP Version is 5.0.8 and when I type bin/cake version i get 5.1.1

May be there is a difference somewhere. I don’t if that’s important.

I’ll continue to dig in.
Anyway thank for your time and advices

Here is the header of cake.pot
date is in august and I’ve tried to rerun i18n extract many times. That’s weird.

# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: CakePHP 5.0.8\n"
"POT-Creation-Date: 2024-08-22 14:39+0000\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#: ./templates/Error/error400.php:40
#: ./templates/Error/error500.php:45
msgid "Error"
msgstr ""

Seems most likely that there’s some issue in your process. You know that i18n extract updates the pot file, not the po? And you then need a second process to merge that most recent pot file into your existing po file, to preserve existing translations but also add new ones?

Thank you for your advice, one more time

Indeed, the management and generation of .pot and .po files has always worked “automagically”.

  • This is surely a problem, because I do not control the details of how it works.
  • Indeed, until now, I had never encountered such symptoms.
  • Everything has always worked “automagically”.

I’m going to dig deeper and redo my whole process of building a translation library.

  • This in order to best master each step, because I absolutely must correct this erratic functioning of my application.
  • I’ll do some git bisect commands too, to check which commit has generated this behavior, just in case…

I will keep this thread open and indicate the solution adopted.

I always generate .po files from .pot files via PoEdit

Indeed, the management and generation of .pot and .po files has always worked “automagically”.

The command bin/cake i18n extract only generates the .pot file, NOT the .po file which actually contains the translated strings for your desired language.

I am on CakePHP 5.1.1 as well without any problems.


You already made sure that Cake knows its set to fr_FR via doing debug(I18n::getLocale());

Therefore there is no problem with setting the language in your config/environment.

I’d guess you have a permission problem reading the file or - as mentioned before - the .po file is not in the correct format because its just a renamed .pot file which may not be 100% intercompatible

I come here to indicate that I found an answer to my problem.

first of all,

the inconsistency of the translation of the same case was due to the fact that the requisition of a field was not done in the same way depending on the field. When the requisition, (CANNOT BE NULL) of a field is defined in the database schema then everything happens as expected. The second field was not defined as required (CAN BE NULL) in the database but in the input form via the option (“required” => true then the message ‘this field cannot be left empty’ is intercepted and translated by the browser (client side) and no longer server side.

Second point,

I confirm the fact that it is optional to transcribe via poedit the .pot files generated via “bin/cake i18n extract”. This works very well by copying them, renaming them into .po files in the appropriate directory. POedit is useless to me because I have very little language to translate. From English to French, this is sufficient in most of the applications I am asked for.

Third point,

you actually have to clear the cache in the tmp directory (tmp/cache/persistent…) but this has never caused me a problem.

Finally, the crux, the core of my problem

was that my .po file had the wrong format. I have no more information, I ended up in desperation, starting my translation procedure from the beginning by deleting my .po file and everything is back to normal.

Thank you both for spending the time to answer me and give me some lines of research.