Skip to content

Commit eecdc1a

Browse files
authored
(@fluent/bundle) Allow only some formatting options to NUMBER and DATETIME (#464)
1 parent 6ac18e1 commit eecdc1a

File tree

3 files changed

+421
-108
lines changed

3 files changed

+421
-108
lines changed

fluent-bundle/src/builtins.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,30 @@ import {
1818
FluentDateTime
1919
} from "./types.js";
2020

21-
function values(opts: Record<string, FluentValue>): Record<string, unknown> {
22-
const unwrapped: Record<string, unknown> = {};
21+
function values(
22+
opts: Record<string, FluentValue>,
23+
allowed: Array<string>
24+
): Record<string, unknown> {
25+
const unwrapped: Record<string, unknown> = Object.create(null);
2326
for (const [name, opt] of Object.entries(opts)) {
24-
unwrapped[name] = opt.valueOf();
27+
if (allowed.includes(name)) {
28+
unwrapped[name] = opt.valueOf();
29+
}
2530
}
2631
return unwrapped;
2732
}
2833

34+
const NUMBER_ALLOWED = [
35+
"unitDisplay",
36+
"currencyDisplay",
37+
"useGrouping",
38+
"minimumIntegerDigits",
39+
"minimumFractionDigits",
40+
"maximumFractionDigits",
41+
"minimumSignificantDigits",
42+
"maximumSignificantDigits",
43+
];
44+
2945
export function NUMBER(
3046
args: Array<FluentValue>,
3147
opts: Record<string, FluentValue>
@@ -37,12 +53,32 @@ export function NUMBER(
3753
}
3854

3955
if (arg instanceof FluentNumber || arg instanceof FluentDateTime) {
40-
return new FluentNumber(arg.valueOf(), { ...arg.opts, ...values(opts) });
56+
return new FluentNumber(arg.valueOf(), {
57+
...arg.opts,
58+
...values(opts, NUMBER_ALLOWED)
59+
});
4160
}
4261

4362
throw new TypeError("Invalid argument to NUMBER");
4463
}
4564

65+
const DATETIME_ALLOWED = [
66+
"dateStyle",
67+
"timeStyle",
68+
"fractionalSecondDigits",
69+
"dayPeriod",
70+
"hour12",
71+
"weekday",
72+
"era",
73+
"year",
74+
"month",
75+
"day",
76+
"hour",
77+
"minute",
78+
"second",
79+
"timeZoneName",
80+
];
81+
4682
export function DATETIME(
4783
args: Array<FluentValue>,
4884
opts: Record<string, FluentValue>
@@ -54,7 +90,10 @@ export function DATETIME(
5490
}
5591

5692
if (arg instanceof FluentNumber || arg instanceof FluentDateTime) {
57-
return new FluentDateTime(arg.valueOf(), { ...arg.opts, ...values(opts) });
93+
return new FluentDateTime(arg.valueOf(), {
94+
...arg.opts,
95+
...values(opts, DATETIME_ALLOWED)
96+
});
5897
}
5998

6099
throw new TypeError("Invalid argument to DATETIME");

fluent-bundle/test/arguments_test.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import ftl from "@fluent/dedent";
55

66
import {FluentBundle} from '../esm/bundle';
77
import {FluentResource} from '../esm/resource';
8-
import {FluentType} from '../esm/types';
8+
import {FluentType, FluentNumber, FluentDateTime} from '../esm/types';
99

1010
suite('Variables', function() {
1111
let bundle, errs;
@@ -171,45 +171,54 @@ suite('Variables', function() {
171171
});
172172

173173
suite('and numbers', function(){
174-
let args;
175-
176174
suiteSetup(function() {
177175
bundle = new FluentBundle('en-US', { useIsolating: false });
178176
bundle.addResource(new FluentResource(ftl`
179177
foo = { $arg }
180178
`));
181-
args = {
182-
arg: 1
183-
};
184179
});
185180

186181
test('can be a number', function(){
187182
const msg = bundle.getMessage('foo');
188-
const val = bundle.formatPattern(msg.value, args, errs);
183+
const val = bundle.formatPattern(msg.value, {arg: 1}, errs);
189184
assert.strictEqual(val, '1');
190185
assert.strictEqual(errs.length, 0);
191186
});
187+
188+
test('can be a FluentNumber', function(){
189+
const arg = new FluentNumber(1, {minimumFractionDigits: 2});
190+
const msg = bundle.getMessage('foo');
191+
const val = bundle.formatPattern(msg.value, {arg}, errs);
192+
assert.strictEqual(val, '1.00');
193+
assert.strictEqual(errs.length, 0);
194+
});
192195
});
193196

194197
suite('and dates', function(){
195-
let args, dtf;
198+
let dtf;
196199

197200
suiteSetup(function() {
198201
dtf = new Intl.DateTimeFormat('en-US');
199202
bundle = new FluentBundle('en-US', { useIsolating: false });
200203
bundle.addResource(new FluentResource(ftl`
201204
foo = { $arg }
202205
`));
203-
args = {
204-
arg: new Date('2016-09-29')
205-
};
206206
});
207207

208208
test('can be a date', function(){
209+
const arg = new Date('2016-09-29');
209210
const msg = bundle.getMessage('foo');
210-
const val = bundle.formatPattern(msg.value, args, errs);
211+
const val = bundle.formatPattern(msg.value, {arg}, errs);
211212
// format the date argument to account for the testrunner's timezone
212-
assert.strictEqual(val, dtf.format(args.arg));
213+
assert.strictEqual(val, dtf.format(arg));
214+
assert.strictEqual(errs.length, 0);
215+
});
216+
217+
test('can be a FluentDateTime', function(){
218+
const arg = new FluentDateTime(new Date('2016-09-29'), {weekday: "long"});
219+
const msg = bundle.getMessage('foo');
220+
const val = bundle.formatPattern(msg.value, {arg}, errs);
221+
assert.strictEqual(val, 'Thursday');
213222
assert.strictEqual(errs.length, 0);
214223
});
215224
});

0 commit comments

Comments
 (0)