Skip to content

Conversation

@cconard96
Copy link
Contributor

@cconard96 cconard96 commented Oct 22, 2025

Checklist before requesting a review

  • I have read the CONTRIBUTING document.
  • I have performed a self-review of my code.

Description

Fixes #21434

  • Removes specific Select2 language setting. This wasn't really needed even before this PR, except for maybe one or two language, as it gets the language from the HTML
  • Removes specific i18n language setting for same reason
  • Moves locale loading before most JS imports, removes reliance on jQuery, and makes the request synchronous
  • Replaces less specific language (en, es, etc) in HTML document with the specific one (en_GB, es_AR, etc) with an attempted automatic conversion from POSIX language codes to BCP 47

// Fetch locale JSON without jQuery to allow fetching before jQuery is loaded
function loadLocales() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '" . \jsescape($locales_path) . "', false); // synchronous request
Copy link
Contributor

Choose a reason for hiding this comment

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

Synchronous ajax requests are deprecated and very bad for UX as the whole browser freeze during the request.

See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Synchronous_and_Asynchronous_Requests#synchronous_request

Synchronous XHR requests often cause hangs on the web, especially with poor network conditions or when the remote server is slow to respond. Synchronous XHR is now deprecated and should be avoided in favor of asynchronous requests.

I understand that there is probably some constraint that lead you to this solution but IMO we should look for an alternative here.

Copy link
Contributor Author

@cconard96 cconard96 Oct 22, 2025

Choose a reason for hiding this comment

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

Synchronous ajax requests are deprecated and very bad for UX as the whole browser freeze during the request.

I am well aware, but so does the initial page load which may have something we can claw back the same performance from. With this, we are talking about maybe 40ms on my side one time and then maybe 2ms after it is cached. Even when I am in development env and it doesn't get cached, I don't notice any more of a delay than I usually see.

I understand that there is probably some constraint that lead you to this solution but IMO we should look for an alternative here.

I have several alternatives. None of which people would be happy about for a bugfix version and I think we will be discussing the frontend design decisions soon. The things I am working on related to frontend/performance are mostly short-term solutions for 11.0.X in my mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Functionally, there is no difference between a synchronous request or awaiting a promise-based one. We need these locale strings to be immediately loaded before any of our JS that uses the translation functions gets loaded.

Copy link
Contributor

Choose a reason for hiding this comment

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

40ms on localhost is not the same as 500ms on a real server.

Especially since there is one request per plugin:

image

It already takes 3s with concurrency in this example, it would be a nightmare with synchronous requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

3 seconds with async calls is insane, as is the main locales taking nearly a second. I will test again with my laptop a bit later this morning to get a little more realistic timings with my PR.

It sounds to me like we already have the one main "downside" of a SPA, being that we have some data that needs loaded client-side before any rendering can take place, without any of the benefits of SPA.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Over wifi, the main locales are taking 100-150ms. If I throttle to 4g speeds, it takes 900ms. By the time the locales start loading, the main page is already rendered even if it may not be fully interactable yet.

$script = "
// Fetch locale JSON without jQuery to allow fetching before jQuery is loaded
function loadLocales() {
const xhr = new XMLHttpRequest();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to use XMLHttpRequest instead of the fetch API here?

Fetch API has been the standard for some times, see: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

The Fetch API provides an interface for fetching resources (including across the network). It is a more powerful and flexible replacement for XMLHttpRequest.

Copy link
Contributor Author

@cconard96 cconard96 Oct 22, 2025

Choose a reason for hiding this comment

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

Fetch is promise-based only, which means async. It could have been done with some more work but I don't see the issue for something small like this which I would want to tear out and completely redo as soon as I am able.

Copy link
Member

@cedric-anne cedric-anne left a comment

Choose a reason for hiding this comment

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

A solution could be to have synchronous <script> tags that refers to a stateless PHP controller that serves a script that defines the locales (with a i18n.loadJSON() call), loaded right after the lib/base.js script. The browser would probably use parallel requests to load them, that would be better that synchronous requests executed sequentially.

Another solution could be, if it is possible, to execute async requests as we are doing currently, but use a proxy object in _*() functions to wait for the corresponding response when it is not yet received.

@cconard96
Copy link
Contributor Author

A solution could be to have synchronous <script> tags that refers to a stateless PHP controller that serves a script that defines the locales (with a i18n.loadJSON() call), loaded right after the lib/base.js script. The browser would probably use parallel requests to load them, that would be better that synchronous requests executed sequentially.

Another solution could be, if it is possible, to execute async requests as we are doing currently, but use a proxy object in _*() functions to wait for the corresponding response when it is not yet received.

I'll take a look.

I was trying to keep things as close as possible to how they are currently, keeping in mind that I struggle to see any kind of fix as more than a short-term solution. I considered doing this kind of blocking call from the translation functions but thought that loading it before it is needed would be a smoother experience. With a more long-term solution, I had already though about making the translations reactive so that you could easily switch languages without needing a page reload, but that really applies with a substantial frontend rework.

Finally, I considered allowing front/locales.php to support returning multiple domains at the same time, but I guess that could be annoying if a single plugin gets disabled and forces the browser to fetch everything again.

@cconard96 cconard96 force-pushed the fix/js_locales_loading branch from ffee8f0 to ac802aa Compare October 27, 2025 19:59
Copy link
Member

@cedric-anne cedric-anne left a comment

Choose a reason for hiding this comment

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

Even with many plugins, the latest solution seems OK to me. The initial load (without cache) may indeed be important, but this is mostly due to the fact that there are a bunch of JS files already in the load queue at this moment, and the browser does not request all resources at the same moment.

@cedric-anne cedric-anne added this to the 11.0.2 milestone Oct 30, 2025
@cedric-anne cedric-anne merged commit 676cd4e into glpi-project:11.0/bugfixes Oct 30, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing dialog translation

3 participants