@angular/common
Version:
Angular - commonly needed directives and services
1,591 lines (1,578 loc) • 208 kB
JavaScript
/**
* @license Angular v5.2.11
* (c) 2010-2018 Google, Inc. https://angular.io/
* License: MIT
*/
import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, Directive, ElementRef, EventEmitter, Host, Inject, Injectable, InjectionToken, Input, IterableDiffers, KeyValueDiffers, LOCALE_ID, NgModule, NgModuleRef, Optional, Pipe, Renderer2, TemplateRef, Version, ViewContainerRef, WrappedValue, isDevMode, ɵisListLikeIterable, ɵisObservable, ɵisPromise, ɵstringify } from '@angular/core';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* This class should not be used directly by an application developer. Instead, use
* {\@link Location}.
*
* `PlatformLocation` encapsulates all calls to DOM apis, which allows the Router to be platform
* agnostic.
* This means that we can have different implementation of `PlatformLocation` for the different
* platforms that angular supports. For example, `\@angular/platform-browser` provides an
* implementation specific to the browser environment, while `\@angular/platform-webworker` provides
* one suitable for use with web workers.
*
* The `PlatformLocation` class is used directly by all implementations of {\@link LocationStrategy}
* when they need to interact with the DOM apis like pushState, popState, etc...
*
* {\@link LocationStrategy} in turn is used by the {\@link Location} service which is used directly
* by the {\@link Router} in order to navigate between routes. Since all interactions between {\@link
* Router} /
* {\@link Location} / {\@link LocationStrategy} and DOM apis flow through the `PlatformLocation`
* class they are all platform independent.
*
* \@stable
* @abstract
*/
class PlatformLocation {
}
/**
* \@whatItDoes indicates when a location is initialized
* \@experimental
*/
const LOCATION_INITIALIZED = new InjectionToken('Location Initialized');
/**
* A serializable version of the event from onPopState or onHashChange
*
* \@experimental
* @record
*/
/**
* \@experimental
* @record
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* `LocationStrategy` is responsible for representing and reading route state
* from the browser's URL. Angular provides two strategies:
* {\@link HashLocationStrategy} and {\@link PathLocationStrategy}.
*
* This is used under the hood of the {\@link Location} service.
*
* Applications should use the {\@link Router} or {\@link Location} services to
* interact with application route state.
*
* For instance, {\@link HashLocationStrategy} produces URLs like
* `http://example.com#/foo`, and {\@link PathLocationStrategy} produces
* `http://example.com/foo` as an equivalent URL.
*
* See these two classes for more.
*
* \@stable
* @abstract
*/
class LocationStrategy {
}
/**
* The `APP_BASE_HREF` token represents the base href to be used with the
* {\@link PathLocationStrategy}.
*
* If you're using {\@link PathLocationStrategy}, you must provide a provider to a string
* representing the URL prefix that should be preserved when generating and recognizing
* URLs.
*
* ### Example
*
* ```typescript
* import {Component, NgModule} from '\@angular/core';
* import {APP_BASE_HREF} from '\@angular/common';
*
* \@NgModule({
* providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]
* })
* class AppModule {}
* ```
*
* \@stable
*/
const APP_BASE_HREF = new InjectionToken('appBaseHref');
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* \@experimental
* @record
*/
/**
* \@whatItDoes `Location` is a service that applications can use to interact with a browser's URL.
* \@description
* Depending on which {\@link LocationStrategy} is used, `Location` will either persist
* to the URL's path or the URL's hash segment.
*
* Note: it's better to use {\@link Router#navigate} service to trigger route changes. Use
* `Location` only if you need to interact with or create normalized URLs outside of
* routing.
*
* `Location` is responsible for normalizing the URL against the application's base href.
* A normalized URL is absolute from the URL host, includes the application's base href, and has no
* trailing slash:
* - `/my/app/user/123` is normalized
* - `my/app/user/123` **is not** normalized
* - `/my/app/user/123/` **is not** normalized
*
* ### Example
* {\@example common/location/ts/path_location_component.ts region='LocationComponent'}
* \@stable
*/
class Location {
/**
* @param {?} platformStrategy
*/
constructor(platformStrategy) {
/**
* \@internal
*/
this._subject = new EventEmitter();
this._platformStrategy = platformStrategy;
const /** @type {?} */ browserBaseHref = this._platformStrategy.getBaseHref();
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
this._platformStrategy.onPopState((ev) => {
this._subject.emit({
'url': this.path(true),
'pop': true,
'type': ev.type,
});
});
}
/**
* Returns the normalized URL path.
* @param {?=} includeHash
* @return {?}
*/
path(includeHash = false) {
return this.normalize(this._platformStrategy.path(includeHash));
}
/**
* Normalizes the given path and compares to the current normalized path.
* @param {?} path
* @param {?=} query
* @return {?}
*/
isCurrentPathEqualTo(path, query = '') {
return this.path() == this.normalize(path + Location.normalizeQueryParams(query));
}
/**
* Given a string representing a URL, returns the normalized URL path without leading or
* trailing slashes.
* @param {?} url
* @return {?}
*/
normalize(url) {
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
}
/**
* Given a string representing a URL, returns the platform-specific external URL path.
* If the given URL doesn't begin with a leading slash (`'/'`), this method adds one
* before normalizing. This method will also add a hash if `HashLocationStrategy` is
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
* @param {?} url
* @return {?}
*/
prepareExternalUrl(url) {
if (url && url[0] !== '/') {
url = '/' + url;
}
return this._platformStrategy.prepareExternalUrl(url);
}
/**
* Changes the browsers URL to the normalized version of the given URL, and pushes a
* new item onto the platform's history.
* @param {?} path
* @param {?=} query
* @return {?}
*/
go(path, query = '') {
this._platformStrategy.pushState(null, '', path, query);
}
/**
* Changes the browsers URL to the normalized version of the given URL, and replaces
* the top item on the platform's history stack.
* @param {?} path
* @param {?=} query
* @return {?}
*/
replaceState(path, query = '') {
this._platformStrategy.replaceState(null, '', path, query);
}
/**
* Navigates forward in the platform's history.
* @return {?}
*/
forward() { this._platformStrategy.forward(); }
/**
* Navigates back in the platform's history.
* @return {?}
*/
back() { this._platformStrategy.back(); }
/**
* Subscribe to the platform's `popState` events.
* @param {?} onNext
* @param {?=} onThrow
* @param {?=} onReturn
* @return {?}
*/
subscribe(onNext, onThrow, onReturn) {
return this._subject.subscribe({ next: onNext, error: onThrow, complete: onReturn });
}
/**
* Given a string of url parameters, prepend with '?' if needed, otherwise return parameters as
* is.
* @param {?} params
* @return {?}
*/
static normalizeQueryParams(params) {
return params && params[0] !== '?' ? '?' + params : params;
}
/**
* Given 2 parts of a url, join them with a slash if needed.
* @param {?} start
* @param {?} end
* @return {?}
*/
static joinWithSlash(start, end) {
if (start.length == 0) {
return end;
}
if (end.length == 0) {
return start;
}
let /** @type {?} */ slashes = 0;
if (start.endsWith('/')) {
slashes++;
}
if (end.startsWith('/')) {
slashes++;
}
if (slashes == 2) {
return start + end.substring(1);
}
if (slashes == 1) {
return start + end;
}
return start + '/' + end;
}
/**
* If url has a trailing slash, remove it, otherwise return url as is. This
* method looks for the first occurence of either #, ?, or the end of the
* line as `/` characters after any of these should not be replaced.
* @param {?} url
* @return {?}
*/
static stripTrailingSlash(url) {
const /** @type {?} */ match = url.match(/#|\?|$/);
const /** @type {?} */ pathEndIdx = match && match.index || url.length;
const /** @type {?} */ droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === '/' ? 1 : 0);
return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx);
}
}
Location.decorators = [
{ type: Injectable },
];
/** @nocollapse */
Location.ctorParameters = () => [
{ type: LocationStrategy, },
];
/**
* @param {?} baseHref
* @param {?} url
* @return {?}
*/
function _stripBaseHref(baseHref, url) {
return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
}
/**
* @param {?} url
* @return {?}
*/
function _stripIndexHtml(url) {
return url.replace(/\/index.html$/, '');
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* \@whatItDoes Use URL hash for storing application location data.
* \@description
* `HashLocationStrategy` is a {\@link LocationStrategy} used to configure the
* {\@link Location} service to represent its state in the
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
* of the browser's URL.
*
* For instance, if you call `location.go('/foo')`, the browser's URL will become
* `example.com#/foo`.
*
* ### Example
*
* {\@example common/location/ts/hash_location_component.ts region='LocationComponent'}
*
* \@stable
*/
class HashLocationStrategy extends LocationStrategy {
/**
* @param {?} _platformLocation
* @param {?=} _baseHref
*/
constructor(_platformLocation, _baseHref) {
super();
this._platformLocation = _platformLocation;
this._baseHref = '';
if (_baseHref != null) {
this._baseHref = _baseHref;
}
}
/**
* @param {?} fn
* @return {?}
*/
onPopState(fn) {
this._platformLocation.onPopState(fn);
this._platformLocation.onHashChange(fn);
}
/**
* @return {?}
*/
getBaseHref() { return this._baseHref; }
/**
* @param {?=} includeHash
* @return {?}
*/
path(includeHash = false) {
// the hash value is always prefixed with a `#`
// and if it is empty then it will stay empty
let /** @type {?} */ path = this._platformLocation.hash;
if (path == null)
path = '#';
return path.length > 0 ? path.substring(1) : path;
}
/**
* @param {?} internal
* @return {?}
*/
prepareExternalUrl(internal) {
const /** @type {?} */ url = Location.joinWithSlash(this._baseHref, internal);
return url.length > 0 ? ('#' + url) : url;
}
/**
* @param {?} state
* @param {?} title
* @param {?} path
* @param {?} queryParams
* @return {?}
*/
pushState(state, title, path, queryParams) {
let /** @type {?} */ url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
if (url.length == 0) {
url = this._platformLocation.pathname;
}
this._platformLocation.pushState(state, title, url);
}
/**
* @param {?} state
* @param {?} title
* @param {?} path
* @param {?} queryParams
* @return {?}
*/
replaceState(state, title, path, queryParams) {
let /** @type {?} */ url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
if (url.length == 0) {
url = this._platformLocation.pathname;
}
this._platformLocation.replaceState(state, title, url);
}
/**
* @return {?}
*/
forward() { this._platformLocation.forward(); }
/**
* @return {?}
*/
back() { this._platformLocation.back(); }
}
HashLocationStrategy.decorators = [
{ type: Injectable },
];
/** @nocollapse */
HashLocationStrategy.ctorParameters = () => [
{ type: PlatformLocation, },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APP_BASE_HREF,] },] },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* \@whatItDoes Use URL for storing application location data.
* \@description
* `PathLocationStrategy` is a {\@link LocationStrategy} used to configure the
* {\@link Location} service to represent its state in the
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
* browser's URL.
*
* If you're using `PathLocationStrategy`, you must provide a {\@link APP_BASE_HREF}
* or add a base element to the document. This URL prefix that will be preserved
* when generating and recognizing URLs.
*
* For instance, if you provide an `APP_BASE_HREF` of `'/my/app'` and call
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* Similarly, if you add `<base href='/my/app'/>` to the document and call
* `location.go('/foo')`, the browser's URL will become
* `example.com/my/app/foo`.
*
* ### Example
*
* {\@example common/location/ts/path_location_component.ts region='LocationComponent'}
*
* \@stable
*/
class PathLocationStrategy extends LocationStrategy {
/**
* @param {?} _platformLocation
* @param {?=} href
*/
constructor(_platformLocation, href) {
super();
this._platformLocation = _platformLocation;
if (href == null) {
href = this._platformLocation.getBaseHrefFromDOM();
}
if (href == null) {
throw new Error(`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
}
this._baseHref = href;
}
/**
* @param {?} fn
* @return {?}
*/
onPopState(fn) {
this._platformLocation.onPopState(fn);
this._platformLocation.onHashChange(fn);
}
/**
* @return {?}
*/
getBaseHref() { return this._baseHref; }
/**
* @param {?} internal
* @return {?}
*/
prepareExternalUrl(internal) {
return Location.joinWithSlash(this._baseHref, internal);
}
/**
* @param {?=} includeHash
* @return {?}
*/
path(includeHash = false) {
const /** @type {?} */ pathname = this._platformLocation.pathname +
Location.normalizeQueryParams(this._platformLocation.search);
const /** @type {?} */ hash = this._platformLocation.hash;
return hash && includeHash ? `${pathname}${hash}` : pathname;
}
/**
* @param {?} state
* @param {?} title
* @param {?} url
* @param {?} queryParams
* @return {?}
*/
pushState(state, title, url, queryParams) {
const /** @type {?} */ externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
this._platformLocation.pushState(state, title, externalUrl);
}
/**
* @param {?} state
* @param {?} title
* @param {?} url
* @param {?} queryParams
* @return {?}
*/
replaceState(state, title, url, queryParams) {
const /** @type {?} */ externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
this._platformLocation.replaceState(state, title, externalUrl);
}
/**
* @return {?}
*/
forward() { this._platformLocation.forward(); }
/**
* @return {?}
*/
back() { this._platformLocation.back(); }
}
PathLocationStrategy.decorators = [
{ type: Injectable },
];
/** @nocollapse */
PathLocationStrategy.ctorParameters = () => [
{ type: PlatformLocation, },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APP_BASE_HREF,] },] },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// THIS CODE IS GENERATED - DO NOT MODIFY
// See angular/tools/gulp-tasks/cldr/extract.js
/**
* \@internal
*/
const CURRENCIES = {
'AOA': [, 'Kz'],
'ARS': [, '$'],
'AUD': ['A$', '$'],
'BAM': [, 'KM'],
'BBD': [, '$'],
'BDT': [, '৳'],
'BMD': [, '$'],
'BND': [, '$'],
'BOB': [, 'Bs'],
'BRL': ['R$'],
'BSD': [, '$'],
'BWP': [, 'P'],
'BYN': [, 'р.'],
'BZD': [, '$'],
'CAD': ['CA$', '$'],
'CLP': [, '$'],
'CNY': ['CN¥', '¥'],
'COP': [, '$'],
'CRC': [, '₡'],
'CUC': [, '$'],
'CUP': [, '$'],
'CZK': [, 'Kč'],
'DKK': [, 'kr'],
'DOP': [, '$'],
'EGP': [, 'E£'],
'ESP': [, '₧'],
'EUR': ['€'],
'FJD': [, '$'],
'FKP': [, '£'],
'GBP': ['£'],
'GEL': [, '₾'],
'GIP': [, '£'],
'GNF': [, 'FG'],
'GTQ': [, 'Q'],
'GYD': [, '$'],
'HKD': ['HK$', '$'],
'HNL': [, 'L'],
'HRK': [, 'kn'],
'HUF': [, 'Ft'],
'IDR': [, 'Rp'],
'ILS': ['₪'],
'INR': ['₹'],
'ISK': [, 'kr'],
'JMD': [, '$'],
'JPY': ['¥'],
'KHR': [, '៛'],
'KMF': [, 'CF'],
'KPW': [, '₩'],
'KRW': ['₩'],
'KYD': [, '$'],
'KZT': [, '₸'],
'LAK': [, '₭'],
'LBP': [, 'L£'],
'LKR': [, 'Rs'],
'LRD': [, '$'],
'LTL': [, 'Lt'],
'LVL': [, 'Ls'],
'MGA': [, 'Ar'],
'MMK': [, 'K'],
'MNT': [, '₮'],
'MUR': [, 'Rs'],
'MXN': ['MX$', '$'],
'MYR': [, 'RM'],
'NAD': [, '$'],
'NGN': [, '₦'],
'NIO': [, 'C$'],
'NOK': [, 'kr'],
'NPR': [, 'Rs'],
'NZD': ['NZ$', '$'],
'PHP': [, '₱'],
'PKR': [, 'Rs'],
'PLN': [, 'zł'],
'PYG': [, '₲'],
'RON': [, 'lei'],
'RUB': [, '₽'],
'RUR': [, 'р.'],
'RWF': [, 'RF'],
'SBD': [, '$'],
'SEK': [, 'kr'],
'SGD': [, '$'],
'SHP': [, '£'],
'SRD': [, '$'],
'SSP': [, '£'],
'STD': [, 'Db'],
'SYP': [, '£'],
'THB': [, '฿'],
'TOP': [, 'T$'],
'TRY': [, '₺'],
'TTD': [, '$'],
'TWD': ['NT$', '$'],
'UAH': [, '₴'],
'USD': ['$'],
'UYU': [, '$'],
'VEF': [, 'Bs'],
'VND': ['₫'],
'XAF': ['FCFA'],
'XCD': ['EC$', '$'],
'XOF': ['CFA'],
'XPF': ['CFPF'],
'ZAR': [, 'R'],
'ZMW': [, 'ZK'],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// THIS CODE IS GENERATED - DO NOT MODIFY
// See angular/tools/gulp-tasks/cldr/extract.js
/**
* @param {?} n
* @return {?}
*/
function plural(n) {
let /** @type {?} */ i = Math.floor(Math.abs(n)), /** @type {?} */ v = n.toString().replace(/^[^.]*\.?/, '').length;
if (i === 1 && v === 0)
return 1;
return 5;
}
var localeEn = [
'en',
[
['a', 'p'],
['AM', 'PM'],
],
[
['AM', 'PM'],
,
],
[
['S', 'M', 'T', 'W', 'T', 'F', 'S'], ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
],
,
[
['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
[
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
'October', 'November', 'December'
]
],
,
[['B', 'A'], ['BC', 'AD'], ['Before Christ', 'Anno Domini']], 0, [6, 0],
['M/d/yy', 'MMM d, y', 'MMMM d, y', 'EEEE, MMMM d, y'],
['h:mm a', 'h:mm:ss a', 'h:mm:ss a z', 'h:mm:ss a zzzz'],
[
'{1}, {0}',
,
'{1} \'at\' {0}',
],
['.', ',', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'],
['#,##0.###', '#,##0%', '¤#,##0.00', '#E0'], '$', 'US Dollar', plural
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* \@experimental i18n support is experimental.
*/
const LOCALE_DATA = {};
/**
* Register global data to be used internally by Angular. See the
* {\@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import additional locale data.
*
* \@experimental i18n support is experimental.
* @param {?} data
* @param {?=} localeId
* @param {?=} extraData
* @return {?}
*/
function registerLocaleData(data, localeId, extraData) {
if (typeof localeId !== 'string') {
extraData = localeId;
localeId = data[0 /* LocaleId */];
}
localeId = localeId.toLowerCase().replace(/_/g, '-');
LOCALE_DATA[localeId] = data;
if (extraData) {
LOCALE_DATA[localeId][18 /* ExtraData */] = extraData;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/** @enum {number} */
const NumberFormatStyle = {
Decimal: 0,
Percent: 1,
Currency: 2,
Scientific: 3,
};
NumberFormatStyle[NumberFormatStyle.Decimal] = "Decimal";
NumberFormatStyle[NumberFormatStyle.Percent] = "Percent";
NumberFormatStyle[NumberFormatStyle.Currency] = "Currency";
NumberFormatStyle[NumberFormatStyle.Scientific] = "Scientific";
/** @enum {number} */
const Plural = {
Zero: 0,
One: 1,
Two: 2,
Few: 3,
Many: 4,
Other: 5,
};
Plural[Plural.Zero] = "Zero";
Plural[Plural.One] = "One";
Plural[Plural.Two] = "Two";
Plural[Plural.Few] = "Few";
Plural[Plural.Many] = "Many";
Plural[Plural.Other] = "Other";
/** @enum {number} */
const FormStyle = {
Format: 0,
Standalone: 1,
};
FormStyle[FormStyle.Format] = "Format";
FormStyle[FormStyle.Standalone] = "Standalone";
/** @enum {number} */
const TranslationWidth = {
Narrow: 0,
Abbreviated: 1,
Wide: 2,
Short: 3,
};
TranslationWidth[TranslationWidth.Narrow] = "Narrow";
TranslationWidth[TranslationWidth.Abbreviated] = "Abbreviated";
TranslationWidth[TranslationWidth.Wide] = "Wide";
TranslationWidth[TranslationWidth.Short] = "Short";
/** @enum {number} */
const FormatWidth = {
Short: 0,
Medium: 1,
Long: 2,
Full: 3,
};
FormatWidth[FormatWidth.Short] = "Short";
FormatWidth[FormatWidth.Medium] = "Medium";
FormatWidth[FormatWidth.Long] = "Long";
FormatWidth[FormatWidth.Full] = "Full";
/** @enum {number} */
const NumberSymbol = {
Decimal: 0,
Group: 1,
List: 2,
PercentSign: 3,
PlusSign: 4,
MinusSign: 5,
Exponential: 6,
SuperscriptingExponent: 7,
PerMille: 8,
Infinity: 9,
NaN: 10,
TimeSeparator: 11,
CurrencyDecimal: 12,
CurrencyGroup: 13,
};
NumberSymbol[NumberSymbol.Decimal] = "Decimal";
NumberSymbol[NumberSymbol.Group] = "Group";
NumberSymbol[NumberSymbol.List] = "List";
NumberSymbol[NumberSymbol.PercentSign] = "PercentSign";
NumberSymbol[NumberSymbol.PlusSign] = "PlusSign";
NumberSymbol[NumberSymbol.MinusSign] = "MinusSign";
NumberSymbol[NumberSymbol.Exponential] = "Exponential";
NumberSymbol[NumberSymbol.SuperscriptingExponent] = "SuperscriptingExponent";
NumberSymbol[NumberSymbol.PerMille] = "PerMille";
NumberSymbol[NumberSymbol.Infinity] = "Infinity";
NumberSymbol[NumberSymbol.NaN] = "NaN";
NumberSymbol[NumberSymbol.TimeSeparator] = "TimeSeparator";
NumberSymbol[NumberSymbol.CurrencyDecimal] = "CurrencyDecimal";
NumberSymbol[NumberSymbol.CurrencyGroup] = "CurrencyGroup";
/** @enum {number} */
const WeekDay = {
Sunday: 0,
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6,
};
WeekDay[WeekDay.Sunday] = "Sunday";
WeekDay[WeekDay.Monday] = "Monday";
WeekDay[WeekDay.Tuesday] = "Tuesday";
WeekDay[WeekDay.Wednesday] = "Wednesday";
WeekDay[WeekDay.Thursday] = "Thursday";
WeekDay[WeekDay.Friday] = "Friday";
WeekDay[WeekDay.Saturday] = "Saturday";
/**
* The locale id for the chosen locale (e.g `en-GB`).
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocaleId(locale) {
return findLocaleData(locale)[0 /* LocaleId */];
}
/**
* Periods of the day (e.g. `[AM, PM]` for en-US).
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} formStyle
* @param {?} width
* @return {?}
*/
function getLocaleDayPeriods(locale, formStyle, width) {
const /** @type {?} */ data = findLocaleData(locale);
const /** @type {?} */ amPmData = /** @type {?} */ ([data[1 /* DayPeriodsFormat */], data[2 /* DayPeriodsStandalone */]]);
const /** @type {?} */ amPm = getLastDefinedValue(amPmData, formStyle);
return getLastDefinedValue(amPm, width);
}
/**
* Days of the week for the Gregorian calendar (e.g. `[Sunday, Monday, ... Saturday]` for en-US).
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} formStyle
* @param {?} width
* @return {?}
*/
function getLocaleDayNames(locale, formStyle, width) {
const /** @type {?} */ data = findLocaleData(locale);
const /** @type {?} */ daysData = /** @type {?} */ ([data[3 /* DaysFormat */], data[4 /* DaysStandalone */]]);
const /** @type {?} */ days = getLastDefinedValue(daysData, formStyle);
return getLastDefinedValue(days, width);
}
/**
* Months of the year for the Gregorian calendar (e.g. `[January, February, ...]` for en-US).
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} formStyle
* @param {?} width
* @return {?}
*/
function getLocaleMonthNames(locale, formStyle, width) {
const /** @type {?} */ data = findLocaleData(locale);
const /** @type {?} */ monthsData = /** @type {?} */ ([data[5 /* MonthsFormat */], data[6 /* MonthsStandalone */]]);
const /** @type {?} */ months = getLastDefinedValue(monthsData, formStyle);
return getLastDefinedValue(months, width);
}
/**
* Eras for the Gregorian calendar (e.g. AD/BC).
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} width
* @return {?}
*/
function getLocaleEraNames(locale, width) {
const /** @type {?} */ data = findLocaleData(locale);
const /** @type {?} */ erasData = /** @type {?} */ (data[7 /* Eras */]);
return getLastDefinedValue(erasData, width);
}
/**
* First day of the week for this locale, based on english days (Sunday = 0, Monday = 1, ...).
* For example in french the value would be 1 because the first day of the week is Monday.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocaleFirstDayOfWeek(locale) {
const /** @type {?} */ data = findLocaleData(locale);
return data[8 /* FirstDayOfWeek */];
}
/**
* Range of days in the week that represent the week-end for this locale, based on english days
* (Sunday = 0, Monday = 1, ...).
* For example in english the value would be [6,0] for Saturday to Sunday.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocaleWeekEndRange(locale) {
const /** @type {?} */ data = findLocaleData(locale);
return data[9 /* WeekendRange */];
}
/**
* Date format that depends on the locale.
*
* There are four basic date formats:
* - `full` should contain long-weekday (EEEE), year (y), long-month (MMMM), day (d).
*
* For example, English uses `EEEE, MMMM d, y`, corresponding to a date like
* "Tuesday, September 14, 1999".
*
* - `long` should contain year, long-month, day.
*
* For example, `MMMM d, y`, corresponding to a date like "September 14, 1999".
*
* - `medium` should contain year, abbreviated-month (MMM), day.
*
* For example, `MMM d, y`, corresponding to a date like "Sep 14, 1999".
* For languages that do not use abbreviated months, use the numeric month (MM/M). For example,
* `y/MM/dd`, corresponding to a date like "1999/09/14".
*
* - `short` should contain year, numeric-month (MM/M), and day.
*
* For example, `M/d/yy`, corresponding to a date like "9/14/99".
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} width
* @return {?}
*/
function getLocaleDateFormat(locale, width) {
const /** @type {?} */ data = findLocaleData(locale);
return getLastDefinedValue(data[10 /* DateFormat */], width);
}
/**
* Time format that depends on the locale.
*
* The standard formats include four basic time formats:
* - `full` should contain hour (h/H), minute (mm), second (ss), and zone (zzzz).
* - `long` should contain hour, minute, second, and zone (z)
* - `medium` should contain hour, minute, second.
* - `short` should contain hour, minute.
*
* Note: The patterns depend on whether the main country using your language uses 12-hour time or
* not:
* - For 12-hour time, use a pattern like `hh:mm a` using h to mean a 12-hour clock cycle running
* 1 through 12 (midnight plus 1 minute is 12:01), or using K to mean a 12-hour clock cycle
* running 0 through 11 (midnight plus 1 minute is 0:01).
* - For 24-hour time, use a pattern like `HH:mm` using H to mean a 24-hour clock cycle running 0
* through 23 (midnight plus 1 minute is 0:01), or using k to mean a 24-hour clock cycle running
* 1 through 24 (midnight plus 1 minute is 24:01).
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} width
* @return {?}
*/
function getLocaleTimeFormat(locale, width) {
const /** @type {?} */ data = findLocaleData(locale);
return getLastDefinedValue(data[11 /* TimeFormat */], width);
}
/**
* Date-time format that depends on the locale.
*
* The date-time pattern shows how to combine separate patterns for date (represented by {1})
* and time (represented by {0}) into a single pattern. It usually doesn't need to be changed.
* What you want to pay attention to are:
* - possibly removing a space for languages that don't use it, such as many East Asian languages
* - possibly adding a comma, other punctuation, or a combining word
*
* For example:
* - English uses `{1} 'at' {0}` or `{1}, {0}` (depending on date style), while Japanese uses
* `{1}{0}`.
* - An English formatted date-time using the combining pattern `{1}, {0}` could be
* `Dec 10, 2010, 3:59:49 PM`. Notice the comma and space between the date portion and the time
* portion.
*
* There are four formats (`full`, `long`, `medium`, `short`); the determination of which to use
* is normally based on the date style. For example, if the date has a full month and weekday
* name, the full combining pattern will be used to combine that with a time. If the date has
* numeric month, the short version of the combining pattern will be used to combine that with a
* time. English uses `{1} 'at' {0}` for full and long styles, and `{1}, {0}` for medium and short
* styles.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} width
* @return {?}
*/
function getLocaleDateTimeFormat(locale, width) {
const /** @type {?} */ data = findLocaleData(locale);
const /** @type {?} */ dateTimeFormatData = /** @type {?} */ (data[12 /* DateTimeFormat */]);
return getLastDefinedValue(dateTimeFormatData, width);
}
/**
* Number symbol that can be used to replace placeholders in number formats.
* See {\@link NumberSymbol} for more information.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} symbol
* @return {?}
*/
function getLocaleNumberSymbol(locale, symbol) {
const /** @type {?} */ data = findLocaleData(locale);
const /** @type {?} */ res = data[13 /* NumberSymbols */][symbol];
if (typeof res === 'undefined') {
if (symbol === NumberSymbol.CurrencyDecimal) {
return data[13 /* NumberSymbols */][NumberSymbol.Decimal];
}
else if (symbol === NumberSymbol.CurrencyGroup) {
return data[13 /* NumberSymbols */][NumberSymbol.Group];
}
}
return res;
}
/**
* Number format that depends on the locale.
*
* Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00`
* when used to format the number 12345.678 could result in "12'345,67". That would happen if the
* grouping separator for your language is an apostrophe, and the decimal separator is a comma.
*
* <b>Important:</b> The characters `.` `,` `0` `#` (and others below) are special placeholders;
* they stand for the decimal separator, and so on, and are NOT real characters.
* You must NOT "translate" the placeholders; for example, don't change `.` to `,` even though in
* your language the decimal point is written with a comma. The symbols should be replaced by the
* local equivalents, using the Number Symbols for your language.
*
* Here are the special characters used in number patterns:
*
* | Symbol | Meaning |
* |--------|---------|
* | . | Replaced automatically by the character used for the decimal point. |
* | , | Replaced by the "grouping" (thousands) separator. |
* | 0 | Replaced by a digit (or zero if there aren't enough digits). |
* | # | Replaced by a digit (or nothing if there aren't enough). |
* | ¤ | This will be replaced by a currency symbol, such as $ or USD. |
* | % | This marks a percent format. The % symbol may change position, but must be retained. |
* | E | This marks a scientific format. The E symbol may change position, but must be retained. |
* | ' | Special characters used as literal characters are quoted with ASCII single quotes. |
*
* You can find more information
* [on the CLDR website](http://cldr.unicode.org/translation/number-patterns)
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} type
* @return {?}
*/
function getLocaleNumberFormat(locale, type) {
const /** @type {?} */ data = findLocaleData(locale);
return data[14 /* NumberFormats */][type];
}
/**
* The symbol used to represent the currency for the main country using this locale (e.g. $ for
* the locale en-US).
* The symbol will be `null` if the main country cannot be determined.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocaleCurrencySymbol(locale) {
const /** @type {?} */ data = findLocaleData(locale);
return data[15 /* CurrencySymbol */] || null;
}
/**
* The name of the currency for the main country using this locale (e.g. USD for the locale
* en-US).
* The name will be `null` if the main country cannot be determined.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocaleCurrencyName(locale) {
const /** @type {?} */ data = findLocaleData(locale);
return data[16 /* CurrencyName */] || null;
}
/**
* The locale plural function used by ICU expressions to determine the plural case to use.
* See {\@link NgPlural} for more information.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocalePluralCase(locale) {
const /** @type {?} */ data = findLocaleData(locale);
return data[17 /* PluralCase */];
}
/**
* @param {?} data
* @return {?}
*/
function checkFullData(data) {
if (!data[18 /* ExtraData */]) {
throw new Error(`Missing extra locale data for the locale "${data[0 /* LocaleId */]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`);
}
}
/**
* Rules used to determine which day period to use (See `dayPeriods` below).
* The rules can either be an array or a single value. If it's an array, consider it as "from"
* and "to". If it's a single value then it means that the period is only valid at this exact
* value.
* There is always the same number of rules as the number of day periods, which means that the
* first rule is applied to the first day period and so on.
* You should fallback to AM/PM when there are no rules available.
*
* Note: this is only available if you load the full locale data.
* See the {\@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import additional locale
* data.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function getLocaleExtraDayPeriodRules(locale) {
const /** @type {?} */ data = findLocaleData(locale);
checkFullData(data);
const /** @type {?} */ rules = data[18 /* ExtraData */][2 /* ExtraDayPeriodsRules */] || [];
return rules.map((rule) => {
if (typeof rule === 'string') {
return extractTime(rule);
}
return [extractTime(rule[0]), extractTime(rule[1])];
});
}
/**
* Day Periods indicate roughly how the day is broken up in different languages (e.g. morning,
* noon, afternoon, midnight, ...).
* You should use the function {\@link getLocaleExtraDayPeriodRules} to determine which period to
* use.
* You should fallback to AM/PM when there are no day periods available.
*
* Note: this is only available if you load the full locale data.
* See the {\@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import additional locale
* data.
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @param {?} formStyle
* @param {?} width
* @return {?}
*/
function getLocaleExtraDayPeriods(locale, formStyle, width) {
const /** @type {?} */ data = findLocaleData(locale);
checkFullData(data);
const /** @type {?} */ dayPeriodsData = /** @type {?} */ ([
data[18 /* ExtraData */][0 /* ExtraDayPeriodFormats */],
data[18 /* ExtraData */][1 /* ExtraDayPeriodStandalone */]
]);
const /** @type {?} */ dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || [];
return getLastDefinedValue(dayPeriods, width) || [];
}
/**
* Returns the first value that is defined in an array, going backwards.
*
* To avoid repeating the same data (e.g. when "format" and "standalone" are the same) we only
* add the first one to the locale data arrays, the other ones are only defined when different.
* We use this function to retrieve the first defined value.
*
* \@experimental i18n support is experimental.
* @template T
* @param {?} data
* @param {?} index
* @return {?}
*/
function getLastDefinedValue(data, index) {
for (let /** @type {?} */ i = index; i > -1; i--) {
if (typeof data[i] !== 'undefined') {
return data[i];
}
}
throw new Error('Locale data API: locale data undefined');
}
/**
* Extract the hours and minutes from a string like "15:45"
* @param {?} time
* @return {?}
*/
function extractTime(time) {
const [h, m] = time.split(':');
return { hours: +h, minutes: +m };
}
/**
* Finds the locale data for a locale id
*
* \@experimental i18n support is experimental.
* @param {?} locale
* @return {?}
*/
function findLocaleData(locale) {
const /** @type {?} */ normalizedLocale = locale.toLowerCase().replace(/_/g, '-');
let /** @type {?} */ match = LOCALE_DATA[normalizedLocale];
if (match) {
return match;
}
// let's try to find a parent locale
const /** @type {?} */ parentLocale = normalizedLocale.split('-')[0];
match = LOCALE_DATA[parentLocale];
if (match) {
return match;
}
if (parentLocale === 'en') {
return localeEn;
}
throw new Error(`Missing locale data for the locale "${locale}".`);
}
/**
* Return the currency symbol for a given currency code, or the code if no symbol available
* (e.g.: format narrow = $, format wide = US$, code = USD)
*
* \@experimental i18n support is experimental.
* @param {?} code
* @param {?} format
* @return {?}
*/
function getCurrencySymbol(code, format) {
const /** @type {?} */ currency = CURRENCIES[code] || [];
const /** @type {?} */ symbolNarrow = currency[1];
if (format === 'narrow' && typeof symbolNarrow === 'string') {
return symbolNarrow;
}
return currency[0] || code;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @deprecated from v5
*/
const DEPRECATED_PLURAL_FN = new InjectionToken('UseV4Plurals');
/**
* \@experimental
* @abstract
*/
class NgLocalization {
}
/**
* Returns the plural category for a given value.
* - "=value" when the case exists,
* - the plural category otherwise
* @param {?} value
* @param {?} cases
* @param {?} ngLocalization
* @param {?=} locale
* @return {?}
*/
function getPluralCategory(value, cases, ngLocalization, locale) {
let /** @type {?} */ key = `=${value}`;
if (cases.indexOf(key) > -1) {
return key;
}
key = ngLocalization.getPluralCategory(value, locale);
if (cases.indexOf(key) > -1) {
return key;
}
if (cases.indexOf('other') > -1) {
return 'other';
}
throw new Error(`No plural message found for value "${value}"`);
}
/**
* Returns the plural case based on the locale
*
* \@experimental
*/
class NgLocaleLocalization extends NgLocalization {
/**
* @param {?} locale
* @param {?=} deprecatedPluralFn
*/
constructor(locale, /** @deprecated from v5 */
deprecatedPluralFn) {
super();
this.locale = locale;
this.deprecatedPluralFn = deprecatedPluralFn;
}
/**
* @param {?} value
* @param {?=} locale
* @return {?}
*/
getPluralCategory(value, locale) {
const /** @type {?} */ plural = this.deprecatedPluralFn ? this.deprecatedPluralFn(locale || this.locale, value) :
getLocalePluralCase(locale || this.locale)(value);
switch (plural) {
case Plural.Zero:
return 'zero';
case Plural.One:
return 'one';
case Plural.Two:
return 'two';
case Plural.Few:
return 'few';
case Plural.Many:
return 'many';
default:
return 'other';
}
}
}
NgLocaleLocalization.decorators = [
{ type: Injectable },
];
/** @nocollapse */
NgLocaleLocalization.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [LOCALE_ID,] },] },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DEPRECATED_PLURAL_FN,] },] },
];
/**
* Returns the plural case based on the locale
*
* @deprecated from v5 the plural case function is in locale data files common/locales/*.ts
* \@experimental
* @param {?} locale
* @param {?} nLike
* @return {?}
*/
function getPluralCase(locale, nLike) {
// TODO(vicb): lazy compute
if (typeof nLike === 'string') {
nLike = parseInt(/** @type {?} */ (nLike), 10);
}
const /** @type {?} */ n = /** @type {?} */ (nLike);
const /** @type {?} */ nDecimal = n.toString().replace(/^[^.]*\.?/, '');
const /** @type {?} */ i = Math.floor(Math.abs(n));
const /** @type {?} */ v = nDecimal.length;
const /** @type {?} */ f = parseInt(nDecimal, 10);
const /** @type {?} */ t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0;
const /** @type {?} */ lang = locale.split('-')[0].toLowerCase();
switch (lang) {
case 'af':
case 'asa':
case 'az':
case 'bem':
case 'bez':
case 'bg':
case 'brx':
case 'ce':
case 'cgg':
case 'chr':
case 'ckb':
case 'ee':
case 'el':
case 'eo':
case 'es':
case 'eu':
case 'fo':
case 'fur':
case 'gsw':
case 'ha':
case 'haw':
case 'hu':
case 'jgo':
case 'jmc':
case 'ka':
case 'kk':
case 'kkj':
case 'kl':
case 'ks':
case 'ksb':
case 'ky':
case 'lb':
case 'lg':
case 'mas':
case 'mgo':
case 'ml':
case 'mn':
case 'nb':
case 'nd':
case 'ne':
case 'nn':
case 'nnh':
case 'nyn':
case 'om':
case 'or':
case 'os':
case 'ps':
case 'rm':
case 'rof':
case 'rwk':
case 'saq':
case 'seh':
case 'sn':
case 'so':
case 'sq':
case 'ta':
case 'te':
case 'teo':
case 'tk':
case 'tr':
case 'ug':
case 'uz':
case 'vo':
case 'vun':
case 'wae':
case 'xog':
if (n === 1)
return Plural.One;
return Plural.Other;
case 'ak':
case 'ln':
case 'mg':
case 'pa':
case 'ti':
if (n === Math.floor(n) && n >= 0 && n <= 1)
return Plural.One;
return Plural.Other;
case 'am':
case 'as':
case 'bn':
case 'fa':
case 'gu':
case 'hi':
case 'kn':
case 'mr':
case 'zu':
if (i === 0 || n === 1)
return Plural.One;
return Plural.Other;
case 'ar':
if (n === 0)
return Plural.Zero;
if (n === 1)
return Plural.One;
if (n === 2)
return Plural.Two;
if (n % 100 === Math.floor(n % 100) && n % 100 >= 3 && n % 100 <= 10)
return Plural.Few;
if (n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 99)
return Plural.Many;
return Plural.Other;
case 'ast':
case 'ca':
case 'de':
case 'en':
case 'et':
case 'fi':
case 'fy':
case 'gl':
case 'it':
case 'nl':
case 'sv':
case 'sw':
case 'ur':
case 'yi':
if (i === 1 && v === 0)
return Plural.One;
return Plural.Other;
case 'be':
if (n % 10 === 1 && !(n % 100 === 11))
return Plural.One;
if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 4 &&
!(n % 100 >= 12 && n % 100 <= 14))
return Plural.Few;
if (n % 10 === 0 || n % 10 === Math.floor(n % 10) && n % 10 >= 5 && n % 10 <= 9 ||
n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 14)
return Plural.Many;
return Plural.Other;
case 'br':
if (n % 10 === 1 && !(n % 100 === 11 || n % 100 === 71 || n % 100 === 91))
return Plural.One;
if (n % 10 === 2 && !(n % 100 === 12 || n % 100 === 72 || n % 100 === 92))
return Plural.Two;
if (n % 10 === Math.floor(n % 10) && (n % 10 >= 3 && n % 10 <= 4 || n % 10 === 9) &&
!(n % 100 >= 10 && n % 100 <= 19 || n % 100 >= 70 && n % 100 <= 79 ||
n % 100 >= 90 && n % 100 <= 99))
return Plural.Few;
if (!(n === 0) && n % 1e6 === 0)
return Plural.Many;
return Plural.Other;
case 'bs':
case 'hr':
case 'sr':
if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11))
return Plural.One;
if