Ember-intl: Using inline handlebars helpers within translation strings

Created on 3 Dec 2018  Â·  8Comments  Â·  Source: ember-intl/ember-intl

  • [x] I am on the latest ember-intl version
  • [x] I have searched the issues of this repo and found issues dating back to a couple of years #102 and #421.

Environment

  • Ember Version: 2.16.4
  • Ember CLI Version: 2.18.2
  • Ember Intl Version: 3.5.0
  • Browser(s): Chrome 70
  • Node Version: 8.11.1

Steps to Reproduce

I've run into a rather interesting problem while working with ember-intl regarding usage of {{helpers}} (and some components) within the translation strings - particularly {{action}}, {{link-to}} and {{input}}. I'm working on a localization project for the first time so I'm not really well versed with conventions regarding internationalization, but I believe having inline actions and links would be a very common scenario. For example, consider the following string:

You can <a href="#" {{action 'allow'}}>allow</a> incoming message notifications 
or <a href="#" {{action 'disable'}}>disable</a> them entirely. You can also {{#link-to 
'settings.notifications' user}}manage notifications{{/link-to}} for your own profile.

Another variant of the above string used extensively within the app I'm working on uses inline {{input}} within the text for some controls, and sometimes even custom components.

Reading the discussions in #102 and #421 seem to suggest that there were discussions for a possible solution to this a couple of years ago but seems like nothing concrete really materialized from them. Are there any current guidelines or best practices for tackling these scenarios since I wasn't able to figure out the best approach from the documentation?

enhancement requires research

Most helpful comment

In cases where there's only one type of segment that appears in a particular string, you can leave out the ifs.

{{#t-split "translation.key" as |segment|~}}
  <a href="#" {{action "didClickLink"}}>
    {{~#segment.content as |innerSegment|~}}
      <strong>{{innerSegment.content}}</strong>
    {{~/segment.content~}}
  </a>
{{~/t-split}}

All 8 comments

@UzEE not a solved problem. For link-to's I tend to use ember-href-to to solve that.

I'll keep this issue opened to start a dialog in discovering what it will take to support this.

@jasonmit Yes, link-to seems to be the easiest to work around with by using the ember-href-to addon. Unfortunately for my use case, the {{action}} helper usage far outweighs the {{link-to}} usage, so I'm still investigating possible solutions that work with that as well.

Hello! My team has been using a custom i18n solution, but we'd like to switch to using ember-intl. We have a solution for this called t-split. It can take a translation string like

Text before the link
{{#link}}
  First text in the link
  {{#strong}}text in the strong{{/strong}},
  final text in the link
{{/link}}
text after the link

and be used in the template like this:

{{#t-split "translation.key" as |segment|~}}
  {{~#if (eq segment.key "link")~}}
    <a href="#" {{action "didClickLink"}}>
      {{~#segment.content as |innerSegment|~}}
        {{~#if (eq innerSegment.key "strong")~}}
          <strong>
            {{~innerSegment.content~}}
          </strong>
        {{~/if~}}
      {{~/segment.content~}}
    </a>
  {{~/if~}}
{{~/t-split}}

It finds the special {{#separator}} and {{/separator}} tags in the translation string, converts into the appropriate nested structure, then yields them as nested contextual components. Any text not wrapped in special tags gets rendered automatically, so it only needs to handle the special text.

We could implement the same thing in ember-intl.

I really like your idea @mongoose700. I'm only wondering whether the syntax can be condensed somehow. 🤔

In cases where there's only one type of segment that appears in a particular string, you can leave out the ifs.

{{#t-split "translation.key" as |segment|~}}
  <a href="#" {{action "didClickLink"}}>
    {{~#segment.content as |innerSegment|~}}
      <strong>{{innerSegment.content}}</strong>
    {{~/segment.content~}}
  </a>
{{~/t-split}}

This looks great, @mongoose700 — I want to try it.

Hmm, I was able to do this using a wrapper component and public methods from the service:

// t-plus.js
export default class TPlusComponent extends Component {
  @service
  intl;

  get tokens() {
    return this.intl.formatMessage(this.intl.lookup(this.args.path), this.args.options);
  }
}
{{! t-plus.hbs }}
{{#each this.tokens as |token|~}}
  {{~token~}}
{{/each}}
pas-form:
  project-geography:
    header: Project Geography
    description: >
      '<strong>' Is the proposed Project Area located in a current or former '<a href="https://www1.nyc.gov/site/hpd/services-and-information/urban-renewal.page" target="_blank" rel="noopener noreferrer">' Urban Renewal Area '<sup>' 
      {faIcon}
      '</sup></a>'?'</strong>
<TPlus
  @path='pas-form.project-geography.description'
  @options={{hash faIcon=(component 'fa-icon' icon='external-link-alt' prefix='fas' fixedWidth=true)}}
/>

Is there something obviously wrong with this approach? Unsafe?

It looks like that could work, though that implementation doesn't have quite as much flexibility. You need anything you want to include in TPlus to be its own component, so it's harder to use for child components that themselves use contextual components that you want to customize, for example. I don't think it cleanly supports nesting.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

karellm picture karellm  Â·  7Comments

gossi picture gossi  Â·  5Comments

wopian picture wopian  Â·  4Comments

jacobq picture jacobq  Â·  6Comments

Kilowhisky picture Kilowhisky  Â·  4Comments