Issue description generated with the help of an LLM. Debug and solution is brain-made.
Problem/Motivation
If a JavaScript translation file (assets://languages/[langcode]_[hash].js) is deleted from disk, it is not regenerated. locale_js_translate() only rebuilds it when new JS files are parsed or when caches are rebuilt. Until then, every page that references it tries to aggregate a file that no longer exists.
In a basic/standard setup, this produces a warning:
file_get_contents(assets://languages/es_bujkrUn1sDCUHrPFyAbdcHT8QVedCwVJoD_ufmvGZSo.js): Failed to open stream...
But after #3592731: JavaScript translation files are now stored in the assets:// path location it causes real problems on multi-head setups that do not share the assets directory between nodes:
- The translation file only exists on the node that generated it.
- The other nodes still have the file name in state (
locale.translation.javascript), so they try to include it in the aggregate. That fails with afile_get_contents()warning and produces an aggregate with a different hash. - Each node then 301-redirects requests for the other node's aggregate hash to its own. Behind a CDN/Varnish with consistent hashing, this can turn into a redirect loop.
Steps to reproduce
- Enable JS aggregation on a site with a non-English interface language (Spanish in this example).
- Remove the generated files:
rm -rf web/sites/default/files/languages/* web/sites/default/files/js/* - In a new browser tab, open DevTools > Network, filter by
js_and load a page. - The listed
js_*files match the ones on disk, and the translation file is created:
$ ls -lh web/sites/default/files/languages/ web/sites/default/files/js/*js -rw-rw-r-- 1 jonhattan jonhattan 209K nov 5 09:47 web/sites/default/files/js/js_dpq2jaQYyc0E0lAkfz8VMOHuUcWV1ydluMqj3E2H0oc.js -rw-rw-r-- 1 jonhattan jonhattan 61K nov 5 09:47 web/sites/default/files/js/js_rYjaONBsQxBUG-dL6VjVZhQrnQsmAZCFrdznvaSIH34.js -rw-rw-r-- 1 jonhattan jonhattan 233 nov 5 09:47 web/sites/default/files/js/js_U-381Wzwm6zvcOob9_ZT5wCYdBaZQ581ih9PoHQPHGk.js -rw-rw-r-- 1 jonhattan jonhattan 34K nov 5 09:47 web/sites/default/files/js/js_vxuQTvLZNYQJJ9dttg2FoukK8aPaYWNnLFJmFOBl-8E.js web/sites/default/files/languages/: -rw-rw-r-- 1 jonhattan jonhattan 19K nov 5 09:47 es_bujkrUn1sDCUHrPFyAbdcHT8QVedCwVJoD_ufmvGZSo.jsWatchdog:
locale Warning JavaScript translation file bujkrUn1sDCUHrPFyAbdcHT8QVedCwVJoD_ufmvGZSo.js was lost. locale Notice Created JavaScript translation file for the language Español. - Remove the files again and repeat step 3 in another new tab, so the first result stays visible. This simulates a request hitting another node where neither the aggregates nor the translation file exist. Results:
- The translation file is not regenerated.
- A new aggregate with a different hash is created:
js/js_cRxt1dBfhSiG3lzYl97YgGAq0DPWCQoY_2FBiVuVuGY.js - Watchdog:
Warning: file_get_contents(assets://languages/es_bujkrUn1sDCUHrPFyAbdcHT8QVedCwVJoD_ufmvGZSo.js): Failed to open stream: "Drupal\Core\StreamWrapper\AssetsStream::stream_open" call failed in Drupal\Core\Asset\JsOptimizer->optimize() (line 41 of core/lib/Drupal/Core/Asset/JsOptimizer.php).
- Request the URL of the first aggregate. Drupal redirects it to the new hash:
$ curl -I "https://example.com/sites/default/files/js/js_dpq2jaQYyc0E0lAkfz8VMOHuUcWV1ydluMqj3E2H0oc.js?scope=footer&delta=1&language=es&theme=eada&include=eJxl..." HTTP/2 301 location: /sites/default/files/js/js_cRxt1dBfhSiG3lzYl97YgGAq0DPWCQoY_2FBiVuVuGY.js?scope=footer&delta=1&language=es&theme=eada&include=eJxl...On a multi-head setup, every node does this with its own hash.
Proposed resolution
In locale_js_translate() regenerate the file is it is supposed to exist and is not present on disk
Remaining tasks
- Open an MR with the fix.
- Review.
User interface changes
None.
API changes
None.
Data model changes
None.
Release notes snippet
N/A
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | issue-3635501-4.patch | 1.31 KB | jonhattan |
Issue fork drupal-3635501
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
jonhattanComment #4
jonhattanPatch for 11.4
Comment #5
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #6
jonhattanComment #7
pfrenssenTested on main with a throwaway functional test. It translates a string in
locale.admin.js, loads the page, deletes the translation file and loads the page again.jsTranslate()runs fromhook_js_alter(), only on a cache miss inAssetResolver::getJsAssets(). A second web server that shares the cache gets a cache hit.LocaleJs.php:87invalidates all languages when the current language's file is missing.LocaleTranslationUiTest::testJavaScriptTranslation()deletes the file but callsrebuild()directly.locale/translationslibrary aggregate returns 400 "Invalid filename.", also when the file exists.JsAssetController::getGroups()passes only the placeholder tojsAlter(), sojsTranslate()returns NULL.The Nightwatch failure is a logout timeout in
machineNameTransliterationTest.js. Earlier pipelines passed Nightwatch.AI disclosure: Used AI to search for edge cases that are not covered by the patch, and to write this comment.