Ember-intl uses the format.js library under the hood which uses the ICU Message Syntax. This syntax has many provisions for handling different types of interpolated data such as plural
and select
. But it lacks list formatting. Deep in the ICU Documentation it hints at the correct course of action:
Format the parameters separately (recommended)
You can format the parameter as you need before calling MessageFormat, and then passing the resulting string as a parameter to MessageFormat.
Luckily, JavaScript has a solution for this in the specs with Intl.ListFormat. All we need to do is integrate this with our use of ember-intl and the ICU message formatting syntax.
For this I’ll use this example translation
favorite-pasta-list: >-
{count, plural,
one {My favorite pasta type is {pastas}.}
other {My favorite pasta types are {pastas}.}
}
Side note:
It is tempting to cover only a minimal part of a message string with a complex argument (e.g., plural). However, this is difficult for translators for two reasons: 1. They might have trouble understanding how the sentence fragments in the argument sub-messages interact with the rest of the sentence, and 2. They will not know whether and how they can shrink or grow the extent of the part of the sentence that is inside the argument to make the whole message work for their language.
Recommendation: If possible, use complex arguments as the outermost structure of a message, and write full sentences in their sub-messages.
— ICU Messages § Complex Argument Types (emphasis theirs)
With this message syntax we will need to control two arguments: count
and pastas
which will be a number
and a string
respectively. For this example I will use the following template:
Last is to make the format-list
helper:
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import type IntlService from 'ember-intl/services/intl';
interface ListFormatOptions {
localeMatcher?: 'best fit' | 'lookup';
style?: 'long' | 'short' | 'narrow';
type?: 'conjunction' | 'disjunction' | 'unit';
}
export interface Args {
Positional: [string[]];
Named: ListFormatOptions;
}
export function formatList(
list: string[],
options: ListFormatOptions,
intl: IntlService,
) {
// Prevent TS error: https://github.com/microsoft/TypeScript/issues/46907
// @ts-ignore
let formatter = new Intl.ListFormat(intl.locales, options);
return formatter.format(list);
}
export default class FormatListHelper extends Helper {
@service declare intl: IntlService;
compute([list]: Args['Positional'], options: Args['Named']) {
return formatList(list, options, this.intl);
}
}
And this will produce (for en-us anyway)…
<p>My favorite pasta types are Fettuccine, Rotelle, and Festoni.</p>