diff --git a/fluent-bundle/src/types.ts b/fluent-bundle/src/types.ts index a78fa9c1..78fb49b5 100644 --- a/fluent-bundle/src/types.ts +++ b/fluent-bundle/src/types.ts @@ -108,14 +108,16 @@ export class FluentNumber extends FluentType { /** * Format this `FluentNumber` to a string. */ - toString(scope: Scope): string { - try { - const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts); - return nf.format(this.value); - } catch (err) { - scope.reportError(err); - return this.value.toString(10); + toString(scope?: Scope): string { + if (scope) { + try { + const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts); + return nf.format(this.value); + } catch (err) { + scope.reportError(err); + } } + return this.value.toString(10); } } @@ -190,6 +192,10 @@ export class FluentDateTime extends FluentType { this.opts = opts; } + [Symbol.toPrimitive](hint: "number" | "string" | "default"): string | number { + return hint === "string" ? this.toString() : this.toNumber(); + } + /** * Convert this `FluentDateTime` to a number. * Note that this isn't always possible due to the nature of Temporal objects. @@ -214,18 +220,20 @@ export class FluentDateTime extends FluentType { /** * Format this `FluentDateTime` to a string. */ - toString(scope: Scope): string { - try { - const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts); - return dtf.format( - this.value as Parameters[0] - ); - } catch (err) { - scope.reportError(err); - if (typeof this.value === "number" || this.value instanceof Date) { - return new Date(this.value).toISOString(); + toString(scope?: Scope): string { + if (scope) { + try { + const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts); + return dtf.format( + this.value as Parameters[0] + ); + } catch (err) { + scope.reportError(err); } - return this.value.toString(); } + if (typeof this.value === "number" || this.value instanceof Date) { + return new Date(this.value).toISOString(); + } + return this.value.toString(); } } diff --git a/fluent-bundle/test/functions_runtime_test.js b/fluent-bundle/test/functions_runtime_test.js index 7ed960e4..ddcc26a3 100644 --- a/fluent-bundle/test/functions_runtime_test.js +++ b/fluent-bundle/test/functions_runtime_test.js @@ -1,18 +1,17 @@ -import assert from "assert"; import ftl from "@fluent/dedent"; +import assert from "assert"; -import { FluentBundle } from "../esm/bundle.js"; -import { FluentResource } from "../esm/resource.js"; -import { FluentNumber } from "../esm/types.js"; +import { + FluentBundle, + FluentDateTime, + FluentNumber, + FluentResource, +} from "../esm/index.js"; suite("Runtime-specific functions", function () { - let bundle, errs; - - setup(function () { - errs = []; - }); - suite("passing into the constructor", function () { + let bundle, errs; + suiteSetup(function () { bundle = new FluentBundle("en-US", { useIsolating: false, @@ -33,6 +32,7 @@ suite("Runtime-specific functions", function () { } `) ); + errs = []; }); test("works for strings", function () { @@ -56,4 +56,75 @@ suite("Runtime-specific functions", function () { assert.strictEqual(errs.length, 0); }); }); + + suite("firefox-devtools/profiler@9c8fb55", () => { + /** @type {FluentBundle} */ + let bundle; + + suiteSetup(() => { + const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000; + const ONE_YEAR_IN_MS = 365 * ONE_DAY_IN_MS; + + const DATE_FORMATS = { + thisDay: { hour: "numeric", minute: "numeric" }, + thisYear: { + month: "short", + day: "numeric", + hour: "numeric", + minute: "numeric", + }, + ancient: { + year: "numeric", + month: "short", + day: "numeric", + }, + }; + + const SHORTDATE = args => { + const date = args[0]; + const nowTimestamp = Number(new Date("2025-02-15T12:00")); + + const timeDifference = nowTimestamp - +date; + if (timeDifference < 0 || timeDifference > ONE_YEAR_IN_MS) { + return new FluentDateTime(date, DATE_FORMATS.ancient); + } + if (timeDifference > ONE_DAY_IN_MS) { + return new FluentDateTime(date, DATE_FORMATS.thisYear); + } + return new FluentDateTime(date, DATE_FORMATS.thisDay); + }; + + const messages = ftl`\nkey = { SHORTDATE($date) }\n`; + const resource = new FluentResource(messages); + bundle = new FluentBundle("en-US", { functions: { SHORTDATE } }); + bundle.addResource(resource); + }); + + test("works with difference in hours", function () { + const msg = bundle.getMessage("key"); + const date = new Date("2025-02-15T10:30"); + const errs = []; + const val = bundle.formatPattern(msg.value, { date }, errs); + assert.strictEqual(val, "10:30 AM"); + assert.strictEqual(errs.length, 0); + }); + + test("works with difference in days", function () { + const msg = bundle.getMessage("key"); + const date = new Date("2025-02-03T10:30"); + const errs = []; + const val = bundle.formatPattern(msg.value, { date }, errs); + assert.strictEqual(val, "Feb 3, 10:30 AM"); + assert.strictEqual(errs.length, 0); + }); + + test("works with difference in years", function () { + const msg = bundle.getMessage("key"); + const date = new Date("2023-02-03T10:30"); + const errs = []; + const val = bundle.formatPattern(msg.value, { date }, errs); + assert.strictEqual(val, "Feb 3, 2023"); + assert.strictEqual(errs.length, 0); + }); + }); }); diff --git a/fluent-bundle/test/temporal_test.js b/fluent-bundle/test/temporal_test.js index 6219b617..2af66a66 100644 --- a/fluent-bundle/test/temporal_test.js +++ b/fluent-bundle/test/temporal_test.js @@ -66,7 +66,7 @@ suite("Temporal support", function () { test("can be converted to a number", function () { arg = new FluentDateTime(arg); - assert.strictEqual(arg.toNumber(), 0); + assert.strictEqual(+arg, 0); }); }); @@ -94,7 +94,7 @@ suite("Temporal support", function () { test("can be converted to a number", function () { arg = new FluentDateTime(arg); - assert.strictEqual(arg.toNumber(), 0); + assert.strictEqual(+arg, 0); }); }); @@ -123,7 +123,7 @@ suite("Temporal support", function () { test("can be converted to a number", function () { arg = new FluentDateTime(arg); - assert.strictEqual(arg.toNumber(), 0); + assert.strictEqual(+arg, 0); }); }); } @@ -171,7 +171,7 @@ suite("Temporal support", function () { test("cannot be converted to a number", function () { arg = new FluentDateTime(arg); - assert.throws(() => arg.toNumber(), TypeError); + assert.throws(() => +arg, TypeError); }); }); @@ -203,7 +203,7 @@ suite("Temporal support", function () { test("cannot be converted to a number", function () { arg = new FluentDateTime(arg); - assert.throws(() => arg.toNumber(), TypeError); + assert.throws(() => +arg, TypeError); }); }); });