Skip to content

Updated text refresh for dynamic translators - #631

Open
sfisol wants to merge 1 commit into
masterfrom
updated-text-refresh
Open

sfisol wants to merge 1 commit into
masterfrom
updated-text-refresh

Conversation

@sfisol

@sfisol sfisol commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Status: parked — planned, but postponed for now

Following the review, this stays as a draft and isn't meant for merging in its current form. The problem is real, but the solution here is too specialized to live in the core as it is. Below is a summary to come back to.

The problem

Page translators replace the app's text nodes with nodes of their own. These include the one built into Chrome and Edge, Firefox's full-page translation, translating proxies, and some extensions. Chrome, for example, puts a <font> element in place of each translated text. The app keeps references to the original text nodes, which are no longer in the page. That leads to three things:

  • Text the app updates afterwards changes in a detached node, so the page keeps showing the old translated text.
  • A page translated before the app starts doesn't match the server's markup. Hydration reverts it to the original language and logs hundreds of errors.
  • On a copy of the page served from another origin (a translating proxy, a web archive), <base> points back to the original site. The app would request its data from there, which the browser doesn't allow, while its links stay with the copy.

Nothing crashes: every DOM command is wrapped and reports its own failure.

What this branch does

  • MapNodes uses a MutationObserver to track nodes that something else put in place of the app's texts. An updated text goes in as a new node where the stand-in is, so the translator picks it up again.
  • The loader starts watching the page before the app starts, and hydration adopts replacements made in the meantime. It doesn't look for <font> or any other translator-specific markup.
  • The loader doesn't start the app when <base> points to another origin.

Why it's postponed

Other frameworks don't special-case translators in their core either:

In the meantime, an app can handle the foreign-copy case on its own. A <script type="module"> in <head> runs before the loader and can remove data-vertigo-run-wasm when document.baseURI has a different origin. In-browser translation remains a known limitation.

Possible ways forward

Each of these is generic and not tied to translators:

  1. More resilient hydration. When the DOM differs from the server's markup, hydration would adopt what it can or rebuild cleanly instead of fighting the page (similar to Svelte #16248).
  2. Set texts through CharacterData.data instead of textContent. DOM-level shims such as translation-resilience intercept the data and nodeValue setters but not textContent. With this one-line change, an app could opt into such a shim without anything in vertigo.
  3. An extension point in the driver. It would let an app map a node vertigo holds to the node currently standing in for it, so this logic could live in a separate, opt-in package.
  4. An opt-in loader attribute for "don't start on a copy served from another origin", rather than doing it always.

@sfisol
sfisol requested a review from szagi3891 November 3, 2026 14:45
…an extension) follow the app's changes, also when replaced before hydration; no start on a copy of the page served from another origin
@sfisol
sfisol force-pushed the updated-text-refresh branch from 4301878 to 07f120e Compare November 3, 2026 14:45
@sfisol sfisol changed the title Updated text refresh Updated text refresh for dynamic translators Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant