angular-l10n
Version:
An Angular library to translate messages, dates and numbers
1,467 lines (1,454 loc) • 136 kB
JavaScript
/**
* @license Angular l10n
* Copyright Roberto Simonetti
* MIT license
* https://github.com/robisim74/angular-l10n
*/
import { Injector, Injectable, InjectionToken, Inject, Optional, ChangeDetectorRef, Pipe, Input, Directive, ElementRef, Renderer2, forwardRef, Component, HostBinding, NgModule } from '@angular/core';
import { ReplaySubject, Subject, BehaviorSubject, race, combineLatest, Observable, merge, concat } from 'rxjs';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { Location } from '@angular/common';
import { filter, timeout, map, takeUntil } from 'rxjs/operators';
import { __awaiter } from 'tslib';
import { HttpHeaders, HttpParams, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Title, Meta, DomSanitizer } from '@angular/platform-browser';
import { NG_VALIDATORS } from '@angular/forms';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* Allows to get the dependencies at the root level.
*/
class InjectorRef {
/**
* @param {?} injector
*/
constructor(injector) {
this.injector = injector;
/**
* TranslationService instances.
*/
this.translations = [];
InjectorRef.injector = this.injector;
}
/**
* @template T
* @param {?} token
* @param {?=} notFoundValue
* @return {?}
*/
static get(token, notFoundValue) {
return InjectorRef.injector.get(token, notFoundValue);
}
}
InjectorRef.injector = Injector.NULL;
InjectorRef.decorators = [
{ type: Injectable }
];
/** @nocollapse */
InjectorRef.ctorParameters = () => [
{ type: Injector }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/** @type {?} */
const L10N_CONFIG = new InjectionToken('L10N_CONFIG');
/**
* @param {?} l10nConfig
* @return {?}
*/
function l10nConfigFactory(l10nConfig) {
return {
locale: l10nConfig.locale || {},
translation: l10nConfig.translation || {},
logger: l10nConfig.logger || {},
localizedRouting: l10nConfig.localizedRouting || {},
search: l10nConfig.search || {},
localeInterceptor: l10nConfig.localeInterceptor || {}
};
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/** @enum {number} */
const StorageStrategy = {
Session: 0,
Local: 1,
Cookie: 2,
Disabled: 3,
};
StorageStrategy[StorageStrategy.Session] = 'Session';
StorageStrategy[StorageStrategy.Local] = 'Local';
StorageStrategy[StorageStrategy.Cookie] = 'Cookie';
StorageStrategy[StorageStrategy.Disabled] = 'Disabled';
/** @enum {number} */
const ProviderType = {
Fallback: 0,
Static: 1,
WebAPI: 2,
};
ProviderType[ProviderType.Fallback] = 'Fallback';
ProviderType[ProviderType.Static] = 'Static';
ProviderType[ProviderType.WebAPI] = 'WebAPI';
/** @enum {string} */
const ISOCode = {
Language: 'languageCode',
Country: 'countryCode',
Script: 'scriptCode',
};
/** @enum {string} */
const ExtraCode = {
NumberingSystem: 'numberingSystem',
Calendar: 'calendar',
Currency: 'currency',
Timezone: 'timezone',
};
/** @enum {string} */
const LogLevel = {
Error: 'error',
Warn: 'warn',
Info: 'info',
Log: 'log',
Off: 'off',
};
/** @enum {number} */
const NumberFormatStyle = {
Decimal: 0,
Percent: 1,
Currency: 2,
};
NumberFormatStyle[NumberFormatStyle.Decimal] = 'Decimal';
NumberFormatStyle[NumberFormatStyle.Percent] = 'Percent';
NumberFormatStyle[NumberFormatStyle.Currency] = 'Currency';
/** @type {?} */
const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
/** @type {?} */
const ISO8601_DATE_REGEX = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
/** @type {?} */
const FORMAT_ALIASES = {
'short': { year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
'medium': { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' },
'long': { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' },
'full': { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' },
'shortDate': { year: 'numeric', month: 'numeric', day: 'numeric' },
'mediumDate': { year: 'numeric', month: 'short', day: 'numeric' },
'longDate': { year: 'numeric', month: 'long', day: 'numeric' },
'fullDate': { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' },
'shortTime': { hour: 'numeric', minute: 'numeric' },
'mediumTime': { hour: 'numeric', minute: 'numeric', second: 'numeric' }
};
/** @type {?} */
const LOG_MESSAGES = {
'missingOnInit': "Missing 'ngOnInit' method: required by AoT compilation",
'missingOnDestroy': "Missing 'ngOnDestroy' method to cancel subscriptions: required by AoT compilation",
'missingLang': "Missing 'lang' parameter",
'missingDefaultLocale': "Missing 'defaultLocale' parameter",
'missingCurrency': "Missing 'currency' parameter",
'invalidNumberFormatAlias': "Invalid number format alias: the default format will be used",
'invalidDateFormatAlias': "Invalid date format alias: the default format will be used"
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class Logger {
/**
* @param {?} configuration
*/
constructor(configuration) {
this.configuration = configuration;
Logger.level = this.configuration.logger.level || LogLevel.Off;
if (Logger.level != LogLevel.Off) {
Logger.buffer.subscribe((log) => {
this.send(log);
});
}
}
/**
* @param {?} name
* @param {?} message
* @return {?}
*/
static log(name, message) {
if (Logger.level == LogLevel.Off)
return;
Logger.buffer.next({ name: name, message: message });
}
/**
* @param {?} log
* @return {?}
*/
send(log) {
/** @type {?} */
const message = `angular-l10n (${log.name}): ${LOG_MESSAGES[log.message]}`;
((/** @type {?} */ (console)))[(/** @type {?} */ (Logger.level))](message);
}
}
Logger.level = null;
Logger.buffer = new ReplaySubject();
Logger.decorators = [
{ type: Injectable }
];
/** @nocollapse */
Logger.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [L10N_CONFIG,] }] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class Caching {
constructor() {
this.cache = {};
}
/**
* @param {?} key
* @param {?} request
* @return {?}
*/
read(key, request) {
/** @type {?} */
const cached = this.cache[key];
if (cached)
return cached;
/** @type {?} */
const buffer = new ReplaySubject(1);
request.subscribe((value) => buffer.next(value), (error) => {
buffer.error(error);
this.cache[key] = undefined;
}, () => buffer.complete());
/** @type {?} */
const response = buffer.asObservable();
this.cache[key] = response;
return response;
}
}
Caching.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* Provides the methods to check if Intl APIs are supported.
*/
class IntlAPI {
/**
* @return {?}
*/
static hasIntl() {
/** @type {?} */
const hasIntl = typeof Intl === "object" && !!Intl;
return hasIntl;
}
/**
* @return {?}
*/
static hasDateTimeFormat() {
return IntlAPI.hasIntl() && Intl.hasOwnProperty("DateTimeFormat");
}
/**
* @return {?}
*/
static hasTimezone() {
if (IntlAPI.hasIntl() && IntlAPI.hasDateTimeFormat()) {
try {
new Intl.DateTimeFormat('en-US', { timeZone: 'America/Los_Angeles' }).format(new Date());
}
catch (e) {
return false;
}
return true;
}
return false;
}
/**
* @return {?}
*/
static hasNumberFormat() {
return IntlAPI.hasIntl() && Intl.hasOwnProperty("NumberFormat");
}
/**
* @return {?}
*/
static hasCollator() {
return IntlAPI.hasIntl() && Intl.hasOwnProperty("Collator");
}
/**
* @return {?}
*/
static hasRelativeTimeFormat() {
return IntlAPI.hasIntl() && Intl.hasOwnProperty("RelativeTimeFormat");
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @param {?} name
* @return {?}
*/
function getLocalStorage(name) {
return localStorage.getItem(name);
}
/**
* @param {?} name
* @return {?}
*/
function getSessionStorage(name) {
return sessionStorage.getItem(name);
}
/**
* @param {?} name
* @return {?}
*/
function getCookie(name) {
/** @type {?} */
let result = null;
if (typeof document !== "undefined") {
result = new RegExp("(?:^|; )" + encodeURIComponent(name) + "=([^;]*)").exec(document.cookie);
}
return result ? result[1] : null;
}
/**
* @param {?} name
* @param {?} value
* @return {?}
*/
function setLocalStorage(name, value) {
localStorage.setItem(name, value);
}
/**
* @param {?} name
* @param {?} value
* @return {?}
*/
function setSessionStorage(name, value) {
sessionStorage.setItem(name, value);
}
/**
* @param {?} name
* @param {?} value
* @param {?=} expiration
* @return {?}
*/
function setCookie(name, value, expiration) {
/** @type {?} */
let expires = "";
if (expiration != null) {
/** @type {?} */
const expirationDate = new Date();
expirationDate.setTime(expirationDate.getTime() +
(expiration * 24 * 60 * 60 * 1000));
expires = "; expires=" + expirationDate.toUTCString();
}
if (typeof document !== "undefined") {
document.cookie = name + "=" + value + expires + "; path=/";
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* Implement this class-interface to create a custom storage for default locale, currency & timezone.
* @abstract
*/
class LocaleStorage {
}
LocaleStorage.decorators = [
{ type: Injectable }
];
class L10nStorage {
/**
* @param {?} configuration
*/
constructor(configuration) {
this.configuration = configuration;
this.hasCookie = typeof navigator !== "undefined" && navigator.cookieEnabled;
this.hasStorage = typeof Storage !== "undefined";
}
/**
* @param {?} name
* @return {?}
*/
read(name) {
return __awaiter(this, void 0, void 0, function* () {
/** @type {?} */
let value = null;
if (this.configuration.locale.storage != StorageStrategy.Disabled) {
if (this.configuration.locale.storage == StorageStrategy.Local && this.hasStorage) {
value = getLocalStorage(this.getName(name));
}
else if (this.configuration.locale.storage == StorageStrategy.Session && this.hasStorage) {
value = getSessionStorage(this.getName(name));
}
else if (this.configuration.locale.storage == StorageStrategy.Cookie && this.hasCookie) {
value = getCookie(this.getName(name));
}
}
return value;
});
}
/**
* @param {?} name
* @param {?} value
* @return {?}
*/
write(name, value) {
return __awaiter(this, void 0, void 0, function* () {
if (this.configuration.locale.storage != StorageStrategy.Disabled) {
if (this.configuration.locale.storage == StorageStrategy.Local && this.hasStorage) {
setLocalStorage(this.getName(name), value);
}
else if (this.configuration.locale.storage == StorageStrategy.Session && this.hasStorage) {
setSessionStorage(this.getName(name), value);
}
else if (this.configuration.locale.storage == StorageStrategy.Cookie && this.hasCookie) {
setCookie(this.getName(name), value, this.configuration.locale.cookieExpiration);
}
}
});
}
/**
* @param {?} name
* @return {?}
*/
getName(name) {
if (this.configuration.locale.storageNames) {
return ((/** @type {?} */ (this.configuration.locale.storageNames)))[name] || name;
}
return name;
}
}
L10nStorage.decorators = [
{ type: Injectable }
];
/** @nocollapse */
L10nStorage.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [L10N_CONFIG,] }] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class DefaultLocale {
/**
* @return {?}
*/
get value() {
return this.formattedValue;
}
/**
* @param {?} defaultLocale
* @return {?}
*/
set value(defaultLocale) {
this.formattedValue = defaultLocale;
this.parseValue();
}
/**
* @param {?} languageCode
* @param {?=} countryCode
* @param {?=} scriptCode
* @param {?=} numberingSystem
* @param {?=} calendar
* @return {?}
*/
build(languageCode, countryCode, scriptCode, numberingSystem, calendar) {
this.languageCode = languageCode;
this.scriptCode = scriptCode;
this.countryCode = countryCode;
this.numberingSystem = numberingSystem;
this.calendar = calendar;
/** @type {?} */
let value = languageCode;
value += !!scriptCode ? "-" + scriptCode : "";
value += !!countryCode ? "-" + countryCode : "";
// Adds the 'u' (Unicode) extension.
value += (!!numberingSystem || !!calendar) ? "-u" : "";
value += !!numberingSystem ? "-nu-" + numberingSystem : "";
value += !!calendar ? "-ca-" + calendar : "";
this.formattedValue = value;
}
/**
* @return {?}
*/
parseValue() {
if (!!this.value) {
/** @type {?} */
let value = this.value;
// Looks for the 'u' (Unicode) extension.
/** @type {?} */
const index = value.search("-u");
if (index != -1) {
/** @type {?} */
const extensions = value.substring(index + 1).split("-");
switch (extensions.length) {
case 3:
if (extensions[1] == "nu") {
this.numberingSystem = extensions[2];
this.calendar = undefined;
}
else if (extensions[1] == "ca") {
this.numberingSystem = undefined;
this.calendar = extensions[2];
}
break;
default:
this.numberingSystem = extensions[2];
this.calendar = extensions[4];
}
// Extracts the codes.
value = value.substring(0, index);
}
/** @type {?} */
const codes = value.split("-");
switch (codes.length) {
case 1:
this.languageCode = codes[0];
this.scriptCode = undefined;
this.countryCode = undefined;
break;
case 2:
this.languageCode = codes[0];
this.scriptCode = undefined;
this.countryCode = codes[1];
break;
default:
this.languageCode = codes[0];
this.scriptCode = codes[1];
this.countryCode = codes[2];
}
}
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class IntlFormatter {
/**
* @param {?} value
* @param {?} defaultLocale
* @param {?} style
* @param {?=} digits
* @param {?=} currency
* @param {?=} currencyDisplay
* @return {?}
*/
static formatNumber(value, defaultLocale, style, digits, currency, currencyDisplay) {
if (!IntlAPI.hasNumberFormat())
return currency ? value + " " + currency : value;
value = typeof value === "string" && !isNaN(+value - parseFloat(value)) ? +value : value;
return IntlFormatter.numberFormatter(value, defaultLocale, style, digits, currency, currencyDisplay);
}
/**
* @param {?} value
* @param {?} defaultLocale
* @param {?} format
* @param {?=} timezone
* @return {?}
*/
static formatDate(value, defaultLocale, format, timezone) {
if (!IntlAPI.hasDateTimeFormat())
return value;
/** @type {?} */
let date;
if (typeof value === "string") {
value = value.trim();
}
if (IntlFormatter.isDate(value)) {
date = value;
}
else if (!isNaN(value - parseFloat(value))) {
date = new Date(parseFloat(value));
}
else if (typeof value === "string" && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
const [y, m, d] = value.split('-').map((val) => parseInt(val, 10));
date = new Date(y, m - 1, d);
}
else {
date = new Date(value);
}
if (!IntlFormatter.isDate(date)) {
/** @type {?} */
let match;
if ((typeof value === "string") && (match = value.match(ISO8601_DATE_REGEX))) {
date = IntlFormatter.isoStringToDate(match);
}
}
return IntlFormatter.dateTimeFormatter(date, defaultLocale, format, timezone);
}
/**
* @param {?} value
* @param {?} unit
* @param {?} defaultLocale
* @param {?=} format
* @return {?}
*/
static formatRelativeTime(value, unit, defaultLocale, format) {
if (!IntlAPI.hasRelativeTimeFormat())
return value + " " + unit;
value = typeof value === "string" && !isNaN(+value - parseFloat(value)) ? +value : value;
return IntlFormatter.relativeTimeFormatter(value, unit, defaultLocale, format);
}
/**
* @param {?} num
* @param {?} defaultLocale
* @param {?} style
* @param {?=} digits
* @param {?=} currency
* @param {?=} currencyDisplay
* @return {?}
*/
static numberFormatter(num, defaultLocale, style, digits, currency, currencyDisplay) {
/** @type {?} */
let options = {};
if (digits) {
if (typeof digits === "string") {
/** @type {?} */
const digitsOptions = formatDigitsAliases(digits);
if (digitsOptions != null) {
options = digitsOptions;
}
else {
Logger.log('IntlFormatter', 'invalidNumberFormatAlias');
}
}
else {
options = digits;
}
}
options.style = NumberFormatStyle[style].toLowerCase();
if (style == NumberFormatStyle.Currency) {
options.currency = currency;
options.currencyDisplay = currencyDisplay;
}
return new Intl.NumberFormat(defaultLocale, options).format(num);
}
/**
* @param {?} date
* @param {?} defaultLocale
* @param {?} format
* @param {?=} timezone
* @return {?}
*/
static dateTimeFormatter(date, defaultLocale, format, timezone) {
/** @type {?} */
let options = {};
if (format) {
if (typeof format === "string") {
/** @type {?} */
const dateTimeOptions = FORMAT_ALIASES[format];
if (dateTimeOptions) {
options = dateTimeOptions;
}
else {
Logger.log('IntlFormatter', 'invalidDateFormatAlias');
}
}
else {
options = format;
}
}
options.timeZone = IntlAPI.hasTimezone() ? timezone : 'UTC';
return new Intl.DateTimeFormat(defaultLocale, options).format(date).replace(/[\u200e\u200f]/g, "");
}
/**
* @param {?} value
* @param {?} unit
* @param {?} defaultLocale
* @param {?=} format
* @return {?}
*/
static relativeTimeFormatter(value, unit, defaultLocale, format) {
/** @type {?} */
let options = {};
if (format) {
options = format;
}
return new Intl.RelativeTimeFormat(defaultLocale, options).format(value, unit);
}
/**
* @param {?} value
* @return {?}
*/
static isDate(value) {
return value instanceof Date && !isNaN(value.valueOf());
}
/**
* @param {?} match
* @return {?}
*/
static isoStringToDate(match) {
/** @type {?} */
const date = new Date(0);
/** @type {?} */
let tzHour = 0;
/** @type {?} */
let tzMin = 0;
/** @type {?} */
const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear;
/** @type {?} */
const timeSetter = match[8] ? date.setUTCHours : date.setHours;
if (match[9]) {
tzHour = +(match[9] + match[10]);
tzMin = +(match[9] + match[11]);
}
dateSetter.call(date, +(match[1]), +(match[2]) - 1, +(match[3]));
/** @type {?} */
const h = +(match[4] || '0') - tzHour;
/** @type {?} */
const m = +(match[5] || '0') - tzMin;
/** @type {?} */
const s = +(match[6] || '0');
/** @type {?} */
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
timeSetter.call(date, h, m, s, ms);
return date;
}
}
/**
* @param {?} digits
* @return {?}
*/
function formatDigitsAliases(digits) {
/** @type {?} */
const parts = digits.match(NUMBER_FORMAT_REGEXP);
if (parts == null)
return null;
/** @type {?} */
const digitsOptions = {};
if (parts[1] != null) {
digitsOptions.minimumIntegerDigits = parseInt(parts[1]);
}
if (parts[3] != null) {
digitsOptions.minimumFractionDigits = parseInt(parts[3]);
}
if (parts[5] != null) {
digitsOptions.maximumFractionDigits = parseInt(parts[5]);
}
return digitsOptions;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* Manages language, default locale, currency & timezone.
*/
class LocaleService {
/**
* @param {?} configuration
* @param {?} storage
*/
constructor(configuration, storage) {
this.configuration = configuration;
this.storage = storage;
/**
* Fired when the language changes. Returns the language code.
*/
this.languageCodeChanged = new Subject();
/**
* Fired when the default locale changes. Returns the default locale.
*/
this.defaultLocaleChanged = new Subject();
/**
* Fired when the currency changes. Returns the currency code.
*/
this.currencyCodeChanged = new Subject();
/**
* Fired when the timezone changes. Returns the timezone.
*/
this.timezoneChanged = new Subject();
this.defaultLocale = new DefaultLocale();
}
/**
* @return {?}
*/
getConfiguration() {
return this.configuration.locale;
}
/**
* @return {?}
*/
init() {
return __awaiter(this, void 0, void 0, function* () {
yield this.initLanguage();
yield this.initDefaultLocale();
yield this.initCurrency();
yield this.initTimezone();
});
}
/**
* @return {?}
*/
getBrowserLanguage() {
/** @type {?} */
let browserLanguage = null;
if (typeof navigator !== "undefined" && navigator.language) {
browserLanguage = navigator.language;
}
if (browserLanguage != null) {
browserLanguage = browserLanguage.split("-")[0];
}
return browserLanguage;
}
/**
* @return {?}
*/
getAvailableLanguages() {
/** @type {?} */
let languages = [];
if (this.configuration.locale.languages) {
languages = this.configuration.locale.languages.map((language) => language.code);
}
return languages;
}
/**
* @param {?=} languageCode
* @return {?}
*/
getLanguageDirection(languageCode = this.defaultLocale.languageCode) {
/** @type {?} */
const matchedLanguage = this.matchLanguage(languageCode);
return matchedLanguage ? matchedLanguage.dir : "";
}
/**
* @return {?}
*/
getCurrentLanguage() {
return this.defaultLocale.languageCode;
}
/**
* @return {?}
*/
getCurrentCountry() {
return this.defaultLocale.countryCode || "";
}
/**
* @return {?}
*/
getCurrentScript() {
return this.defaultLocale.scriptCode || "";
}
/**
* Returns the well formatted locale as {languageCode}[-scriptCode][-countryCode]
* @return {?}
*/
getCurrentLocale() {
/** @type {?} */
let locale = this.defaultLocale.languageCode;
locale += !!this.defaultLocale.scriptCode ? "-" + this.defaultLocale.scriptCode : "";
locale += !!this.defaultLocale.countryCode ? "-" + this.defaultLocale.countryCode : "";
return locale;
}
/**
* @return {?}
*/
getCurrentNumberingSystem() {
return this.defaultLocale.numberingSystem || "";
}
/**
* @return {?}
*/
getCurrentCalendar() {
return this.defaultLocale.calendar || "";
}
/**
* @return {?}
*/
getDefaultLocale() {
return this.defaultLocale.value;
}
/**
* @return {?}
*/
getCurrentCurrency() {
return this.currencyCode;
}
/**
* @param {?} currencyDisplay
* @param {?=} defaultLocale
* @param {?=} currency
* @return {?}
*/
getCurrencySymbol(currencyDisplay, defaultLocale, currency) {
/** @type {?} */
let currencySymbol = this.currencyCode;
if (IntlAPI.hasNumberFormat()) {
/** @type {?} */
const localeZero = this.formatDecimal(0, '1.0-0', defaultLocale);
/** @type {?} */
const localeValue = this.formatCurrency(0, '1.0-0', currencyDisplay, defaultLocale, currency);
currencySymbol = localeValue.replace(localeZero, "");
currencySymbol = currencySymbol.trim();
}
return currencySymbol;
}
/**
* @return {?}
*/
getCurrentTimezone() {
return this.timezone;
}
/**
* @param {?} languageCode
* @return {?}
*/
setCurrentLanguage(languageCode) {
if (this.defaultLocale.languageCode != languageCode) {
this.rollbackLanguageCode = this.defaultLocale.languageCode;
this.defaultLocale.build(languageCode);
this.releaseLanguage();
}
}
/**
* @param {?} languageCode
* @param {?=} countryCode
* @param {?=} scriptCode
* @param {?=} numberingSystem
* @param {?=} calendar
* @return {?}
*/
setDefaultLocale(languageCode, countryCode, scriptCode, numberingSystem, calendar) {
if (this.defaultLocale.languageCode != languageCode ||
this.defaultLocale.countryCode != countryCode ||
this.defaultLocale.scriptCode != scriptCode ||
this.defaultLocale.numberingSystem != numberingSystem ||
this.defaultLocale.calendar != calendar) {
this.rollbackDefaultLocale = this.defaultLocale.value;
this.defaultLocale.build(languageCode, countryCode, scriptCode, numberingSystem, calendar);
this.releaseDefaultLocale();
}
}
/**
* @param {?} currencyCode
* @return {?}
*/
setCurrentCurrency(currencyCode) {
if (this.currencyCode != currencyCode) {
this.rollbackCurrencyCode = this.currencyCode;
this.currencyCode = currencyCode;
this.releaseCurrency();
}
}
/**
* @param {?} zoneName
* @return {?}
*/
setCurrentTimezone(zoneName) {
if (this.timezone != zoneName) {
this.rollbackTimezone = this.timezone;
this.timezone = zoneName;
this.releaseTimezone();
}
}
/**
* Formats a date according to default locale.
* @param {?} value A Date, a number (milliseconds since UTC epoch) or an ISO string
* @param {?=} format An alias or a DateTimeOptions object of the format. Default is 'mediumDate'
* @param {?=} defaultLocale The default locale to use. Default is the current locale
* @param {?=} timezone The time zone name. Default is the current timezone
* @return {?}
*/
formatDate(value, format, defaultLocale, timezone) {
return IntlFormatter.formatDate(value, defaultLocale || this.defaultLocale.value, format || 'mediumDate', timezone || this.timezone);
}
/**
* Formats a relative time according to default locale.
* @param {?} value Negative (or positive) number
* @param {?} unit Unit of the value. Possible values are: 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second'
* @param {?=} format RelativeTimeOptions object of the format
* @param {?=} defaultLocale The default locale to use. Default is the current locale
* @return {?}
*/
formatRelativeTime(value, unit, format, defaultLocale) {
return IntlFormatter.formatRelativeTime(value, unit, defaultLocale || this.defaultLocale.value, format);
}
/**
* Formats a decimal number according to default locale.
* @param {?} value The number to be formatted
* @param {?=} digits An alias or a DigitsOptions object of the format
* @param {?=} defaultLocale The default locale to use. Default is the current locale
* @return {?}
*/
formatDecimal(value, digits, defaultLocale) {
return IntlFormatter.formatNumber(value, defaultLocale || this.defaultLocale.value, NumberFormatStyle.Decimal, digits);
}
/**
* Formats a number as a percentage according to default locale.
* @param {?} value The number to be formatted
* @param {?=} digits An alias or a DigitsOptions object of the format
* @param {?=} defaultLocale The default locale to use. Default is the current locale
* @return {?}
*/
formatPercent(value, digits, defaultLocale) {
return IntlFormatter.formatNumber(value, defaultLocale || this.defaultLocale.value, NumberFormatStyle.Percent, digits);
}
/**
* Formats a number as a currency according to default locale.
* @param {?} value The number to be formatted
* @param {?=} digits An alias or a DigitsOptions object of the format
* @param {?=} currencyDisplay The format for the currency. Possible values are 'code', 'symbol', 'name'. Default is 'symbol'
* @param {?=} defaultLocale The default locale to use. Default is the current locale
* @param {?=} currency The currency to use. Default is the current currency
* @return {?}
*/
formatCurrency(value, digits, currencyDisplay, defaultLocale, currency) {
return IntlFormatter.formatNumber(value, defaultLocale || this.defaultLocale.value, NumberFormatStyle.Currency, digits, currency || this.currencyCode, currencyDisplay || 'symbol');
}
/**
* @param {?} codes
* @return {?}
*/
composeLocale(codes) {
/** @type {?} */
let locale = "";
if (this.defaultLocale.languageCode) {
for (const code of codes) {
switch (code) {
case ISOCode.Script:
locale += "-" + this.defaultLocale.scriptCode;
break;
case ISOCode.Country:
locale += "-" + this.defaultLocale.countryCode;
break;
default:
locale += this.defaultLocale.languageCode;
}
}
}
return locale;
}
/**
* Rollbacks to previous language, default locale, currency & timezone.
* @return {?}
*/
rollback() {
if (this.rollbackLanguageCode && this.rollbackLanguageCode != this.defaultLocale.languageCode) {
this.defaultLocale.value = this.rollbackLanguageCode;
this.releaseLanguage();
}
if (this.rollbackDefaultLocale && this.rollbackDefaultLocale != this.defaultLocale.value) {
this.defaultLocale.value = this.rollbackDefaultLocale;
this.releaseDefaultLocale();
}
if (this.rollbackCurrencyCode && this.rollbackCurrencyCode != this.currencyCode) {
this.currencyCode = this.rollbackCurrencyCode;
this.releaseCurrency();
}
if (this.rollbackTimezone && this.rollbackTimezone != this.timezone) {
this.timezone = this.rollbackTimezone;
this.releaseTimezone();
}
}
/**
* @return {?}
*/
initLanguage() {
return __awaiter(this, void 0, void 0, function* () {
if (this.configuration.locale.language) {
if (!this.defaultLocale.languageCode) {
// Tries to get the language from the storage.
/** @type {?} */
const defaultLocale = yield this.storage.read("defaultLocale");
if (!!defaultLocale) {
this.defaultLocale.value = defaultLocale;
}
else {
// Tries to get the language from the browser.
/** @type {?} */
const browserLanguage = this.getBrowserLanguage();
/** @type {?} */
const matchedLanguage = this.matchLanguage(browserLanguage);
if (!!browserLanguage && matchedLanguage) {
this.defaultLocale.build(browserLanguage);
}
else {
// Uses the language set in the configuration.
this.defaultLocale.build(this.configuration.locale.language);
}
this.storage.write("defaultLocale", this.defaultLocale.value);
}
this.rollbackLanguageCode = this.defaultLocale.languageCode;
this.sendLanguageEvents();
}
}
});
}
/**
* @return {?}
*/
initDefaultLocale() {
return __awaiter(this, void 0, void 0, function* () {
if (this.configuration.locale.defaultLocale) {
if (!this.defaultLocale.value) {
/** @type {?} */
const defaultLocale = yield this.storage.read("defaultLocale");
if (!!defaultLocale) {
this.defaultLocale.value = defaultLocale;
}
else {
this.defaultLocale.build(this.configuration.locale.defaultLocale.languageCode, this.configuration.locale.defaultLocale.countryCode, this.configuration.locale.defaultLocale.scriptCode, this.configuration.locale.defaultLocale.numberingSystem, this.configuration.locale.defaultLocale.calendar);
this.storage.write("defaultLocale", this.defaultLocale.value);
}
this.rollbackDefaultLocale = this.defaultLocale.value;
this.sendDefaultLocaleEvents();
}
}
});
}
/**
* @return {?}
*/
initCurrency() {
return __awaiter(this, void 0, void 0, function* () {
if (this.configuration.locale.currency) {
if (!this.currencyCode) {
/** @type {?} */
const currencyCode = yield this.storage.read("currency");
if (!!currencyCode) {
this.currencyCode = currencyCode;
}
else {
this.currencyCode = this.configuration.locale.currency;
this.storage.write("currency", this.currencyCode);
}
this.rollbackCurrencyCode = this.currencyCode;
this.sendCurrencyEvents();
}
}
});
}
/**
* @return {?}
*/
initTimezone() {
return __awaiter(this, void 0, void 0, function* () {
if (this.configuration.locale.timezone) {
if (!this.timezone) {
/** @type {?} */
const zoneName = yield this.storage.read("timezone");
if (!!zoneName) {
this.timezone = zoneName;
}
else {
this.timezone = this.configuration.locale.timezone;
this.storage.write("timezone", this.timezone);
}
this.rollbackTimezone = this.timezone;
this.sendTimezoneEvents();
}
}
});
}
/**
* @param {?} languageCode
* @return {?}
*/
matchLanguage(languageCode) {
/** @type {?} */
let matchedLanguage;
if (this.configuration.locale.languages && languageCode != null) {
matchedLanguage = this.configuration.locale.languages.find((language) => language.code == languageCode);
}
return matchedLanguage;
}
/**
* @return {?}
*/
releaseLanguage() {
this.storage.write("defaultLocale", this.defaultLocale.value);
this.sendLanguageEvents();
}
/**
* @return {?}
*/
releaseDefaultLocale() {
this.storage.write("defaultLocale", this.defaultLocale.value);
this.sendDefaultLocaleEvents();
}
/**
* @return {?}
*/
releaseCurrency() {
this.storage.write("currency", this.currencyCode);
this.sendCurrencyEvents();
}
/**
* @return {?}
*/
releaseTimezone() {
this.storage.write("timezone", this.timezone);
this.sendTimezoneEvents();
}
/**
* @return {?}
*/
sendLanguageEvents() {
this.languageCodeChanged.next(this.defaultLocale.languageCode);
}
/**
* @return {?}
*/
sendDefaultLocaleEvents() {
this.defaultLocaleChanged.next(this.defaultLocale.value);
}
/**
* @return {?}
*/
sendCurrencyEvents() {
this.currencyCodeChanged.next(this.currencyCode);
}
/**
* @return {?}
*/
sendTimezoneEvents() {
this.timezoneChanged.next(this.timezone);
}
}
LocaleService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
LocaleService.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [L10N_CONFIG,] }] },
{ type: LocaleStorage }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class LocalizedRouting {
/**
* @param {?} configuration
* @param {?} locale
*/
constructor(configuration, locale) {
this.configuration = configuration;
this.locale = locale;
}
/**
* @return {?}
*/
get router() {
return InjectorRef.get(Router);
}
/**
* @return {?}
*/
get location() {
return InjectorRef.get(Location);
}
/**
* @return {?}
*/
init() {
if (this.configuration.localizedRouting.format) {
// Parses the url to find the locale when the app starts.
/** @type {?} */
const path = this.location.path(true);
this.parsePath(path);
// Parses the url to find the locale when a navigation starts.
this.router.events.pipe(filter((event) => event instanceof NavigationStart)).subscribe((data) => {
this.redirectToPath(data.url);
});
// Replaces url when a navigation ends.
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((data) => {
/** @type {?} */
const url = (!!data.url && data.url != "/" && data.url == data.urlAfterRedirects) ?
data.url :
data.urlAfterRedirects;
this.replacePath(this.locale.composeLocale((/** @type {?} */ (this.configuration.localizedRouting.format))), url);
});
// Replaces url when locale changes.
this.locale.languageCodeChanged.subscribe(() => {
this.replacePath(this.locale.composeLocale((/** @type {?} */ (this.configuration.localizedRouting.format))));
});
this.locale.defaultLocaleChanged.subscribe(() => {
this.replacePath(this.locale.composeLocale((/** @type {?} */ (this.configuration.localizedRouting.format))));
});
}
}
/**
* Parses path to find the locale.
* @param {?=} path The path to be parsed
* @return {?}
*/
parsePath(path) {
if (!path)
return;
/** @type {?} */
const segment = this.getLocalizedSegment(path);
if (segment != null) {
/** @type {?} */
const locale = (/** @type {?} */ (segment)).replace(/\//gi, "");
/** @type {?} */
const localeCodes = this.splitLocale(locale, (/** @type {?} */ (this.configuration.localizedRouting.format)));
// Unrecognized segment.
if (this.configuration.localizedRouting.schema) {
if (!this.compareLocale(localeCodes, {
languageCode: localeCodes.languageCode,
scriptCode: this.getSchema(ISOCode.Script, localeCodes),
countryCode: this.getSchema(ISOCode.Country, localeCodes)
}))
return;
}
if (this.configuration.locale.language) {
this.locale.setCurrentLanguage(localeCodes.languageCode);
}
if (this.configuration.locale.defaultLocale) {
this.locale.setDefaultLocale(localeCodes.languageCode, localeCodes.countryCode || this.getSchema(ISOCode.Country, localeCodes), localeCodes.scriptCode || this.getSchema(ISOCode.Script, localeCodes), this.getSchema(ExtraCode.NumberingSystem, localeCodes), this.getSchema(ExtraCode.Calendar, localeCodes));
}
if (this.configuration.locale.currency) {
/** @type {?} */
const currency = this.getSchema(ExtraCode.Currency, localeCodes);
if (currency) {
this.locale.setCurrentCurrency(currency);
}
}
if (this.configuration.locale.timezone) {
/** @type {?} */
const timezone = this.getSchema(ExtraCode.Timezone, localeCodes);
if (timezone) {
this.locale.setCurrentTimezone(timezone);
}
}
}
}
/**
* Removes the locale from the path and navigates without pushing a new state into history.
* @param {?} path Localized path
* @return {?}
*/
redirectToPath(path) {
/** @type {?} */
const segment = this.getLocalizedSegment(path);
if (segment != null) {
/** @type {?} */
const url = path.replace(segment, "/");
// navigateByUrl keeps the query params.
this.router.navigateByUrl(url, { skipLocationChange: true });
}
}
/**
* Replaces the path with the locale without pushing a new state into history.
* @param {?} locale The current locale
* @param {?=} path The path to be replaced
* @return {?}
*/
replacePath(locale, path) {
if (path) {
if (!this.isDefaultRouting()) {
this.location.replaceState(this.getLocalizedPath(locale, path));
}
}
else {
path = this.location.path(true);
// Parses the path to find the locale.
/** @type {?} */
const segment = this.getLocalizedSegment(path);
if (segment != null) {
// Removes the locale from the path.
path = path.replace(segment, "/");
if (this.isDefaultRouting()) {
this.location.replaceState(path);
}
}
if (!this.isDefaultRouting()) {
this.location.replaceState(this.getLocalizedPath(locale, path));
}
}
}
/**
* @param {?} path
* @return {?}
*/
getLocalizedSegment(path) {
for (const lang of this.locale.getAvailableLanguages()) {
/** @type {?} */
const regex = new RegExp(`(\/${lang}\/)|(\/${lang}$)|(\/${lang}-.*?\/)|(\/${lang}-.*?$)`);
/** @type {?} */
const segments = path.match(regex);
if (segments != null) {
return segments[0];
}
}
return null;
}
/**
* @param {?} locale
* @param {?} codes
* @return {?}
*/
splitLocale(locale, codes) {
/** @type {?} */
const values = locale.split("-");
/** @type {?} */
const localeCodes = { languageCode: "" };
if (codes.length > 0) {
for (let i = 0; i < codes.length; i++) {
if (values[i])
localeCodes[codes[i]] = values[i];
}
}
return localeCodes;
}
/**
* @param {?} locale1
* @param {?} locale2
* @return {?}
*/
compareLocale(locale1, locale2) {
return locale1.languageCode == locale2.languageCode &&
(!!locale1.scriptCode ?
locale1.scriptCode == locale2.scriptCode : true) &&
(!!locale1.countryCode ?
locale1.countryCode == locale2.countryCode : true);
}
/**
* @param {?} code
* @param {?} locale
* @return {?}
*/
getSchema(code, locale) {
if (!this.configuration.localizedRouting.schema)
return undefined;
/** @type {?} */
const schema = this.configuration.localizedRouting.schema
.find(((s) => this.compareLocale(locale, s)));
return schema ? schema[code] : undefined;
}
/**
* @return {?}
*/
isDefaultRouting() {
if (!this.configuration.localizedRouting.defaultRouting)
return false;
if (this.configuration.locale.language) {
return this.locale.getCurrentLanguage() == this.configuration.locale.language;
}
if (this.configuration.locale.defaultLocale) {
return this.compareLocale({
languageCode: this.locale.getCurrentLanguage(),
scriptCode: this.locale.getCurrentScript(),
countryCode: this.locale.getCurrentCountry()
}, this.configuration.locale.defaultLocale);
}
return false;
}
/**
* @param {?} locale
* @param {?} path
* @return {?}
*/
getLocalizedPath(locale, path) {
return Location.stripT