Closed jeremytm closed 6 years ago
With some help on Stack Overflow, I've created a component which adds this functionality.
Usage: {{t-t 'key' club=(component 'comp-name') bid=1000}}
The problem is that components are objects that are in charge of their DOM lifecycle, but t
emits bare strings, not DOM hierarchies.
My t-t component solves this problem by intertwining t's string output with the DOM from components.
I'm sure it could be improved, but it works for me. What do you think?
I'd love to see some use-cases. We can add it to the wiki or you could release it as an ember addon and link to it from here.
Well there's my case for one. A sports management game. The game is made up of sports "clubs", owned by managers.
I have made a component which displays a club, including a club image and name. The component contains semi-complex DOM for displaying a placeholder if the club hasn't uploaded an image, or if they have uploaded an image, preloads the image and fades the image once loaded.
Here is a screenshot of this club component in use with the t-t component:
I'd be happy to make an addon at some point, once I find time to learn how.
for me it was quite similar.
I had text that included some icons. I wanted to have the icons displayed by a component that I would just change the icon component once I use svg spritesheets.
That did not work and I had to display the icons at the end of the text in the template (which is sad).
@jeremytm thanks for the pointer. I will look into it to solve my problem.
A related question came up in the #topic-i18n channel recently: "is it possible to include actions in a translation?" It's not, but you can do it manually in a component:
i18n: Ember.inject.service(),
text: computed('i18n.locale', function() {
const i18n = this.get('i18n');
const part1 = Ember.String.htmlSafe(`<a href='#' data-action='part1'>${i18n.t('part1')}</a>`);
const part2 = Ember.String.htmlSafe(`<a href='#' data-action='part2'>${i18n.t('part2')}</a>`);
return i18n.t('parts', { part1, part2 });
}),
click(e) {
if ($(e.target).is('[data-action=part1]')) {
...
}
if ($(e.target).is('[data-action=part2]')) {
...
}
}
While we're here, it's also impossible to interpolation an <a {{action}}>
.
One solution would be to allow the translations to be full HTMLBars templates, but there are some problems with that:
"Need help? <a {{action 'needHelp'}}>Click here</a>."
. If a translator makes a typo, it could totally break the app.I don't have any good ideas. I keep hoping the core team will step in and provide some direction.
I favour breaking the translations up when you want to do stuff like that. E.g. in the sports management example don't you only need 'by' translating. {{value}} {{t 'by'}} {{comp-name}}
Same for 'Need help?' and 'Click here'. Although arguably you shouldn't use 'click here' at all and instead just link the need help, but that's an aside.
The problem with "breaking translations up," also known as "composition instead of interpolation" is that it doesn't work for all languages. See, for example, this post on German and English sentence structure. Or this article on the relative position of adjectives and the nouns they modify in different languages.
Thus, {{value}} {{t 'by'}} {{comp-name}}
might need to be {{t 'by'}} {{value}} {{comp-name}}
in another language.
Yeah, that's a good point. Hadn't thought about how values may fit into a sentence at different points.
On a project I work on, I have extended ember-i18n to support "decorators" which allow us to keep html out of our translations. Here is an example:
// template
{{t "greeting" bold=(tag-decorator "b")}}
// translation
greeting: "Hello, [bold world]!"
// output
Hello, <b>world</b>!
I've done this by modifying app/utils/i18n/compile-template.js
to look like this:
import get from 'ember-metal/get';
import { htmlSafe } from 'ember-string';
import Ember from 'ember';
const { Handlebars: { Utils: { escapeExpression } } } = Ember;
const tripleStache = /\{\{\{\s*(.*?)\s*\}\}\}/g;
const doubleStache = /\{\{\s*(.*?)\s*\}\}/g;
const brackets = /\[(.*?) (.*?)\]/g;
const placeholder = '{}';
export default function compileTemplate(template, rtl = false) {
return function renderTemplate(data) {
let result = template
.replace(tripleStache, (i, match) => get(data, match))
.replace(doubleStache, (i, match) => escapeExpression(get(data, match)))
.replace(brackets, (i, decorator, content) => get(data, decorator).replace(placeholder, content));
let wrapped = rtl ? `\u202B${result}\u202C` : result;
return htmlSafe(wrapped);
};
}
and by adding decorator helpers like this:
import { helper } from 'ember-helper';
export function tagDecorator([tagName], options) {
let attributes = Object.keys(options)
.map((k) => ` ${k}=\"${options[k]}\"`)
.join('');
return `<${tagName}${attributes}>{}</${tagName}>`;
}
export default helper(tagDecorator);
When you write
{{t 'foo.baz' baz=(component 'some-thing')}}
the {{t}}
helper gets a baz
that's a CurlyComponentDefinition
. That's not part of Ember's public API and it's not clear how we would (a) get its HTML or (b) manage its life-cycle (e.g. call willInsertElement
, didInsertElement
).
jamesarosen/ember-i18n has been deprecated in favor of ember-intl.
Is this possible? I've tried passing in a component helper:
{{t 'key' club=(component 'comp-name') bid=1000}}
with content like:
{{club}} made a bid of {{bid}}
But no luck.
I would use helpers but they don't cut it for what I need to do.
Any suggestions?