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

Commit

Permalink
t helper supports context object
Browse files Browse the repository at this point in the history
Previously, the `{{t}}` helper, like the `i18n.t` utility, accepted a
translation key and a context hash. This worked when the Handlebars template
had the individual keys and values (or value bindings) for the context,
but didn't when there was a pre-built object that represented the context.

Now it accepts a second ordered (non-hash) argument that represents the
context as an object. Hash context properties override those from
the context object.

```hbs
{{t 'some.key' someObject prop=value}}
```

is approximately the same as

```js
i18n.t('some.key', Object.assign({}, someObject, { prop: value }))
```

This is a workaround for the fact that Handlebars does not yet have a syntax
for splatting an object into hash arguments.

See handlebars-lang/handlebars.js#1050
See handlebars-lang/handlebars.js#1128
See handlebars-lang/handlebars.js#1149
Closes #423
  • Loading branch information
k-fish authored and jamesarosen committed Dec 20, 2016
1 parent 7d932a0 commit 0854a3f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 10 deletions.
26 changes: 19 additions & 7 deletions addon/helper.js
@@ -1,15 +1,27 @@
import Ember from "ember";

export default Ember.Helper.extend({
i18n: Ember.inject.service(),
const { get, inject, Helper, Object: EmberObject, observer } = Ember;

compute: function(params, interpolations) {
const key = params[0];
const i18n = this.get('i18n');
return i18n.t(key, interpolations);
function mergedContext(objectContext, hashContext) {
return EmberObject.extend({
unknownProperty(key) {
const fromHash = get(hashContext, key);
return fromHash === undefined ? get(objectContext, key) : fromHash;
}
}).create();
}

export default Helper.extend({
i18n: inject.service(),

compute([key, contextObject = {}, ...rest], interpolations) {
const mergedInterpolations = mergedContext(contextObject, interpolations);

const i18n = get(this, 'i18n');
return i18n.t(key, mergedInterpolations);
},

_recomputeOnLocaleChange: Ember.observer('i18n.locale', function() {
_recomputeOnLocaleChange: observer('i18n.locale', function() {
this.recompute();
})
});
20 changes: 17 additions & 3 deletions addon/legacy/helper.js
@@ -1,20 +1,34 @@
import Stream from "./stream";
import { readHash } from "./stream";
import Ember from 'ember';

export default function tHelper(params, hash, options, env) {
const assign = Ember.assign || Ember.merge;

export default function tHelper([i18nKey, contextObject = { value: () => {} }], hash, options, env) {
const i18n = env.data.view.container.lookup('service:i18n');
const i18nKey = params[0];



var out = new Stream(function() {
const value = i18nKey.isStream ? i18nKey.value() : i18nKey;
return value === undefined ? '' : i18n.t(value, readHash(hash));

const contextObjectValue = contextObject.value();
const mergedHash = {};
assign(mergedHash, contextObjectValue);
assign(mergedHash, hash);

return value === undefined ? '' : i18n.t(value, readHash(mergedHash));
});

// Once the view is destroyed destroy the steam as well
env.data.view.one('willDestroyElement', out, function() {
this.destroy();
});

if (contextObject && contextObject.isStream) {
contextObject.subscribe(out.notify, out);
}

// observe any hash arguments that are streams:
Object.keys(hash).forEach(function(key) {
const value = hash[key];
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/helper-test.js
Expand Up @@ -61,6 +61,61 @@ test('interpolations', function(assert) {
assert.equal(this.$().text(), 'A bowl of clam chowder');
});

test('interpolations with passed context', function(assert) {
this.addTranslations('en', { 'bowl of soup': 'A bowl of {{soup}}' });
this.set('soup', 'bisque');

this.set('contextObject', Ember.computed('soup', function() {
return {
soup: this.get('soup')
};
}));

this.render(hbs`{{t 'bowl of soup' contextObject}}`);
assert.equal(this.$().text(), 'A bowl of bisque');

run(this, 'set', 'soup', 'clam chowder');
assert.equal(this.$().text(), 'A bowl of clam chowder');
});

test('interpolations with passed context and a hash', function(assert) {
this.addTranslations('en', { 'bowl of soup': 'A bowl of {{soup}} or {{salad}}' });
this.set('soup', 'bisque');
this.set('salad', 'mixed greens');

this.set('contextObject', Ember.computed('soup', function() {
return {
soup: this.get('soup')
};
}));

this.render(hbs`{{t 'bowl of soup' contextObject salad=salad}}`);
assert.equal(this.$().text(), 'A bowl of bisque or mixed greens');

run(this, 'set', 'soup', 'clam chowder');
run(this, 'set', 'salad', 'cobb salad');
assert.equal(this.$().text(), 'A bowl of clam chowder or cobb salad');
});

test('interpolations with passed context and a hash - hash overrides context', function(assert) {
this.addTranslations('en', { 'A delicious entree': 'A delicious {{entree}}' });
this.set('entreeFromContext', 'steak');
this.set('entreeFromHash', 'pasta');

this.set('contextObject', Ember.computed('entreeFromContext', function() {
return {
soup: this.get('entreeFromContext')
};
}));

this.render(hbs`{{t 'A delicious entree' contextObject entree=entreeFromHash}}`);
assert.equal(this.$().text(), 'A delicious pasta');

run(this, 'set', 'entreeFromContext', 'burger');
run(this, 'set', 'entreeFromHash', 'pizza');
assert.equal(this.$().text(), 'A delicious pizza');
});

test('locale change', function(assert) {
this.addTranslations('en', { soup: 'Soup' });
this.addTranslations('zh', { soup: '湯' });
Expand Down

0 comments on commit 0854a3f

Please sign in to comment.