React Native中的.toLocaleString()在安卓设备上无法工作

64

2022年更新:启用了hermes您现在应该没有问题了。

我在react-native上使用.toLocaleString()进行数字输出。所有的IOS都能正常工作,但似乎在Android上无法正常工作。这是正常的吗?我需要使用一个函数来处理小数吗?

输入图像描述此处


1
RN使用操作系统集成的JavaScript引擎。JS引擎很可能是特定平台上使用的浏览器中的其中一个,正如你所看到的,它们有差异。我建议使用lib以在不同平台上获得相同的结果。 - Andreyco
1
这是RN Android中已知的问题:https://github.com/facebook/react-native/issues/16867 - Jeremy
到了2022年,可以通过使用最新版本的React Native和Hermes JS引擎来解决这个问题,这现在被推荐给每个人使用(我想)。 - Chrispher
1
是的,赫尔墨斯修复它。 - EQuimper
显示剩余2条评论
16个回答

59

不要使用polyfill或外部依赖,改变您的Android应用构建时所使用的JSC。对于较新版本的react-native,请在app/build.gradle中添加或覆盖以下行:

def jscFlavor = 'org.webkit:android-jsc-intl:+'

5
据我所知,这也是最全面和灵活的答案,因为它为JavaScript核心添加了国际化支持。请记住,这个运行时比其对应物稍微大一些(约6MB)。 - santamanno
是的,这应该被标记为正确答案。上面那个根本没有考虑设备语言环境... - Dion
这对我有用。我只想补充一点,即在执行 yarn add jsc-android 后才对我起作用。 - rcorrie

18

在新版本的 RN >0.62 上,您可以更改 JSC(JavaScriptCore)构建变体,以支持/包含 ICU i18n 库和必要数据,从而可以使用例如 Date.toLocaleStringString.localeCompare

请在您的 android/app/build.gradle 文件中替换此行

def jscFlavor = 'org.webkit:android-jsc:+'

使用这行代码

def jscFlavor = 'org.webkit:android-jsc-intl:+'

清理构建并运行react-native run android

注意

这个变体比默认设置多约6MiB。因此,如果使用def enableSeparateBuildPerCPUArchitecture = true并且对每个APK架构进行单独构建,则每个APK架构的构建都将导致APK大小增加约4MB,并且如果禁用每个架构的单独构建,则APK更大。


17
你可以使用

number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")

77
这很丢脸。 - iuliu.net
10
如果你正在使用本地化或尝试格式化货币金额,这将不会有帮助。 - Bajju
1
喜欢这个解决方案! - Itamar
3
似乎在小数点后面添加逗号,这是不可取的。 0.0123 变成了 0.0,123 - Zack
1
我正在编写一个使用Expo的应用程序,它不允许修改app/build.gradle,所以我使用了这个解决方案。为了解决@Zack指出的问题,该方法检查小数点number.toString().indexOf('.') > 1,如果存在,则进行拆分const numberParts = number.toString().split(/\./);。然后将正则表达式应用于numberParts[0],然后附加'.' + numberParts[1]。如果没有小数,则直接将正则表达式应用于number - knot22
显示剩余4条评论

16
这是一个与在Android中运行React Native时使用的Javascript核心有关的问题,而不是与React Native本身有关。要解决这个问题,您需要将最新的Javascript核心集成到您的Android构建中,或者升级React Native到0.59版本。
详细信息已在JSC Android Buildscripts存储库中记录。
现在,对于那些希望进行本地化字符串格式化而不需要集成整个Javascript核心的人来说,Javascript有一个国际化API,它可以让您将数字格式化为与语言相关的格式。文档可在MDN上找到。
这个API在Android中不可用,需要使用Intl进行填充。
在您的项目根目录中,安装Intl库。
yarn add intl

然后在您项目的索引文件(index.js)中,在文件顶部添加以下代码:
if(Platform.OS === 'android') { // only android needs polyfill
  require('intl'); // import intl object
  require('intl/locale-data/jsonp/en-IN'); // load the required locale details
}

在完成上述两个步骤之后,您现在可以在项目的任何地方获取本地化字符串。
new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(10000000);

如果您需要为其他区域设置格式化数字,所有区域设置代码的详细信息都可以在 intl/locale-data/jsonp/ 目录下找到。只需在您的 index.js 文件中引用您需要的代码即可。

只需在您的index.js文件中引用所需的文件即可。是否可以找到设备的语言环境并仅要求该文件,而不是要求数百个语言环境文件? - kunambi
您可以使用此方法获取设备语言环境 - https://dev59.com/0FwX5IYBdhLWcg3wvBZj#47349998,然后选择所需的语言环境文件... - Dani Akash
我正在使用React Native 0.59版本,但在Android上仍然看到这个问题。 - Joshua Pinter
正如我在另一个SO答案中提到的那样,这个解决方案需要注意intl npm包已经停止更新,最后一次更新是在2016年。 - Henrique Bruno
在 React Native 0.64 版本的 Android 上仍无法在生产环境中工作。 - Vivek Mahajan
显示剩余3条评论

6

3
(value) => {
    if (typeof value === 'number') {
      const [currency, cents] = (value / 100).toFixed(2).toString().split('.');

      return `${currency.replace(/\B(?=(\d{3})+(?!\d))/g, '.')},${cents}`;
    }

    return '0,00';
  }

一些简单易懂的解释可能会提高你的回答。 - de.
这似乎是一个返回小数点后数字的 TypeScript 函数。原先被接受的答案只使用正则表达式模式进行替换,没有返回任何小数点后面的数字。这可能仅会返回小数点后两位,如 toFixed(2) 所示。 - Alen Saqe
唯一的陷阱是,如果提供的值不是数字而是字符串数字,则会返回0.00,而不会尝试将其解析为Int。 - Alen Saqe

2

这是一个更加新的和轻量级的版本,请查看

  1. 首先安装:

yarn add @formatjs/intl-getcanonicallocales @formatjs/intl-locale @formatjs/intl-pluralrules @formatjs/intl-numberformat

  1. 检查是否需要填充

import {shouldPolyfill} from '@formatjs/intl-numberformat/should-polyfill'

if (shouldPolyfill()) {
    require('@formatjs/intl-getcanonicallocales/polyfill');
    require('@formatjs/intl-locale/polyfill');
    require('@formatjs/intl-pluralrules/polyfill');
    require('@formatjs/intl-numberformat/polyfill');
    require('@formatjs/intl-numberformat/locale-data/en-US');
}

查看源代码:https://formatjs.io/docs/polyfills/intl-numberformat/


1

解决方法:1

前往android/app/build.gradle

将此行替换为def jscFlavor = 'org.webkit:android-jsc-intl:+'

停止Metro并重新构建您的应用程序。

解决方法:2

否则,您可以使用此软件包https://www.npmjs.com/package/luxon

import import {DateTime} from 'luxon';

const date = DateTime.fromISO(new Date().toISOString());

const formatted = date.toLocaleString(DateTime.DATETIME_MED);

console.log(formatted);


1

在React Native中显示货币值

零依赖方案:

const parseCurr = (value) =>
   Platform.OS === 'android'  
      ?  '$' + price.toFixed(2)  
      :  price.toLocaleString('en-US', { style: 'currency', currency:'USD' });


parseCurr(25.75) // => $25.75

一个现实生活的例子(货币价值乘以100以获得更好的分币精度),并将其转换为巴西雷亚尔(R$)

export const getBRPrice = (price: number) => {
   const parsedPrice = 
      ( price / 100 ).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });

    return Platform.OS === 'android'
        ? `R$${ ( price / 100 ).toFixed(2) }`
        : parsedPrice;
};


// getBRPrice(450) => R$4,50

1
我使用自定义函数解决了这个问题。
function numberToMoney(amount, simbol = '$', decimalCount = 2, decimal 
   = ".", thousands = ",") {
   decimalCount = Math.abs(decimalCount)
   decimalCount = isNaN(decimalCount) ? 2 : decimalCount

   const negativeSign = amount < 0 ? "-" : ""

   const i = parseInt(amount = Math.abs(Number(amount) || 
             0).toFixed(decimalCount)).toString()
   const j = (i.length > 3) ? i.length % 3 : 0

   return simbol + negativeSign + (j ? i.substr(0, j) + thousands : '') + 
   i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ? 
   decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "")
};

无需安装额外的包。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接