Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent translations from being loaded too early #47113

Merged
merged 17 commits into from
Jun 25, 2024
Merged

Prevent translations from being loaded too early #47113

merged 17 commits into from
Jun 25, 2024

Conversation

swissspidy
Copy link
Contributor

Submission Review Guidelines:

Changes proposed in this Pull Request:

Since WordPress 4.6, WordPress has supported so-called just-in-time translation loading. Whenever it encounters a __() call, it tries to load the translations for that domain if it hasn't already. That means one theoretically doesn't need load_plugin_textdomain() anymore, though it still can be used.

Ideally, this should only happen after the init hook, as that's when the current user has been fully set up in WordPress. This is important as users can set their locale in the user profile, which is used in the admin. Triggering translation loading before that means they will be loaded in the wrong locale. Unfortunately, some plugins are _doing_it_wrong() and load translations right when the plugin is executed, or upon plugins_loaded.

In my attempt to identify such plugins, I noticed that WooCommerce was a particularly stubborn case. There is a lot of code triggering just-in-time translation loading too early. This is particularly suboptimal because WooCommerce itself calls load_plugin_textdomain() later on on init, causing yet another translation call, which is just unnecessary at this point and not good for performance.

This PR is an attempt at identifying and eliminating all cases in WooCommerce triggering just-in-time translation loading before init.

This way, if core starts adding warnings for such cases, there won't be any warnings anymore for WooCommerce.

How to test the changes in this Pull Request:

Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:

Some examples:

Remove autoloader

  1. Set site language to German (de_DE)
  2. Set user language to French (fr_FR)
  3. Remove vendor/autoload_packages.php file so the missing autoloader warning kicks in
  4. Visit dashboard
  5. Without this PR: See autoloader admin notice in fr_FR, but error log entry in de_DE
  6. With this PR: the error log entry is in en_US, as I removed the translation call

Temporarily remove load_plugin_textdomain() call

  1. Set site language to German (de_DE)
  2. Set user language to French (fr_FR)
  3. Temporarily do an early return in \WooCommerce::load_plugin_textdomain() so that load_plugin_textdomain() isn't called
  4. Visit some WooCommerce admin screen
  5. Without this PR: Everything is in en_US or de_DE
  6. With this PR: All strings are in fr_FR

With core PR

  1. Apply Add warnings if loading translations too early WordPress/wordpress-develop#6462
  2. Activate WooCommerce
  3. See _doing_it_wrong() warnings

Changelog entry

  • Automatically create a changelog entry from the details below.
    Adjust code loading order to prevent just-in-time translation loading from happening too early.

Significance

  • Patch
  • Minor
  • Major

Type

  • Fix - Fixes an existing bug
  • Add - Adds functionality
  • Update - Update existing functionality
  • Dev - Development related task
  • Tweak - A minor adjustment to the codebase
  • Performance - Address performance issues
  • Enhancement - Improvement to existing functionality

Message

Comment

@github-actions github-actions bot added plugin: woocommerce Issues related to the WooCommerce Core plugin. type: community contribution labels May 2, 2024
@woocommercebot woocommercebot requested review from a team, jorgeatorres, alexflorisca, gigitux, chihsuan and moon0326 and removed request for a team May 2, 2024 12:31
Copy link
Contributor

github-actions bot commented May 2, 2024

Hi , @woocommerce/mothra

Apart from reviewing the code changes, please make sure to review the testing instructions as well.

You can follow this guide to find out what good testing instructions should look like:
https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions

@adrianduffell adrianduffell removed request for a team, chihsuan and moon0326 May 18, 2024 01:44
@alexflorisca alexflorisca removed their request for review May 21, 2024 13:53
@ralucaStan
Copy link
Contributor

@swissspidy thank you for taking the time to work on this PR. There are some conflicts on your PR as there were some changes on the Additional fields side. Would you mind fixing those and updating the branch?

@ralucaStan ralucaStan requested a review from senadir May 21, 2024 15:19
@swissspidy
Copy link
Contributor Author

Sure, addressed now! 👍

@coreymckrill coreymckrill requested review from coreymckrill and removed request for jorgeatorres May 21, 2024 16:45
Copy link
Collaborator

@coreymckrill coreymckrill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@swissspidy Thanks for this PR! I'm excited that this could potentially be a performance improvement for WooCommerce, and also improve how we implement translations.

I'm going to take another pass at this to do some specific testing (and others may as well) but I wanted to get some initial thoughts out here.

Switching a bunch of things from one action hook (plugins_loaded) to a later hook (init) makes me nervous because there are so many other plugins that integrate with WooCommerce, we can't really anticipate what assumptions they might be making about when things will be available, or what will break if we make a change like this. That said, it looks like several of these changes you make are in our Internal namespace, and so are probably relatively safe. I did note a couple that seem unnecessary because they don't look like they are actually triggering any gettext calls, but I could be wrong about that.

This is particularly suboptimal because WooCommerce itself calls load_plugin_textdomain() later on on init, causing yet another translation call, which is just unnecessary at this point and not good for performance.

This is a good call out, and it's something I had actually noticed just recently, and was wondering if we still needed it. I notice that you didn't remove it as part of this PR though. Do you think that would be worth doing here?

@@ -26,7 +26,7 @@ public static function load() {
add_action( 'pre_set_site_transient_update_themes', array( __CLASS__, 'transient_update_themes' ), 21, 1 );
add_action( 'upgrader_process_complete', array( __CLASS__, 'upgrader_process_complete' ) );
add_action( 'upgrader_pre_download', array( __CLASS__, 'block_expired_updates' ), 10, 2 );
add_action( 'plugins_loaded', array( __CLASS__, 'add_hook_for_modifying_update_notices' ) );
add_action( 'init', array( __CLASS__, 'add_hook_for_modifying_update_notices' ) );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Does this one really need to change? Looks like it doesn't actually use any gettext functions in the callback, just queues up a callback on another action hook that's after init...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it's not. I was getting fatal errors from WC_Woo_Update_Manager_Plugin::is_plugin_active() though because is_plugin_active_for_network wasn't yet available. That can be addressed better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this in 2fad07c, but not happy with it.

Actually, it would be even better to change this to admin_init, because the hooks there are only relevant for the admin, as they add admin notices.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in c5c3e26

plugins/woocommerce/includes/class-woocommerce.php Outdated Show resolved Hide resolved
Comment on lines 482 to 502
/**
* Returns the keys of all core fields.
*
* @return array An array of field keys.
*/
public function get_core_fields_keys() {
return [
'email',
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'country',
'city',
'state',
'postcode',
'phone',
];
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why this is added, so that fields_locations can be populated in the constructor, but it's obviously not ideal since it means the keys have to be updated in two places if they change. But, since both the core_fields and the fields_locations properties in this class are set to private, we could do the same treatment for fields_locations, i.e. create a new private method called get_fields_locations and use that instead of referencing the class property. Then you could remove the property assignment from the class constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the duplication is not ideal. A slight difference here is that fields_locations can be mutated via register_checkout_field()/deregister_checkout_field. Plus, it's accessed in quite a few places throughout the class. Any suggestions on how best to handle that?

@swissspidy
Copy link
Contributor Author

I think I resolved the Packages issue without re-introducing the translation issue.

The stack trace is basically this:

Packages -> \Automattic\WooCommerce\Blocks\Package -> \Automattic\WooCommerce\Blocks\Domain\Bootstrap -> ShippingController -> LocalPickupUtils::is_local_pickup_enabled() -> __()

The current workaround works, but makes it easy to call too early translation calls elsehwere, because everything in Packages runs so early.


The init_jetpack_connection_config case I "fixed" by not calling a translation function. Not great, but a better fix requires a change in that package.

@swissspidy swissspidy requested a review from senadir May 29, 2024 08:54
@swissspidy
Copy link
Contributor Author

@coreymckrill @senadir can someone help approve the workflow runs here?

@senadir
Copy link
Member

senadir commented Jun 3, 2024

Closing and reopening to force them to start.

@senadir senadir closed this Jun 3, 2024
@senadir senadir reopened this Jun 3, 2024
@swissspidy

This comment was marked as resolved.

@senadir
Copy link
Member

senadir commented Jun 3, 2024

I did approve them before closing and opening, just did that again.

Copy link
Member

@senadir senadir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this yesterday and everything seems to be working fine in Checkout flows, what's left is more testing with some plugins and figuring out why tests aren't passing.

@swissspidy
Copy link
Contributor Author

Tests should be passing now, but they need to be approved again.

@senadir
Copy link
Member

senadir commented Jun 9, 2024

FWIW Build Live Branch / build (pull_request) will continue to fail for now until we fix it in trunk (it has an issue with external authors).

@senadir
Copy link
Member

senadir commented Jun 9, 2024

I took anther look at the PR code and it's honestly not touching any critical stuff, the main stuff I can think about are the blocks related code (which I tested) + order attribution (with @layoutd tested).

I think this is good to go but I'd love for us to avoid reintroducing this in the future. @swissspidy is it possible for us to add a unit test that would check if translation is loaded too early and will fail if so? If you're ensure how to write the test, just an example of how that hook would look like is enough for me.

@swissspidy
Copy link
Contributor Author

Discussed this with @senadir in person at WCEU:

I am planning to add warnings for translations being loaded too early in WP 6.7, see WordPress/wordpress-develop#6462 and https://core.trac.wordpress.org/ticket/44937. This will make it easy to detect regressions in the future. Until then there is not a very good way to test this.

So my suggestion would be to merge this for now, accepting the small risk of a regression between now and WP 6.7.

@juliaamosova juliaamosova added the impact: high To be added by the PR author/reviewer if the PR could very likely cause SEV-1 or SEV-2 issues label Jun 18, 2024
@senadir
Copy link
Member

senadir commented Jun 19, 2024

Hey @swissspidy I discussed with internally with the release team and while I feel confident we can merge this, merging this today means it will land on WooCommerce 9.1 and won't get a lot of testing time, so I'd wait and merge it tomorrow so it lands on 9.2 and gives it even more time in local developer devs and testing time.

@senadir senadir merged commit e5b7f84 into woocommerce:trunk Jun 25, 2024
38 of 40 checks passed
@github-actions github-actions bot added this to the 9.2.0 milestone Jun 25, 2024
@github-actions github-actions bot added the needs: analysis Indicates if the PR requires a PR testing scrub session. label Jun 25, 2024
@rodelgc rodelgc added needs: internal testing Indicates if the PR requires further testing conducted by Solaris status: analysis complete Indicates if a PR has been analysed by Solaris and removed needs: analysis Indicates if the PR requires a PR testing scrub session. labels Jun 26, 2024
thealexandrelara pushed a commit that referenced this pull request Jul 18, 2024
* Prevent translations from being loaded too early

* Undo accidental changes to `CheckoutFields`

* Keep plugins_loaded, but include required file

* Update order in the meantime

* Change to admin_init

* Add changelog file

* Undo `init_jetpack_connection_config` change

* Undo change to Packages class

* Fix LocalPickupUtils instead

* PHPCS fixes

* Update method name in test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
impact: high To be added by the PR author/reviewer if the PR could very likely cause SEV-1 or SEV-2 issues needs: internal testing Indicates if the PR requires further testing conducted by Solaris plugin: woocommerce Issues related to the WooCommerce Core plugin. status: analysis complete Indicates if a PR has been analysed by Solaris type: community contribution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants