Skip to content
This repository has been archived by the owner on Apr 18, 2023. It is now read-only.

Can't observe i18n.locale in a component #299

Closed
imbrou opened this issue Sep 1, 2015 · 14 comments
Closed

Can't observe i18n.locale in a component #299

imbrou opened this issue Sep 1, 2015 · 14 comments

Comments

@imbrou
Copy link

imbrou commented Sep 1, 2015

Hi James, hi Kelly, hi dockyard, hi folks!

Using Bootstrap 3 tooltips in a component, I can't find a way to translate its content on a locale change.
Component's template:

<span {{action "rate" "1"}} data-toggle="tooltip" title={{t "rateItem.relevancy.-1"}}>★</span>

Component's logic:

i18n: Ember.inject.service(),

...

initTooltips: function() {
   $('[data-toggle="tooltip"]').tooltip() ;
   // empties the elements "title" attribute, and sets the tooltips content to its value.
}.on('init').observes('i18n.locale')

As you can see, the Bootstrap API makes use of the HTML title attribute to provide a good fallback to classic tooltips if needed. Then of course, to avoid double tooltips, the $().tooltip() call saves its content, and empties the attribute.
My problem is then the following:
My observer on i18n.locale is never triggered ; after a language switch, my "saved" tooltip content is never updated to the translated one.
Concretely, after a (English ➜ Dutch) switch, I end up in this situation:
tooltip-not-translated
Fortunately, sounds like @kellyselden faced the same problem last year on @DockYard's fork. He even implemented a nice plugin proposing a pretty clean (?) workaround.

What about integrating it? Would somebody else be interested in this feature?

@jamesarosen
Copy link
Owner

Which version of ember-i18n are you using?

@imbrou
Copy link
Author

imbrou commented Sep 1, 2015

I'm using:

  • ember-i18n 4.1.1
  • ember 1.13.1

@jamesarosen
Copy link
Owner

I'm surprised the observer doesn't fire on your component. It does in the helper.

How are you switching locales?

@imbrou
Copy link
Author

imbrou commented Sep 1, 2015

I'm using the wiki example, more or less: I turned it into a bootstrap default navbar dropdown button.
My edits are:

  • tagName and classNames properties ;
  • templates elements (optionli) ;
  • moved the change: listener in my actions: map.

Template looks like:

...
<ul class="dropdown-menu" role="menu">
{{#each locales as |loc|}}
   <li><a href="#" {{action "change" loc.id}}>{{loc.text}}</a></li>
{{/each}}
</ul>

Logic looks like:

const { Component, computed, inject } = Ember;
i18n: inject.service(),
...
actions: {
   change: function(locId) {
     this.get('i18n').set('locale', locId);
   }
}

I am using a computed property for that purpose in another component, and it works perfectly. I thus guess that the event is triggered...
Do you think that injecting i18n in all my components (via the initializer) can help ?

@jamesarosen
Copy link
Owner

Do you think that injecting i18n in all my components (via the initializer) can help ?

It's worth a shot, but I doubt that's the problem.

Is the title attribute getting updated to the new value when the locale changes? Are other translations?

@imbrou
Copy link
Author

imbrou commented Sep 2, 2015

Yes: the title attribute is updated. It's actually why I end up with the yellow tooltip that you can see on my screenshot. I only have EN and NL translations.
Injecting the service in all my components surprisingly works !
Please note that thanks to this stackoverflow post, I separated my code in two initTooltips and refreshTooltips methods, in order to use the better .tooltip('fixTitle') in my observer. Thanks to this blog article, I also understood that I needed to schedule my JQuery calls on afterRender.
Here is my final observer:

refreshTooltips: function() {
   Ember.run.scheduleOnce( 'afterRender', this, function() {
      $('[data-toggle="tooltip"]').tooltip('fixTitle') ;
   }) ;
}.observes('i18n.locale')

The bug is still sounds real.
I tried adding a console.log in the observer — just before the Ember.run.scheduleOnce(...) call.
The results are:

  • i18n individual injection in my component
    = i18n: Ember.inject.service() in my component
    ➜ observer not triggered
  • i18n global injection in all components
    = application.inject('component', 'i18n', 'service:i18n') in my i18n initializer
    ➜ observer triggered

@digitalcora
Copy link
Contributor

To add another data point, I also encountered this recently. I have a component that uses the i18n: Ember.inject.service() approach, with a computed property that depends on i18n.locale that wouldn't trigger when the locale was changed. Notably, like @imbrou I am also setting the locale from within a (different) component that uses the service injection. It's almost as though the two components are not actually seeing the same object.

@rwjblue
Copy link
Contributor

rwjblue commented Sep 2, 2015

Services are only materialized if you actually attempt to retrieve them. The helper (as @jamesarosen pointed out) works because it retrieves the service and uses it during its compute function (which happens as part of normal rendering in a helper).

I could have missed it (I'm on mobile and couldn't see if there was a full demo/reproduction), but in the snippets above I don't see where the component is calling 'this.get("i18n")'. If the service is never retrieved it is never observed (well, actually it is, but it's value is always undefined).

The manual injection via initializer works, because that forces the service to be instantiated when the object itself is instantiated (this is how the container/registry DI system works).

@jamesarosen
Copy link
Owner

In that case, you might be able to do the following:

i18n: Ember.inject.service(),

didInitAttrs() {
  this.get('i18n');
}

@imbrou
Copy link
Author

imbrou commented Sep 3, 2015

👍 Exact! Wow, thanks a lot for your help guys, tricky spotting for my intermediate ember level.
So... I understand this is an optimization best practice.
Maybe we could have a little reflection on this one :-)
not-bug-but-feature-meme

@imbrou
Copy link
Author

imbrou commented Sep 3, 2015

@rwjblue Got it: future readers... RTFM 👍
Thanks again for the help!

@kellyselden
Copy link
Contributor

@imbrou +1 for docs/wiki include. I could definitely see myself stumbling on this if not for this thread.

@digitalcora
Copy link
Contributor

The didInitAttrs approach worked for my component as well.

It does seem unintuitive/surprising that a get call would mutate some internal state that affects the operation of computed properties elsewhere – and that such computed properties don't fail or complain in any way when this crucial bit of state has not been set up. It almost guarantees you'll encounter a frustrating Heisenbug at some point. ("It only breaks when I'm not looking at it!")

@jamesarosen
Copy link
Owner

I'm going to close this and add a comment to the wiki.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants