@optionfactory/fml 9.0.0-rc30 → 9.0.0-rc31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/custom-elements.json +8 -1
- package/dist/fml.css +6 -6
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +234 -77
- package/dist/fml.iife.js.map +1 -1
- package/dist/fml.iife.min.js +1 -1
- package/dist/fml.iife.min.js.map +1 -1
- package/dist/ftl.d.mts +32 -9
- package/dist/ftl.iife.js +89 -25
- package/dist/ftl.iife.js.map +1 -1
- package/dist/ftl.iife.min.js +1 -1
- package/dist/ftl.iife.min.js.map +1 -1
- package/dist/ftl.min.mjs +1 -1
- package/dist/ftl.min.mjs.map +1 -1
- package/dist/ftl.mjs +89 -25
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.css +6 -6
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +57 -62
- package/dist/ful.iife.js +145 -51
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +147 -52
- package/dist/ful.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +5 -1
- package/dist/web-types.json +10 -2
- package/package.json +2 -2
package/dist/ftl.d.mts
CHANGED
|
@@ -36,6 +36,16 @@ declare class Expressions {
|
|
|
36
36
|
* `{{{ }}}` (html) or `{{{{ }}}}` (node) interpolation, in order.
|
|
37
37
|
*/
|
|
38
38
|
static MODE_TEMPLATED: symbol;
|
|
39
|
+
/**
|
|
40
|
+
* The key under which the receiver of a module function answers a lookup
|
|
41
|
+
* against the bound data alone: `this[Expressions.BOUND](name)` resolves
|
|
42
|
+
* the name in the layers the scope was created with (a registry's data,
|
|
43
|
+
* the data handed to a Template factory) and never in an overlay pushed
|
|
44
|
+
* afterwards, by `data-tpl-each`, `data-tpl-with` or `withOverlay`. It is
|
|
45
|
+
* how a module reads what the page configured without the rendered data
|
|
46
|
+
* being able to supply or replace it. No name on the data stack reaches it.
|
|
47
|
+
*/
|
|
48
|
+
static BOUND: symbol;
|
|
39
49
|
/**
|
|
40
50
|
* Parses an expression, caching the ast by mode and text.
|
|
41
51
|
* @param {string} expression
|
|
@@ -50,38 +60,45 @@ declare class Expressions {
|
|
|
50
60
|
* @param {any[]} dataStack innermost overlay last
|
|
51
61
|
* @param {any} ast what `parse` answered
|
|
52
62
|
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] the mode it was parsed in
|
|
63
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
53
64
|
* @returns {any} the value, or the list of parts in MODE_TEMPLATED
|
|
54
65
|
*/
|
|
55
66
|
static evaluate(modules: {
|
|
56
67
|
[k: string]: any;
|
|
57
|
-
} | null | undefined, dataStack: any[], ast: any, mode?: (typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED) | null): any;
|
|
68
|
+
} | null | undefined, dataStack: any[], ast: any, mode?: (typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED) | null, bound?: number): any;
|
|
58
69
|
/**
|
|
59
70
|
* Parses and evaluates an expression.
|
|
60
71
|
* @param {{ [x: string]: any; } | null | undefined} modules
|
|
61
72
|
* @param {any[]} dataStack innermost overlay last
|
|
62
73
|
* @param {string} expression
|
|
63
74
|
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
|
|
75
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
64
76
|
* @returns {any} the value, or the list of parts in MODE_TEMPLATED
|
|
65
77
|
*/
|
|
66
78
|
static interpret(modules: {
|
|
67
79
|
[x: string]: any;
|
|
68
|
-
} | null | undefined, dataStack: any[], expression: string, mode?: (typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED) | null): any;
|
|
80
|
+
} | null | undefined, dataStack: any[], expression: string, mode?: (typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED) | null, bound?: number): any;
|
|
69
81
|
}
|
|
70
82
|
/**
|
|
71
83
|
* A scope: the modules that `#name:fn()` resolves against and the data stack
|
|
72
84
|
* that a bare identifier resolves against, as a single value. Adding an overlay
|
|
73
85
|
* returns a new evaluator instead of modifying this one, so a template can add
|
|
74
86
|
* data for one subtree without affecting the rest of the render.
|
|
87
|
+
*
|
|
88
|
+
* The layers an evaluator is created with are its bound data; the overlays
|
|
89
|
+
* pushed afterwards are not. A bare identifier resolves against both, while a
|
|
90
|
+
* module function can ask for the bound data alone through `Expressions.BOUND`.
|
|
75
91
|
*/
|
|
76
92
|
declare class ExpressionEvaluator {
|
|
77
93
|
#private;
|
|
78
94
|
/**
|
|
79
95
|
* @param {{ [k: string]: any } | null | undefined} modules
|
|
80
96
|
* @param {any[]} dataStack innermost overlay last
|
|
97
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
81
98
|
*/
|
|
82
99
|
constructor(modules: {
|
|
83
100
|
[k: string]: any;
|
|
84
|
-
} | null | undefined, dataStack: any[]);
|
|
101
|
+
} | null | undefined, dataStack: any[], bound?: number);
|
|
85
102
|
/**
|
|
86
103
|
* A new evaluator with one more module: `#name:fn()` resolves into it, and an
|
|
87
104
|
* empty name merges a whole map whose functions resolve bare, as `#fn()`.
|
|
@@ -92,7 +109,7 @@ declare class ExpressionEvaluator {
|
|
|
92
109
|
withModule(name: string, value: object): ExpressionEvaluator;
|
|
93
110
|
/**
|
|
94
111
|
* A new evaluator with the data pushed as the innermost overlays, the last
|
|
95
|
-
* one winning a shared name.
|
|
112
|
+
* one winning a shared name. The bound data stays what it was.
|
|
96
113
|
* @param {...any} data
|
|
97
114
|
* @returns {ExpressionEvaluator}
|
|
98
115
|
*/
|
|
@@ -565,7 +582,9 @@ declare class Registry {
|
|
|
565
582
|
}): Registry;
|
|
566
583
|
/**
|
|
567
584
|
* Defines every element deferred so far; from then on, defineElement takes
|
|
568
|
-
* effect immediately.
|
|
585
|
+
* effect immediately. Readiness waits for the first call: a page that
|
|
586
|
+
* configures late (translations fetched first) gets its ftl:ready once the
|
|
587
|
+
* elements this defines have upgraded, not at DOMContentLoaded.
|
|
569
588
|
* @returns {Registry} this registry
|
|
570
589
|
*/
|
|
571
590
|
configure(): Registry;
|
|
@@ -592,9 +611,11 @@ declare class Registry {
|
|
|
592
611
|
/**
|
|
593
612
|
* Waits for this registry's readiness: resolves right after its ftl:ready
|
|
594
613
|
* event is dispatched on the document, and immediately when that already
|
|
595
|
-
* happened.
|
|
596
|
-
*
|
|
597
|
-
*
|
|
614
|
+
* happened. The event is dispatched once DOMContentLoaded has passed,
|
|
615
|
+
* configure() has been called and the queue has drained, so a registry
|
|
616
|
+
* that is never configured is never ready. Never rejects: a failed
|
|
617
|
+
* upgrade counts as settled, and its rejection is left unhandled so it
|
|
618
|
+
* still reaches the console and the page's error reporting.
|
|
598
619
|
* @returns {Promise<void>}
|
|
599
620
|
*/
|
|
600
621
|
ready(): Promise<void>;
|
|
@@ -811,7 +832,9 @@ export type Receiver = {
|
|
|
811
832
|
* The translations as a template module: `#l10n:t()` for messages, plus date,
|
|
812
833
|
* number and bytes formatting in the page's locale. The template form and the
|
|
813
834
|
* imperative `of()` facade both resolve through the registry's scope, so they
|
|
814
|
-
* always produce the same result for the same key.
|
|
835
|
+
* always produce the same result for the same key. A template reads `l10n`
|
|
836
|
+
* and `locale` from its bound data only, the most recent layer winning: an
|
|
837
|
+
* overlay pushed while rendering (a row, a response) never reaches them.
|
|
815
838
|
*/
|
|
816
839
|
declare class Localization {
|
|
817
840
|
/**
|
package/dist/ftl.iife.js
CHANGED
|
@@ -2850,6 +2850,8 @@ var ftl = (function (exports) {
|
|
|
2850
2850
|
return undefined;
|
|
2851
2851
|
};
|
|
2852
2852
|
|
|
2853
|
+
const BOUND = Symbol('BOUND');
|
|
2854
|
+
|
|
2853
2855
|
/**
|
|
2854
2856
|
* Evaluates a parsed expression against a scope. One instance per evaluation:
|
|
2855
2857
|
* it holds the modules and the data stack that names resolve against, and keeps
|
|
@@ -2858,9 +2860,11 @@ var ftl = (function (exports) {
|
|
|
2858
2860
|
class EvaluatingVisitor {
|
|
2859
2861
|
#modules;
|
|
2860
2862
|
#dataStack;
|
|
2861
|
-
|
|
2863
|
+
#bound;
|
|
2864
|
+
constructor(modules, dataStack, bound) {
|
|
2862
2865
|
this.#modules = modules;
|
|
2863
2866
|
this.#dataStack = dataStack;
|
|
2867
|
+
this.#bound = bound ?? dataStack.length;
|
|
2864
2868
|
}
|
|
2865
2869
|
#resolve(prop) {
|
|
2866
2870
|
return resolveInStack(this.#dataStack, prop);
|
|
@@ -2884,8 +2888,9 @@ var ftl = (function (exports) {
|
|
|
2884
2888
|
#cached_resolve_proxy;
|
|
2885
2889
|
#resolve_proxy() {
|
|
2886
2890
|
if (!this.#cached_resolve_proxy) {
|
|
2891
|
+
const bound = (name) => resolveInStack(this.#dataStack.slice(0, this.#bound), name);
|
|
2887
2892
|
this.#cached_resolve_proxy = new Proxy(this.#dataStack, {
|
|
2888
|
-
get: (target, prop) => this.#resolve(prop),
|
|
2893
|
+
get: (target, prop) => (prop === BOUND ? bound : this.#resolve(prop)),
|
|
2889
2894
|
});
|
|
2890
2895
|
}
|
|
2891
2896
|
return this.#cached_resolve_proxy;
|
|
@@ -3038,6 +3043,16 @@ var ftl = (function (exports) {
|
|
|
3038
3043
|
* `{{{ }}}` (html) or `{{{{ }}}}` (node) interpolation, in order.
|
|
3039
3044
|
*/
|
|
3040
3045
|
static MODE_TEMPLATED = Symbol('MODE_TEMPLATED');
|
|
3046
|
+
/**
|
|
3047
|
+
* The key under which the receiver of a module function answers a lookup
|
|
3048
|
+
* against the bound data alone: `this[Expressions.BOUND](name)` resolves
|
|
3049
|
+
* the name in the layers the scope was created with (a registry's data,
|
|
3050
|
+
* the data handed to a Template factory) and never in an overlay pushed
|
|
3051
|
+
* afterwards, by `data-tpl-each`, `data-tpl-with` or `withOverlay`. It is
|
|
3052
|
+
* how a module reads what the page configured without the rendered data
|
|
3053
|
+
* being able to supply or replace it. No name on the data stack reaches it.
|
|
3054
|
+
*/
|
|
3055
|
+
static BOUND = BOUND;
|
|
3041
3056
|
|
|
3042
3057
|
static #astCache = new BoundedCache(1000);
|
|
3043
3058
|
|
|
@@ -3062,10 +3077,11 @@ var ftl = (function (exports) {
|
|
|
3062
3077
|
* @param {any[]} dataStack innermost overlay last
|
|
3063
3078
|
* @param {any} ast what `parse` answered
|
|
3064
3079
|
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] the mode it was parsed in
|
|
3080
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
3065
3081
|
* @returns {any} the value, or the list of parts in MODE_TEMPLATED
|
|
3066
3082
|
*/
|
|
3067
|
-
static evaluate(modules, dataStack, ast, mode) {
|
|
3068
|
-
return new EvaluatingVisitor(modules, dataStack).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
|
|
3083
|
+
static evaluate(modules, dataStack, ast, mode, bound) {
|
|
3084
|
+
return new EvaluatingVisitor(modules, dataStack, bound).visitRoot(ast, mode === Expressions.MODE_TEMPLATED);
|
|
3069
3085
|
}
|
|
3070
3086
|
/**
|
|
3071
3087
|
* Parses and evaluates an expression.
|
|
@@ -3073,10 +3089,11 @@ var ftl = (function (exports) {
|
|
|
3073
3089
|
* @param {any[]} dataStack innermost overlay last
|
|
3074
3090
|
* @param {string} expression
|
|
3075
3091
|
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode] MODE_EXPRESSION when omitted
|
|
3092
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
3076
3093
|
* @returns {any} the value, or the list of parts in MODE_TEMPLATED
|
|
3077
3094
|
*/
|
|
3078
|
-
static interpret(modules, dataStack, expression, mode) {
|
|
3079
|
-
return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode);
|
|
3095
|
+
static interpret(modules, dataStack, expression, mode, bound) {
|
|
3096
|
+
return Expressions.evaluate(modules, dataStack, Expressions.parse(expression, mode), mode, bound);
|
|
3080
3097
|
}
|
|
3081
3098
|
}
|
|
3082
3099
|
|
|
@@ -3085,17 +3102,24 @@ var ftl = (function (exports) {
|
|
|
3085
3102
|
* that a bare identifier resolves against, as a single value. Adding an overlay
|
|
3086
3103
|
* returns a new evaluator instead of modifying this one, so a template can add
|
|
3087
3104
|
* data for one subtree without affecting the rest of the render.
|
|
3105
|
+
*
|
|
3106
|
+
* The layers an evaluator is created with are its bound data; the overlays
|
|
3107
|
+
* pushed afterwards are not. A bare identifier resolves against both, while a
|
|
3108
|
+
* module function can ask for the bound data alone through `Expressions.BOUND`.
|
|
3088
3109
|
*/
|
|
3089
3110
|
class ExpressionEvaluator {
|
|
3090
3111
|
#modules;
|
|
3091
3112
|
#dataStack;
|
|
3113
|
+
#bound;
|
|
3092
3114
|
/**
|
|
3093
3115
|
* @param {{ [k: string]: any } | null | undefined} modules
|
|
3094
3116
|
* @param {any[]} dataStack innermost overlay last
|
|
3117
|
+
* @param {number} [bound] how many layers, from the outermost, are the bound data; all of them when omitted
|
|
3095
3118
|
*/
|
|
3096
|
-
constructor(modules, dataStack) {
|
|
3119
|
+
constructor(modules, dataStack, bound) {
|
|
3097
3120
|
this.#modules = modules;
|
|
3098
3121
|
this.#dataStack = dataStack;
|
|
3122
|
+
this.#bound = bound ?? dataStack.length;
|
|
3099
3123
|
}
|
|
3100
3124
|
/**
|
|
3101
3125
|
* A new evaluator with one more module: `#name:fn()` resolves into it, and an
|
|
@@ -3106,11 +3130,11 @@ var ftl = (function (exports) {
|
|
|
3106
3130
|
*/
|
|
3107
3131
|
withModule(name, value) {
|
|
3108
3132
|
const module = name ? { [name]: value } : value;
|
|
3109
|
-
return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack);
|
|
3133
|
+
return new ExpressionEvaluator({ ...this.#modules, ...module }, this.#dataStack, this.#bound);
|
|
3110
3134
|
}
|
|
3111
3135
|
/**
|
|
3112
3136
|
* A new evaluator with the data pushed as the innermost overlays, the last
|
|
3113
|
-
* one winning a shared name.
|
|
3137
|
+
* one winning a shared name. The bound data stays what it was.
|
|
3114
3138
|
* @param {...any} data
|
|
3115
3139
|
* @returns {ExpressionEvaluator}
|
|
3116
3140
|
*/
|
|
@@ -3118,6 +3142,7 @@ var ftl = (function (exports) {
|
|
|
3118
3142
|
return new ExpressionEvaluator(
|
|
3119
3143
|
this.#modules,
|
|
3120
3144
|
data.length === 0 ? this.#dataStack : [...this.#dataStack, ...data],
|
|
3145
|
+
this.#bound,
|
|
3121
3146
|
);
|
|
3122
3147
|
}
|
|
3123
3148
|
/**
|
|
@@ -3135,7 +3160,13 @@ var ftl = (function (exports) {
|
|
|
3135
3160
|
* @returns {any}
|
|
3136
3161
|
*/
|
|
3137
3162
|
evaluateExpression(expression) {
|
|
3138
|
-
return Expressions.interpret(
|
|
3163
|
+
return Expressions.interpret(
|
|
3164
|
+
this.#modules,
|
|
3165
|
+
this.#dataStack,
|
|
3166
|
+
expression,
|
|
3167
|
+
Expressions.MODE_EXPRESSION,
|
|
3168
|
+
this.#bound,
|
|
3169
|
+
);
|
|
3139
3170
|
}
|
|
3140
3171
|
/**
|
|
3141
3172
|
* Evaluates a templated text, the `{{ }}` form, against this scope.
|
|
@@ -3143,7 +3174,7 @@ var ftl = (function (exports) {
|
|
|
3143
3174
|
* @returns {{ type: symbol, value: any }[]} its parts, as MODE_TEMPLATED describes
|
|
3144
3175
|
*/
|
|
3145
3176
|
evaluateTemplated(text) {
|
|
3146
|
-
return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED);
|
|
3177
|
+
return Expressions.interpret(this.#modules, this.#dataStack, text, Expressions.MODE_TEMPLATED, this.#bound);
|
|
3147
3178
|
}
|
|
3148
3179
|
}
|
|
3149
3180
|
|
|
@@ -4039,14 +4070,22 @@ var ftl = (function (exports) {
|
|
|
4039
4070
|
#ready = new Promise((resolve) => {
|
|
4040
4071
|
this.#readyResolve = resolve;
|
|
4041
4072
|
});
|
|
4073
|
+
#configured = /** @type {PromiseWithResolvers<void>} */ (Promise.withResolvers());
|
|
4042
4074
|
constructor() {
|
|
4043
|
-
Nodes.waitDomContentLoaded(document).then(() => {
|
|
4075
|
+
Promise.all([Nodes.waitDomContentLoaded(document), this.#configured.promise]).then(() => {
|
|
4044
4076
|
this.#start();
|
|
4045
4077
|
});
|
|
4046
4078
|
}
|
|
4047
4079
|
ready() {
|
|
4048
4080
|
return this.#ready;
|
|
4049
4081
|
}
|
|
4082
|
+
/**
|
|
4083
|
+
* Marks the registry as configured: until then nothing is defined, so an
|
|
4084
|
+
* empty queue says nothing about the page and readiness is held back.
|
|
4085
|
+
*/
|
|
4086
|
+
configured() {
|
|
4087
|
+
this.#configured.resolve();
|
|
4088
|
+
}
|
|
4050
4089
|
async #start() {
|
|
4051
4090
|
await this.settled();
|
|
4052
4091
|
document.dispatchEvent(
|
|
@@ -4089,7 +4128,10 @@ var ftl = (function (exports) {
|
|
|
4089
4128
|
* @returns {Promise<void>}
|
|
4090
4129
|
*/
|
|
4091
4130
|
settled() {
|
|
4092
|
-
return this.#drain(
|
|
4131
|
+
return this.#drain(
|
|
4132
|
+
() => true,
|
|
4133
|
+
(entry) => entry.finished,
|
|
4134
|
+
);
|
|
4093
4135
|
}
|
|
4094
4136
|
/**
|
|
4095
4137
|
* Waits for the accepted upgrades, the ones enqueued meanwhile included,
|
|
@@ -4343,7 +4385,9 @@ var ftl = (function (exports) {
|
|
|
4343
4385
|
}
|
|
4344
4386
|
/**
|
|
4345
4387
|
* Defines every element deferred so far; from then on, defineElement takes
|
|
4346
|
-
* effect immediately.
|
|
4388
|
+
* effect immediately. Readiness waits for the first call: a page that
|
|
4389
|
+
* configures late (translations fetched first) gets its ftl:ready once the
|
|
4390
|
+
* elements this defines have upgraded, not at DOMContentLoaded.
|
|
4347
4391
|
* @returns {Registry} this registry
|
|
4348
4392
|
*/
|
|
4349
4393
|
configure() {
|
|
@@ -4352,6 +4396,7 @@ var ftl = (function (exports) {
|
|
|
4352
4396
|
delete this.#tagToClass[tag];
|
|
4353
4397
|
}
|
|
4354
4398
|
this.#configured = true;
|
|
4399
|
+
this.#upgradeQueue.configured();
|
|
4355
4400
|
return this;
|
|
4356
4401
|
}
|
|
4357
4402
|
/**
|
|
@@ -4383,9 +4428,11 @@ var ftl = (function (exports) {
|
|
|
4383
4428
|
/**
|
|
4384
4429
|
* Waits for this registry's readiness: resolves right after its ftl:ready
|
|
4385
4430
|
* event is dispatched on the document, and immediately when that already
|
|
4386
|
-
* happened.
|
|
4387
|
-
*
|
|
4388
|
-
*
|
|
4431
|
+
* happened. The event is dispatched once DOMContentLoaded has passed,
|
|
4432
|
+
* configure() has been called and the queue has drained, so a registry
|
|
4433
|
+
* that is never configured is never ready. Never rejects: a failed
|
|
4434
|
+
* upgrade counts as settled, and its rejection is left unhandled so it
|
|
4435
|
+
* still reaches the console and the page's error reporting.
|
|
4389
4436
|
* @returns {Promise<void>}
|
|
4390
4437
|
*/
|
|
4391
4438
|
ready() {
|
|
@@ -4729,12 +4776,27 @@ var ftl = (function (exports) {
|
|
|
4729
4776
|
*/
|
|
4730
4777
|
|
|
4731
4778
|
/**
|
|
4732
|
-
* The receiver contract of the module functions:
|
|
4733
|
-
*
|
|
4779
|
+
* The receiver contract of the module functions: a plain object carrying the
|
|
4780
|
+
* translations and the locale, as Localization.of() binds them, or the proxy
|
|
4781
|
+
* over the data stack a template calls them with.
|
|
4734
4782
|
*
|
|
4735
4783
|
* @typedef {{ l10n?: Messages, locale?: string }} Receiver
|
|
4736
4784
|
*/
|
|
4737
4785
|
|
|
4786
|
+
/**
|
|
4787
|
+
* The translations or the locale of a receiver. From a template they are read
|
|
4788
|
+
* from the bound data alone (the registry's overlays, or the data handed to
|
|
4789
|
+
* the Template factory): the rows and responses a render pushes on top are
|
|
4790
|
+
* what arrives later, and must not supply or replace the page's own messages.
|
|
4791
|
+
* @param {any} receiver
|
|
4792
|
+
* @param {'l10n' | 'locale'} name
|
|
4793
|
+
* @returns {any}
|
|
4794
|
+
*/
|
|
4795
|
+
const configured = (receiver, name) => {
|
|
4796
|
+
const bound = receiver[Expressions.BOUND];
|
|
4797
|
+
return typeof bound === 'function' ? bound(name) : receiver[name];
|
|
4798
|
+
};
|
|
4799
|
+
|
|
4738
4800
|
const PLACEHOLDER = /\{(\w+)\}/g;
|
|
4739
4801
|
const POSITIONAL = /\{(\d+)\}/g;
|
|
4740
4802
|
/** @param {any} v @returns {v is Record<string, string>} */
|
|
@@ -4769,7 +4831,9 @@ var ftl = (function (exports) {
|
|
|
4769
4831
|
* The translations as a template module: `#l10n:t()` for messages, plus date,
|
|
4770
4832
|
* number and bytes formatting in the page's locale. The template form and the
|
|
4771
4833
|
* imperative `of()` facade both resolve through the registry's scope, so they
|
|
4772
|
-
* always produce the same result for the same key.
|
|
4834
|
+
* always produce the same result for the same key. A template reads `l10n`
|
|
4835
|
+
* and `locale` from its bound data only, the most recent layer winning: an
|
|
4836
|
+
* overlay pushed while rendering (a row, a response) never reaches them.
|
|
4773
4837
|
*/
|
|
4774
4838
|
class Localization {
|
|
4775
4839
|
/**
|
|
@@ -4788,7 +4852,7 @@ var ftl = (function (exports) {
|
|
|
4788
4852
|
* @returns {string} the message, or the key itself when the translations do not carry it
|
|
4789
4853
|
*/
|
|
4790
4854
|
static t(key, ...args) {
|
|
4791
|
-
const messages = this
|
|
4855
|
+
const messages = configured(this, 'l10n') ?? {};
|
|
4792
4856
|
let message = Object.hasOwn(messages, key) ? messages[key] : undefined;
|
|
4793
4857
|
if (message === undefined) {
|
|
4794
4858
|
warnOnce(`missing message "${key}"`);
|
|
@@ -4800,7 +4864,7 @@ var ftl = (function (exports) {
|
|
|
4800
4864
|
warnOnce(`plural message "${key}" needs a numeric {count}`);
|
|
4801
4865
|
message = message.other;
|
|
4802
4866
|
} else {
|
|
4803
|
-
const locale = this
|
|
4867
|
+
const locale = configured(this, 'locale') ?? navigator?.language ?? 'en';
|
|
4804
4868
|
const category = formatter(Intl.PluralRules, locale).select(named.count);
|
|
4805
4869
|
message = message[category] ?? message.other;
|
|
4806
4870
|
}
|
|
@@ -4840,7 +4904,7 @@ var ftl = (function (exports) {
|
|
|
4840
4904
|
* @returns {string}
|
|
4841
4905
|
*/
|
|
4842
4906
|
static date(value, options) {
|
|
4843
|
-
return formatter(Intl.DateTimeFormat, this
|
|
4907
|
+
return formatter(Intl.DateTimeFormat, configured(this, 'locale'), options).format(value);
|
|
4844
4908
|
}
|
|
4845
4909
|
|
|
4846
4910
|
/**
|
|
@@ -4852,7 +4916,7 @@ var ftl = (function (exports) {
|
|
|
4852
4916
|
* @returns {string}
|
|
4853
4917
|
*/
|
|
4854
4918
|
static number(value, options) {
|
|
4855
|
-
return formatter(Intl.NumberFormat, this
|
|
4919
|
+
return formatter(Intl.NumberFormat, configured(this, 'locale'), options).format(value);
|
|
4856
4920
|
}
|
|
4857
4921
|
|
|
4858
4922
|
/**
|
|
@@ -4865,7 +4929,7 @@ var ftl = (function (exports) {
|
|
|
4865
4929
|
* @returns {string}
|
|
4866
4930
|
*/
|
|
4867
4931
|
static bytes(value) {
|
|
4868
|
-
const format = formatter(Intl.NumberFormat, this
|
|
4932
|
+
const format = formatter(Intl.NumberFormat, configured(this, 'locale'), { maximumFractionDigits: 2 }).format;
|
|
4869
4933
|
if (value >= 1024 * 1024 * 1024) {
|
|
4870
4934
|
return `${format(value / 1024 / 1024 / 1024)}GiB`;
|
|
4871
4935
|
}
|