You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
5.5 KiB

// @flow
import {StringType, NumberType} from '../types.js';
import type {Expression, SerializedExpression} from '../expression.js';
import type EvaluationContext from '../evaluation_context.js';
import type ParsingContext from '../parsing_context.js';
import type {Type} from '../types.js';
declare var Intl: {
NumberFormat: Class<Intl$NumberFormat>
};
declare class Intl$NumberFormat {
constructor (
locales?: string | string[],
options?: NumberFormatOptions
): Intl$NumberFormat;
static (
locales?: string | string[],
options?: NumberFormatOptions
): Intl$NumberFormat;
format(a: number): string;
resolvedOptions(): any;
}
type NumberFormatOptions = {
style?: 'decimal' | 'currency' | 'percent' | 'unit';
currency?: null | string;
unit?: null | string;
minimumFractionDigits?: null | string;
maximumFractionDigits?: null | string;
};
export default class NumberFormat implements Expression {
type: Type;
number: Expression;
locale: Expression | null; // BCP 47 language tag
currency: Expression | null; // ISO 4217 currency code, required if style=currency
unit: Expression | null; // Simple units sanctioned for use in ECMAScript, required if style=unit. https://tc39.es/proposal-unified-intl-numberformat/section6/locales-currencies-tz_proposed_out.html#sec-issanctionedsimpleunitidentifier
minFractionDigits: Expression | null; // Default 0
maxFractionDigits: Expression | null; // Default 3
constructor(number: Expression,
locale: Expression | null,
currency: Expression | null,
unit: Expression | null,
minFractionDigits: Expression | null,
maxFractionDigits: Expression | null) {
this.type = StringType;
this.number = number;
this.locale = locale;
this.currency = currency;
this.unit = unit;
this.minFractionDigits = minFractionDigits;
this.maxFractionDigits = maxFractionDigits;
}
static parse(args: $ReadOnlyArray<mixed>, context: ParsingContext): ?Expression {
if (args.length !== 3)
return context.error(`Expected two arguments.`);
const number = context.parse(args[1], 1, NumberType);
if (!number) return null;
const options = (args[2]: any);
if (typeof options !== "object" || Array.isArray(options))
return context.error(`NumberFormat options argument must be an object.`);
let locale = null;
if (options['locale']) {
locale = context.parse(options['locale'], 1, StringType);
if (!locale) return null;
}
let currency = null;
if (options['currency']) {
currency = context.parse(options['currency'], 1, StringType);
if (!currency) return null;
}
let unit = null;
if (options['unit']) {
unit = context.parse(options['unit'], 1, StringType);
if (!unit) return null;
}
let minFractionDigits = null;
if (options['min-fraction-digits']) {
minFractionDigits = context.parse(options['min-fraction-digits'], 1, NumberType);
if (!minFractionDigits) return null;
}
let maxFractionDigits = null;
if (options['max-fraction-digits']) {
maxFractionDigits = context.parse(options['max-fraction-digits'], 1, NumberType);
if (!maxFractionDigits) return null;
}
return new NumberFormat(number, locale, currency, unit, minFractionDigits, maxFractionDigits);
}
evaluate(ctx: EvaluationContext): string {
return new Intl.NumberFormat(this.locale ? this.locale.evaluate(ctx) : [],
{
style:
(this.currency && "currency") ||
(this.unit && "unit") ||
"decimal",
currency: this.currency ? this.currency.evaluate(ctx) : undefined,
unit: this.unit ? this.unit.evaluate(ctx) : undefined,
minimumFractionDigits: this.minFractionDigits ? this.minFractionDigits.evaluate(ctx) : undefined,
maximumFractionDigits: this.maxFractionDigits ? this.maxFractionDigits.evaluate(ctx) : undefined,
}).format(this.number.evaluate(ctx));
}
eachChild(fn: (_: Expression) => void) {
fn(this.number);
if (this.locale) {
fn(this.locale);
}
if (this.currency) {
fn(this.currency);
}
if (this.unit) {
fn(this.unit);
}
if (this.minFractionDigits) {
fn(this.minFractionDigits);
}
if (this.maxFractionDigits) {
fn(this.maxFractionDigits);
}
}
outputDefined(): boolean {
return false;
}
serialize(): SerializedExpression {
const options = {};
if (this.locale) {
options['locale'] = this.locale.serialize();
}
if (this.currency) {
options['currency'] = this.currency.serialize();
}
if (this.unit) {
options['unit'] = this.unit.serialize();
}
if (this.minFractionDigits) {
options['min-fraction-digits'] = this.minFractionDigits.serialize();
}
if (this.maxFractionDigits) {
options['max-fraction-digits'] = this.maxFractionDigits.serialize();
}
return ["number-format", this.number.serialize(), options];
}
}