├── .prettierignore ├── .npmignore ├── src ├── index.ts ├── math-utils.ts ├── format-utils.ts └── format.ts ├── .gitignore ├── .travis.yml ├── tslint.json ├── tsconfig.json ├── rollup.config.ts ├── priv ├── zh.js └── es.js ├── package.json ├── locales └── en.ts ├── README.md └── test ├── format.test.ts └── locale-data.ts /.prettierignore: -------------------------------------------------------------------------------- 1 | test/locale-data.ts -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .rpt2_cache/* 2 | src 3 | tsconfig.json 4 | rollup.config.ts 5 | tslint.json 6 | .travis.yml 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { compactFormat } from './format'; 2 | 3 | import { compactFormat } from './format'; 4 | export default compactFormat; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # docs 2 | docs/* 3 | 4 | # typescript 5 | dist 6 | 7 | # dependencies 8 | /node_modules 9 | .rpt2_cache/* 10 | 11 | # misc 12 | /connect.lock 13 | /coverage/* 14 | npm-debug.log* 15 | yarn-error.log 16 | .vscode 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "8" 5 | - "10" 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | notifications: 16 | email: false 17 | 18 | script: 19 | - npm run test:ci 20 | 21 | after_success: 22 | - npm run report-coverage 23 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended", 5 | "tslint-eslint-rules", 6 | "tslint-config-prettier" 7 | ], 8 | "linterOptions": { 9 | "exclude": [ 10 | "test/locale-data.ts" 11 | ] 12 | }, 13 | "jsRules": {}, 14 | "rules": { 15 | "prettier": [ 16 | true, 17 | { 18 | "singleQuote": true 19 | } 20 | ] 21 | }, 22 | "rulesDirectory": [ 23 | "tslint-plugin-prettier" 24 | ] 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 5 | "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 7 | "declarationDir": "dist/types", 8 | "stripInternal": true, 9 | "sourceMap": true, /* Generates corresponding '.map' file. */ 10 | "outDir": "dist/es", /* Redirect output structure to the directory. */ 11 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 12 | "strict": true, /* Enable all strict type-checking options. */ 13 | "allowSyntheticDefaultImports": true, 14 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import camelCase from 'lodash.camelcase'; 2 | import resolve from 'rollup-plugin-node-resolve'; 3 | import sourceMaps from 'rollup-plugin-sourcemaps'; 4 | import typescript from 'rollup-plugin-typescript2'; 5 | 6 | const pkg = require('./package.json'); 7 | const libraryName = 'cldr-compact-number'; 8 | 9 | export default { 10 | external: [], 11 | input: `dist/es/src/index.js`, 12 | output: [ 13 | { file: pkg.main, name: camelCase(libraryName), format: 'umd' }, 14 | { file: pkg.module, format: 'es' } 15 | ], 16 | plugins: [ 17 | typescript({ 18 | typescript: require('typescript') 19 | }), 20 | // Allow node_modules resolution, so you can use 'external' to control 21 | // which external modules to include in the bundle 22 | // https://github.com/rollup/rollup-plugin-node-resolve#usage 23 | resolve(), 24 | 25 | // Resolve source maps to the original source 26 | sourceMaps() 27 | ], 28 | // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash') 29 | watch: { 30 | include: 'dist/es/**' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /priv/zh.js: -------------------------------------------------------------------------------- 1 | let zh_CN = { 2 | zh_CN: { 3 | "locale": "zh-cn", 4 | "parentLocale": "zh" 5 | } 6 | } 7 | 8 | let zh = { 9 | zh: { 10 | "locale": "zh", 11 | "numbers": { 12 | "decimal": { 13 | "long": [[1000, { 14 | "other": ["0", 1] 15 | }], [10000, { 16 | "other": ["0万", 1] 17 | }], [100000, { 18 | "other": ["00万", 2] 19 | }], [1000000, { 20 | "other": ["000万", 3] 21 | }], [10000000, { 22 | "other": ["0000万", 4] 23 | }], [100000000, { 24 | "other": ["0亿", 1] 25 | }], [1000000000, { 26 | "other": ["00亿", 2] 27 | }], [10000000000, { 28 | "other": ["000亿", 3] 29 | }], [100000000000, { 30 | "other": ["0000亿", 4] 31 | }], [1000000000000, { 32 | "other": ["0兆", 1] 33 | }], [10000000000000, { 34 | "other": ["00兆", 2] 35 | }], [100000000000000, { 36 | "other": ["000兆", 3] 37 | }]], 38 | "short": [[1000, { 39 | "other": ["0", 1] 40 | }], [10000, { 41 | "other": ["0万", 1] 42 | }], [100000, { 43 | "other": ["00万", 2] 44 | }], [1000000, { 45 | "other": ["000万", 3] 46 | }], [10000000, { 47 | "other": ["0000万", 4] 48 | }], [100000000, { 49 | "other": ["0亿", 1] 50 | }], [1000000000, { 51 | "other": ["00亿", 2] 52 | }], [10000000000, { 53 | "other": ["000亿", 3] 54 | }], [100000000000, { 55 | "other": ["0000亿", 4] 56 | }], [1000000000000, { 57 | "other": ["0兆", 1] 58 | }], [10000000000000, { 59 | "other": ["00兆", 2] 60 | }], [100000000000000, { 61 | "other": ["000兆", 3] 62 | }]] 63 | } 64 | } 65 | } 66 | } 67 | 68 | let zh_HK = { 69 | 'zh-HK': { 70 | "locale": "zh-hk", 71 | "parentLocale": "zh" 72 | } 73 | } 74 | 75 | let zh_TW = { 76 | 'zh-TW': { 77 | "locale": "zh-tw", 78 | "parentLocale": "zh" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cldr-compact-number", 3 | "version": "0.4.0", 4 | "description": "Compact number formatting logic", 5 | "main": "dist/cldr-compact-number.umd.js", 6 | "module": "dist/cldr-compact-number.es5.js", 7 | "jsnext:main": "dist/cldr-compact-number.es5.js", 8 | "types": "dist/types/index.d.ts", 9 | "scripts": { 10 | "build": "tsc && tsc --outDir dist/es && rollup -c rollup.config.ts", 11 | "lint": "tslint -t codeFrame 'src/**/*.ts' 'test/format.test.ts'", 12 | "prepare": "npm run build", 13 | "prepublishOnly": "npm run test:ci", 14 | "preversion": "npm run lint", 15 | "start": "tsc -w & rollup -c rollup.config.ts -w", 16 | "prebuild": "rimraf dist", 17 | "test": "jest", 18 | "test:ci": "npm test && npm run lint" 19 | }, 20 | "jest": { 21 | "transform": { 22 | ".(ts|tsx)": "ts-jest" 23 | }, 24 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 25 | "moduleFileExtensions": [ 26 | "ts", 27 | "tsx", 28 | "js" 29 | ] 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+ssh://git@github.com/snewcomer/cldr-compact-number.git" 34 | }, 35 | "keywords": [ 36 | "short", 37 | "number", 38 | "formatting" 39 | ], 40 | "author": "Scott Newcomer", 41 | "license": "ISC", 42 | "bugs": { 43 | "url": "https://github.com/snewcomer/cldr-compact-number/issues" 44 | }, 45 | "homepage": "https://github.com/snewcomer/cldr-compact-number#readme", 46 | "devDependencies": { 47 | "@types/jest": "^24.0.23", 48 | "@types/node": "^10.12.2", 49 | "jest": "^23.6.0", 50 | "lodash.camelcase": "^4.3.0", 51 | "prettier": "^1.19.1", 52 | "rimraf": "^3.0.0", 53 | "rollup": "^1.27.2", 54 | "rollup-plugin-node-resolve": "^4.0.0", 55 | "rollup-plugin-sourcemaps": "^0.4.2", 56 | "rollup-plugin-typescript2": "^0.18.1", 57 | "ts-jest": "^24.1.0", 58 | "tslint": "^5.20.1", 59 | "tslint-config-prettier": "^1.18.0", 60 | "tslint-eslint-rules": "^5.4.0", 61 | "tslint-plugin-prettier": "^2.0.1", 62 | "typescript": "^3.7.2" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/math-utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Meant to either localize a number with toLocaleString or return an Integer 3 | * localization accepts 3 arguments 4 | * - significantDigits 5 | * - minimumFractionDigits 6 | * - maximumFractionDigits 7 | */ 8 | export function normalizeNumber( 9 | decimal: number, 10 | arbitraryPrecision: number, 11 | sign: number, 12 | locale: string, 13 | { 14 | significantDigits = 0, 15 | minimumFractionDigits = 0, 16 | maximumFractionDigits = 2 17 | } 18 | ): string | number | undefined { 19 | if (significantDigits) { 20 | return toLocaleFixed(toFixed(decimal, significantDigits) * sign, locale, { 21 | maximumFractionDigits, 22 | minimumFractionDigits 23 | }); 24 | } 25 | 26 | return withRounding(decimal, arbitraryPrecision) * sign; 27 | } 28 | 29 | export function extractIntPart( 30 | decimal: number, 31 | range: number, 32 | numberOfDigits: number 33 | ): number { 34 | // 1734 -> 1.734 35 | // 17345 -> 17.345 36 | // 999949 -> 999.9K with one significant digit or 999,9 mil in Spanish 37 | // this gives us the "int" (LHS) part of the number with the remains on the RHS 38 | return (decimal / range) * Math.pow(10, numberOfDigits - 1); 39 | } 40 | 41 | function toFixed(decimal: number, significantDigits: number): number { 42 | // solves issues with toFixed returning a string 43 | // e.g. 999.94 -> 999.9 44 | // e.g. 999.95 -> 1000 instead of (999.95).toFixed(1) -> '1000.1' 45 | const powOf10 = Math.pow(10, significantDigits); 46 | return Math.round(decimal * powOf10) / powOf10; 47 | } 48 | 49 | function withRounding(decimal: number, arbitraryPrecision: number): number { 50 | if (decimal <= 1) { 51 | // We do not want to round up to nearest 10 (Math.pow(10, 1)) when < 1. 52 | // Just round decimal 53 | return Math.round(decimal); 54 | } 55 | 56 | // rounding on floating point numbers 57 | // e.g. 99.5 -> 100 58 | const powOf10 = Math.pow(10, arbitraryPrecision); 59 | return Math.round(decimal / powOf10) * powOf10; 60 | } 61 | 62 | function toLocaleFixed( 63 | value: number, 64 | locale: string, 65 | digitsConfig: object 66 | ): string | undefined { 67 | if (value && typeof value === 'number') { 68 | return value.toLocaleString(locale, digitsConfig); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/format-utils.ts: -------------------------------------------------------------------------------- 1 | export function replaceNumber(normalized: any, format: string): string { 2 | // 1.734 -> 1K 3 | // replace 0's with absolute number while preserving space and remaining text 4 | // return format.replace(/0*(\s*)(\w+)/, Math.round(number) + '$1$2'); 5 | return format.replace(/0*/, normalized); 6 | } 7 | 8 | export function polishString(str: string): string { 9 | return str.replace("'.'", '.'); 10 | } 11 | 12 | export function normalizeLocale(locale: string | string[]): string { 13 | if (locale instanceof Array) { 14 | return locale[0].replace(/_/, '-').toLowerCase(); 15 | } 16 | return locale.replace(/_/, '-').toLowerCase(); 17 | } 18 | 19 | /** 20 | * If rule only contains 0, it indicates no short number formatting applied 21 | * e.g. "ja" 1234 -> 1234 and not 1K 22 | */ 23 | export function needsFormatting(format: string): RegExpMatchArray | null { 24 | return format.match(/[^0]/); 25 | } 26 | 27 | /** 28 | * Given a format: { af: {locale: "af", numbers: {…}} af-na: {locale: "af-NA", parentLocale: "af"} } 29 | * recursively find numbers hash 30 | * 31 | * @method findLocaleData 32 | * @param localeData 33 | * @param locale 34 | */ 35 | export function findLocaleData( 36 | localeData: any, 37 | locale: string | string[] 38 | ): object | undefined { 39 | locale = Array.isArray(locale) ? locale[0] : locale; 40 | const topLevelData = 41 | localeData[locale] || localeData[normalizeLocale(locale)]; 42 | if (!topLevelData) { 43 | return; 44 | } 45 | 46 | let numbersHash = topLevelData.numbers; 47 | const parentLocale = topLevelData.parentLocale; 48 | 49 | if (!numbersHash && parentLocale) { 50 | numbersHash = findLocaleData(localeData, parentLocale); 51 | } 52 | 53 | return numbersHash; 54 | } 55 | 56 | export function findMatchingLocale( 57 | localeData: any, 58 | locale: string | string[] 59 | ): object | undefined { 60 | locale = Array.isArray(locale) ? locale[0] : locale; 61 | const topLevelData = 62 | localeData[locale] || localeData[normalizeLocale(locale)]; 63 | if (!topLevelData) { 64 | return; 65 | } 66 | 67 | let numbersHash = topLevelData.numbers; 68 | const parentLocale = topLevelData.parentLocale; 69 | 70 | if (!numbersHash && parentLocale) { 71 | numbersHash = findLocaleData(localeData, parentLocale); 72 | } 73 | 74 | return numbersHash; 75 | } 76 | -------------------------------------------------------------------------------- /locales/en.ts: -------------------------------------------------------------------------------- 1 | let enLocaleData = { 2 | en: { 3 | locale: "en", 4 | numbers: { 5 | decimal: { 6 | long: [[1000, { 7 | one: ["0 thousand", 1], 8 | other: ["0 thousand", 1] 9 | }], [10000, { 10 | one: ["00 thousand", 2], 11 | other: ["00 thousand", 2] 12 | }], [100000, { 13 | one: ["000 thousand", 3], 14 | other: ["000 thousand", 3] 15 | }], [1000000, { 16 | one: ["0 million", 1], 17 | other: ["0 million", 1] 18 | }], [10000000, { 19 | one: ["00 million", 2], 20 | other: ["00 million", 2] 21 | }], [100000000, { 22 | one: ["000 million", 3], 23 | other: ["000 million", 3] 24 | }], [1000000000, { 25 | one: ["0 billion", 1], 26 | other: ["0 billion", 1] 27 | }], [10000000000, { 28 | one: ["00 billion", 2], 29 | other: ["00 billion", 2] 30 | }], [100000000000, { 31 | one: ["000 billion", 3], 32 | other: ["000 billion", 3] 33 | }], [1000000000000, { 34 | one: ["0 trillion", 1], 35 | other: ["0 trillion", 1] 36 | }], [10000000000000, { 37 | one: ["00 trillion", 2], 38 | other: ["00 trillion", 2] 39 | }], [100000000000000, { 40 | one: ["000 trillion", 3], 41 | other: ["000 trillion", 3] 42 | }]], 43 | short: [[1000, { 44 | one: ["0K", 1], 45 | other: ["0K", 1] 46 | }], [10000, { 47 | one: ["00K", 2], 48 | other: ["00K", 2] 49 | }], [100000, { 50 | one: ["000K", 3], 51 | other: ["000K", 3] 52 | }], [1000000, { 53 | one: ["0M", 1], 54 | other: ["0M", 1] 55 | }], [10000000, { 56 | one: ["00M", 2], 57 | other: ["00M", 2] 58 | }], [100000000, { 59 | one: ["000M", 3], 60 | other: ["000M", 3] 61 | }], [1000000000, { 62 | one: ["0B", 1], 63 | other: ["0B", 1] 64 | }], [10000000000, { 65 | one: ["00B", 2], 66 | other: ["00B", 2] 67 | }], [100000000000, { 68 | one: ["000B", 3], 69 | other: ["000B", 3] 70 | }], [1000000000000, { 71 | one: ["0T", 1], 72 | other: ["0T", 1] 73 | }], [10000000000000, { 74 | one: ["00T", 2], 75 | other: ["00T", 2] 76 | }], [100000000000000, { 77 | one: ["000T", 3], 78 | other: ["000T", 3] 79 | }]] 80 | } 81 | } 82 | } 83 | } 84 | 85 | enLocaleData = JSON.parse(JSON.stringify(enLocaleData)); 86 | 87 | export default enLocaleData; 88 | -------------------------------------------------------------------------------- /src/format.ts: -------------------------------------------------------------------------------- 1 | import enLocaleData from '../locales/en'; 2 | import { 3 | findLocaleData, 4 | needsFormatting, 5 | normalizeLocale, 6 | polishString, 7 | replaceNumber 8 | } from './format-utils'; 9 | import { extractIntPart, normalizeNumber } from './math-utils'; 10 | 11 | export function compactFormat( 12 | value: number | string, 13 | locale: string | string[], 14 | localeData?: object, 15 | options: any = {} 16 | ): string | number | undefined { 17 | let num = Number(value); 18 | if (!value || typeof num !== 'number') { 19 | return value; 20 | } 21 | 22 | localeData = { ...enLocaleData, ...localeData }; 23 | 24 | // figure out which numbers hash based on the locale 25 | const data: any | undefined = findLocaleData(localeData, locale); 26 | if (!data) { 27 | return value; 28 | } 29 | 30 | // take the absolute value and stash sign to apply at end 31 | let sign = 1; 32 | if (num < 0) { 33 | sign = -1; 34 | num = Math.abs(num); 35 | } 36 | 37 | // find specific rules: short or long 38 | const { 39 | financialFormat = false, 40 | long = false, 41 | significantDigits = 0, 42 | threshold = 0.05 43 | } = options; 44 | const rules = long ? data.decimal.long : data.decimal.short; 45 | if (!rules || num < 1000) { 46 | return value; 47 | } 48 | 49 | // 1. Take number and determine range it is in 50 | // 2. Extract specific rule from hash - ["0K", 1] meaning which value from the rule and number of zeros 51 | let matchingRule; 52 | let arbitraryPrecision = 0; 53 | for (let i = 0; i <= rules.length; i++) { 54 | if (num <= rules[i][0]) { 55 | const [testRangeHigh] = rules[i]; 56 | // always use previous rule until within 5% threshold of upper limit 57 | if (!financialFormat && 1 - num / testRangeHigh > threshold) { 58 | // e.g use 950K instead of 1M 59 | // e.g use 101K instead of 0.1M 60 | matchingRule = rules[i - 1]; 61 | } else { 62 | matchingRule = rules[i]; 63 | if (!significantDigits || !financialFormat) { 64 | // if we want to round up, we need to prevent numbers like 99,499 from rounding down to 99K 65 | // /-private/math-utils will use this variable to round a number like 91 to 100 since we are within the threshold 66 | arbitraryPrecision = 1; 67 | } 68 | } 69 | break; 70 | } 71 | } 72 | 73 | // 3. Normalise number by converting to decimal and cropping to number of digits 74 | // 1000 -> 1.000 -> 1K 75 | // 1600 -> 1.600 -> 2K 76 | // 4. Format according to formatter e.g. "0K" 77 | const [range, opts] = matchingRule; 78 | // cldr data is either `one` or `other`. Defaulting to `one` for now 79 | const [formatter, numberOfDigits] = opts.one || opts.other; 80 | 81 | if (!needsFormatting(formatter)) { 82 | return value; 83 | } 84 | 85 | const normalizedLocale: string = normalizeLocale(locale); 86 | const normalized = normalizeNumber( 87 | extractIntPart(num, range, numberOfDigits), 88 | arbitraryPrecision, 89 | sign, 90 | normalizedLocale, 91 | options 92 | ); 93 | 94 | return polishString(replaceNumber(normalized, formatter)); 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cldr-compact-number 2 | ============================================================================== 3 | ![Download count all time](https://img.shields.io/npm/dt/cldr-compact-number.svg) 4 | [![npm version](https://badge.fury.io/js/cldr-compact-number.svg)](http://badge.fury.io/js/cldr-compact-number) 5 | 6 | [![Dependency Status](https://david-dm.org/snewcomer/cldr-compact-number.svg)](https://david-dm.org/snewcomer/cldr-compact-number) 7 | [![devDependency Status](https://david-dm.org/snewcomer/cldr-compact-number/dev-status.svg)](https://david-dm.org/snewcomer/cldr-compact-number#info=devDependencies) 8 | 9 | Compact number formatting based on CLDR locale data. Particularly useful for __statistical data__, showing financial numbers in __charts__, and __abbreviating number of ratings__ across a range of languages. 10 | 11 | - `1234` is converted to `1K` in **English** 12 | - `101234` is converted to `101K` in **English** and `101.1K` if need 1 significant digit 13 | - `1234` is converted to `1 mil` in **Español** 14 | - `101234` is converted to `101,1 mil` in **Español** if need 1 significant digit 15 | - `1234` is converted to `1234` in **Japanese** 16 | - `101234` is converted to `10.1万` in **Japanese** if need 1 significant digit 17 | 18 | Depends on data from [cldr-numbers-full](https://github.com/unicode-cldr/cldr-numbers-full). Here is the related proposal for [Compact Decimal Format](https://github.com/tc39/ecma402/issues/37) that this addon is based on. This is why there are no browser API's baked into something like `Intl.NumberFormat`. 19 | 20 | Installation 21 | ------------------------------------------------------------------------------ 22 | 23 | ``` 24 | npm install cldr-compact-number --save 25 | ``` 26 | 27 | Usage 28 | ------------------------------------------------------------------------------ 29 | The following APIs take the language code as the the second argument based on [ISO 639-1](http://www.loc.gov/standards/iso639-2/php/code_list.php). You can also pass `en_GB` and we will normalize it to `en-GB` as well. 30 | 31 | ### API 32 | 33 | We default this library with `en` `localeData`. What is `localeData`? 34 | 35 | `localeData` for most cases comes through a build tool. You define what languages you want upfront so as to avoid bloating your application bundle and the build tool parses CLDR data and formats it. See `priv/` for examples of what shape the data should be in if you want to manually construct this data. 36 | 37 | ```js 38 | import compactFormat from 'cldr-compact-number'; 39 | 40 | compactFormat(19634, 'en', localeData); 41 | // 19K 42 | ``` 43 | 44 | ```js 45 | compactFormat(19634, 'en', localeData, { 46 | significantDigits: 1, 47 | minimumFractionDigits: 1, 48 | maximumFractionDigits: 2 49 | }); 50 | // 19.6K 51 | ``` 52 | 53 | ```js 54 | compactFormat(101, 'en', localeData, { 55 | significantDigits: 1, 56 | financialFormat: true 57 | }); 58 | // 0.1M 59 | ``` 60 | 61 | ```js 62 | compactFormat(19634, 'ja', localeData); 63 | // 2万 64 | ``` 65 | 66 | ```js 67 | compactFormat(19634, 'es', localeData, { significantDigits: 1 }); 68 | // 19,6 mil 69 | ``` 70 | 71 | * Note when using significantDigits, this addon utilizes [`toLocaleString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString). 72 | 73 | 74 | ### Long Formatting 75 | 76 | "Wait, I thought this addon was for compact number formatting?" Well it can be a misnomer depending on the language. Let's look at some examples. 77 | 78 | This doesn't seem shorter!!!! (╯°□°)╯︵ ┻━┻ 79 | ```js 80 | compactFormat(101000, 'en', localeData, { long: true }); 81 | // 101 thousand 82 | ``` 83 | 84 | But this does! ʘ‿ʘ 85 | ```js 86 | compactFormat(101000, 'ja', localeData, { long: true }); 87 | // 101万 88 | ``` 89 | 90 | So we will just go with `cldr-compact-number` for now. 91 | 92 | 93 | Other 94 | ------------------------------------------------------------------------------ 95 | Currently this only shortens with latin digits 0..9 96 | 97 | For your information, known number systems include: 98 | 99 | [adlm, ahom, arab, arabext, armn, armnlow, bali, beng, bhks, brah, 100 | cakm, cham, cyrl, deva, ethi, fullwide, geor, grek, greklow, gujr, 101 | guru, hanidays, hanidec, hans, hansfin, hant, hantfin, hebr, hmng, 102 | java, jpan, jpanfin, kali, khmr, knda, lana, lanatham, laoo, latn, 103 | lepc, limb, mathbold, mathdbl, mathmono, mathsanb, mathsans, mlym, 104 | modi, mong, mroo, ...] 105 | 106 | 107 | Contributing 108 | ------------------------------------------------------------------------------ 109 | 110 | ### Installation 111 | 112 | * `git clone git@github.com:snewcomer/cldr-compact-number.git` 113 | * `cd cldr-compact-number` 114 | * `npm install` 115 | 116 | ### Running tests 117 | 118 | * `npm run test` – Runs the test suite on the current Ember version 119 | 120 | License 121 | ------------------------------------------------------------------------------ 122 | 123 | This project is licensed under the [MIT License](LICENSE.md). 124 | -------------------------------------------------------------------------------- /test/format.test.ts: -------------------------------------------------------------------------------- 1 | import compactFormat from '../src'; 2 | import { compactFormat as format } from '../src/format'; 3 | import { en, es, es_MX, fi } from './locale-data'; 4 | 5 | // due to regex replacement 6 | function normalizeWhitespace(val: any) { 7 | return val.replace(/\s+/, ' '); 8 | } 9 | 10 | describe('format number', () => { 11 | it('returns value if no localeData provided with default export', () => { 12 | const localeData = {}; 13 | let result = compactFormat(1234, 'en-gb', localeData); 14 | expect(result).toBe(1234); 15 | result = compactFormat(1234, 'es-MX', localeData); 16 | expect(result).toBe(1234); 17 | }); 18 | 19 | it('works with negative values', () => { 20 | const localeData = {}; 21 | let result = compactFormat(-1234, 'en-gb', localeData); 22 | expect(result).toBe(-1234); 23 | result = compactFormat(-1234, 'es-MX', localeData); 24 | expect(result).toBe(-1234); 25 | }); 26 | 27 | it('returns value if locale is an array (ember-intl)', () => { 28 | const localeData = {}; 29 | let result = compactFormat(1234, ['en-gb'], localeData); 30 | expect(result).toBe(1234); 31 | result = compactFormat(1234, ['es-MX'], localeData); 32 | expect(result).toBe(1234); 33 | }); 34 | 35 | it('returns value if no localeData provided', () => { 36 | const localeData = {}; 37 | let result = format(1234, 'en-gb', localeData); 38 | expect(result).toBe(1234); 39 | result = format(1234, 'es-MX', localeData); 40 | expect(result).toBe(1234); 41 | }); 42 | 43 | it('returns value if localeData provided', () => { 44 | const localeData = es; 45 | let result = format(1234, 'es', localeData); 46 | expect(normalizeWhitespace(result)).toBe('1 mil'); 47 | result = format(11234, 'es', localeData); 48 | expect(normalizeWhitespace(result)).toBe('11 mil'); 49 | result = format(94999, 'es', localeData); 50 | expect(normalizeWhitespace(result)).toBe('95 mil'); 51 | result = format(95000, 'es', localeData); 52 | expect(normalizeWhitespace(result)).toBe('95 mil'); 53 | result = format(95001, 'es', localeData); 54 | expect(normalizeWhitespace(result)).toBe('100 mil'); 55 | result = format(100000, 'es', localeData); 56 | expect(normalizeWhitespace(result)).toBe('100 mil'); 57 | }); 58 | 59 | it('returns value if threshold provided', () => { 60 | const localeData = es; 61 | let result = format(1234, 'es', localeData, { threshold: 0.1 }); 62 | expect(normalizeWhitespace(result)).toBe('1 mil'); 63 | result = format(11234, 'es', localeData, { threshold: 0.1 }); 64 | expect(normalizeWhitespace(result)).toBe('11 mil'); 65 | result = format(89999, 'es', localeData, { threshold: 0.1 }); 66 | expect(normalizeWhitespace(result)).toBe('90 mil'); 67 | result = format(90001, 'es', localeData, { threshold: 0.1 }); 68 | expect(normalizeWhitespace(result)).toBe('90 mil'); 69 | // result = format(95001, 'es', localeData, { threshold: 0.1 }); 70 | // expect(replaceWhitespace(result)).toBe('100 mil'); 71 | }); 72 | 73 | it('returns with significantDigits', () => { 74 | const localeData = es; 75 | let result = format(1234, 'es', localeData, { 76 | significantDigits: 1 77 | }); 78 | expect(normalizeWhitespace(result)).toBe('1,2 mil'); 79 | result = format(11234, 'es', localeData, { 80 | significantDigits: 1 81 | }); 82 | expect(normalizeWhitespace(result)).toBe('11,2 mil'); 83 | result = format(91934, 'es', localeData, { 84 | significantDigits: 2 85 | }); 86 | expect(normalizeWhitespace(result)).toBe('91,93 mil'); 87 | }); 88 | 89 | it('returns with significantDigits and negative number', () => { 90 | const localeData = es; 91 | let result = format(-1234, 'es', localeData, { 92 | significantDigits: 1 93 | }); 94 | expect(normalizeWhitespace(result)).toBe('-1,2 mil'); 95 | result = format(-11234, 'es', localeData, { 96 | significantDigits: 1 97 | }); 98 | expect(normalizeWhitespace(result)).toBe('-11,2 mil'); 99 | result = format(-91934, 'es', localeData, { 100 | significantDigits: 2 101 | }); 102 | expect(normalizeWhitespace(result)).toBe('-91,93 mil'); 103 | }); 104 | 105 | it('returns with financial format', () => { 106 | const localeData = en; 107 | let result = format(1234, 'en', localeData, { 108 | financialFormat: true, 109 | significantDigits: 1 110 | }); 111 | expect(normalizeWhitespace(result)).toBe('1.2K'); 112 | result = format(101000, 'en', localeData, { 113 | financialFormat: true, 114 | significantDigits: 1 115 | }); 116 | expect(normalizeWhitespace(result)).toBe('0.1M'); 117 | }); 118 | 119 | it("replaces `'.'` in the formatting string with just a .", () => { 120 | const localeData = fi; 121 | const result = format(1234, 'fi', localeData, { 122 | significantDigits: 1 123 | }); 124 | 125 | expect(normalizeWhitespace(result)).toBe('1,2 t.'); 126 | }); 127 | 128 | it('defaults with english', () => { 129 | const result = format(1234, 'en'); 130 | expect(normalizeWhitespace(result)).toBe('1K'); 131 | }); 132 | 133 | it('works with es_MX', () => { 134 | const result = format(1234, 'es-MX', es_MX); 135 | expect(normalizeWhitespace(result)).toBe('1 k'); 136 | }); 137 | 138 | it('works with multiple locales', () => { 139 | const localeData = { ...es, ...fi }; 140 | let result = format(1234, 'es', localeData); 141 | expect(normalizeWhitespace(result)).toBe('1 mil'); 142 | 143 | result = format(1234, 'en', localeData); 144 | expect(normalizeWhitespace(result)).toBe('1K'); 145 | 146 | result = format(1234, 'fi', localeData); 147 | expect(normalizeWhitespace(result)).toBe('1 t.'); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /test/locale-data.ts: -------------------------------------------------------------------------------- 1 | export const es = { 2 | es: { 3 | "locale": "es", 4 | "numbers": { 5 | "decimal": { 6 | "long": [ 7 | [1000, { "one": ["0 mil", 1], "other": ["0 mil", 1] }], [10000, { "one": ["00 mil", 2], "other": ["00 mil", 2] }], [100000, { "one": ["000 mil", 3], "other": ["000 mil", 3] }], [1000000, { "one": ["0 millón", 1], "other": ["0 millones", 1] }], [10000000, { "one": ["00 millones", 2], "other": ["00 millones", 2] }], [100000000, { "one": ["000 millones", 3], "other": ["000 millones", 3] }], [1000000000, { "one": ["0 mil millones", 1], "other": ["0 mil millones", 1] }], [10000000000, { "one": ["00 mil millones", 2], "other": ["00 mil millones", 2] }], [100000000000, { "one": ["000 mil millones", 3], "other": ["000 mil millones", 3] }], [1000000000000, { "one": ["0 billón", 1], "other": ["0 billones", 1] }], [10000000000000, { "one": ["00 billones", 2], "other": ["00 billones", 2] }], [100000000000000, { "one": ["000 billones", 3], "other": ["000 billones", 3] }] 8 | ], 9 | "short": [ 10 | [1000, { "one": ["0 mil", 1], "other": ["0 mil", 1] }], [10000, { "one": ["00 mil", 2], "other": ["00 mil", 2] }], [100000, { "one": ["000 mil", 3], "other": ["000 mil", 3] }], [1000000, { "one": ["0 M", 1], "other": ["0 M", 1] }], [10000000, { "one": ["00 M", 2], "other": ["00 M", 2] }], [100000000, { "one": ["000 M", 3], "other": ["000 M", 3] }], [1000000000, { "one": ["0000 M", 4], "other": ["0000 M", 4] }], [10000000000, { "one": ["00 mil M", 2], "other": ["00 mil M", 2] }], [100000000000, { "one": ["000 mil M", 3], "other": ["000 mil M", 3] }], [1000000000000, { "one": ["0 B", 1], "other": ["0 B", 1] }], [10000000000000, { "one": ["00 B", 2], "other": ["00 B", 2] }], [100000000000000, { "one": ["000 B", 3], "other": ["000 B", 3] }] 11 | ] 12 | } 13 | } 14 | } 15 | }; 16 | 17 | export const es_MX = { 18 | 'es-MX': { 19 | "locale": "es-MX", 20 | "parentLocale": "es-419", 21 | "numbers": { 22 | "decimal": { 23 | "long": [ 24 | [1000, { "one": ["0 mil", 1], "other": ["0 mil", 1] }], [10000, { "one": ["00 mil", 2], "other": ["00 mil", 2] }], [100000, { "one": ["000 mil", 3], "other": ["000 mil", 3] }], [1000000, { "one": ["0 millón", 1], "other": ["0 millones", 1] }], [10000000, { "one": ["00 millones", 2], "other": ["00 millones", 2] }], [100000000, { "one": ["000 millones", 3], "other": ["000 millones", 3] }], [1000000000, { "one": ["0 mil millones", 1], "other": ["0 mil millones", 1] }], [10000000000, { "one": ["00 mil millones", 2], "other": ["00 mil millones", 2] }], [100000000000, { "one": ["000 mil millones", 3], "other": ["000 mil millones", 3] }], [1000000000000, { "one": ["0 billón", 1], "other": ["0 billones", 1] }], [10000000000000, { "one": ["00 billones", 2], "other": ["00 billones", 2] }], [100000000000000, { "one": ["000 billones", 3], "other": ["000 billones", 3] }] 25 | ], 26 | "short": [[1000, { "one": ["0 k", 1], "other": ["0 k", 1] }], [10000, { "one": ["00 k", 2], "other": ["00 k", 2] }], [100000, { "one": ["000 k", 3], "other": ["000 k", 3] }], [1000000, { "one": ["0 M", 1], "other": ["0 M", 1] }], [10000000, { "one": ["00 M", 2], "other": ["00 M", 2] }], [100000000, { "one": ["000 M", 3], "other": ["000 M", 3] }], [1000000000, { "one": ["0000 M", 4], "other": ["0000 M", 4] }], [10000000000, { "one": ["00 mil M", 2], "other": ["00 mil M", 2] }], [100000000000, { "one": ["000 mil M", 3], "other": ["000 mil M", 3] }], [1000000000000, { "one": ["0 B", 1], "other": ["0 B", 1] }], [10000000000000, { "one": ["00 B", 2], "other": ["00 B", 2] }], [100000000000000, { "one": ["000 B", 3], "other": ["000 B", 3] }]] 27 | } 28 | } 29 | } 30 | }; 31 | 32 | export const en = { 33 | en: { 34 | "locale": "en", 35 | "numbers": { 36 | "decimal": { 37 | "long": [ 38 | [1000, { "one": ["0 thousand", 1], "other": ["0 thousand", 1] }], [10000, { "one": ["00 thousand", 2], "other": ["00 thousand", 2] }], [100000, { "one": ["000 thousand", 3], "other": ["000 thousand", 3] }], [1000000, { "one": ["0 million", 1], "other": ["0 million", 1] }], [10000000, { "one": ["00 million", 2], "other": ["00 million", 2] }], [100000000, { "one": ["000 million", 3], "other": ["000 million", 3] }], [1000000000, { "one": ["0 billion", 1], "other": ["0 billion", 1] }], [10000000000, { "one": ["00 billion", 2], "other": ["00 billion", 2] }], [100000000000, { "one": ["000 billion", 3], "other": ["000 billion", 3] }], [1000000000000, { "one": ["0 trillion", 1], "other": ["0 trillion", 1] }], [10000000000000, { "one": ["00 trillion", 2], "other": ["00 trillion", 2] }], [100000000000000, { "one": ["000 trillion", 3], "other": ["000 trillion", 3] }] 39 | ], 40 | "short": [ 41 | [1000, { "one": ["0K", 1], "other": ["0K", 1] }], [10000, { "one": ["00K", 2], "other": ["00K", 2] }], [100000, { "one": ["000K", 3], "other": ["000K", 3] }], [1000000, { "one": ["0M", 1], "other": ["0M", 1] }], [10000000, { "one": ["00M", 2], "other": ["00M", 2] }], [100000000, { "one": ["000M", 3], "other": ["000M", 3] }], [1000000000, { "one": ["0B", 1], "other": ["0B", 1] }], [10000000000, { "one": ["00B", 2], "other": ["00B", 2] }], [100000000000, { "one": ["000B", 3], "other": ["000B", 3] }], [1000000000000, { "one": ["0T", 1], "other": ["0T", 1] }], [10000000000000, { "one": ["00T", 2], "other": ["00T", 2] }], [100000000000000, { "one": ["000T", 3], "other": ["000T", 3] }] 42 | ] 43 | } 44 | } 45 | } 46 | } 47 | 48 | export const fi = { 49 | fi: { 50 | "locale": "fi", 51 | "numbers": { 52 | "decimal": { 53 | "long": [ 54 | [1000, { "one": ["0 tuhat", 1], "other": ["0 tuhatta", 1] }], [10000, { "one": ["00 tuhatta", 2], "other": ["00 tuhatta", 2] }], [100000, { "one": ["000 tuhatta", 3], "other": ["000 tuhatta", 3] }], [1000000, { "one": ["0 miljoona", 1], "other": ["0 miljoonaa", 1] }], [10000000, { "one": ["00 miljoonaa", 2], "other": ["00 miljoonaa", 2] }], [100000000, { "one": ["000 miljoonaa", 3], "other": ["000 miljoonaa", 3] }], [1000000000, { "one": ["0 miljardi", 1], "other": ["0 miljardia", 1] }], [10000000000, { "one": ["00 miljardia", 2], "other": ["00 miljardia", 2] }], [100000000000, { "one": ["000 miljardia", 3], "other": ["000 miljardia", 3] }], [1000000000000, { "one": ["0 biljoona", 1], "other": ["0 biljoonaa", 1] }], [10000000000000, { "one": ["00 biljoonaa", 2], "other": ["00 biljoonaa", 2] }], [100000000000000, { "one": ["000 biljoonaa", 3], "other": ["000 biljoonaa", 3] }] 55 | ], 56 | "short": [ 57 | [1000, { "one": ["0 t'.'", 1], "other": ["0 t'.'", 1] }], [10000, { "one": ["00 t'.'", 2], "other": ["00 t'.'", 2] }], [100000, { "one": ["000 t'.'", 3], "other": ["000 t'.'", 3] }], [1000000, { "one": ["0 milj'.'", 1], "other": ["0 milj'.'", 1] }], [10000000, { "one": ["00 milj'.'", 2], "other": ["00 milj'.'", 2] }], [100000000, { "one": ["000 milj'.'", 3], "other": ["000 milj'.'", 3] }], [1000000000, { "one": ["0 mrd'.'", 1], "other": ["0 mrd'.'", 1] }], [10000000000, { "one": ["00 mrd'.'", 2], "other": ["00 mrd'.'", 2] }], [100000000000, { "one": ["000 mrd'.'", 3], "other": ["000 mrd'.'", 3] }], [1000000000000, { "one": ["0 bilj'.'", 1], "other": ["0 bilj'.'", 1] }], [10000000000000, { "one": ["00 bilj'.'", 2], "other": ["00 bilj'.'", 2] }], [100000000000000, { "one": ["000 bilj'.'", 3], "other": ["000 bilj'.'", 3] }] 58 | ] 59 | } 60 | } 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /priv/es.js: -------------------------------------------------------------------------------- 1 | let esLocaleData = { 2 | es: { 3 | "locale": "es", 4 | "numbers": { 5 | "decimal": { 6 | "long": [[1000, { 7 | "one": ["0 mil", 1], 8 | "other": ["0 mil", 1] 9 | }], [10000, { 10 | "one": ["00 mil", 2], 11 | "other": ["00 mil", 2] 12 | }], [100000, { 13 | "one": ["000 mil", 3], 14 | "other": ["000 mil", 3] 15 | }], [1000000, { 16 | "one": ["0 millón", 1], 17 | "other": ["0 millones", 1] 18 | }], [10000000, { 19 | "one": ["00 millones", 2], 20 | "other": ["00 millones", 2] 21 | }], [100000000, { 22 | "one": ["000 millones", 3], 23 | "other": ["000 millones", 3] 24 | }], [1000000000, { 25 | "one": ["0 mil millones", 1], 26 | "other": ["0 mil millones", 1] 27 | }], [10000000000, { 28 | "one": ["00 mil millones", 2], 29 | "other": ["00 mil millones", 2] 30 | }], [100000000000, { 31 | "one": ["000 mil millones", 3], 32 | "other": ["000 mil millones", 3] 33 | }], [1000000000000, { 34 | "one": ["0 billón", 1], 35 | "other": ["0 billones", 1] 36 | }], [10000000000000, { 37 | "one": ["00 billones", 2], 38 | "other": ["00 billones", 2] 39 | }], [100000000000000, { 40 | "one": ["000 billones", 3], 41 | "other": ["000 billones", 3] 42 | }]], 43 | "short": [[1000, { 44 | "one": ["0 mil", 1], 45 | "other": ["0 mil", 1] 46 | }], [10000, { 47 | "one": ["00 mil", 2], 48 | "other": ["00 mil", 2] 49 | }], [100000, { 50 | "one": ["000 mil", 3], 51 | "other": ["000 mil", 3] 52 | }], [1000000, { 53 | "one": ["0 M", 1], 54 | "other": ["0 M", 1] 55 | }], [10000000, { 56 | "one": ["00 M", 2], 57 | "other": ["00 M", 2] 58 | }], [100000000, { 59 | "one": ["000 M", 3], 60 | "other": ["000 M", 3] 61 | }], [1000000000, { 62 | "one": ["0000 M", 4], 63 | "other": ["0000 M", 4] 64 | }], [10000000000, { 65 | "one": ["00 mil M", 2], 66 | "other": ["00 mil M", 2] 67 | }], [100000000000, { 68 | "one": ["000 mil M", 3], 69 | "other": ["000 mil M", 3] 70 | }], [1000000000000, { 71 | "one": ["0 B", 1], 72 | "other": ["0 B", 1] 73 | }], [10000000000000, { 74 | "one": ["00 B", 2], 75 | "other": ["00 B", 2] 76 | }], [100000000000000, { 77 | "one": ["000 B", 3], 78 | "other": ["000 B", 3] 79 | }]] 80 | } 81 | } 82 | } 83 | } 84 | 85 | let es_419 = { 86 | es_419: { 87 | "locale": "es-419", 88 | "parentLocale": "es", 89 | "numbers": { 90 | "decimal": { 91 | "long": [[1000, { 92 | "one": ["0 mil", 1], 93 | "other": ["0 mil", 1] 94 | }], [10000, { 95 | "one": ["00 mil", 2], 96 | "other": ["00 mil", 2] 97 | }], [100000, { 98 | "one": ["000 mil", 3], 99 | "other": ["000 mil", 3] 100 | }], [1000000, { 101 | "one": ["0 millón", 1], 102 | "other": ["0 millones", 1] 103 | }], [10000000, { 104 | "one": ["00 millones", 2], 105 | "other": ["00 millones", 2] 106 | }], [100000000, { 107 | "one": ["000 millones", 3], 108 | "other": ["000 millones", 3] 109 | }], [1000000000, { 110 | "one": ["0 mil millones", 1], 111 | "other": ["0 mil millones", 1] 112 | }], [10000000000, { 113 | "one": ["00 mil millones", 2], 114 | "other": ["00 mil millones", 2] 115 | }], [100000000000, { 116 | "one": ["000 mil millones", 3], 117 | "other": ["000 mil millones", 3] 118 | }], [1000000000000, { 119 | "one": ["0 billón", 1], 120 | "other": ["0 billón", 1] 121 | }], [10000000000000, { 122 | "one": ["00 billones", 2], 123 | "other": ["00 billones", 2] 124 | }], [100000000000000, { 125 | "one": ["000 billones", 3], 126 | "other": ["000 billones", 3] 127 | }]], 128 | "short": [[1000, { 129 | "one": ["0 K", 1], 130 | "other": ["0 K", 1] 131 | }], [10000, { 132 | "one": ["00 k", 2], 133 | "other": ["00 k", 2] 134 | }], [100000, { 135 | "one": ["000 k", 3], 136 | "other": ["000 k", 3] 137 | }], [1000000, { 138 | "one": ["0 M", 1], 139 | "other": ["0 M", 1] 140 | }], [10000000, { 141 | "one": ["00 M", 2], 142 | "other": ["00 M", 2] 143 | }], [100000000, { 144 | "one": ["000 M", 3], 145 | "other": ["000 M", 3] 146 | }], [1000000000, { 147 | "one": ["0k M", 1], 148 | "other": ["0k M", 1] 149 | }], [10000000000, { 150 | "one": ["00k M", 2], 151 | "other": ["00k M", 2] 152 | }], [100000000000, { 153 | "one": ["000k M", 3], 154 | "other": ["000k M", 3] 155 | }], [1000000000000, { 156 | "one": ["0 B", 1], 157 | "other": ["0 B", 1] 158 | }], [10000000000000, { 159 | "one": ["00 B", 2], 160 | "other": ["00 B", 2] 161 | }], [100000000000000, { 162 | "one": ["000 B", 3], 163 | "other": ["000 B", 3] 164 | }]] 165 | } 166 | } 167 | } 168 | } 169 | 170 | let es_ES = { 171 | 'es-es': { 172 | "locale": "es-es", 173 | "parentLocale": "es" 174 | } 175 | } 176 | 177 | let es_MX = { 178 | 'es-mx': { 179 | "locale": "es-mx", 180 | "parentLocale": "es-419", 181 | "numbers": { 182 | "decimal": { 183 | "long": [[1000, { 184 | "one": ["0 mil", 1], 185 | "other": ["0 mil", 1] 186 | }], [10000, { 187 | "one": ["00 mil", 2], 188 | "other": ["00 mil", 2] 189 | }], [100000, { 190 | "one": ["000 mil", 3], 191 | "other": ["000 mil", 3] 192 | }], [1000000, { 193 | "one": ["0 millón", 1], 194 | "other": ["0 millones", 1] 195 | }], [10000000, { 196 | "one": ["00 millones", 2], 197 | "other": ["00 millones", 2] 198 | }], [100000000, { 199 | "one": ["000 millones", 3], 200 | "other": ["000 millones", 3] 201 | }], [1000000000, { 202 | "one": ["0 mil millones", 1], 203 | "other": ["0 mil millones", 1] 204 | }], [10000000000, { 205 | "one": ["00 mil millones", 2], 206 | "other": ["00 mil millones", 2] 207 | }], [100000000000, { 208 | "one": ["000 mil millones", 3], 209 | "other": ["000 mil millones", 3] 210 | }], [1000000000000, { 211 | "one": ["0 billón", 1], 212 | "other": ["0 billones", 1] 213 | }], [10000000000000, { 214 | "one": ["00 billones", 2], 215 | "other": ["00 billones", 2] 216 | }], [100000000000000, { 217 | "one": ["000 billones", 3], 218 | "other": ["000 billones", 3] 219 | }]], 220 | "short": [[1000, { 221 | "one": ["0 k", 1], 222 | "other": ["0 k", 1] 223 | }], [10000, { 224 | "one": ["00 k", 2], 225 | "other": ["00 k", 2] 226 | }], [100000, { 227 | "one": ["000 k", 3], 228 | "other": ["000 k", 3] 229 | }], [1000000, { 230 | "one": ["0 M", 1], 231 | "other": ["0 M", 1] 232 | }], [10000000, { 233 | "one": ["00 M", 2], 234 | "other": ["00 M", 2] 235 | }], [100000000, { 236 | "one": ["000 M", 3], 237 | "other": ["000 M", 3] 238 | }], [1000000000, { 239 | "one": ["0000 M", 4], 240 | "other": ["0000 M", 4] 241 | }], [10000000000, { 242 | "one": ["00 mil M", 2], 243 | "other": ["00 mil M", 2] 244 | }], [100000000000, { 245 | "one": ["000 mil M", 3], 246 | "other": ["000 mil M", 3] 247 | }], [1000000000000, { 248 | "one": ["0 B", 1], 249 | "other": ["0 B", 1] 250 | }], [10000000000000, { 251 | "one": ["00 B", 2], 252 | "other": ["00 B", 2] 253 | }], [100000000000000, { 254 | "one": ["000 B", 3], 255 | "other": ["000 B", 3] 256 | }]] 257 | } 258 | } 259 | } 260 | } 261 | 262 | let es_XL = { 263 | 'es-xl': { 264 | "locale": "es-xl", 265 | "parentLocale": "es" 266 | } 267 | } 268 | 269 | --------------------------------------------------------------------------------