release: WRNexusJS 0.8.0
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-02 23:18:51 +05:30
parent 87507edf59
commit 586a6db8ff
625 changed files with 243608 additions and 11210 deletions
+59 -9
View File
@@ -73,7 +73,11 @@ export interface LocaleFormatter {
list(values: string[], options?: Intl.ListFormatOptions): string;
}
export function createLocaleFormatter(locale: string, timeZone?: string): LocaleFormatter {
export function createLocaleFormatter(
locale: string,
timeZone?: string,
calendar?: string,
): LocaleFormatter {
const numberCache = new Map<string, Intl.NumberFormat>();
const dateCache = new Map<string, Intl.DateTimeFormat>();
const key = (value: unknown) => JSON.stringify(value ?? {});
@@ -93,7 +97,11 @@ export function createLocaleFormatter(locale: string, timeZone?: string): Locale
);
},
date(value, options = { dateStyle: "medium" }) {
const resolved = { ...options, ...(timeZone ? { timeZone } : {}) };
const resolved = {
...options,
...(timeZone ? { timeZone } : {}),
...(calendar ? { calendar } : {}),
};
const cacheKey = key(resolved);
let format = dateCache.get(cacheKey);
if (!format) {
@@ -108,18 +116,60 @@ export function createLocaleFormatter(locale: string, timeZone?: string): Locale
};
}
/** Lightweight plural templates: `{count, plural, one {# item} other {# items}}`. */
function complexExpression(template: string, start: number) {
const header = /^\{(\w+),\s*(plural|select),\s*/.exec(template.slice(start));
if (!header) return null;
let cursor = start + header[0].length;
const choices: Record<string, string> = {};
while (cursor < template.length) {
while (/\s/.test(template[cursor] ?? "")) cursor++;
if (template[cursor] === "}")
return { name: header[1]!, kind: header[2]!, choices, end: cursor + 1 };
const key = /^(=?[\w-]+)/.exec(template.slice(cursor));
if (!key) return null;
cursor += key[0].length;
while (/\s/.test(template[cursor] ?? "")) cursor++;
if (template[cursor] !== "{") return null;
const bodyStart = ++cursor;
let depth = 1;
while (cursor < template.length && depth) {
if (template[cursor] === "{") depth++;
else if (template[cursor] === "}") depth--;
cursor++;
}
if (depth) return null;
choices[key[0]] = template.slice(bodyStart, cursor - 1);
}
return null;
}
/** ICU-style plural/select templates with exact values and recursive interpolation. */
export function formatMessage(
template: string,
params: Record<string, string | number>,
locale: string,
): string {
const plural = /\{(\w+),\s*plural,\s*one\s*\{([^{}]*)\}\s*other\s*\{([^{}]*)\}\s*\}/g;
let result = template.replace(plural, (_match, name: string, one: string, other: string) => {
const value = Number(params[name] ?? 0);
const selected = new Intl.PluralRules(locale).select(value) === "one" ? one : other;
return selected.replace(/#/g, String(value));
});
let result = "";
for (let cursor = 0; cursor < template.length;) {
const expression = template[cursor] === "{" ? complexExpression(template, cursor) : null;
if (!expression) {
result += template[cursor++]!;
continue;
}
const raw = params[expression.name];
const selector = expression.kind === "plural" ? `=${Number(raw ?? 0)}` : String(raw ?? "other");
const category =
expression.kind === "plural"
? new Intl.PluralRules(locale).select(Number(raw ?? 0))
: selector;
const selected =
expression.choices[selector] ??
expression.choices[category] ??
expression.choices.other ??
"";
result += formatMessage(selected.replace(/#/g, String(raw ?? 0)), params, locale);
cursor = expression.end;
}
result = result.replace(/\{(\w+)\}/g, (_match, name: string) =>
name in params ? String(params[name]) : `{${name}}`,
);