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

Ember i18n lazy lookup #154

Closed
sbounmy opened this issue Oct 17, 2014 · 26 comments
Closed

Ember i18n lazy lookup #154

sbounmy opened this issue Oct 17, 2014 · 26 comments

Comments

@sbounmy
Copy link

sbounmy commented Oct 17, 2014

Hello,

I found Rails lazy lookup great and looking to implementing it for ember i18n.
Basically when the translation key starts with a dot, it will prefix the key with the template path/name e.g:

// in templates/users/new.hbs
{{t ('.title') }} // will lookup users.new.title
Sign up!
// in templates/users/_form.hbs
{{t ('.password.label') }} // will lookup users._form.password.label
Enter your password

// Our i18n would look like :
Ember.I18n.translations = {
  users: {
    new: {
      title: 'Sign up!'
    },
    _form: {
     password: {
       label: 'Enter your password'
     }
   }
  }
}

Any guideline on how I could fetch the template name/path where I am calling {{ t 'my_key'}} translation helper ?
Note I'm using ember-cli and es6 transpiler

@jamesarosen
Copy link
Owner

I'm intrigued by this idea as well, but I don't know how to get the name of the current template from within the helper. Try setting a debugger breakpoint inside the {{t}} helper function and poking around at the arguments. If you can find a way to get the template name, we can make this happen!

@jamesarosen jamesarosen added this to the v3 milestone Oct 17, 2014
@sbounmy
Copy link
Author

sbounmy commented Oct 17, 2014

thanks @jamesarosen for your quick feedback.
I already gave a try putting debugger in findTemplate but couldnt find anything related to current template name.
Maybe someone who are more familiar about ember internals ?

@sbounmy
Copy link
Author

sbounmy commented Oct 17, 2014

only information I have is http://emberjs.com/guides/understanding-ember/debugging/#toc_get-current-route-name-path but couldnt reproduce it

@RSSchermer
Copy link

Heya,

I'm also very interested in this. I did some poking around and came across and interesting value on the options object passed into a handlebars helper: options.data.view.get('renderedName').

I made a cli app to do some testing: https://github.com/RSSchermer/ember-template-name-testing.

It seems to do roughly what we'd want I think. Some interesting things:

  • When including a partial, the name returned inside the partial is not the name of the partial, but of the including template. You could see this as a positive: your resource/-form partial could render 'Create' on the submit button when included into resource/new and 'Update' when included into resource/edit.
  • When used inside a component it returns nothing.

I'd be happy to make pull request that implements this, if this value indeed behaves as you want it to.

@jamesarosen
Copy link
Owner

I think the partials-use-the-including-template's-name is a feature.

I'd love to see a go at this technique -- with tests of course :)

@RSSchermer
Copy link

I'll have a go tomorrow. I had a look at the I18n source and I think it should be a simple change to get it working with the {{t}} helper.

A harder problem would be getting it to work with translatable attributes. It would be very nice if that would work out as well, also for consistency. I'll experiment with that, but maybe that's something for a separate pull request.

@RSSchermer
Copy link

See #155.

@jamesarosen jamesarosen removed this from the v3 milestone Oct 22, 2014
@ZenCocoon
Copy link

An other approach to this problem can be found at https://github.com/BookingSync/ember-cli-i18n-lazy-lookup

@jamesarosen
Copy link
Owner

Do you want to try this again on v4.0.0?

@sbounmy
Copy link
Author

sbounmy commented Jul 1, 2015

I would love to, I have a working prototype on my app however I've found myself monkey patching ember-i18n to make it work.
@jamesarosen which version of ember is ember-i18n running against ? I'm still stuck at 1.8.X because of ember-easyformatm

@jamesarosen
Copy link
Owner

The wiki suggests you should be on Ember-I18n v3.x.

@cibernox
Copy link
Collaborator

About this topic, I found it very interesting, specially in the context of translating error messages in forms.

That's why if first made this PR #230 to ember-i18n (released in v4.1.0) to enable me to build some mechanism to translate my errors in the context of the current route with an addon https://github.com/cibernox/ember-i18n-errors

This although my addon is focused in errors, this pattern of fallbacks based in the route hierarchy is useful in any context, and ember-i18n can eventually embrace it and absorb my addon.

In my mind the ideal syntax for "route-scoped" translations could be just implicitly do it for those keys starting with a dot, like rails-i18n does.

That way while {{t 'some.trans.lation'}} is an absolute path, {{t '.trans.lation'}} is a relative path to the current route.

Being the current route name parent.child.grandchild, the relative path would try to find the better translation following this fallback chain:

parent.child.grandchild.trans.lation
parent.child.trans.lation
parent.trans.lation
trans.lation # or maybe application.trans.lation because the top-level route is the Application route.

The main difference is that, while in rails the translation lookup is done using the name of the erb/haml file, in ember the translation will use the name of the route, both the name of a template is not accesible, and because I find the route hierarchy to be a better context for the translation than the name of template/partial.

@jamesarosen
Copy link
Owner

I like @cibernox's suggestion to use the route name. It feels much more Ember-ey to me. And it works well with the new Fallback Keys support. It even works pretty well for addons that want to let you override some text in different ways in different parts of your app:

i18n.t('.pagination.next', { default: [ 'my-addon.pagination.next' ] })

How does Rails resolve the ordering of the two different kinds of fallbacks in that call?

  1. route-default-route-default: route.pagination.next, route.my-addon.pagination.next, pagination.next, my-addon.pagination.next?
  2. route-route-default-deafult: route.pagination.next, pagination.next, route.my-addon.pagination.next, my-addon.pagination.next?
  3. route-route-default: route.pagination.next, pagination.next, my-addon.pagination.next?
  4. something else?

@cibernox
Copy link
Collaborator

For me the ideal behaviour should be (without having looked at what rails does):

If the translation key is relative (starts with a dot) then the routes take preference, and the provided fallbacks go after that.

Perhaps a more accurate term than is expandable keys. If a key (p.e ".free-hugs") starts with a dot it's considered "expandable" and "compiles" to

[
  'grandeparent-route.parent-route.current-route.free-hugs',
  'grandeparent-route.parent-route.free-hugs',
  'grandeparent-route.free-hugs',
  'free-hugs'
]

And any other fallback is applied after them.

However, there is a small quirk in this approach (that haven't bitten me, but i'm aware it's still there):

Lets revive my ascii-art capabilities to explain it.

// translations.js
{
  parent {
    favouritePhrase: "Kids today ....",
    child: {
      favouritePhrase: "Y'know bro?",
    }
  }  
}
// When in /parent
----------------------------------------------------------------------------------------------------
| {{!-- parent.hbs --}}                                                                            |
| {{t '.favouritePhrase'}} // Translates to "Kids today ...."                                      |
|                                                                                                  |
| {{outlet}}                                                                                       |
|                                                                                                  |
----------------------------------------------------------------------------------------------------

// When you transition to /parent/child and the outlet is rendered
----------------------------------------------------------------------------------------------------
| {{!-- parent.hbs --}}                                                                            |
| {{t '.favouritePhrase'}}    // Still translates to "Kids today ....". Route name is not observed |
|                                                                                                  |
| -----------------------------------------------------------------------------------------------  |
| | {{!-- child.hbs --}}                                                                         | |
| | {{t '.favouritePhrase'}} // Translates to "Y'know bro?"                                      | |
| |                                                                                              | |
| -----------------------------------------------------------------------------------------------  |
|                                                                                                  |
----------------------------------------------------------------------------------------------------

// Now you reload being in /parent/child
----------------------------------------------------------------------------------------------------
| {{!-- parent.hbs --}}                                                                            |
| {{t '.favouritePhrase'}}    // Still translates to "Y'know bro?". Route name is "parent.child"   |
|                                                                                                  |
| -----------------------------------------------------------------------------------------------  |
| | {{!-- child.hbs --}}                                                                         | |
| | {{t '.favouritePhrase'}} // Translates to "Y'know bro?"                                      | |
| |                                                                                              | |
| -----------------------------------------------------------------------------------------------  |
|                                                                                                  |

The problem is that when within a given route, the translation in the template of the parent one will still use the translation of the child one when available if the key is the same and the translation is different.
While a bit unlikely, this might bite someone.

I don't know if there is a way of freeze a translations to the route name of the {{outlet}} in which it's rendered, regardless of if a more nested route is active. This is the only caveat I see in this approach.

@cibernox
Copy link
Collaborator

@mixonic Can we ask for your insight of how this works in the ember view layer? Is it possible to a helper/component know the outlet in which it's going to be rendered instead of the route name?

@mixonic
Copy link

mixonic commented Jul 19, 2015

@cibernox the only way I believe a helper or component could know the route (or possibly sub-route) being entered/rendered would possibly be through the routing service. that service is not public or well-defined yet though, perhaps there is a use case here that informs it.

There has been some discussion about a using the outlet keyword to hop to the routable component hosting your component: emberjs/rfcs#78 This may be an applicable use-case for that feature, but in that case it would not be strongly coupled to the route but to some component state.

I strongly suggest trying to design something that works with DDAU today, and we can see what the pressure points it applies to the framework are. Interesting idea, I'll be thinking it over!

@sbounmy
Copy link
Author

sbounmy commented Aug 24, 2015

hmm AFAIK the current easiest implementation would be using controller:application#currentRouteName or router:main#currentRouteName to return the current route e.g parent.child. I believe it wont support @cibernox described approach but its a quick win if we could find a way to inject / fetch this currentRouteName in Ember I18n t helper.

@mixonic
Copy link

mixonic commented Aug 24, 2015

@sbounmy if you use the Ember.Helper.extend version of a helper definition, you can Ember.inject.controller('application')

@sbounmy
Copy link
Author

sbounmy commented Aug 24, 2015

@mixonic Yes, I've tried but "Defining an injected controller property on a non-controller is not allowed." error is raised :(

There is currently no way to inject something into a helper e.g:

application.inject('helper:t', 'application', 'controller:application') // not supported

@mixonic
Copy link

mixonic commented Aug 24, 2015

Ah yeah, Ember.inject.controller has a validation. I don't see why application.inject in an initializer would fail though. Can you provide a JSBin?

@cibernox
Copy link
Collaborator

Regardless of the technical details of how have access to the currentRouteName the fact that makes me resilent of implementhing this is the one I exposed a few messages up.

If a "relative" translation is in a parent outlet, the translation may differ depending on if you transition to the child route from the parent one or you reach that route directly. I know that the chances are small, but is a nasty edge case I'd rather not have. Feels inconsistent.

Being able to know the name of the outlet in which a route is rendered would remove any inconsistency.

@sbounmy
Copy link
Author

sbounmy commented Aug 24, 2015

@mixonic as ember + jsbin seems broken I've setup a quick twiddle http://ember-twiddle.com/b5cb246f24f45ddfb55d this is backed by this ticket emberjs/ember.js#11021

@cibernox you're definitely right, might want to find another workaround until emberjs/rfcs#78

@mixonic
Copy link

mixonic commented Aug 24, 2015

@sbounmy there were some issues with that Twiddle. Injection of a controller onto a helper works fine in http://ember-twiddle.com/0141c59e45a15eec8ff5

You used register instead of inject most notably.

@GCorbel
Copy link

GCorbel commented Sep 1, 2016

Is there any update on the subject. I have the same problem.

@sandstrom
Copy link
Contributor

There is now a router service we could use (since Ember 2.15).

https://github.com/emberjs/ember.js/pull/14805/files#diff-19d10596bb077873e825b9abe0f5fb71R22

@jamesarosen
Copy link
Owner

jamesarosen/ember-i18n has been deprecated in favor of ember-intl.

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

8 participants