Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Support of translation inside ng-pluralize directive #15

Closed
lpaini opened this issue Apr 25, 2013 · 66 comments
Closed

Support of translation inside ng-pluralize directive #15

lpaini opened this issue Apr 25, 2013 · 66 comments
Milestone

Comments

@lpaini
Copy link

lpaini commented Apr 25, 2013

Is it possible to integrate it to work with ng-pluralize directive?
I'm having this issue right now, and this project seems to be one of the best that works with the pluralization.

Well, thank you very much!

@0x-r4bbit
Copy link
Member

Hey @lpaini! I've already planned to build support for ng-pluralized, but we're currently working hard on #4 . So, if you have time, and want to get this in as soon as possible, feel free to contribute! :)

@0x-r4bbit 0x-r4bbit mentioned this issue Apr 25, 2013
29 tasks
@mormegil-cz
Copy link

Just an opinion: ng-pluralize is quite unflexible regarding localization. It allows you to “pluralize” a whole phrase based on a single number only. But what about cases like “X new comment(s) by Y user(s)”? In my attempt of an AngularJS localization tool, I went for another filter, which is flexible, but the resulting syntax is quite cumbersome:
{{ "commentcount" | i18nparsed:12:3 }} with commentcount: "$1 {{args[1] | plural:{one:'comment',other:'comments'} }} from $2 {{args[2] | plural:{one:'user',other:'users'} }}
I was inspired here by the localization of MediaWiki.

@knalli
Copy link
Member

knalli commented May 7, 2013

Should it be not more simple to use expression in the translation directly? In that case, you are more flexible with language/culture specific stuff (0, 1, 2, more than 1, etc.) Interpolated translation, actually.

@mormegil-cz
Copy link

Well, yes, but that’s exactly what I’m doing. The translation of commentcount includes an interpolation; only that I provided the plural filter so that I don’t have to repeat myself declaring the pluralization rules of a language, and refer only to the CLDR plural classes. (Sure, for English, having {{ count == 1 && 'comment' || 'comments' }} is not that difficult, but you definitely don’t want to repeat the rules e.g. for Croatian.)

@knalli
Copy link
Member

knalli commented May 8, 2013

Well, but if support for multiple pluralize variantes is required, there will be no other possibility. Either the translation step depends on exactly one variable (one, few, many, none, other) and can be managed.. or it depends on more than one (which means string concatenation automatically ) and it must be done by the translation setup itself. Any suggestions?

@0x-r4bbit
Copy link
Member

I wonder if there's a way in angular to detect a specific directive on a custom directive and execute its functionality. Then we could just do something like:

$translateProvider.translations({
    'NO_BEER': 'No beer.',
    'ONE_BEER': 'One beer.',
    'TWO_BEERS': 'Two beers.'
});
<ng-pluralize count="2" when="{ '0': NO_BEER', '1': ONE_BEER', '2': 'TWO_BEERS'}" translate></ng-pluralize>

Or we say, we try to detect an ng-pluralize within another element which gets actually translated:

<p translate>
<ng-pluralize count="2" when="{ '0': NO_BEER', '1': ONE_BEER', '2': 'TWO_BEERS'}"></ng-pluralize>
</p>

Therefor we have to make sure, that ng-pluralize is executed before translate directive. I tried something like that by manipulating translate directives priority but it didn't work.

@mormegil-cz
Copy link

@knalli The point is that one translation step (one translatable message, e.g. commentcount) might include multiple pluralizable runtime parameters affecting more parts of the one message (e.g. number of comments and number of commenting users). If you have your pluralization step as the root (like ng-pluralize wants to do), you are unable to do that. (Note that GNU gettext does the same, IIRC.) In that case, you are forced to pluralize the various pieces of the message (“X comments”, “Y users”) separately and build artificial connecting messages (“$1 by $2”) to sew it up. And even then you lose some flexibility, because in some languages, some sentences might use more complex dependencies.
If you allow interpolations (or any kind of evaluating expressions) inside your translations, you are completely flexible. But in AngularJS, this means (IIANM) the pluralization tool needs to be a filter instead of a directive like ng-pluralize, which is unusable in this context.

@mormegil-cz
Copy link

@PascalPrecht I would say this construction is an overcomplicated attempt to be able to use ng-pluralize just because it is there, even though it is not really that useful. See my above comments: How would you do “X comment(s) by Y user(s)” with this? A filter is the only way to go, I am afraid.

The advantage of ng-pluralize (well, AngularJS in general) is that it already knows the CLDR pluralization rules. Which is why I (mis)used it, and I call $locale.pluralCat instead of building my own rules, even though I know this is not a documented API (yet?).

@knalli
Copy link
Member

knalli commented May 8, 2013

Yep,

 (“$1 by $2”) 

where $1 and $2 are themselves variable is exactly what I mean, too.

So, instead of

{
  'beerMyDear' : 'beers drunk by {{users}} users.'
}

it have to be something like

{
  'beerMyDear' : {
    'one' : 'beers drunk by one user',
    'many' : 'beers drunk by {{users}} users',
    ...
  }
}

But to support multiples, that have to be dynamic. Proposal:

Problem:

{
  'beerMyDear' : '{beers} beers drunk by {users} users.'
}

Solution:

{
  'beerMyDear' : function (values) {
    // bullshit impl, just demo
    if (this.many(values.beers) && this.many(users)) {
      return '{{beers}} beers drunk by {{users}} users';
    } else if (this.one(values.beers) && this.none(users)) {
      return 'one beer drunk by no user';
    } else {
      return '{{beers}} beers drunk by {{users}} users';
    }
  }
}

this' scope is directed internally to an util which provides simple convienent functions none, one, ... (booleans).

@knalli
Copy link
Member

knalli commented May 8, 2013

Last one to implement "statically" would require a permutation of [none,one,few,many,other] and the params [users, beers] which would result -- drastically and pessimisticly -- into 25 combinations. I think a function is more comfortable.

@mormegil-cz
Copy link

@knalli Yes, this is quite a different approach which might work. The only downside I see here is that translators can be generally a completely different group of people than application developers. But you are forcing translators to learn full-fledged JavaScript (plus your API) instead of just a simpler syntax. (And you need to review the translation for severe bugs and security issues.)

@0x-r4bbit
Copy link
Member

Okay turned out this topic is pretty hard. Could we first of all find a solution which does.. at leat a bit of the job? I mean it doesn't have to be the master solution at all, it'd be awesome enough if we could provide a solution to use ngTranslate and pluralization in at least some cases?

What do you guys say, is it possible? @knalli @mormegil-cz

@knalli
Copy link
Member

knalli commented May 10, 2013

Have a look, will look later this eve here again. Now, out.

@knalli
Copy link
Member

knalli commented May 11, 2013

@mormegil-cz @lpaini please take a look.

I'm still not sure if this is the ideal solution. Could we please build a test table of the results? Something like

apples pears result
0 0 No apples and no pears.
0 1 No apples and one pear.
0 2 No apples and 2 pears.
0 5 No apples and 5 pears.
0 10 No apples and 10 pears.
0 100 No apples and a lot of pears.
1 0 ...
1 1 ...
1 2 ...
1 5 ...
1 10 ...
1 100 ...

etcpp.

I'm not familiar with multi lingual numbers written-out (one, two, three instead of 1, 2, 3), bit this could be achieved with an (optional) global map/switch.

The built-in solution by AngularJS (ng-pluralize) has another feature: the offset. Do we have to re-implement this one? That one requires the counter have to be modified (by the offset) to compute the 2nd part of the translation

@0x-r4bbit
Copy link
Member

I'm still wondering, if getting a combination of ng-pluralize with angular-translate working, isn't and option.

And something like

How would you do “X comment(s) by Y user(s)” with this?

Couldn't this actually be achieved using the offset property? Or am I getting that whole thing wrong?

@mormegil-cz
Copy link

@PascalPrecht No idea what you mean. What I mean is:

function ComplexMessage(commentCount, userCount) {
    return commentCount + " comment" + (commentCount == 1 ? "" : "s") + " by " + userCount + " user" + (userCount== 1 ? "" : "s");
}

written in a translatable way. There are two parameters here (commentCount, userCount), the offset property does not change the fact ng-pluralize works with just one parameter (count). The offset functionality is IMHO just a bells-and-whistles feature (just substracting a constant from the numeric parameter), which is quite unnecessary given much more important features are missing.

@0x-r4bbit
Copy link
Member

So how should happen with this one. Thinking about to put that one into a 1.1.x version or so, so 1.0.0 can be released soonish.

@DWand
Copy link
Member

DWand commented May 22, 2013

@PascalPrecht, can you implement a simple integration with ng-pluralize based on the 1 value in the 1.0 version please? So it will be full according to your checklist. And after that in 1.* versions pluralization might be improved/fixed/enhanced as you like.

@DWand
Copy link
Member

DWand commented May 22, 2013

I have read all your comments just now. And I'd want to say that I like @knalli's variant. But I have few proposals.

The first one:
Now keys like "one", "few" and so on make a collision with namespaces feature. So, is it possible to separate them?
@knalli proposed the anonymous function earlier. But this looks a bit terrible. Is it possible to merge this two proposals in one solution? I mean something like this:

{
  "normal-key": "translation",
  "ns-key": {
    "subkey": "subtranslation"
  },
  "pluralized-key": plural({
    "thing1=1&thing2=2": "translation variant 1",
    "thing1=few&thing2=0": "translation varint 2"
  })
}

where plural is a function like this:

function plural(pluralForms) {
  return pluralForms;
}

The second one:
I think that it's important to implement a fallback functionality for the key. So, we have to try to check "raw" keys firstly (1, 2, 3, 45, ...) and only after that transform them to one, few,...

UPD:
The third one:
We have to use a standard Angular's function to get one, few, ... forms. If it's not possible we have to give the user a mechanism to implement this functions by himself. This is because all languages have different rules.

@mormegil-cz
Copy link

@DWand The encoding-by-a-function solution would make the result not JSON, which would be a downside, I guess. You could do that in a bit more opaque way by wrapping it in an array instead of a function call, e.g.

{
    "normal-key": "translation",
    "ns-key": {
        "subkey": "subtranslation"
    },
    "pluralized-key": [{
        "thing1=1&thing2=2": "translation variant 1",
        "thing1=few&thing2=0": "translation varint 2"
    }]
}

but I agree it is not really beautiful. :-) But more to the point, I have reservations against this solution because of the multiplication of combinations, see my comment on the proposed patch.

@DWand
Copy link
Member

DWand commented May 24, 2013

@mormegil-cz, oops, of course! Sorry :)
As for multiplication, I don't see another solution. If you want to have a complete good translation which depends on many things at one time, you have to tell the program how to translate each combination. So, if you don't want to do that, you can try to simplify you texts or, maybe, to split translations on smaller and simpler parts. Maybe, recursive calls of the translate filter might help too.
I think, it's possible to decrease a number of the combinations by providing a smarter (richer?) syntax of the combination keys. Just like you proposed in your comment. But, I think, it's would hard to implement such functionality. To do that we have to provide at least:

  • a list of supporting tokens
  • rules of replacing tokens (1 = one = * = ...) to discover which tokens might be equal
  • a hierarchy of tokens (2 -> few -> *)

and implement that all in the module. This way it might be possible to collapse few combinations in the one rule.

@knalli
Copy link
Member

knalli commented May 25, 2013

Well, just some additional thoughts of myself.

We could simplify the thing if we make another approach.

The main idea of pluralize is that the translation itself decides the pluralization.

So, let's start with this one.

{
  "apples_and_pears": "He eats {apples} apples and {pears} pears."
}

In order to get this one pluralized, we can extend the inner interpolated values.

{
  "apples_and_pears": "He eats {apples: {'0': 'no apples', '1': 'one apple', 'other': '{0} apples'}} and {pears: {'0': 'no pears', '1': 'one pear', 'other': '{0} pears'}}."
}

Because the keys of the inner object are only strings, we can easily extend them with additional keys. The open point is only: How we get the keys and their interpretation? If every language and locale has its own (valid) keys, we cannot assume anything. In that case, the language provider (in this case: the language file itself!) has to provide this. I would suggest a global key in the language file with a function which can be implemented. That's one optional code. Once written, the translator shouldn't touch it any time.

@DWand
Copy link
Member

DWand commented May 25, 2013

Looks interesting.
Another approach to solve the problem of keys interpretation is to use "standard" language keys. So, we could get those functions from the ngLocale service. But, this is not a public API. And it's not very good also because of creating a new dependencies.

@mormegil-cz
Copy link

@knalli: Well, that’s basically the approach I took, isn’t it? Only that I used AngularJS’s native interpolation syntax ({{expression}}, provided by $interpolate).

And as I have already said: the set of possible keys is fixed (and every language uses a subset of those), and the function number→key is a per-locale constant. I.e. you could either use the already-implemented version from ngLocale (and ask for it to be publicized as a valid API), or copy/reimplement yourself in the localization framework. There is no reason every project/translator should reimplement that.

@knalli
Copy link
Member

knalli commented Jun 9, 2013

fyi http://jamuhl.github.io/i18next/pages/doc_features.html#plurals

not for multiple plurals, but single ones.

@lpaini
Copy link
Author

lpaini commented Jun 10, 2013

Hi guys, I believe the best way is the @knalli proposal, we're implementing a lot of modules right now, but we think that we can develop this issue in a few weeks if it's not solved by then!

@0x-r4bbit
Copy link
Member

@lpaini Pul requests are ALWAYS welcome! :)

@fhemberger
Copy link

There is already a standardized format for translation of such cases called ICU Message Format and a good JavaScript implementation for it.

It also handles cases such as multiple plural forms (e.g. in some eastern languages), male/female versions, multiple placeholders, etc. This is a example usecase:

{GENDER, select,
    male {He}
  female {She}
   other {They}
} found {NUM_RESULTS, plural,
            one {1 result}
          other {# results}
        } in {NUM_CATEGORIES, plural,
                  one {1 category}
                other {# categories}
             }.

@Narretz
Copy link

Narretz commented Jun 27, 2013

+1 for using a standardized way (if sensible)

@0x-r4bbit
Copy link
Member

Here is an interpolation adapter for messageformat https://github.com/PascalPrecht/angular-translate-interpolation-message-format/blob/master/src/translateMessageFormatInterpolation.js

Now test integration with angular-translate

@0x-r4bbit
Copy link
Member

Here we go, pluralization in angular-translate: 5596e8b

Of course, the code needs some finetuning. Interpolation services actually need a provide to configure initial locale n stuff like that.

What do you say?

@Narretz
Copy link

Narretz commented Jul 18, 2013

Could you provide an example of what this looks like? It's hard to keep up when you don't know the interals well.

@0x-r4bbit
Copy link
Member

I would like to leave a few thoughts on this approach:

The advantages

Making angular-translate's interpolation engine replaceable, gives us high flexibility when it comes to different interpolation implementations. With that solution we're now able to use messageformat to take care of our translation interpretation, which is great because we can now use translated contents containing pluralization.

Another good thing is, just like @saabi said, if someone comes up with a smaller or similar, or maybe a totally different, implementation of interpolation which can handle pluralization, one could easily plug it onto angular-translate and use its whole features.

This means also, people can use angular-translate and don't have to worry about pluralization, once this lands in master. Now, if we find a solution to implement a kind of pluralization inside angular-translate default interpolation (which is currently just angular's), we can extend it if we want. But til then, people are already able to have pluralization.

Unfortunately (like all things in the world), this approach also has some drawbacks.

The disadvantages

Using another interpolation implementation then angular-translate's default also means, we lose a few cool features. Things like angular expressions can not get interpolated as they have to e.g.

$interpolate('Hello, I have some interpolation directives in me! {{ 5 + 5 }}')();
// returns 'Hello, I have some interpolation directives in me! 10'

Whereas messageformat's interpolation implementation isn't able to evaluate javascript expressions at all:

mf = new MessageFormat('en');
mf.compile('Hello, I have some interpolation in me! { 5 + 5 }')();
// wouldn't work!

As you can see, not only that messageformat does not come with the features angular's interpolation provide, it also has a different syntax.

In addition to that, angular's filters would also not work within translations when using messageformat interpolation.

I really like the feature to plug messageformat onto angular-translate, but these drawbacks are bit hard, since we're building angular apps. Building angular apps without angular features feels weird. So I thought about that and came up with the following solution.

The Solution

Basically, the idea is to use messageformat interpolation like this:

angular.module('myApp', ['pascalprecht.translate']);

angular.module('myApp').config(function ($translateProvider) {
  // tell angular-translate to use messageformat interpolation instead of default interpolation
  $translateProvider.useMessageFormatInterpolation();

  // register translations using message format syntax
  $translateProvider.translations('en', {
    'HELLO': 'Hello there! MessageFormat {value}!'
  });

  // tell angular-translate to use 'en' as language
  $translateProvider.preferredLanguage('en');
});

And in HTML we can simply use angular-translate directives and filters, but with messageformat interpolation syntax:

<!-- filter usage -->
<h1>{{ 'HELLO' | translate:'{ value: "style" }' }}</h1>

<!-- directive usage -->
<h1 translate="HELLO" values="{ value: 'style' }"></h1>

So nothing changes on the highest level when it comes to usage.

Now again, when using messageformat interpolation we have to stick with it through the entire app when it comes to i18n. To get around this, I think it'd be cool to 'override' the used interpolation implementation for specific translations.

Let's say we have a translation table like this:

{
  "ONE": "This uses angular-translate's default interpolation",
  "TWO": "This uses messageformat interpolation"
}

Then in HTML we need a directive to tell angular-translate to use a different interpolation for that specific translation id:

<p>{{ 'ONE' | translate }}</p>

<p translate="TWO" translate-interpolation="messageformat"></p>

This would mean you're able to set an interpolation style application-wide by using $translateProvider.useWHATEVERInterpolation() and when having a translation id which uses a different interpolation style, like in the example above, one could override it for that specific case.

With that approach one is able to further use angular's great features when it comes to interpolation, but also able to use strange pluralization cases with messageformat or any other interpolation implementation.

Things that have to be clarified then

Now, when we decide to implement it that way, we have to clarify a few things:

  1. How does angular-translate know, that translate-interpolation="messageformat" means, it has to use MessageFormat and not the default interpolation (or anything else)? So we probably have to extend the structure of an interpolation service and have to expect a provider or something which has a method to tell angular-translate that that specific key messageformat maps to $translateMessageFormatInterpolation. Because if there's another interpolation implementation X the usage would probably look like
<p translate="THREE" translate-interpolation="X"></p>
  1. If we say, we could have different interpolation styles at runtime, it'd be useful to not only provide a way to use just one (default) or another (maybe messageformat). Maybe there's a case when someone needs the interpolation of messageformat and in addition to that, he/she has translation id which uses just another interpolation, which means there are actually three kind of interpolation styles.

So it could be a good thing to teach angular-translate not only to override the interpolation style for specific translation id's, but also to not be limited to two interpolation styles. A user would tell which interpolation style to use application-wide by default and in addition to that he could add different interpolation engines which could be used in specific cases. This could be done with some thing like this:

$translateProvider.useMessageFormatInterpolation();
// or
$translateProvider.useInterpolation('customInterpolationService');
// or if non of them, it'd use default interpolation

// add additional interpolation styles which could be used in the app
$translateProvider.addInterpolation('anotherInterpolationService');
$translateProvider.addInterpolation('fooInterpolationService');

In HTML translate directives and filters will use the interpolation which is configured as default, but could also use another one if needed.

Now I need YOUR thoughts on that. @knalli @lpaini @DWand

@0x-r4bbit
Copy link
Member

Hey @Narretz, just like described above you would use angular-translate as you always did, but you're able to use different interpolation syntax within translations.

@knalli
Copy link
Member

knalli commented Jul 18, 2013

As you can see, not only that messageformat does not come with the features angular's interpolation provide,

That's uncool. :( A different syntax.. well, that could be archived.. even with a little bit regular expression maybe? I am not familiar with that MessageFormat.. are you able to hook into its own interpolation?

@0x-r4bbit
Copy link
Member

@knalli unfortunately not, which is why I came up with our own interpolation-hooking-system to give users any way of interpolation they want to use in their apps.

@knalli
Copy link
Member

knalli commented Jul 18, 2013

Anyway.. so long I do not require useMessageFormatInterpolation(), everything works like before. Seems to be fair enough.. okay!

How we archive the dynamic mapping of

translate-interpolation="X"

to

XInterpolationService or so?

@0x-r4bbit
Copy link
Member

@knalli :) !

Yea that's a point we have to clarify. So first of all, I think we should say that an interpolation service has to provide a provider with specific methods, which angular-translate uses.

For example, messageformat actually need a locale when requesting an instance of it. So you have something like:

var mf = new MessageFormat(locale); // 'locale' could be 'en', 'ar' or whatever

// then you're able to compile translations with that locale in mind
var message = mf.compile('Some text which uses pluralization rules of arabians so we need a 'ar' mf instance here');
message();

So first I thought, yea we need a provider to set a locale so the interpolation service is able to instantiate it accordingly.

But this isn't a solution at all, since angular-translate is able to change locale during runtime, which means in the case of messageformat, the interpolation service has to be able to manage different instance of messageformat depending on the current used locale (which currently happens here: https://github.com/PascalPrecht/angular-translate-interpolation-messageformat/blob/master/src/translateMessageFormatInterpolation.js#L17).

As you can see, this is also not a cool thing, since the service has to check each time it is executed.

Which is why I think we have to find a way to let an interpolation service know, that a language has changed. There are two ways to solve that:

  1. We not only have an interpolation service which returns a function which does interpolation. We could say that an interpolation service has to return an Interpolator or so, which has an interface to set a locale. The Interpolator has also a method to interpolate things like Interpolator.interpolate(string, interpolateParams); which locale is actually used by Interpolator.interpolate() depends on which locale is currently set through Interpolator.

With that we can extend $translate.uses() that it uses Interpolator.setLocale() or so, to set a locale, then, later in $translate() when Interpolator.interpolate() is executed, the services knows, which locale to use. The Interpolator(Provider) could also have a method to return a "key" which is used by angular-translate to resolve a mapping. Which means, in time when $translate gets instantiated, it has to collect all interpolator identifiers first (if there are any).

  1. The second way to let an interpolation service know, if a language has changed, is to expose language key (or locale) on $translationChangeSuccess event or so.

@knalli
Copy link
Member

knalli commented Jul 18, 2013

Well, isn't using $translationChangeSuccess or even a dedicated interface method in the have-to-be-provided-service the smartest option?

@0x-r4bbit
Copy link
Member

Yea it's just a matter of how much do users/interpolationBuilders have to care. They either have to provide a method that angular-translate is able to set a locale, or they have to take care of that by themselfs by listening for $translateChangeSuccess event.

@0x-r4bbit
Copy link
Member

I would say it's the best to keep the interpolators as dumb as possible. Saying, they just have to provide a method to set a locale and angular-translate takes care of which will be set. Since it also knows about the fallback language.

@0x-r4bbit
Copy link
Member

Landed in extract-engine.

Service implementation: 5e20e24

Filter implementation: 46f03cc

Directive implementation: bf3dbbb

In the tests you can see how an interpolation service has to look like. These things have to follow:

  • set locale of interpolation service when language has been changed
  • provide proper error handling when adding uncorrect interpolation services
  • documentation

@0x-r4bbit
Copy link
Member

Interpolators now get informed when they have to change their locale, or actually angular-translate takes care of it: e59b141

@0x-r4bbit
Copy link
Member

Landed in canary now. Play with it, if you want to.

@Narretz
Copy link

Narretz commented Jul 19, 2013

Thanks for the swift work. I will test this soon.

@DWand
Copy link
Member

DWand commented Jul 21, 2013

Hi everyone. Sorry, I'm late. I have just read all things above. And I'm confused a bit :)

You say, that we can provide interpolation services. I'm not a native English speaker, so can you explain me a difference between "translation" and "interpolation" in this context? Is interpolation used only for pluralization?

Also, we have already discussed something similar here: #57 and #72.

But, it's not I'm worrying about. Maybe, it's silly, but is there any use cases for supporting two or more "interpolation" engines at the same time? Does it mean that any such engine cannot do it's work properly?

Of course, this is a good feature for JS, but not for HTML, I think. Let's go a little more deeper here.
While using an internationalization module, one of the main advantages is that you could write an HTML once and change content only in the translation file. By providing a plugin-like system we still have the same advantage for the translation core. This is cool :)
But if you have a syntax like this:

<p translate="THREE" translate-interpolation="X"></p>

and you need to replace your plugin - BOOM - app isn't working properly. Ok, you can replace it in HTML. But it could be terrible.
Another disadvantage is that you can change your translations in the future and this could affect on values of translate-interpolation attribute. So, it hardly depends on the current translation and it's not very good too.
I want to say that translate-interpolation is a very low-level option and I don't know is there any advantages to place it into HTML.
Maybe, it's better to set interpolation service only once (in JS code)?

@DWand
Copy link
Member

DWand commented Jul 21, 2013

And what is the difference between this two approaches? What is good or bad?
Architercture

@0x-r4bbit
Copy link
Member

Hey @DWand !

You got that thing partyl right! So the point is the following:

angular-translate uses default interpolation ($interpolate service) to interpolate translations against values. To bring pluralization to angular-translate, we want to use MessageFormat.js. MessageFormat uses a different syntax. So you have to decide to either use the one or the other syntax by configuring $translate service through $translateProvider.

If you do nothing, $translate services uses its default interpolation (which is actually angulars $interpolate service). If you say $translateProvider.useMessageFormatInterpolation() $translate service would use another interpolation service. By using messageformat interpolation, we lose angular's features in its interpolation syntax (filters, expression evaluation etc).

So, just deciding to use one or another interpolation is not good, because once you have a translation which requires pluralization and therefore messageformat syntax, you have to make a switch and all other translations have to be updated.

What we need is a way to say: "angular-translate, please use this interpolation service when translating contents application-wide, but in certain cases, use another one (e.g. messageformat)."

So what you do is $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); . This does nothing but telling angular-translate that there is another interpolation service, which could be used.

Now, you app would use the default interpolation by default and in the cases, where your translation needs messageformat syntax, you can override the used interpolation service with translate-interpolation.

Of course, this would mean you have to know, which translation id uses messageformat syntax. But you already know which translation id you have, so this isn't a real drawback.

Doing it like this way let users use angular-translate just like they always did, and on top of that, they can use messageformat syntax to achieve pluralization (done right).

@DWand
Copy link
Member

DWand commented Jul 21, 2013

Hmmm, but why not just separate AngularJS responsibility and Angular-translate responsibility? So, you have (or not) an interpolation service (let's call it IS) which handles translation and AngularJS interpolation service (let's call it AS) as a technical support. So, you can call IS and AS sequentially. Here you can get advantages of the IS while still having advantages of the AS.

UPD:
useMessageFormatInterpolation is not very good name in this case, because of flexibility. useInterpolation or so on is better, I think.

@0x-r4bbit
Copy link
Member

How should the sequentially invokation of interpolation services work?

@0x-r4bbit
Copy link
Member

@DWand useMessageFormatInterpolation() is just a shortcut for this specific interpolation type :) angular-translate would have built-in support. useInterpolation() is also implemented, for custom interpolation services :)

@DWand
Copy link
Member

DWand commented Jul 22, 2013

I meant something like this: 69ca4e4

@0x-r4bbit
Copy link
Member

Okay I will close this issue now as resolved, since we have a solution using messageformat and an interpolation service replacement. The feature already landed in canary, you guys rock!

@tamtakoe
Copy link
Contributor

tamtakoe commented Nov 9, 2013

Bad syntax. Better:

phrase: April 15, 2013 Tom bought 1 pear and 2 apples

Template:
{{ 'SmthBoughtPearsAndApples' | i18n:date:name:sex:pearNum:appleNum}}

Locale en-EN:
'SmthBoughtPearsAndApples': '{1,date:fullDate} {2} bought {4} {4|pear|pears} and {5} {5|apple|apples}'

Locale ru-RU:
'SmthBoughtPearsAndApples': '{1,date:fullDate} {2} {3|купил|купила} {4} {4|груш|грушу|груши|груши} и {5} {5|яблок|яблоко|яблока|яблок}'

@0x-r4bbit
Copy link
Member

@tamtakoe also interesting! :)

@mwunderlich
Copy link

Hi,

I've just come across this discussion from last year while trying to find a way to support (multiple) plural forms in angular-translate. From the thread above, I understand that the current solution is somehow using messageformat.js to provide this functionality.
Is there a code example somewhere that demonstrates how to support languages with several plural forms (such as Russian, Arabic or Gaelic)? Thanks a lot.

Cheers,

Martin

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants