@@ -39,30 +39,15 @@ export interface MessageNumberPart extends MessageExpressionPart {
3939
4040const INT = Symbol ( 'INT' ) ;
4141
42- /**
43- * `number` accepts a number, BigInt or string representing a JSON number as input
44- * and formats it with the same options as
45- * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat | Intl.NumberFormat}.
46- * It also supports plural category selection via
47- * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules | Intl.PluralRules}.
48- *
49- * @beta
50- */
51- export function number (
52- ctx : MessageFunctionContext ,
53- options : Record < string | symbol , unknown > ,
54- input ?: unknown
55- ) : MessageNumber {
56- const { source } = ctx ;
57- const opt : Intl . NumberFormatOptions &
58- Intl . PluralRulesOptions & { select ?: 'exact' | 'cardinal' | 'ordinal' } = {
59- localeMatcher : ctx . localeMatcher
60- } ;
61- let value = input ;
42+ export function readNumericOperand (
43+ value : unknown ,
44+ source : string
45+ ) : { value : number | bigint ; options : unknown } {
46+ let options : unknown = undefined ;
6247 if ( typeof value === 'object' ) {
6348 const valueOf = value ?. valueOf ;
6449 if ( typeof valueOf === 'function' ) {
65- Object . assign ( opt , ( value as { options : unknown } ) . options ) ;
50+ options = ( value as { options : unknown } ) . options ;
6651 value = valueOf . call ( value ) ;
6752 }
6853 }
@@ -77,7 +62,33 @@ export function number(
7762 const msg = 'Input is not numeric' ;
7863 throw new MessageResolutionError ( 'bad-operand' , msg , source ) ;
7964 }
80- for ( const [ name , optval ] of Object . entries ( options ) ) {
65+ return { value, options } ;
66+ }
67+
68+ /**
69+ * `number` accepts a number, BigInt or string representing a JSON number as input
70+ * and formats it with the same options as
71+ * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat | Intl.NumberFormat}.
72+ * It also supports plural category selection via
73+ * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules | Intl.PluralRules}.
74+ *
75+ * @beta
76+ */
77+ export function number (
78+ ctx : MessageFunctionContext ,
79+ exprOpt : Record < string | symbol , unknown > ,
80+ operand ?: unknown
81+ ) : MessageNumber {
82+ const { source } = ctx ;
83+ const options : Intl . NumberFormatOptions &
84+ Intl . PluralRulesOptions & { select ?: 'exact' | 'cardinal' | 'ordinal' } = {
85+ localeMatcher : ctx . localeMatcher
86+ } ;
87+ const input = readNumericOperand ( operand , source ) ;
88+ const value = input . value ;
89+ Object . assign ( options , input . options ) ;
90+
91+ for ( const [ name , optval ] of Object . entries ( exprOpt ) ) {
8192 if ( optval === undefined ) continue ;
8293 try {
8394 switch ( name ) {
@@ -91,17 +102,17 @@ export function number(
91102 case 'maximumSignificantDigits' :
92103 case 'roundingIncrement' :
93104 // @ts -expect-error TS types don't know about roundingIncrement
94- opt [ name ] = asPositiveInteger ( optval ) ;
105+ options [ name ] = asPositiveInteger ( optval ) ;
95106 break ;
96107 case 'useGrouping' : {
97108 const strval = asString ( optval ) ;
98109 // @ts -expect-error TS type is wrong
99- opt [ name ] = strval === 'never' ? false : strval ;
110+ options [ name ] = strval === 'never' ? false : strval ;
100111 break ;
101112 }
102113 default :
103114 // @ts -expect-error Unknown options will be ignored
104- opt [ name ] = asString ( optval ) ;
115+ options [ name ] = asString ( optval ) ;
105116 }
106117 } catch {
107118 const msg = `Value ${ optval } is not valid for :number option ${ name } ` ;
@@ -110,11 +121,11 @@ export function number(
110121 }
111122
112123 const num =
113- Number . isFinite ( value ) && options [ INT ]
124+ Number . isFinite ( value ) && exprOpt [ INT ]
114125 ? Math . round ( value as number )
115126 : value ;
116127
117- const lc = mergeLocales ( ctx . locales , input , options ) ;
128+ const lc = mergeLocales ( ctx . locales , operand , exprOpt ) ;
118129 let locale : string | undefined ;
119130 let dir = ctx . dir ;
120131 let nf : Intl . NumberFormat | undefined ;
@@ -128,24 +139,24 @@ export function number(
128139 return dir ;
129140 } ,
130141 get locale ( ) {
131- return ( locale ??= Intl . NumberFormat . supportedLocalesOf ( lc , opt ) [ 0 ] ) ;
142+ return ( locale ??= Intl . NumberFormat . supportedLocalesOf ( lc , options ) [ 0 ] ) ;
132143 } ,
133144 get options ( ) {
134- return { ...opt } ;
145+ return { ...options } ;
135146 } ,
136147 selectKey ( keys ) {
137148 const str = String ( num ) ;
138149 if ( keys . has ( str ) ) return str ;
139- if ( opt . select === 'exact' ) return null ;
140- const pluralOpt = opt . select
141- ? { ...opt , select : undefined , type : opt . select }
142- : opt ;
150+ if ( options . select === 'exact' ) return null ;
151+ const pluralOpt = options . select
152+ ? { ...options , select : undefined , type : options . select }
153+ : options ;
143154 // Intl.PluralRules needs a number, not bigint
144155 cat ??= new Intl . PluralRules ( lc , pluralOpt ) . select ( Number ( num ) ) ;
145156 return keys . has ( cat ) ? cat : null ;
146157 } ,
147158 toParts ( ) {
148- nf ??= new Intl . NumberFormat ( lc , opt ) ;
159+ nf ??= new Intl . NumberFormat ( lc , options ) ;
149160 const parts = nf . formatToParts ( num ) ;
150161 locale ??= nf . resolvedOptions ( ) . locale ;
151162 dir ??= getLocaleDir ( locale ) ;
@@ -154,7 +165,7 @@ export function number(
154165 : [ { type : 'number' , source, locale, parts } ] ;
155166 } ,
156167 toString ( ) {
157- nf ??= new Intl . NumberFormat ( lc , opt ) ;
168+ nf ??= new Intl . NumberFormat ( lc , options ) ;
158169 str ??= nf . format ( num ) ;
159170 return str ;
160171 } ,
0 commit comments