Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Handle translations in lazy loaded files #4392

Closed
wants to merge 18 commits into from

Conversation

mikejolley
Copy link
Member

@mikejolley mikejolley commented Jun 23, 2021

Adds support for loading translations for lazy-loaded components.

RequireChunkCallbackPlugin is borrowed from Calypso.

On the blocks side, we use addRequireChunkTranslationsHandler to provide a list of Chunk IDs containing translations. getTranslationChunkFileUrl is a callback that maps the Chunk ID to a JSON filename in the WP_LANG dir. This consists of the textdomain, locale, and a hash for the script file itself.

When a file is imported by React, addRequireChunkTranslationsHandler will run and fetch the JSON file. If successful, we simply pass the body to setLocaleData so the translation is then performed by the @wordpress/i18n package.

Fixes #4019
Closes #4392

Testing

Testing translations:

  1. Add the all products block to a page and save.
  2. Ensure a product is on sale that's visible in this block.
  3. Switch to a non US language with translations e.g. Brazillian Portuguese.
  4. View the page on the frontend with the all products block. Ensure "sale" is translated.

Testing builds:

Also, test builds (dev and production) because this required an update to Webpack 5x.

Core plugin

TBC - We need a way to test under core context because the domain and paths are different. I've added handling for this but it's not tested.

Changelog

Fixed string translations within the All Products Block.

@mikejolley mikejolley self-assigned this Jun 23, 2021
@mikejolley mikejolley requested a review from a team as a code owner June 23, 2021 15:03
@mikejolley mikejolley requested review from opr and removed request for a team June 23, 2021 15:03
@mikejolley mikejolley marked this pull request as draft June 23, 2021 15:04
@github-actions
Copy link
Contributor

github-actions bot commented Jun 23, 2021

Size Change: -62.8 kB (-6%) ✅

Total Size: 1.04 MB

Filename Size Change
build/active-filters-frontend.js 7.13 kB -1.12 kB (-14%) 👏
build/active-filters.js 7.38 kB -447 B (-6%)
build/all-products-frontend.js 26.2 kB +3.27 kB (+14%) ⚠️
build/all-products.js 36.1 kB -989 B (-3%)
build/all-reviews.js 9.08 kB -494 B (-5%)
build/atomic-block-components/add-to-cart--atomic-block-components/button--atomic-block-components/image---a7e2bb9b.js 0 B -2.56 kB (removed) 🏆
build/atomic-block-components/add-to-cart--atomic-block-components/button.js 0 B -1.82 kB (removed) 🏆
build/atomic-block-components/add-to-cart--atomic-block-components/image--atomic-block-components/title.js 0 B -334 B (removed) 🏆
build/atomic-block-components/add-to-cart-frontend.js 8.27 kB -113 B (-1%)
build/atomic-block-components/add-to-cart.js 7.58 kB -138 B (-2%)
build/atomic-block-components/button-frontend.js 1.8 kB +59 B (+3%)
build/atomic-block-components/button.js 913 B +37 B (+4%)
build/atomic-block-components/category-list-frontend.js 512 B +42 B (+9%) 🔍
build/atomic-block-components/category-list.js 514 B +38 B (+8%) 🔍
build/atomic-block-components/image-frontend.js 1.95 kB +67 B (+4%)
build/atomic-block-components/image.js 1.41 kB +64 B (+5%) 🔍
build/atomic-block-components/price-frontend.js 7.53 kB +5.43 kB (+259%) 🆘
build/atomic-block-components/price.js 2.11 kB +6 B (0%)
build/atomic-block-components/rating-frontend.js 597 B +36 B (+6%) 🔍
build/atomic-block-components/rating.js 601 B +34 B (+6%) 🔍
build/atomic-block-components/sale-badge-frontend.js 912 B +52 B (+6%) 🔍
build/atomic-block-components/sale-badge.js 921 B +53 B (+6%) 🔍
build/atomic-block-components/sku-frontend.js 425 B +36 B (+9%) 🔍
build/atomic-block-components/sku.js 427 B +33 B (+8%) 🔍
build/atomic-block-components/stock-indicator-frontend.js 647 B +35 B (+6%) 🔍
build/atomic-block-components/stock-indicator.js 653 B +41 B (+7%) 🔍
build/atomic-block-components/summary-frontend.js 939 B +35 B (+4%)
build/atomic-block-components/summary.js 938 B +29 B (+3%)
build/atomic-block-components/tag-list-frontend.js 508 B +40 B (+9%) 🔍
build/atomic-block-components/tag-list.js 509 B +37 B (+8%) 🔍
build/atomic-block-components/title-frontend.js 1.48 kB +45 B (+3%)
build/atomic-block-components/title.js 1.33 kB +44 B (+3%)
build/attribute-filter-frontend.js 16.5 kB -1.53 kB (-9%)
build/attribute-filter.js 11.2 kB -650 B (-5%)
build/blocks-checkout.js 19.9 kB -1.3 kB (-6%)
build/cart-frontend.js 69.4 kB -7.81 kB (-10%) 👏
build/cart.js 41.9 kB -3.7 kB (-8%)
build/checkout-blocks/sample.js 190 B +16 B (+9%) 🔍
build/checkout-frontend.js 74.2 kB -7.13 kB (-9%)
build/checkout-i2-frontend.js 46 kB -5.38 kB (-10%) 👏
build/checkout-i2.js 44.5 kB -3.88 kB (-8%)
build/checkout.js 43.9 kB -4.68 kB (-10%) 👏
build/featured-category.js 6.99 kB -379 B (-5%)
build/featured-product.js 8.72 kB -760 B (-8%)
build/handpicked-products.js 5.89 kB -446 B (-7%)
build/price-filter-frontend.js 12.8 kB -1.44 kB (-10%) 👏
build/price-filter.js 9.06 kB -518 B (-5%)
build/price-format.js 1.26 kB -113 B (-8%)
build/product-best-sellers.js 5.93 kB -761 B (-11%) 👏
build/product-categories.js 3.66 kB +275 B (+8%) 🔍
build/product-category.js 6.86 kB -691 B (-9%)
build/product-new.js 6.08 kB -764 B (-11%) 👏
build/product-on-sale.js 6.45 kB -748 B (-10%) 👏
build/product-search.js 2.93 kB +262 B (+10%) ⚠️
build/product-tag.js 6.26 kB -403 B (-6%)
build/product-top-rated.js 6.05 kB -759 B (-11%) 👏
build/products-by-attribute.js 7.06 kB -724 B (-9%)
build/reviews-by-category.js 10.5 kB -1.09 kB (-9%)
build/reviews-by-product.js 11.8 kB -1.29 kB (-10%) 👏
build/reviews-frontend.js 8.32 kB -685 B (-8%)
build/single-product-frontend.js 29.4 kB +3.62 kB (+14%) ⚠️
build/single-product.js 9.96 kB +178 B (+2%)
build/vendors--atomic-block-components/add-to-cart-frontend.js 0 B -20 kB (removed) 🏆
build/vendors--atomic-block-components/price-frontend.js 0 B -5.71 kB (removed) 🏆
build/wc-blocks-data.js 8.95 kB -1.88 kB (-17%) 👏
build/wc-blocks-google-analytics.js 1.76 kB -218 B (-11%) 👏
build/wc-blocks-middleware.js 1.33 kB -135 B (-9%)
build/wc-blocks-registry.js 2.64 kB -100 B (-4%)
build/wc-blocks-shared-context.js 1.59 kB +56 B (+4%)
build/wc-blocks-shared-hocs.js 1.56 kB -183 B (-10%) 👏
build/wc-blocks-vendors.js 237 kB -15.5 kB (-6%)
build/wc-blocks.js 3.76 kB +259 B (+7%) 🔍
build/wc-payment-method-bacs.js 614 B -192 B (-24%) 🎉
build/wc-payment-method-cheque.js 609 B -197 B (-24%) 🎉
build/wc-payment-method-cod.js 708 B -190 B (-21%) 🎉
build/wc-payment-method-paypal.js 641 B -198 B (-24%) 🎉
build/wc-payment-method-stripe.js 12 kB -258 B (-2%)
build/wc-settings.js 2.91 kB -3 B (0%)
build/422-frontend.js 19.5 kB +19.5 kB (new file) 🆕
build/775.js 1.19 kB +1.19 kB (new file) 🆕
build/868.js 2.36 kB +2.36 kB (new file) 🆕
build/925.js 361 B +361 B (new file) 🆕
ℹ️ View Unchanged
Filename Size
build/wc-blocks-editor-style-rtl.css 15.4 kB
build/wc-blocks-editor-style.css 15.4 kB
build/wc-blocks-style-rtl.css 19.9 kB
build/wc-blocks-style.css 19.9 kB
build/wc-blocks-vendors-style-rtl.css 1.05 kB
build/wc-blocks-vendors-style.css 1.05 kB

compressed-size-action

@mikejolley mikejolley marked this pull request as ready for review June 25, 2021 15:03
@mikejolley mikejolley requested a review from senadir June 25, 2021 15:03
Copy link

@jsnajdr jsnajdr left a comment

Choose a reason for hiding this comment

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

The RequireChunkCallbackPlugin has one flaw that prevents it from working correctly when multiple blocks that use it (i.e., multiple independent webpack compilations) are loaded into one HTML document: there is only one global window.__requireChunkCallback__ object and different blocks will overwrite each other's data.

One solution is to not assign __requireChunkCallback__ to window, but to make it into a special webpack-interpreted global variable. Webpack has many such module variables, like __webpack_public_path__.

Here is the webpack code that finds these variables in the AST and outputs code to interpret them specially: https://github.com/webpack/webpack/blob/master/lib/APIPlugin.js

@yuliyan This could be a valuable improvement of the plugin. And if it gets popular, we could move it to the Gutenberg repo and make it a standard part of the block building workflow.

assets/js/base/utils/i18n.ts Outdated Show resolved Hide resolved
// This fixes an issue with multiple webpack projects using chunking
// overwriting each other's chunk loader function.
// See https://webpack.js.org/configuration/output/#outputjsonpfunction
jsonpFunction: 'webpackWcBlocksJsonp',
Copy link

Choose a reason for hiding this comment

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

What has changed so that the JSONP functions from different blocks no longer conflict and don't need to be assigned different names?

Copy link
Member

Choose a reason for hiding this comment

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

jsonpFunction is no longer an option in webpack 5, it's now replaced with chunkLoadingGlobal.
However, I couldn't find any reference for this webpackWcBlocksJsonp. I'm not sure what we're trying to solve here.
The original PR that introduced this (2 years ago) provides very little rationale about what was happening here.
Deleting it doesn't seem to break anything in my testing.

);
return `${ blocksConfig.languageUrl }/${ domain }-${ blocksConfig.locale }-${ hash }.json`;
},
translatedChunks: [
Copy link

Choose a reason for hiding this comment

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

I'm not sure if having the list of translation chunks hardcoded is the best approach. For languages with incomplete translations, some of the language files for the listed chunks might be missing, which would result in 404 for the translation request.

Would it be possible to generate the translated chunks list from the back-end and provide it to the app using the blocksConfig object, e.g.:

$translated_chunks = array_map(
	'basename',
	glob( WP_LANG_DIR . '/plugins/woo-gutenberg-products-block-' . get_locale() . '*.json' )
);

Copy link
Member

Choose a reason for hiding this comment

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

We still have to do some cross-checking to know which file to load I guess right? which means we still need to generate the hash on the frontend again? $translated_chunks returns 227 files for the Japanse translation of WooCommerce blocks.

Copy link
Member

Choose a reason for hiding this comment

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

I applied this feedback @yuliyan if you want to take another look.

I'm not fully convinced of this approach. Passing the full list of files being translated adds 16kb to the page size (227) while we might need only 4 or 5. So on a later PR I might just pass down atomic blocks (the ones being dynamically loaded).

Copy link

@yuliyan yuliyan Jul 5, 2021

Choose a reason for hiding this comment

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

I didn't expect the files to be that many, but yeah, in this case it doesn't make a lot of sense to provide all that data to the front-end and increase the page size unnecessarily.

As you suggested, it would be better if you could pass only the files that are relevant to the front, maybe something like that could do the job:

$files_with_translations = array_map(
	'md5',
	array(
		'build/atomic-block-components/add-to-cart.js',
		'build/atomic-block-components/button-frontend.js',
		'build/atomic-block-components/rating.js',
		'build/atomic-block-components/title.js',
		'build/atomic-block-components/tag-list.js',
		...
	) // hardcode the atomic block filepaths, or get them dynamically with another glob.
);

And then the glob pattern would be:

glob( WP_LANG_DIR . '/plugins/woo-gutenberg-products-block-' . get_locale() . "-{{$files_with_translations}}" . '*.json', GLOB_BRACE );

@senadir senadir assigned senadir and unassigned mikejolley Jul 1, 2021
@senadir
Copy link
Member

senadir commented Jul 2, 2021

@jsnajdr I wasn't able to reliably add a module variable, the webpack page doesn't provide any meaningful info about adding. I tried both ProvidePlugin and DefinePlugin but they both didn't work for my case.
I ended up just using a locally scoped variable with let.
Because we don't yet have a case in which multiple blocks inserted on the same page dynamically load JS, I couldn't test my solution. Inserting the same block twice with different inner block templates worked fine in my case.

@mikejolley mikejolley force-pushed the fix/4019-lazy-translations branch from 473eae3 to f158a9a Compare August 9, 2021 11:36
@mikejolley mikejolley self-assigned this Aug 9, 2021
@mikejolley
Copy link
Member Author

@senadir @opr I updated how we pass in supported files. It seems to be working well now. Can you take a final look at this, please?

@mikejolley
Copy link
Member Author

@senadir I've tried another approach to this in #4897

@nerrad nerrad reopened this Oct 20, 2021
@nerrad nerrad closed this Oct 20, 2021
@nielslange nielslange added the focus: i18n The issue/PR is related to internationalization. label May 18, 2022
@nielslange nielslange deleted the fix/4019-lazy-translations branch April 19, 2023 23:46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
focus: i18n The issue/PR is related to internationalization.
Projects
None yet
6 participants