Skip to content

Commit

Permalink
Fix inconsistent number formatting between mobile and web (#6384)
Browse files Browse the repository at this point in the history
* Manual truncation & identify factor points for each lang

* Reduce indirection

* Add test

Co-authored-by: khuddite <[email protected]>

* Handle big numbers, clarify special case

* Clarify the reason

---------

Co-authored-by: Dan Abramov <[email protected]>
  • Loading branch information
khuddite and gaearon authored Nov 23, 2024
1 parent 4dd62e2 commit e428474
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 6 deletions.
92 changes: 92 additions & 0 deletions src/view/com/util/numeric/__tests__/format-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {describe, expect, it} from '@jest/globals'

import {APP_LANGUAGES} from '#/locale/languages'
import {formatCount} from '../format'

const formatCountRound = (locale: string, num: number) => {
const options: Intl.NumberFormatOptions = {
notation: 'compact',
maximumFractionDigits: 1,
}
return new Intl.NumberFormat(locale, options).format(num)
}

const formatCountTrunc = (locale: string, num: number) => {
const options: Intl.NumberFormatOptions = {
notation: 'compact',
maximumFractionDigits: 1,
// @ts-ignore
roundingMode: 'trunc',
}
return new Intl.NumberFormat(locale, options).format(num)
}

// prettier-ignore
const testNums = [
1,
5,
9,
11,
55,
99,
111,
555,
999,
1111,
5555,
9999,
11111,
55555,
99999,
111111,
555555,
999999,
1111111,
5555555,
9999999,
11111111,
55555555,
99999999,
111111111,
555555555,
999999999,
1111111111,
5555555555,
9999999999,
11111111111,
55555555555,
99999999999,
111111111111,
555555555555,
999999999999,
1111111111111,
5555555555555,
9999999999999,
11111111111111,
55555555555555,
99999999999999,
111111111111111,
555555555555555,
999999999999999,
1111111111111111,
5555555555555555,
]

describe('formatCount', () => {
for (const appLanguage of APP_LANGUAGES) {
const locale = appLanguage.code2
it('truncates for ' + locale, () => {
const mockI8nn = {
locale,
number(num: number) {
return formatCountRound(locale, num)
},
}
for (const num of testNums) {
const formatManual = formatCount(mockI8nn as any, num)
const formatOriginal = formatCountTrunc(locale, num)
expect(formatManual).toEqual(formatOriginal)
}
})
}
})
47 changes: 41 additions & 6 deletions src/view/com/util/numeric/format.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
import type {I18n} from '@lingui/core'
import {I18n} from '@lingui/core'

const truncateRounding = (num: number, factors: Array<number>): number => {
for (let i = factors.length - 1; i >= 0; i--) {
let factor = factors[i]
if (num >= 10 ** factor) {
if (factor === 10) {
// CA and ES abruptly jump from "9999,9 M" to "10 mil M"
factor--
}
const precision = 1
const divisor = 10 ** (factor - precision)
return Math.floor(num / divisor) * divisor
}
}
return num
}

const koFactors = [3, 4, 8, 12]
const hiFactors = [3, 5, 7, 9, 11, 13]
const esCaFactors = [3, 6, 10, 12]
const itDeFactors = [6, 9, 12]
const jaZhFactors = [4, 8, 12]
const restFactors = [3, 6, 9, 12]

export const formatCount = (i18n: I18n, num: number) => {
return i18n.number(num, {
const locale = i18n.locale
let truncatedNum: number
if (locale === 'hi') {
truncatedNum = truncateRounding(num, hiFactors)
} else if (locale === 'ko') {
truncatedNum = truncateRounding(num, koFactors)
} else if (locale === 'es' || locale === 'ca') {
truncatedNum = truncateRounding(num, esCaFactors)
} else if (locale === 'ja' || locale === 'zh-CN' || locale === 'zh-TW') {
truncatedNum = truncateRounding(num, jaZhFactors)
} else if (locale === 'it' || locale === 'de') {
truncatedNum = truncateRounding(num, itDeFactors)
} else {
truncatedNum = truncateRounding(num, restFactors)
}
return i18n.number(truncatedNum, {
notation: 'compact',
maximumFractionDigits: 1,
// `1,953` shouldn't be rounded up to 2k, it should be truncated.
// @ts-expect-error: `roundingMode` doesn't seem to be in the typings yet
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#roundingmode
roundingMode: 'trunc',
// Ideally we'd use roundingMode: 'trunc' but it isn't supported on RN.
})
}

0 comments on commit e428474

Please sign in to comment.