Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): handle pluralize functions that expect a number #36901

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 6 additions & 17 deletions packages/core/src/i18n/localization.ts
Expand Up @@ -8,26 +8,15 @@

import {getLocalePluralCase} from './locale_data_api';

const pluralMapping = ['zero', 'one', 'two', 'few', 'many'];

/**
* Returns the plural case based on the locale
*/
export function getPluralCase(value: any, locale: string): string {
const plural = getLocalePluralCase(locale)(value);

switch (plural) {
case 0:
return 'zero';
case 1:
return 'one';
case 2:
return 'two';
case 3:
return 'few';
case 4:
return 'many';
default:
return 'other';
}
export function getPluralCase(value: string, locale: string): string {
petebacondarwin marked this conversation as resolved.
Show resolved Hide resolved
const plural = getLocalePluralCase(locale)(parseInt(value, 10));
const result = pluralMapping[plural];
return (result !== undefined) ? result : 'other';
}

/**
Expand Down
67 changes: 66 additions & 1 deletion packages/core/test/acceptance/i18n_spec.ts
Expand Up @@ -10,6 +10,7 @@
import '@angular/localize/init';

import {CommonModule, registerLocaleData} from '@angular/common';
import localeEs from '@angular/common/locales/es';
import localeRo from '@angular/common/locales/ro';
import {computeMsgId} from '@angular/compiler';
import {Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
Expand Down Expand Up @@ -770,7 +771,20 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.nativeElement.innerHTML).toEqual(`<div>autre - 4<!--ICU 5--></div>`);
});

it('should return the correct plural form for ICU expressions when using a specific locale', () => {
it('should return the correct plural form for ICU expressions when using "ro" locale', () => {
// The "ro" locale has a complex plural function that can handle muliple options
// (and string inputs)
//
// function plural(n: number): number {
// let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length;
// if (i === 1 && v === 0) return 1;
// if (!(v === 0) || n === 0 ||
// !(n === 1) && n % 100 === Math.floor(n % 100) && n % 100 >= 1 && n % 100 <= 19)
// return 3;
// return 5;
// }
//
// Compare this to the "es" locale in the next test
loadTranslations({
[computeMsgId(
'{VAR_PLURAL, plural, =0 {no email} =one {one email} =few {a few emails} =other {lots of emails}}')]:
Expand Down Expand Up @@ -814,6 +828,57 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 2-->');
});

it(`should return the correct plural form for ICU expressions when using "es" locale`, () => {
// The "es" locale has a simple plural function that can only handle a few options
// (and not string inputs)
//
// function plural(n: number): number {
// if (n === 1) return 1;
// return 5;
// }
//
// Compare this to the "ro" locale in the previous test
const icuMessage = '{VAR_PLURAL, plural, =0 {no email} =one ' +
'{one email} =few {a few emails} =other {lots of emails}}';
loadTranslations({[computeMsgId(icuMessage)]: icuMessage});
registerLocaleData(localeEs);
TestBed.configureTestingModule({providers: [{provide: LOCALE_ID, useValue: 'es'}]});
petebacondarwin marked this conversation as resolved.
Show resolved Hide resolved
// We could also use `TestBed.overrideProvider(LOCALE_ID, {useValue: 'es'});`
const fixture = initWithTemplate(AppComp, `
{count, plural,
=0 {no email}
=one {one email}
=few {a few emails}
=other {lots of emails}
}`);

expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 2-->');

// Change detection cycle, no model changes
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 2-->');

fixture.componentInstance.count = 3;
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 2-->');

fixture.componentInstance.count = 1;
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('one email<!--ICU 2-->');

fixture.componentInstance.count = 10;
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 2-->');

fixture.componentInstance.count = 20;
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('lots of emails<!--ICU 2-->');

fixture.componentInstance.count = 0;
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('no email<!--ICU 2-->');
});

it('projection', () => {
loadTranslations({
[computeMsgId('{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}')]:
Expand Down