Preact: Chrome's automatic page translation breaking Preact rendering

Created on 29 Nov 2017  路  15Comments  路  Source: preactjs/preact

It looks like Preact and Chrome automatic translation feature aren't best friends.

As far as I can tell, it can be reproduced on any Preact based web app, but I made a simple test page just to be sure: http://chrome-translate-preact---not.surge.sh/

1- enable Chrome's page translation

step-1

2- interact with the app

step-2

EDIT : I don't really know where to start to fix this, but please tell me if I can help somehow.

has fix known issue workaround

Most helpful comment

I've debugged this some more.
The issue is the combination of preacts diff algorithm and chrome inserting font elements instead of "just" replacing the text.
The issue on preacts side is caused by this line:
https://github.com/developit/preact/blob/86361f020ffb85cea9aac6af5a07f2682da425ed/src/vdom/diff.js#L206

The font element a inserted by chrome has no props (how should it have any ;) ), the font tag also has no splitText method. Because we are "rerendering" the component isHydrating is false.
So the node doesn't get added to innerDiffNodes children array and therefore can't be removed when the diff is done.
When preact tries to diff the text vnode it can't find a textNode in the children array (in fact the children array is completely empty) so preact creates a new textNode and inserts it at the first position.

Chrome then again replaces the new textNode as part of the translation process with a font element and the cycle repeats with preacts next diff.

All 15 comments

Need to take a peek at what Chrome is doing under the hood here. It seems like it's flat-out replacing Text nodes, but that is really odd. It should just be setting their .nodeValue in-place.

I'm also running into this issue and the only "fix" I've been able to find for it is just disabling the translation with by adding this meta tag to the head: <meta name="google" value="notranslate">.

Any ideas on how to get around this issue while still allowing Google to translate the page?

I think we might have to wait for the next version of Preact to fix this, because right now we store too much state in the DOM.

@developit I'am experience the same issue aswell, not to push for it even further but just to let you know of one more case.

@tomprats i tried your meta fix but that only stops the automatic translate, if a user actively says translate this page this wont stop it.

For the record here is a image of how the translate feature pollutes the parent element with alot of font HTML Tags

skaermbillede 2018-06-01 kl 11 35 23

@developit we're running in the same issue, can we somehow workaround it until there is a proper solution?

I would suggest maybe turning off auto translation. Not really sure of a fix, this is kinda a Chrome bug

@developit i happens aswell if the user actively translate the page.

I know we cant guard this, but it would be Nice if the dom dont bloat up as it does. All i鈥檝e Been able to debug on this issue it it seems to happen every time a component updates.

FYI, in addition to <meta name="google" content="notranslate" /> that disables translation for the whole page, it is also possible to disable translation for a specific section of the page only, using class="notranslate" on the root element of the area that should not be translated (see https://cloud.google.com/translate/faq#technical_questions).

Hmm I don't think it 's related to storing state within the DOM.
I've experimented with storing preacts state within a WeakMap<Element, State> (just for testing).
The example from above shows the same behavior: https://j4y0pj9nnv.codesandbox.io/ source

I've debugged this some more.
The issue is the combination of preacts diff algorithm and chrome inserting font elements instead of "just" replacing the text.
The issue on preacts side is caused by this line:
https://github.com/developit/preact/blob/86361f020ffb85cea9aac6af5a07f2682da425ed/src/vdom/diff.js#L206

The font element a inserted by chrome has no props (how should it have any ;) ), the font tag also has no splitText method. Because we are "rerendering" the component isHydrating is false.
So the node doesn't get added to innerDiffNodes children array and therefore can't be removed when the diff is done.
When preact tries to diff the text vnode it can't find a textNode in the children array (in fact the children array is completely empty) so preact creates a new textNode and inserts it at the first position.

Chrome then again replaces the new textNode as part of the translation process with a font element and the cycle repeats with preacts next diff.

Had to disable Chrome's translations by adding translate="no" and class="notranslate" to the <html> element, but definitely not a suitable workaround.

@Kanaye is there a solution for this?

A temporary solution is we can disable re-render for those text elements:

  1. create a NoReRender component like this:
import { h, Component } from 'preact';

export default class NoReRender extends Component {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    const { tagName, children, ...props } = this.props;
    const TagName = tagName || 'span';
    return <TagName {...props}>{children}</TagName>;
  }
}
  1. Update those elements which might be affected with NoReRender

e.g.
change

      <a
        href={href}
        className="my-link"
      >
        My Link
      </a>

to

      <NoReRender
        tagName="a"
        href={href}
        className="my-link"
        key={'set an unique key is very import'}
      >
        My Link
      </NoReRender>

Hi all,

I finally got some time to write up a resilient solution to this:
https://gist.github.com/developit/3e807962630ddbd4977cbd07b597f24f

It intercepts Google Translate when it goes to replace Text nodes with its elements, turns the into a mock-text-node (from Preact's perspective), and rewrites some properties that cause it to be diffed as if it were still the same Text node. It also intercepts updates to the fake "text" node and triggers replacement in order to ensure Google Translate updates the new text with a translated version inline.

Since there's a workaround, let's close this up, if there are any more issues feel free to make a new issue/reopen!

Was this page helpful?
0 / 5 - 0 ratings