-
Notifications
You must be signed in to change notification settings - Fork 1k
Support of translation inside ng-pluralize directive #15
Comments
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: |
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. |
Well, yes, but that’s exactly what I’m doing. The translation of |
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? |
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 |
@knalli The point is that one translation step (one translatable message, e.g. |
@PascalPrecht I would say this construction is an overcomplicated attempt to be able to use The advantage of |
Yep,
where 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';
}
}
}
|
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. |
@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.) |
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 |
Have a look, will look later this eve here again. Now, out. |
@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
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 ( |
I'm still wondering, if getting a combination of ng-pluralize with angular-translate working, isn't and option. And something like
Couldn't this actually be achieved using the offset property? Or am I getting that whole thing wrong? |
@PascalPrecht No idea what you mean. What I mean is:
written in a translatable way. There are two parameters here (commentCount, userCount), the |
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. |
@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. |
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: {
"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: UPD: |
@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.
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. |
@mormegil-cz, oops, of course! Sorry :)
and implement that all in the module. This way it might be possible to collapse few combinations in the one rule. |
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. |
Looks interesting. |
@knalli: Well, that’s basically the approach I took, isn’t it? Only that I used AngularJS’s native interpolation syntax ( 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. |
fyi http://jamuhl.github.io/i18next/pages/doc_features.html#plurals not for multiple plurals, but single ones. |
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! |
@lpaini Pul requests are ALWAYS welcome! :) |
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:
|
+1 for using a standardized way (if sensible) |
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 |
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? |
Could you provide an example of what this looks like? It's hard to keep up when you don't know the interals well. |
I would like to leave a few thoughts on this approach: The advantagesMaking 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 disadvantagesUsing 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 SolutionBasically, 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 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 thenNow, when we decide to implement it that way, we have to clarify a few things:
<p translate="THREE" translate-interpolation="X"></p>
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. |
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. |
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 |
@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. |
Anyway.. so long I do not require How we archive the dynamic mapping of translate-interpolation="X" to
|
@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 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:
With that we can extend
|
Well, isn't using |
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 |
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. |
Landed in 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:
|
Interpolators now get informed when they have to change their locale, or actually angular-translate takes care of it: e59b141 |
Landed in |
Thanks for the swift work. I will test this soon. |
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. <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. |
Hey @DWand ! You got that thing partyl right! So the point is the following: angular-translate uses default interpolation ( If you do nothing, 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 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 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). |
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: |
How should the sequentially invokation of interpolation services work? |
@DWand |
I meant something like this: 69ca4e4 |
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 |
Bad syntax. Better: phrase: April 15, 2013 Tom bought 1 pear and 2 apples Template: Locale en-EN: Locale ru-RU: |
@tamtakoe also interesting! :) |
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. Cheers, Martin |
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!
The text was updated successfully, but these errors were encountered: