fabi1cazenave / webL10n

Client-side internationalization / localization library
http://fabi1cazenave.github.com/webL10n/
278 stars 70 forks source link

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

Open n1k0 opened 10 years ago

n1k0 commented 10 years ago

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 commented 10 years ago

ping ;)

fabi1cazenave commented 10 years ago

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 commented 9 years ago

@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 commented 9 years ago

Looks like I found it:

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

gazal-k commented 9 years ago

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 commented 9 years ago

@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 commented 9 years ago

@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:

gazal-k commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

:+1: