UNPKG

ember-intl

Version:

A internationalization toolbox for ambitious applications.

121 lines (97 loc) 3.74 kB
/** * Copyright 2015, Yahoo! Inc. * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ import { warn } from '@ember/debug'; import type { SafeString } from '@ember/template/-private/handlebars'; import { Formats } from '../../types'; import type IntlMessageFormat from 'intl-messageformat'; export type ValueOf<ObjectType, ValueType extends keyof ObjectType = keyof ObjectType> = ObjectType[ValueType]; // eslint-disable-next-line @typescript-eslint/ban-types const EMPTY_OBJECT: {} = Object.create(null); export interface FormatterConfig { onError: (info: { kind: unknown; error: unknown }) => void; readFormatConfig: () => Formats; } export interface BaseOptions { format?: string; } /** * @private * @hide */ // eslint-disable-next-line @typescript-eslint/ban-types export default abstract class FormatterBase<KnownOptions extends {}> { protected readonly config: FormatterConfig; protected readonly readFormatConfig: () => Formats; static type: keyof Formats; protected abstract readonly createNativeFormatter: ( locales: string | string[], options: KnownOptions ) => Intl.DateTimeFormat | Intl.RelativeTimeFormat | Intl.NumberFormat | IntlMessageFormat; constructor(config: FormatterConfig) { this.config = config; // NOTE: a fn since we lazily grab the formatter from the config // as it can change at runtime by calling intl.set('formats', {...}); this.readFormatConfig = config.readFormatConfig; } get options(): readonly (keyof KnownOptions)[] { return ([] as unknown[]) as readonly (keyof KnownOptions)[]; } /** * Filters out all of the whitelisted formatter options * * @method filterKnownOptions * @param {Object} Options object * @return {Object} Options object containing just whitelisted options * @private */ filterKnownOptions<O extends BaseOptions>(options?: O): { [K in keyof O & keyof KnownOptions]: O[K] } { if (!options) { return EMPTY_OBJECT as { [K in keyof O & keyof KnownOptions]: O[K] }; } const found = {} as { [K in keyof O & keyof KnownOptions]: O[K] }; for (const key in options) { if (this.options.includes((key as unknown) as keyof O & keyof KnownOptions)) { found[(key as unknown) as keyof O & keyof KnownOptions] = options[(key as unknown) as keyof O & keyof KnownOptions]; } } return found; } readOptions<O extends BaseOptions & KnownOptions>(formatOptions?: O) { let formatterOptions = this.filterKnownOptions(formatOptions); if (formatOptions && 'format' in formatOptions) { const namedFormatsOptions = this.getNamedFormat((formatOptions as BaseOptions).format!); formatterOptions = { ...namedFormatsOptions, ...formatterOptions, }; } return formatterOptions; } validateFormatterOptions(locale: string | string[], _formatterOptions: BaseOptions & KnownOptions): void { if (!locale) { // TODO: config.onError instead? warn( `[ember-intl] no locale has been set! See: https://ember-intl.github.io/ember-intl/docs/quickstart#4-configure-ember-intl`, false, { id: 'ember-intl-no-locale-set', } ); } } getNamedFormat(key: string): void | ValueOf<ValueOf<Required<Formats>>> { const formats = this.readFormatConfig(); const namedFormatsForType = formats[(this.constructor as typeof FormatterBase).type]; if (namedFormatsForType && namedFormatsForType[key]) { return namedFormatsForType[key]; } } abstract format( locale: string | string[], value: unknown, formatOptions?: KnownOptions & BaseOptions ): string | SafeString; }