JavaScript translations broken (script_loader_tag / bundled handles)
-
Plugin version: 4.3.3 Summary: JS-side translations (via
wp_set_script_translations()) don’t load anymore. Worked in an earlier version, broke at some point — likely whenprestoComponentsTag()was introduced. Issue 1:script_loader_tagfilter drops the translation<script>blockIn
inc/Services/Scripts.php,Scripts::prestoComponentsTag()is hooked onscript_loader_tag:public function prestoComponentsTag( $tag, $handle, $source ) { if ( 'presto-components' === $handle ) { $tag = '<script src="' . $source . '" type="module" defer></script>'; } return $tag; }WordPress core builds
$taginWP_Scripts::do_item()by prepending the JS translation<script>block (generated fromwp_set_script_translations( 'presto-components', 'presto-player' )) before calling thescript_loader_tagfilter. BecauseprestoComponentsTag()discards the incoming$tagand rebuilds it purely from$source, that prepended translation block is silently dropped for every request. Result:presto-componentsnever gets translated in the browser, no matter what’s in the.po/.mofile.Suggested fix: modify
$tagin place instead of rebuilding it, e.g.:public function prestoComponentsTag( $tag, $handle, $source ) { if ( 'presto-components' === $handle ) { $tag = str_replace( '<script ', '<script type="module" defer ', $tag ); // (adjust as needed to avoid duplicate attributes if $tag already has some) } return $tag; }Note for whoever picks this up:
$tagat this point already contains the translation<script>block prepended byWP_Scripts::do_item()and the actual<script src=...>tag. A naivestr_replace('<script ', '<script type="module" defer ', $tag)would incorrectly addtype="module" deferto the translations tag too — worth only targeting the actual source-bearing<script>(e.g. via a more specific regex on the tag that containssrc="matching$source). Issue 2: script handles containing/can never resolve their translation JSONSeveral handles registered with
wp_set_script_translations( $handle, 'presto-player' )(no explicit$path) contain slashes:surecart/blocks/admin(dist/blocks.js)presto/dashboard/admin(dist/dashboard.js)surecart/divi/admin(dist/divi.js)
WordPress core’s
load_script_textdomain()builds the expected JSON filename as:$handle_filename = $file_base . '-' . $handle . '.json';with the raw, unsanitized handle. For
surecart/blocks/adminthis producespresto-player-<locale>-surecart/blocks/admin.json— a “filename” containing path separators, which no translation tool (Loco Translate,wp i18n make-json, GlotPress) can ever produce, since it would require literal nested directories namedpresto-player-<locale>-surecart/blocks/. This lookup can never succeed.WordPress then falls back to its older src-relative-path method (md5 of the enqueued script’s relative path, e.g.
md5('dist/blocks.js'), looked up inwp-content/languages/plugins/) — but neither Loco Translate norwp i18n make-jsongenerate files under that name either, since both tools name JS translation JSON files after the original, pre-bundle source file referenced in the.potcomments (e.g.admin/blocks/blocks/hosted/HostedPlaceholder.js), not after the bundleddist/*.jsoutput that’s actually enqueued. So the generated JSON never matches what core looks for at runtime, for either lookup path.Suggested fix: either (a) avoid
/in script handles going forward, and/or (b) pass an explicit$pathargument towp_set_script_translations()for a directory laid out to match handle-based filenames without slashes (e.g. rename handles internally, or provide your own translation loading instead of relying on core’s automatic lookup), and (c) document/ship correctly-hashed JSON files (hashed against the actualdist/*.jsrelative paths) as part of the plugin’s own translation packaging, rather than relying on translators’ tools to derive the right filename from.potsource comments. Reproduction- Set up JS strings for translation and get a translated
.po/.mofor domainpresto-player. - View source of a page/admin screen enqueuing
presto-components,surecart/blocks/admin, etc. - Note the absence of any
<script id="{handle}-js-translations">block before the actual script tag, and/or that JS strings render untranslated in the browser despite a complete translation.
Current workaround
In the meantime we’re working around both issues via a small mu-plugin using
script_loader_tag(to restore the translation block forpresto-components) and Loco Translate’sloco_compile_single_json/ WordPress core’sload_script_translation_filefilters (to consolidate all of the domain’s JS strings into one predictably-named file, sidestepping the unresolvable slash-in-handle filenames). Happy to share it if useful as a reference for a proper upstream fix.Happy to provide more detail or test a patch.
You must be logged in to reply to this topic.