feat: add localization methods with fallbacks

pull/3996/head
Arman Ozak 5 years ago
parent b40df5413d
commit 91af69ffec

@ -2,11 +2,12 @@ import { Injectable, NgZone, Optional, SkipSelf } from '@angular/core';
import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { Actions, ofActionSuccessful, Store } from '@ngxs/store';
import { noop, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SetLanguage } from '../actions/session.actions';
import { ApplicationConfiguration } from '../models/application-configuration';
import { Config } from '../models/config';
import { ConfigState } from '../states/config.state';
import { registerLocale } from '../utils/initial-utils';
import { localize, localizeWithFallback } from '../utils/localization-utils';
type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean;
@ -77,30 +78,35 @@ export class LocalizationService {
return this.store.selectSnapshot(ConfigState.getLocalization(key, ...interpolateParams));
}
isLocalized(key, sourceName) {
if (sourceName === '_') {
// A convention to suppress the localization
return true;
localize(resourceName: string, key: string, defaultValue: string): Observable<string> {
return this.store
.select(ConfigState.getOne('localization'))
.pipe(map(localize(resourceName, key, defaultValue)));
}
const localization = this.store.selectSnapshot(
ConfigState.getOne('localization'),
) as ApplicationConfiguration.Localization;
sourceName = sourceName || localization.defaultResourceName;
if (!sourceName) {
return false;
localizeSync(resourceName: string, key: string, defaultValue: string): string {
return localize(
resourceName,
key,
defaultValue,
)(this.store.selectSnapshot(ConfigState.getOne('localization')));
}
const source = localization.values[sourceName];
if (!source) {
return false;
}
const value = source[key];
if (value === undefined) {
return false;
localizeWithFallback(
resourceNames: string[],
keys: string[],
defaultValue: string,
): Observable<string> {
return this.store
.select(ConfigState.getOne('localization'))
.pipe(map(localizeWithFallback(resourceNames, keys, defaultValue)));
}
return true;
localizeWithFallbackSync(resourceNames: string[], keys: string[], defaultValue: string): string {
return localizeWithFallback(
resourceNames,
keys,
defaultValue,
)(this.store.selectSnapshot(ConfigState.getOne('localization')));
}
}

@ -1,7 +1,7 @@
import { Router } from '@angular/router';
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest';
import { Store, Actions } from '@ngxs/store';
import { Observable, of, Subject } from 'rxjs';
import { Actions, Store } from '@ngxs/store';
import { of, Subject } from 'rxjs';
import { LocalizationService } from '../services/localization.service';
describe('LocalizationService', () => {
@ -75,4 +75,172 @@ describe('LocalizationService', () => {
}
});
});
describe('#localize', () => {
test.each`
resource | key | defaultValue | expected
${'foo'} | ${'bar'} | ${'DEFAULT'} | ${'baz'}
${'x'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${'a'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${'foo'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${'x'} | ${'y'} | ${'DEFAULT'} | ${'z'}
${'a'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${'foo'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${'x'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${'a'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${'foo'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${'x'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${'a'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
`(
'should return observable $expected when resource name is $resource and key is $key',
async ({ resource, key, defaultValue, expected }) => {
store.select.andReturn(
of({
values: { foo: { bar: 'baz' }, x: { y: 'z' } },
defaultResourceName: 'x',
}),
);
const result = await service.localize(resource, key, defaultValue).toPromise();
expect(result).toBe(expected);
},
);
});
describe('#localizeSync', () => {
test.each`
resource | key | defaultValue | expected
${'foo'} | ${'bar'} | ${'DEFAULT'} | ${'baz'}
${'x'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${'a'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'}
${'foo'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${'x'} | ${'y'} | ${'DEFAULT'} | ${'z'}
${'a'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'}
${'foo'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${'x'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${'a'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${''} | ${'DEFAULT'} | ${'DEFAULT'}
${'foo'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${'x'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${'a'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${''} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
${undefined} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'}
`(
'should return $expected when resource name is $resource and key is $key',
({ resource, key, defaultValue, expected }) => {
store.selectSnapshot.andReturn({
values: { foo: { bar: 'baz' }, x: { y: 'z' } },
defaultResourceName: 'x',
});
const result = service.localizeSync(resource, key, defaultValue);
expect(result).toBe(expected);
},
);
});
describe('#localizeWithFallback', () => {
test.each`
resources | keys | defaultValue | expected
${['foo']} | ${['bar']} | ${'DEFAULT'} | ${'baz'}
${['x']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${['a', 'b', 'c']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${['']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${[]} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${['foo']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['x']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['a', 'b', 'c']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${[]} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['foo']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'baz'}
${['x']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${['a', 'b', 'c']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${['']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${[]} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${['foo']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['x']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['a', 'b', 'c']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${[]} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['foo']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${['x']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${['a', 'b', 'c']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${['']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${[]} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
`(
'should return observable $expected when resource names are $resources and keys are $keys',
async ({ resources, keys, defaultValue, expected }) => {
store.select.andReturn(
of({
values: { foo: { bar: 'baz' }, x: { y: 'z' } },
defaultResourceName: 'x',
}),
);
const result = await service
.localizeWithFallback(resources, keys, defaultValue)
.toPromise();
expect(result).toBe(expected);
},
);
});
describe('#localizeWithFallbackSync', () => {
test.each`
resources | keys | defaultValue | expected
${['foo']} | ${['bar']} | ${'DEFAULT'} | ${'baz'}
${['x']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${['a', 'b', 'c']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${['']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${[]} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'}
${['foo']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['x']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['a', 'b', 'c']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['']} | ${['y']} | ${'DEFAULT'} | ${'z'}
${[]} | ${['y']} | ${'DEFAULT'} | ${'z'}
${['foo']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'baz'}
${['x']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${['a', 'b', 'c']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${['']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${[]} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'}
${['foo']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['x']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['a', 'b', 'c']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${[]} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'}
${['foo']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${['x']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${['a', 'b', 'c']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${['']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
${[]} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'}
`(
'should return $expected when resource names are $resources and keys are $keys',
({ resources, keys, defaultValue, expected }) => {
store.selectSnapshot.andReturn({
values: { foo: { bar: 'baz' }, x: { y: 'z' } },
defaultResourceName: 'x',
});
const result = service.localizeWithFallbackSync(resources, keys, defaultValue);
expect(result).toBe(expected);
},
);
});
});

@ -0,0 +1,34 @@
import { ApplicationConfiguration } from '../models/application-configuration';
export function localize(resourceName: string, key: string, defaultValue: string) {
return function(localization: ApplicationConfiguration.Localization) {
if (resourceName === '_') return key;
const resource = localization.values[resourceName];
if (!resource) return defaultValue;
return resource[key] || defaultValue;
};
}
export function localizeWithFallback(
resourceNames: string[],
keys: string[],
defaultValue: string,
) {
return function(localization: ApplicationConfiguration.Localization) {
resourceNames = resourceNames.concat(localization.defaultResourceName).filter(Boolean);
for (let i = 0; i < resourceNames.length; i++) {
const resourceName = resourceNames[i];
for (let j = 0; j < keys.length; j++) {
const localized = localize(resourceName, keys[j], null)(localization);
if (localized) return localized;
}
}
return defaultValue;
};
}
Loading…
Cancel
Save