Skip to content

Development notes

Suggest another language without forcing a redirect

A non-modal language suggestion can preserve the URL while moving focus. Real translations, browser preferences, and temporary dismissal need separate checks.

林予晴
Published
  • Multilingual websites
  • HTML
  • State design
  • Progressive enhancement

A reader who opens a Traditional Chinese article in a Japanese-language browser may have arrived through search, a friend’s link, or a link to a particular passage. Browser preferences can prompt the site to offer another version; they do not prove the reader wants to leave the original on this visit.

The site used a language suggestion that left readers on the original page and offered real English or Japanese links for them to choose. On October 26, 2026, I checked its display conditions, dismissal records, and actual focus behavior. “No automatic redirect” did not mean “no interruption at all.”

Check that another version really exists

The suggestion component was rendered only on Traditional Chinese pages, when it had not been disabled and at least one English or Japanese destination existed. It did not guess article URLs from titles or create links for untranslated articles.

Destinations for fixed pages came from the existing language routes. Article pages passed in the set of translations that actually met the publication criteria. Even after the component had been rendered, the script still needed to find a link for the language it intended to suggest before showing the prompt.

For example, if only an English destination existed but the browser preference pointed to Japanese, the implementation did not switch to suggesting English instead. That branch could be confirmed from the code. It did not mean the implementation provided complete language negotiation for every translation gap.

That order follows the principle in Do not generate a fake language page when no translation exists: a suggestion is a route into actual content, not proof that the content exists.

Read the first preference, not the whole language list

navigator.languages provides language tags in preference order, with the most preferred language first. [4] The site read the first item and fell back to navigator.language only if that item was unavailable.

The next rule was simple: if the tag started with the Japanese language code, it suggested Japanese; if it started with the Chinese language code, it showed no suggestion. Otherwise, it tried to suggest English. The code did not traverse the full preference list to find the first language available on the site.

I set test preference values in an isolated browser page and observed the following:

Test preference Result on a Traditional Chinese page
Japanese first, English later Show the Japanese suggestion
Chinese first, English later Do not open a suggestion
English first Show the English suggestion
German first, Japanese second Still apply the English suggestion rule

These were test settings inside the browser, not reader research or the distribution of language preferences on real devices. They also show the rule’s limit: English was a fallback chosen by the implementation, not proof that the reader preferred English.

Non-modal can still mean a focus change

The prompt used the native dialog’s show(), not showModal(). In the local observation, it was open but did not match :modal, and the original Traditional Chinese URL had not changed.

That meant the prompt did not rely on modal state to block interaction with the document. However, HTML’s show() algorithm still runs the dialog focusing steps. [2] Leaving the background available for interaction does not mean focus remains untouched.

After the Japanese prompt appeared, focus was on the suggested Japanese link. That is a trade-off worth keeping explicit: the site did not force a page change, but it could still take keyboard users away from their reading position. If the aim is to avoid even a focus interruption, the presentation needs reassessment, not just a switch from modal to non-modal.

Closing also needs to be checked along the actual interaction path. I focused “Not now” or its Japanese equivalent and pressed Enter to dismiss the prompt, rather than substituting a direct function call for the interaction. Although the component listened for cancel, these observations did not establish that every Escape or outside-click path had been checked.

“Not now” is not a permanent language setting

When the prompt was dismissed, the script recorded the dismissal separately for each suggested language in sessionStorage. It did not store a permanent “current language.”

sessionStorage is scoped to the origin and the top-level tab’s page session and can survive reloads. It is not a cross-device account preference. [5] After I dismissed the Japanese suggestion locally and reloaded the same tab, the prompt did not open again.

Storage reads and writes can fail, so the operations had exception handling. I separately simulated an inability to access session storage. The dismissal button still closed the prompt, but the suggestion reappeared after a reload because the dismissal had not been saved.

This cannot be described as “dismiss it once and you will never see it again.” The supported behavior was narrower: with storage available, the site remembered dismissal of that language’s suggestion for the page session; without storage, the prompt could still be closed at that moment.

A real destination also needs the right state

When creating the suggestion link, the script copied the current query and hash. For example, accepting the English suggestion from the design category on the Traditional Chinese work page preserved those parameters and that category, rather than leaving only the path to the English work page.

I activated the actual English link with the keyboard and arrived at the English work page. The original query, design-category hash, and page language all matched. That was an observation of one specific path, not evidence that every fragment on every other page had an equivalent translated target.

The copy happened only during prompt initialization. If the prompt stayed open and the in-page state changed afterward, the code did not listen for every change and update the destination. An initialization snapshot and ongoing synchronization need separate designs.

English and Japanese pages did not render this suggestion themselves. Without JavaScript, the prompt on Traditional Chinese pages stayed hidden. The main navigation still contained native language links, so readers did not have to open the prompt successfully to find other versions.

That kept the language suggestion in a supporting role: it could offer a link based on a browser signal, but public pages and navigation did not depend on the prompt. Preference rules, real translations, focus, and dismissal records each have separate responsibilities. “Multilingual support” is too broad a label to account for them all.

The October 26 checks did not cover physical phones, screen readers, multiple browsers, or every storage policy. Nor did they use click-through rates or time on page to show that this prompt was more effective. Whether to keep the focus move on opening was still a choice that needed to be judged in the actual reading context.

Sources

[2] https://html.spec.whatwg.org/multipage/interactive-elements.html — WHATWG HTML: Interactive elements [4] https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages — MDN: Navigator languages [5] https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage — MDN: Window sessionStorage