UNPKG

angular-l10n

Version:

An Angular library to translate messages, dates and numbers

1,467 lines (1,454 loc) 136 kB
/** * @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