Make WordPress Core

Opened 3 weeks ago

Closed 3 weeks ago

Last modified 3 weeks ago

#60999 closed defect (bug) (worksforme)

Issues with new i18n logic when handling translation files (MO/PHP)

Reported by: fengyintseng's profile fengyintseng Owned by:
Milestone: Priority: normal
Severity: normal Version: 6.5
Component: I18N Keywords:
Focuses: Cc:

Description

Hi,

Recently I am translating a plugin (also its Pro version). It works fine in v6.4.3 when I placed the MO files under path /wp-content/languages/plugins/.

This Tuesday my site was upgraded to v6.5 and all the translations are set back to the old ones.
Found a new file "xxxxx-zh_TW.l10n.php" generated under /wp-content/languages/plugins/ (and also corresponding PO/MO files), apparently the new translations are from this PHP file now.

(below please find the issues identified during my experiments)

Step 1
I tried to re-generated the PHP file according to the PO file I uploaded to the path with below command:

wp i18n make-php .

With old method (before 6.5), if I upload the MO file which includes only the translated strings, the translated strings will be reflected successfully on Front and Back ends without affecting the untranslated parts.

After I generate the PHP file according to the PO file, as the PO file includes all the strings (translated & un-translated), the un-translated strings will be treated as BLANK/EMPTY and unable to be displayed on Front and Back ends (no placeholder either).

This issue seems already identified in 6.5-RC3 but it can be replicated in 6.5 official release as well.

Step 2
Referring to the article "https://make.wordpress.org/core/2024/02/27/i18n-improvements-6-5-performant-translations/", use below filter to customize the behavior to use MO file instead of PHP file.

<?php
add_filter(
        'translation_file_format',
        static function () {
                return 'mo';
        }
);

By this way, I should be able to avoid the empty string issue addressed in #1; However, the MO file that I uploaded will also be overwritten by the generated MO file based on old translations (from translate.wordpress.org) periodically. Apart from that, I found system still use the strings from PHP file instead of the MO file in the folder.

So it won't solve the problem. And there is issue with the functionality of translation_file_format.

Step 3
Friend suggests me to customize the translation file path, it will be entitled with highest priority for system to decide which translation files to serve.

So I refer to below documents:

Use below filter to specify the translation file path for the specified plugin.

<?php
add_filter( 'load_translation_file', 'my_custom_translation_file', 10, 2 );

/*
 * Replace 'textdomain' with your plugin's textdomain. e.g. 'hello-dolly'. 
 * Define your filename, such as: yourtranslationfile-en_GB.mo
 * Define the location, for example: wp-content/languages/textdomain/yourtranslationfile-en_GB.mo
*/
function my_custom_translation_file( $mofile, $domain ) {
  if ( 'plugin_name' === $domain ) {
    $mofile = WP_CONTENT_DIR . '/my-languages/plugin_name-' . get_locale() . '.mo';
  }
  return $mofile;
}

However, whenever there is MO file (or PHP file) exists under /wp-content/languages/plugins/ it will use that file first, even though the translated strings are already in the MO file under new path /wp-content/my-languages/.

With this filter, it will use the translated strings of the MO file under /wp-content/my-languages/ as supplement (a fallback) when those strings are not existed in the MO file under /wp-content/languages/plugins/.

It looks like the old logic for path priority is no longer applicable in version higher than 6.5

-

The identified issues will affect all the users that used their own translation files on their sites that are already upgraded to 6.5.

Please help check and kindly let me know if I missed anything.

Thanks.

Best regards,
Feng

Change History (6)

#1 @swissspidy
3 weeks ago

  • Keywords reporter-feedback added
  • Severity changed from critical to normal

Hi there and welcome to WordPress Trac!

This Tuesday my site was upgraded to v6.5 and all the translations are set back to the old ones.

Found a new file "xxxxx-zh_TW.l10n.php" generated

That means WordPress downloaded new translations from WordPress.org and added the new files.

It sounds like you previously manually edited the files in that folder, which is risky because WordPress could simply override them during an update. If you customize translations, you should do so via translate.WordPress.org.

After I generate the PHP file according to the PO file, as the PO file includes all the strings (translated & un-translated), the un-translated strings will be treated as BLANK/EMPTY and unable to be displayed on Front and Back ends (no placeholder either).

There was an issue with empty strings in the wp i18n make-php command. Use wp cli update --nightly to get the latest version, and then run wp i18n make-php again.

However, the MO file that I uploaded will also be overwritten by the generated MO file based on old translations (from translate.wordpress.org) periodically.

That's why putting your custom translations there is not a good idea. You should contribute your translations on translate.WordPress.org :-)

Apart from that, I found system still use the strings from PHP file instead of the MO file in the folder.
So it won't solve the problem. And there is issue with the functionality of translation_file_format.

If you use the filter like this, WordPress will always use the MO file, not the PHP file. Are you saying this is not the case for you?

Use below filter to specify the translation file path for the specified plugin.
However, whenever there is MO file (or PHP file) exists under /wp-content/languages/plugins/ it will use that file first, even though the translated strings are already in the MO file under new path /wp-content/my-languages/.

Hmm that doesn't sound right. This filter should work perfectly. Where did you put it? It might be running too late.

#2 follow-up: @fengyintseng
3 weeks ago

Hi @swissspidy

There was an issue with empty strings in the wp i18n make-php command. Use wp cli update --nightly to get the latest version, and then run wp i18n make-php again.

Thanks, I saw your reply in the comment, thought that it will be delivered along with 6.5 as well. Let me try.

That's why putting your custom translations there is not a good idea. You should contribute your translations on translate.WordPress.org :-)

But it's the formal method suggested by almost all the plugin developers that if we would like to put our customized translations in use, it's better to put it under /wp-content/languages/plugins/ instead of /wp-content/plugins/plugin-name/languages/ as it won't be changed during an update.

And there is indeed requirement for user to use their own customized translations, take membership or eLearning sites for example, though people use similar plugins to build up their sites, the critical difference between their sites would be the designs and wordings they used in their sites.

And it's the only way for us to decorate the plugins and let our visitors feel our branding. Otherwise, all the sites with same plugin will just look like replicas

Take Tutor LMS as example, please refer to its instruction about how to translate the plugin offline:
https://docs.themeum.com/tutor-lms/tutorials/how-to-translate-tutor-lms/#translating-tutor-lms-offline
That's why I put the MO files under /wp-content/languages/plugins/

And it works fine under version 6.5

If you use the filter like this, WordPress will always use the MO file, not the PHP file. Are you saying this is not the case for you?

I did some experiments to double check the behavior.
I used PHP with old translations & MO with new translations, both files under /wp-content/languages/plugins/, and the translations displayed on Front/Back ends are from the PHP file.

Hmm that doesn't sound right. This filter should work perfectly. Where did you put it? It might be running too late.

I used code snippets plugin WPCodeBox to insert the codes. Please refer to screenshot https://d.pr/i/2k30Uy for the configurations.

#3 in reply to: ↑ 2 @swissspidy
3 weeks ago

But it's the formal method suggested by almost all the plugin developers that if we would like to put our customized translations in use, it's better to put it under /wp-content/languages/plugins/ instead of /wp-content/plugins/plugin-name/languages/ as it won't be changed during an update.
Take Tutor LMS as example, please refer to its instruction about how to translate the plugin offline:

https://docs.themeum.com/tutor-lms/tutorials/how-to-translate-tutor-lms/#translating-tutor-lms-offline

This is really bad advice.

You should not manually edit plugin files in wp-content/plugins/tutor/*, as the files will be overridden whenever you update a plugin.

Similarly, you should not manually edit translation files in wp-content/languages, as the files will be overridden whenever WordPress updates translations.

This is not new behavior in 6.5. It has always been this way.

And there is indeed requirement for user to use their own customized translations, take membership or eLearning sites for example, though people use similar plugins to build up their sites, the critical difference between their sites would be the designs and wordings they used in their sites.

If you need customized translations, use a custom location like you did with your load_translation_file filter example. Alternatively, there are also plugins such as Loco Translate that allow this.

I did some experiments to double check the behavior.

I used PHP with old translations & MO with new translations, both files under /wp-content/languages/plugins/, and the translations displayed on Front/Back ends are from the PHP file.

I used code snippets plugin WPCodeBox to insert the codes.

I think your code is running to late. If the Tutor plugin loads translations too early, then it might do so before your code is even loaded.

Try putting it in an mu-plugin.

#4 @fengyintseng
3 weeks ago

You should not manually edit plugin files in wp-content/plugins/tutor/*, as the files will be overridden whenever you update a plugin.

Yes, I never update the files in wp-content/plugins/tutor/*

Similarly, you should not manually edit translation files in wp-content/languages, as the files will be overridden whenever WordPress updates translations.

hmmm, it matched with my observation, whenever the WordPress translation (language package) is updated it will auto-generate the PHP/PO/MO files. Then it's really weird and bad suggestion that the plugin developers told us to upload the translation file to the path. Thanks for the reminder.

If you need customized translations, use a custom location like you did with your load_translation_file filter example. Alternatively, there are also plugins such as Loco Translate that allow this.

Thanks. I will give Loco Translate a try. I did not use it because I heard it cannot cover all the strings.

I think your code is running to late. If the Tutor plugin loads translations too early, then it might do so before your code is even loaded.
Try putting it in an mu-plugin.

Do you mean to put WPCodeBox plugin as mu-plugin or I use the functionality feature of WPCodeBox to convert the "load_translation_file" codes as standalone plugin and put it as mu-plugin? Sorry that I am not familiar with it, still learning.

Sounds like if the priority issue is solved then I can use my customize translation with the 2 filters. :)

#5 @fengyintseng
3 weeks ago

  • Resolution set to worksforme
  • Status changed from new to closed

@swissspidy Really appreciated for your help and guidance.

Found that putting the codes as mu-plugin is not hard at all, it's working well now. Just like what you said, Tutor plugin loads translation too early and it leaves no room for us to simply run the codes via ordinary ways.

Thanks for your patience and your time.
I have no further issue now.

#6 @swissspidy
3 weeks ago

  • Keywords reporter-feedback removed
  • Milestone Awaiting Review deleted

Glad to hear you were able to resolve this!

Note: See TracTickets for help on using tickets.