Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for live update of translated contents on DOM change #42

Open
n1k0 opened this issue Apr 1, 2014 · 10 comments
Open

Add support for live update of translated contents on DOM change #42

n1k0 opened this issue Apr 1, 2014 · 10 comments

Comments

@n1k0
Copy link

n1k0 commented Apr 1, 2014

Right now when new elements are added using data-l10n attributes, they're not translated live. Using Mutation Observers may solve the issue and bring a nice feature to the lib.

@n1k0
Copy link
Author

n1k0 commented Apr 8, 2014

ping ;)

@fabi1cazenave
Copy link
Owner

Ooops, pong.

I’m not sure yet I want webL10n to rely on mutation observers, but I can think of a quick function for that.

@gazal-k
Copy link

gazal-k commented Aug 25, 2015

@fabi1cazenave by a quick function, I'm assuming you mean an imperative mechanism to apply localization instead of on DOM getting ready.

Is such a function present in the current API?

@gazal-k
Copy link

gazal-k commented Aug 25, 2015

Looks like I found it:

document.webL10n.translate(element) does the trick for me

@gazal-k
Copy link

gazal-k commented Aug 25, 2015

for a single page application with a lot of DOM changes though, webL10n does not truly give declarative localization. Mutation Observers may be the answer.

@gazal-k
Copy link

gazal-k commented Aug 29, 2015

@n1k0 this works for me:

_ - lodash

  document.addEventListener('localized', function() {
    var observer = new MutationObserver(function(mutations) {
      _.forEach(_.pluck(mutations, 'target'), function(element) {
        document.webL10n.translate(element);
      });
    });
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  });

Another variant that I tried:

  document.addEventListener('localized', function() {
    var translate = function() {
      document.webL10n.translate();
    };
    var observer = new MutationObserver(function(mutations) {
      _.debounce(translate, 250)();
    });
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  });

From some rudimentary profiling it seemed like the first variant performed slightly better.
Also, there is a slight delay in translation with the second variant.

@fabi1cazenave or @Rob--W if this doesn't hurt performance too much, I can submit a PR with this change (a modified version that does not use lodash).
And maybe a config variable can be used to turn this on so that its an optin feature?

@Rob--W
Copy link
Collaborator

Rob--W commented Aug 29, 2015

@gazal-k I'd not take such a PR because it would hurt performance in large applications.

FWIW, there are some implementation flaws in your snippet:

  • In the first snippet, infinite recursion may occur due to the translation triggering the MO.
  • In the second snippet, debounce doesn't work, because you're creating a new debounced function at each call. At the very least the function created by _.debounce should be stored outside the MO callback.

@gazal-k
Copy link

gazal-k commented Aug 30, 2015

Actually I did test both snippets in an application.

The first one does trigger the MO callback an extra time after the translation, but a subsequent call to translate does not modify the DOM, hence no more MO callback.

You're right about the debounce function on the second snippet. I'd originally written and tested it using polymer framework's debounce method which works a little differently from how lodash's works. So, the second snippet can be modified as:

  document.addEventListener('localized', function() {
    var translate = _.debounce(document.webL10n.translate, 250);
    var observer = new MutationObserver(function(mutations) {
      translate();
    });
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  });

The profiling results seemed to indicate that MutationObservers are pretty fast. I might be able to tweak the first snippet further to filter just new non-text nodes and apply weL10n.translate on those. Will have to see whether more calls to translate with smaller sections of DOM or fewer calls with larger sections are faster.

@Rob--W what would you recommend for single page apps?

I'm trying to make this work in an application built using https://github.com/Polymer/polymer/. So another approach I can think of is to leverage polymer's behaviors feature to use the imperative API document.webL10n.get in a template function, hence making it available in a more or less declarative way.

@Rob--W
Copy link
Collaborator

Rob--W commented Aug 30, 2015

The profiling results seemed to indicate that MutationObservers are pretty fast.

Using documents of which complexity? What is "pretty fast"? My experience with permanent MOs and looking for elements (at document load, so when new nodes are being added) in the full subtree is that there are documents where the lot of DOM changes add 10-25% to the load time, and a significant increase in peak memory usage (900MB->2.2GB on http://dromaeo.com/?dom-mod, plus seconds freezing due to garbage collection). This is not a problem with mutation observers, but with using mutation observers on the whole document and reading properties of the mutations (many of these properties are lazily initialized, at least by Chrome).

@Rob--W what would you recommend for single page apps?

I have no definite recommendation, because I haven't found a library yet with which I was completely satisfied.

I'm trying to make this work in an application built using https://github.com/Polymer/polymer/. So another approach I can think of is to leverage polymer's behaviors feature to use the imperative API document.webL10n.get in a template function, hence making it available in a more or less declarative way.

If you use Polymer or web components, you could indeed use element creation / attribute change callbacks to detect a new item for translation.

@gazal-k
Copy link

gazal-k commented Aug 31, 2015

👍

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

No branches or pull requests

4 participants