@@ -46,7 +46,11 @@ function toNumber(value: unknown) {
4646}
4747
4848export type Category = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
49- export type Selector = ( n : number | string , ord ?: boolean ) => Category
49+ export type Selector = (
50+ n : number | string ,
51+ ord ?: boolean ,
52+ compact ?: number
53+ ) => Category
5054export type RangeSelector = ( start : Category , end : Category ) => Category
5155export type PluralRuleType = 'cardinal' | 'ordinal'
5256
@@ -96,14 +100,8 @@ export type PluralRulesOptions = Partial<
96100>
97101
98102function readOptions ( opt : PluralRulesOptions | undefined ) {
99- if ( ! opt )
100- return {
101- type : 'cardinal' ,
102- compactDisplay : 'short' ,
103- nfOpt : undefined
104- } as const
105-
106103 const get = < T extends string > ( name : string , values : T [ ] ) : T => {
104+ if ( ! opt ) return values [ 0 ]
107105 const val = Object . prototype . hasOwnProperty . call ( opt , name )
108106 ? ( opt as Record < string , unknown > ) [ name ]
109107 : undefined
@@ -131,7 +129,7 @@ function readOptions(opt: PluralRulesOptions | undefined) {
131129 minimumSignificantDigits,
132130 maximumSignificantDigits,
133131 roundingIncrement
134- } = opt
132+ } = opt ?? { }
135133 const roundingMode = get ( 'roundingMode' , [
136134 'halfExpand' ,
137135 'ceil' ,
@@ -154,12 +152,12 @@ function readOptions(opt: PluralRulesOptions | undefined) {
154152 ] )
155153
156154 return {
157- type,
158- compactDisplay,
155+ prOpt :
156+ notation === 'compact'
157+ ? { type, notation, compactDisplay }
158+ : { type, notation } ,
159159 nfOpt : {
160160 localeMatcher,
161- notation,
162- compactDisplay : 'short' ,
163161 minimumIntegerDigits,
164162 minimumFractionDigits,
165163 maximumFractionDigits,
@@ -168,11 +166,14 @@ function readOptions(opt: PluralRulesOptions | undefined) {
168166 roundingIncrement,
169167 roundingMode,
170168 roundingPriority,
171- trailingZeroDisplay
169+ trailingZeroDisplay,
170+ useGrouping : false
172171 }
173172 } as const
174173}
175174
175+ const compactExponents : Record < string , number > = { K : 3 , M : 6 , B : 9 , T : 12 }
176+
176177export interface PluralRules {
177178 resolvedOptions ( ) : ResolvedPluralRulesOptions
178179 select ( n : number | string ) : Category
@@ -224,9 +225,14 @@ export default function getPluralRules(
224225 #locale: string
225226 #range: RangeSelector
226227 #select: Selector
227- #type: 'cardinal' | 'ordinal'
228- #compactDisplay: 'short' | 'long'
228+ #isOrdinal: boolean
229+ #opt: {
230+ compactDisplay ?: 'short' | 'long'
231+ notation : 'standard' | 'scientific' | 'engineering' | 'compact'
232+ type : 'ordinal' | 'cardinal'
233+ }
229234 #nf: Intl . NumberFormat
235+ #nfCompact?: Intl . NumberFormat
230236
231237 constructor (
232238 locales : string | readonly string [ ] | undefined = undefined ,
@@ -237,10 +243,17 @@ export default function getPluralRules(
237243 if ( ! this . #select)
238244 throw new Error ( `Selector not found for locale: ${ this . #locale} ` )
239245 this . #range = getRangeSelector ( this . #locale)
240- const res = readOptions ( opt )
241- this . #nf = new NumberFormat ( 'en' , res . nfOpt ) // make-plural expects latin digits with . decimal separator
242- this . #type = res . type
243- this . #compactDisplay = res . compactDisplay
246+ const { prOpt, nfOpt } = readOptions ( opt )
247+ this . #isOrdinal = prOpt . type === 'ordinal'
248+ this . #opt = prOpt
249+ this . #nf = new NumberFormat ( 'en' , nfOpt ) // make-plural expects latin digits with . decimal separator
250+ this . #nfCompact =
251+ prOpt . notation === 'compact'
252+ ? new NumberFormat ( 'en' , {
253+ ...nfOpt ,
254+ notation : 'compact'
255+ } )
256+ : undefined
244257 }
245258
246259 resolvedOptions ( ) : ResolvedPluralRulesOptions {
@@ -250,23 +263,21 @@ export default function getPluralRules(
250263 maximumFractionDigits,
251264 minimumSignificantDigits,
252265 maximumSignificantDigits,
253- notation,
254266 roundingIncrement,
255267 roundingMode,
256268 roundingPriority,
257269 trailingZeroDisplay
258270 } = this . #nf. resolvedOptions ( )
259271 const locale = this . #locale
260- const type = this . #type
261272 return Object . assign (
262- { locale, type , notation } ,
263- notation === 'compact' && { compactDisplay : this . #compactDisplay } ,
273+ { locale } ,
274+ this . #opt ,
264275 { minimumIntegerDigits } ,
265276 typeof minimumSignificantDigits === 'number'
266277 ? { minimumSignificantDigits, maximumSignificantDigits }
267278 : { minimumFractionDigits, maximumFractionDigits } ,
268279 {
269- pluralCategories : getCategories ( locale , type === 'ordinal' ) . slice ( 0 ) ,
280+ pluralCategories : getCategories ( locale , this . #isOrdinal ) . slice ( 0 ) ,
270281 roundingIncrement : roundingIncrement ?? 1 ,
271282 roundingMode : roundingMode ?? 'halfExpand' ,
272283 roundingPriority : roundingPriority ?? 'auto' ,
@@ -280,11 +291,18 @@ export default function getPluralRules(
280291 throw new TypeError ( `select() called on incompatible ${ this } ` )
281292 if ( typeof number !== 'number' ) number = Number ( number )
282293 if ( ! isFinite ( number ) ) return 'other'
283- let fmt = ''
284- for ( const part of this . #nf. formatToParts ( Math . abs ( number ) ) ) {
285- fmt += part . value
294+ let compact = 0
295+ if ( this . #nfCompact) {
296+ for ( const part of this . #nfCompact. formatToParts ( Math . abs ( number ) ) ) {
297+ if ( part . type === 'compact' ) {
298+ compact = compactExponents [ part . value ]
299+ if ( ! compact )
300+ throw new RangeError ( `Unsupported compact key: ${ part . value } ` )
301+ }
302+ }
286303 }
287- return this . #select( fmt , this . #type === 'ordinal' )
304+ const fmt = this . #nf. format ( Math . abs ( number ) )
305+ return this . #select( fmt , this . #isOrdinal, compact )
288306 }
289307
290308 selectRange ( start : number | string , end : number | string ) : Category {
0 commit comments