Documentation
¶
Overview ¶
Package functions provides MessageFormat 2.0 function implementations TypeScript original code: functions/ module
Index ¶
- Constants
- Variables
- func CurrencyFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func DateFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func DatetimeFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func FallbackFunction(source string, locale string) messagevalue.MessageValue
- func GetFirstLocale(locales []string) string
- func IntegerFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func MathFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func NumberFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func OffsetFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func PercentFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func SanitizeOptions(options map[string]any) map[string]any
- func StringFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func TimeFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func UnitFunction(ctx MessageFunctionContext, options map[string]any, operand any) messagevalue.MessageValue
- func UnknownFunction(source string, input any, locale string) messagevalue.MessageValue
- func ValidateOptionKey(key string) error
- func ValidateOptions(options map[string]any) error
- type FunctionRegistry
- type MessageFunction
- type MessageFunctionContext
- func (ctx MessageFunctionContext) Dir() string
- func (ctx MessageFunctionContext) ID() string
- func (ctx MessageFunctionContext) LiteralOptionKeys() map[string]bool
- func (ctx MessageFunctionContext) LocaleMatcher() string
- func (ctx MessageFunctionContext) Locales() []string
- func (ctx MessageFunctionContext) OnError(err error)
- func (ctx MessageFunctionContext) Source() string
- type NumericInput
Constants ¶
const MaxOptionKeyLength = 100
MaxOptionKeyLength defines the maximum allowed length for an option key
const MaxOptionsCount = 50
MaxOptionsCount defines the maximum number of options allowed to prevent DoS
Variables ¶
var DefaultFunctions = map[string]MessageFunction{ "integer": IntegerFunction, "number": NumberFunction, "string": StringFunction, "offset": OffsetFunction, }
DefaultFunctions provides the built-in REQUIRED functions as defined in LDML 48 MessageFormat specification. Reference: https://www.unicode.org/reports/tr35/tr35-76/tr35-messageFormat.html#contents-of-part-9-messageformat
These functions are stable and covered by stability guarantees. They include: :integer, :number, :offset, and :string
TypeScript original code:
export let DefaultFunctions = {
integer,
number,
string
};
DefaultFunctions = Object.freeze(
Object.assign(Object.create(null), DefaultFunctions)
);
var DraftFunctions = map[string]MessageFunction{ "currency": CurrencyFunction, "date": DateFunction, "datetime": DatetimeFunction, "math": MathFunction, "percent": PercentFunction, "time": TimeFunction, "unit": UnitFunction, }
DraftFunctions provides functions classified as DRAFT by the LDML 48 MessageFormat specification. Reference: https://www.unicode.org/reports/tr35/tr35-76/tr35-messageFormat.html#contents-of-part-9-messageformat
These functions are liable to change and are NOT covered by stability guarantees.
Note: As of LDML 48, :currency and :percent have been finalized and are now stable. However, they remain in this collection for backward compatibility. The :unit function is still in DRAFT status.
TypeScript original code:
export let DraftFunctions = {
currency,
date,
datetime,
math,
time,
unit
};
DraftFunctions = Object.freeze(
Object.assign(Object.create(null), DraftFunctions)
);
var ErrNotBoolean = errors.New("not a boolean")
ErrNotBoolean indicates the value cannot be converted to a boolean.
var ErrNotPositiveInteger = errors.New("not a positive integer")
ErrNotPositiveInteger indicates the value cannot be converted to a non-negative integer.
var ErrNotString = errors.New("not a string")
ErrNotString indicates the value cannot be converted to a string.
var (
ErrNotValidJSONNumber = errors.New("not a valid JSON number")
)
Static errors to avoid dynamic error creation
Functions ¶
func CurrencyFunction ¶
func CurrencyFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
CurrencyFunction implements the :currency function for currency value formatting.
Status: Stable (finalized in LDML 48) Specification: https://unicode.org/reports/tr35/tr35-messageFormat.html#currency
The :currency function formats numeric values as currency. It requires either: - An operand containing both a numeric value and currency code - A numeric operand with a currency option
Example:
{$amount :currency currency=USD}
{$price :currency currency=EUR fractionDigits=2}
TypeScript original code: export function currency(
ctx: MessageFunctionContext,
exprOpt: Record<string | symbol, unknown>,
operand?: unknown
): MessageNumber {
const { source } = ctx;
const input = readNumericOperand(operand, source);
const options: MessageNumberOptions = Object.assign({}, input.options, {
localeMatcher: ctx.localeMatcher,
style: 'currency'
} as const);
for (const [name, optval] of Object.entries(exprOpt)) {
if (optval === undefined) continue;
try {
switch (name) {
case 'currency':
case 'currencySign':
case 'roundingMode':
case 'roundingPriority':
case 'trailingZeroDisplay':
case 'useGrouping':
options[name] = asString(optval);
break;
case 'minimumIntegerDigits':
case 'minimumSignificantDigits':
case 'maximumSignificantDigits':
case 'roundingIncrement':
options[name] = asPositiveInteger(optval);
break;
case 'currencyDisplay': {
const strval = asString(optval);
if (strval === 'never') {
ctx.onError(
new MessageResolutionError(
'unsupported-operation',
'Currency display "never" is not yet supported',
source
)
);
} else {
options[name] = strval;
}
break;
}
case 'fractionDigits': {
const strval = asString(optval);
if (strval === 'auto') {
options.minimumFractionDigits = undefined;
options.maximumFractionDigits = undefined;
} else {
const numval = asPositiveInteger(strval);
options.minimumFractionDigits = numval;
options.maximumFractionDigits = numval;
}
break;
}
}
} catch (error) {
if (error instanceof MessageError) {
ctx.onError(error);
} else {
const msg = `Value ${optval} is not valid for :currency option ${name}`;
ctx.onError(new MessageResolutionError('bad-option', msg, source));
}
}
}
if (!options.currency) {
const msg = 'A currency code is required for :currency';
throw new MessageResolutionError('bad-operand', msg, source);
}
return getMessageNumber(ctx, input.value, options, false);
}
func DateFunction ¶
func DateFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
DateFunction implements the :date function Formats only the date portion TypeScript reference: datetime.ts:66-70
func DatetimeFunction ¶
func DatetimeFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
DatetimeFunction implements the :datetime function Formats both date and time portions TypeScript reference: datetime.ts:55-59
func FallbackFunction ¶ added in v0.2.0
func FallbackFunction(source string, locale string) messagevalue.MessageValue
FallbackFunction creates a fallback value for runtime/formatting errors
TypeScript original code:
export const fallback = (source: string = '�'): MessageFallback => ({
type: 'fallback',
source,
toParts: () => [{ type: 'fallback', source }],
toString: () => `{${source}}`
});
func GetFirstLocale ¶ added in v0.4.18
GetFirstLocale returns the first locale from a list, or "en" as fallback
func IntegerFunction ¶
func IntegerFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
IntegerFunction implements the :integer function for integer number formatting TypeScript original code: export function integer(
ctx: MessageFunctionContext,
exprOpt: Record<string, unknown>,
operand?: unknown
) {
const input = readNumericOperand(operand, ctx.source);
const value = Number.isFinite(input.value)
? Math.round(input.value as number)
: input.value;
const options: MessageNumberOptions = Object.assign({}, input.options, {
localeMatcher: ctx.localeMatcher,
maximumFractionDigits: 0
} as const);
for (const [name, optval] of Object.entries(exprOpt)) {
if (optval === undefined) continue;
try {
switch (name) {
case 'minimumIntegerDigits':
case 'maximumSignificantDigits':
options[name] = asPositiveInteger(optval);
break;
case 'select':
case 'signDisplay':
case 'useGrouping':
options[name] = asString(optval);
}
} catch {
const msg = `Value ${optval} is not valid for :integer option ${name}`;
ctx.onError(new MessageResolutionError('bad-option', msg, ctx.source));
}
}
return getMessageNumber(ctx, value, options, true);
}
func MathFunction ¶
func MathFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
MathFunction implements the :math function (DRAFT) math accepts a numeric value as input and adds or subtracts an integer value from it
TypeScript original code: export function math(
ctx: MessageFunctionContext,
exprOpt: Record<string | symbol, unknown>,
operand?: unknown
): MessageNumber {
const { source } = ctx;
let { value, options } = readNumericOperand(operand, source);
let add: number;
let sub: number;
try {
add = 'add' in exprOpt ? asPositiveInteger(exprOpt.add) : -1;
sub = 'subtract' in exprOpt ? asPositiveInteger(exprOpt.subtract) : -1;
} catch (error) {
throw new MessageResolutionError('bad-option', String(error), source);
}
if (add < 0 === sub < 0) {
const msg =
'Exactly one of "add" or "subtract" is required as a :math option';
throw new MessageResolutionError('bad-option', msg, source);
}
const delta = add < 0 ? -sub : add;
if (typeof value === 'number') value += delta;
else value += BigInt(delta);
return number(ctx, {}, { valueOf: () => value, options });
}
func NumberFunction ¶
func NumberFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
NumberFunction implements the :number function for numeric value formatting and selection.
Status: Stable (REQUIRED in LDML 48) Specification: https://www.unicode.org/reports/tr35/tr35-76/tr35-messageFormat.html#the-number-function
The :number function is a selector and formatter for numeric values.
Operand Requirements: - Must be a number, BigInt, or string representing a JSON number - Can be an object with valueOf() method and optional options map
Supported Options: - select: 'exact' | 'plural' | 'ordinal' (controls selection behavior) - minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits (digit size options) - minimumSignificantDigits, maximumSignificantDigits (digit size options) - roundingMode, roundingPriority, roundingIncrement (rounding control) - signDisplay, useGrouping, trailingZeroDisplay (formatting control)
Selection Behavior: - Exact numeric match is preferred over plural category - Supports plural rules (zero, one, two, few, many, other) - The select option must be set by a literal value, not a variable
TypeScript Reference: .reference/messageformat/mf2/messageformat/src/functions/number.ts
TypeScript original code: export function number(
ctx: MessageFunctionContext,
exprOpt: Record<string, unknown>,
operand?: unknown
): MessageNumber {
const input = readNumericOperand(operand, ctx.source);
const value = input.value;
const options: MessageNumberOptions = Object.assign({}, input.options, {
localeMatcher: ctx.localeMatcher,
style: 'decimal'
} as const);
for (const [name, optval] of Object.entries(exprOpt)) {
if (optval === undefined) continue;
try {
switch (name) {
case 'minimumIntegerDigits':
case 'minimumFractionDigits':
case 'maximumFractionDigits':
case 'minimumSignificantDigits':
case 'maximumSignificantDigits':
case 'roundingIncrement':
options[name] = asPositiveInteger(optval);
break;
case 'roundingMode':
case 'roundingPriority':
case 'select':
case 'signDisplay':
case 'trailingZeroDisplay':
case 'useGrouping':
options[name] = asString(optval);
}
} catch {
const msg = `Value ${optval} is not valid for :number option ${name}`;
ctx.onError(new MessageResolutionError('bad-option', msg, ctx.source));
}
}
return getMessageNumber(ctx, value, options, true);
}
func OffsetFunction ¶ added in v0.3.6
func OffsetFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
OffsetFunction accepts a numeric value as input and adds or subtracts an integer value from it TypeScript original code: export function offset(
ctx: MessageFunctionContext,
exprOpt: Record<string | symbol, unknown>,
operand?: unknown
): MessageNumber {
let { value, options } = readNumericOperand(operand);
let add: number;
try {
add = 'add' in exprOpt ? asPositiveInteger(exprOpt.add) : -1;
} catch {
throw new MessageFunctionError(
'bad-option',
`Value ${exprOpt.add} is not valid for :offset option add`
);
}
let sub: number;
try {
sub = 'subtract' in exprOpt ? asPositiveInteger(exprOpt.subtract) : -1;
} catch {
throw new MessageFunctionError(
'bad-option',
`Value ${exprOpt.subtract} is not valid for :offset option subtract`
);
}
if (add < 0 === sub < 0) {
const msg =
'Exactly one of "add" or "subtract" is required as an :offset option';
throw new MessageFunctionError('bad-option', msg);
}
const delta = add < 0 ? -sub : add;
if (typeof value === 'number') value += delta;
else value += BigInt(delta);
return number(ctx, {}, { valueOf: () => value, options });
}
func PercentFunction ¶ added in v0.3.6
func PercentFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
PercentFunction implements the :percent function for percent value formatting.
Status: Stable (finalized in LDML 48) Specification: https://unicode.org/reports/tr35/tr35-messageFormat.html#percent
Important behavior: - When formatting or selecting, the numeric value is multiplied by 100 - The resolved value retains the original value (not multiplied) - Selection uses the multiplied value (e.g., 0.01 matches key "1", 1 matches key "100")
Example:
{0.5 :percent} // Formats as "50%"
.match {$rate :percent}
100 {{Full rate}} // Matches when $rate is 1.0
TypeScript original code: export function percent(
ctx: MessageFunctionContext,
exprOpt: Record<string | symbol, unknown>,
operand?: unknown
): MessageNumber {
const input = readNumericOperand(operand);
const options: MessageNumberOptions = Object.assign({}, input.options, {
localeMatcher: ctx.localeMatcher,
style: 'percent'
} as const);
for (const [name, optval] of Object.entries(exprOpt)) {
if (optval === undefined) continue;
try {
switch (name) {
case 'roundingMode':
case 'roundingPriority':
case 'signDisplay':
case 'trailingZeroDisplay':
case 'useGrouping':
// @ts-expect-error Let Intl.NumberFormat construction fail
options[name] = asString(optval);
break;
case 'minimumFractionDigits':
case 'maximumFractionDigits':
case 'minimumSignificantDigits':
case 'maximumSignificantDigits':
options[name] = asPositiveInteger(optval);
break;
}
} catch {
ctx.onError(
'bad-option',
`Value ${optval} is not valid for :percent option ${name}`
);
}
}
return getMessageNumber(ctx, input.value, options, true);
}
func SanitizeOptions ¶ added in v0.3.6
SanitizeOptions creates a sanitized copy of options map, filtering out invalid keys. This is useful when you want to be permissive but still protect against malicious input.
Returns a new map containing only valid options.
func StringFunction ¶
func StringFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
StringFunction implements the :string function TypeScript original code: export function string(
ctx: Pick<MessageFunctionContext, 'dir' | 'locales' | 'source'>,
_options: Record<string, unknown>,
operand?: unknown
): MessageString {
const str = operand === undefined ? '' : String(operand);
const selStr = str.normalize();
return {
type: 'string',
source: ctx.source,
dir: ctx.dir ?? 'auto',
selectKey: keys => (keys.has(selStr) ? selStr : null),
toParts() {
const { dir } = ctx;
const locale = ctx.locales[0];
return dir === 'ltr' || dir === 'rtl'
? [{ type: 'string', dir, locale, value: str }]
: [{ type: 'string', locale, value: str }];
},
toString: () => str,
valueOf: () => str
};
}
func TimeFunction ¶
func TimeFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
TimeFunction implements the :time function Formats only the time portion TypeScript reference: datetime.ts:78-82
func UnitFunction ¶
func UnitFunction( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
UnitFunction implements the :unit function (DRAFT) unit accepts as input numerical values as well as objects wrapping a numerical value that also include a unit property.
TypeScript original code: export function unit(
ctx: MessageFunctionContext,
exprOpt: Record<string | symbol, unknown>,
operand?: unknown
): MessageNumber {
const input = readNumericOperand(operand, ctx.source);
const options: MessageNumberOptions = Object.assign({}, input.options, {
localeMatcher: ctx.localeMatcher,
style: 'unit'
} as const);
for (const [name, optval] of Object.entries(exprOpt)) {
if (optval === undefined) continue;
try {
switch (name) {
case 'signDisplay':
case 'roundingMode':
case 'roundingPriority':
case 'trailingZeroDisplay':
case 'unit':
case 'unitDisplay':
case 'useGrouping':
options[name] = asString(optval);
break;
case 'minimumIntegerDigits':
case 'minimumFractionDigits':
case 'maximumFractionDigits':
case 'minimumSignificantDigits':
case 'maximumSignificantDigits':
case 'roundingIncrement':
options[name] = asPositiveInteger(optval);
break;
}
} catch (error) {
if (error instanceof MessageError) {
ctx.onError(error);
} else {
const msg = `Value ${optval} is not valid for :currency option ${name}`;
ctx.onError(new MessageResolutionError('bad-option', msg, ctx.source));
}
}
}
if (!options.unit) {
const msg = 'A unit identifier is required for :unit';
throw new MessageResolutionError('bad-operand', msg, ctx.source);
}
return getMessageNumber(ctx, input.value, options, false);
}
func UnknownFunction ¶ added in v0.2.0
func UnknownFunction(source string, input any, locale string) messagevalue.MessageValue
UnknownFunction creates an unknown value for unrecognized input
TypeScript original code: export const unknown = (
source: string,
input: unknown
): MessageUnknownValue => ({
type: 'unknown',
source,
dir: 'auto',
toParts: () => [{ type: 'unknown', value: input }],
toString: () => String(input),
valueOf: () => input
});
func ValidateOptionKey ¶ added in v0.3.6
ValidateOptionKey validates an option key name to prevent security issues. This function prevents potential injection attacks or malformed keys.
Security checks performed: 1. Key length validation (max 100 characters) 2. Character whitelist (alphanumeric, underscore, hyphen only) 3. Forbidden key names (dangerous JavaScript-like keys)
Reference: Inspired by TypeScript fix for prototype pollution (commit 82cd10b4) https://github.com/messageformat/messageformat/commit/82cd10b40e3f922f990bbcf88a6d14b70c0a3ce0
func ValidateOptions ¶ added in v0.3.6
ValidateOptions validates an entire options map. This prevents DoS attacks through excessive options and validates all keys.
Reference: Based on security best practices from TypeScript implementation
Types ¶
type FunctionRegistry ¶
type FunctionRegistry struct {
// contains filtered or unexported fields
}
FunctionRegistry manages function registration and lookup
func NewFunctionRegistry ¶
func NewFunctionRegistry() *FunctionRegistry
NewFunctionRegistry creates a new function registry
func NewFunctionRegistryWithDraft ¶
func NewFunctionRegistryWithDraft() *FunctionRegistry
NewFunctionRegistryWithDraft creates a new function registry including draft functions
func (*FunctionRegistry) Clone ¶
func (fr *FunctionRegistry) Clone() *FunctionRegistry
Clone creates a copy of the registry
func (*FunctionRegistry) Get ¶
func (fr *FunctionRegistry) Get(name string) (MessageFunction, bool)
Get retrieves a function from the registry
func (*FunctionRegistry) List ¶
func (fr *FunctionRegistry) List() []string
List returns all registered function names
func (*FunctionRegistry) Merge ¶
func (fr *FunctionRegistry) Merge(other *FunctionRegistry)
Merge merges another registry into this one
func (*FunctionRegistry) Register ¶
func (fr *FunctionRegistry) Register(name string, fn MessageFunction)
Register adds a function to the registry
type MessageFunction ¶
type MessageFunction func( ctx MessageFunctionContext, options map[string]any, operand any, ) messagevalue.MessageValue
MessageFunction represents a function that can be called within a message TypeScript original code: export type MessageFunction = (
ctx: MessageFunctionContext, options: Record<string, unknown>, operand?: unknown
) => MessageValue;
type MessageFunctionContext ¶
type MessageFunctionContext struct {
// contains filtered or unexported fields
}
MessageFunctionContext provides context for function execution TypeScript original code:
export class MessageFunctionContext {
readonly dir: 'ltr' | 'rtl' | 'auto' | undefined;
readonly id: string | undefined;
readonly source: string;
get literalOptionKeys(): Set<string>;
get localeMatcher(): string;
get locales(): string[];
get onError(): (error: Error) => void;
}
func NewMessageFunctionContext ¶
func NewMessageFunctionContext( locales []string, source string, localeMatcher string, onError func(error), literalOptionKeys map[string]bool, dir string, id string, ) MessageFunctionContext
NewMessageFunctionContext creates a new function context
func (MessageFunctionContext) Dir ¶
func (ctx MessageFunctionContext) Dir() string
Dir returns the text direction override
func (MessageFunctionContext) ID ¶
func (ctx MessageFunctionContext) ID() string
ID returns the unique identifier
func (MessageFunctionContext) LiteralOptionKeys ¶
func (ctx MessageFunctionContext) LiteralOptionKeys() map[string]bool
LiteralOptionKeys returns the set of literal option keys
func (MessageFunctionContext) LocaleMatcher ¶
func (ctx MessageFunctionContext) LocaleMatcher() string
LocaleMatcher returns the locale matcher strategy
func (MessageFunctionContext) Locales ¶
func (ctx MessageFunctionContext) Locales() []string
Locales returns the available locales
func (MessageFunctionContext) OnError ¶
func (ctx MessageFunctionContext) OnError(err error)
OnError calls the error handler
func (MessageFunctionContext) Source ¶
func (ctx MessageFunctionContext) Source() string
Source returns the source string
type NumericInput ¶
NumericInput represents parsed numeric input with value and options TypeScript original code: { value: number | bigint; options: unknown }