feat: add internationalization message platform
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { normalizeLocale, type SupportedLocale } from "./message-contract.js";
|
||||
|
||||
const FORMAT_FALLBACK = "—";
|
||||
|
||||
export type DateFormatStyle = "short" | "medium" | "long";
|
||||
|
||||
export function formatDate(
|
||||
locale: SupportedLocale,
|
||||
value: Date | number,
|
||||
options: Readonly<{
|
||||
dateStyle?: DateFormatStyle;
|
||||
timeZone?: string;
|
||||
}> = {},
|
||||
): string {
|
||||
try {
|
||||
const date = value instanceof Date ? value : new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return FORMAT_FALLBACK;
|
||||
return new Intl.DateTimeFormat(normalizeLocale(locale), {
|
||||
dateStyle: options.dateStyle ?? "medium",
|
||||
timeZone: options.timeZone ?? "UTC",
|
||||
}).format(date);
|
||||
} catch {
|
||||
return FORMAT_FALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatNumber(
|
||||
locale: SupportedLocale,
|
||||
value: number,
|
||||
options: Readonly<{
|
||||
style?: "decimal" | "percent";
|
||||
maximumFractionDigits?: number;
|
||||
}> = {},
|
||||
): string {
|
||||
try {
|
||||
if (!Number.isFinite(value)) return FORMAT_FALLBACK;
|
||||
return new Intl.NumberFormat(normalizeLocale(locale), options).format(value);
|
||||
} catch {
|
||||
return FORMAT_FALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatRelativeTime(
|
||||
locale: SupportedLocale,
|
||||
value: number,
|
||||
unit: Intl.RelativeTimeFormatUnit,
|
||||
): string {
|
||||
try {
|
||||
if (!Number.isFinite(value)) return FORMAT_FALLBACK;
|
||||
return new Intl.RelativeTimeFormat(normalizeLocale(locale), {
|
||||
numeric: "auto",
|
||||
}).format(value, unit);
|
||||
} catch {
|
||||
return FORMAT_FALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatList(
|
||||
locale: SupportedLocale,
|
||||
values: readonly string[],
|
||||
): string {
|
||||
try {
|
||||
return new Intl.ListFormat(normalizeLocale(locale), {
|
||||
style: "long",
|
||||
type: "conjunction",
|
||||
}).format(values);
|
||||
} catch {
|
||||
return values.join(", ");
|
||||
}
|
||||
}
|
||||
|
||||
export function selectPlural(
|
||||
locale: SupportedLocale,
|
||||
value: number,
|
||||
choices: Readonly<
|
||||
Partial<Record<Intl.LDMLPluralRule, string>> & { other: string }
|
||||
>,
|
||||
): string {
|
||||
try {
|
||||
if (!Number.isFinite(value)) return choices.other;
|
||||
return choices[
|
||||
new Intl.PluralRules(normalizeLocale(locale)).select(value)
|
||||
] ?? choices.other;
|
||||
} catch {
|
||||
return choices.other;
|
||||
}
|
||||
}
|
||||
|
||||
export function selectMessage(
|
||||
value: string,
|
||||
choices: Readonly<Record<string, string> & { other: string }>,
|
||||
): string {
|
||||
return choices[value] ?? choices.other;
|
||||
}
|
||||
|
||||
export { FORMAT_FALLBACK };
|
||||
Reference in New Issue
Block a user