国际化(i18n)-数字

背景

平常在项目中,经常会遇到String和Double间的转换,或者将Double、Float等类型格式化后显示。当我们的项目是直接在国内使用的时候,通过String.format、Double.parseDouble等方法转换通常不会出现问题。但是如果产品延伸到国外,通过这种方式通常会抛出异常、结果与实际不符等情况。本篇将整理在其它语言环境下字符和数字在实际使用中遇到的问题和采用的解决办法。

语言编码

  • 国际化:internationalization,由于首尾字母间有18个字符,简称i18n
  • 本地化:localization,由于首尾字母间有10个字符,简称l10n
  • ISO-639 标准使用编码定义了国际上常见的语言,每一种语言由两个小写字母表示。
  • ISO-3166 标准使用编码定义了国家/地区,每个国家/地区由两个大写字母表示。

常用语言编码

国家/地区 语言编码
中文 zh
中文(简体) zh-CN
中文(香港) zh-HK
中文(澳门) zh-MO
英语 en
英语(美国) en-us
法语 fr
德语 de
日语 ja
韩语 ko
俄语 ru

数字格式化

使用Kotlin作为示例代码。没做特殊说明,都采用默认中文作为环境语言。

Double转String

使用String.format:

val doubleValue = 1258.6999999999
val doubleStr1="$doubleValue"
val doubleStr2=String.format("%f",doubleValue)
val doubleStr3=String.format("%1\$s",doubleValue)
//保留小数点后五位
val doubleStr4=String.format("%.3f",doubleValue)

当语言环境为中文时,结果:
doubleStr2和doubleStr4采用四舍五入的方式,对字符转进行了处理。


当语言环境为英文时,结果:
doubleStr2和doubleStr4采用四舍五入的方式,对字符转进行了处理。
千分位没有分隔符。

当语言环境为俄文时,结果:
doubleStr2和doubleStr4采用四舍五入的方式,对字符转进行了处理。
千分位没有分隔符。
doubleStr1和doubleStr3结果与俄文格式不符。

如果不考虑精度的情况从上面三种情况可以看出,将有小数的数字转换为字符串展现出来,需要使用 String.format("%f",doubleValue)String.format("%.3f",doubleValue)的方式。

使用NumberFormat:

使用NumberFormat,我们可以设置保留多少小数位,设置结果计算方式(RoundingMode)等

fun getDoubleStr(value: Double, digits: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.CHINA): String {
            try {
                val format = NumberFormat.getNumberInstance(locale)
                //等同于NumberFormat.getNumberInstance
                //val format = DecimalFormat.getNumberInstance(locale)
                //设置小数点后最小位数
                format.minimumFractionDigits = digits
                //设置小数点后最大位数
                format.maximumFractionDigits = digits
                format.roundingMode = roundingMode
                //format.isGroupingUsed=false  //取消整数位分隔符
                return format.format(value)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return "$value"
        }
 ...
 private val digits = 5
//输出中文字符串
val chineseStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,Locale.CHINA)
//输出俄文字符串
val russianStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,russianLocale)

结果:

使用DecimalFormat:

占位符有0和#,当使用0的时候会严格按照样式来进行匹配,不够的时候会补0,而使用#时会将前后的0进行忽略。
DecimalFormat还可以对展示的字符样式进行自定义,比如设置小数分隔符、整数位分隔符用什么符号表示(Char类型),这里不做补充说明。

        fun getDefaultDoubleStr(value: Double, partten: String, locale: Locale = Locale.CHINA): String {
            try {
                //val format = DecimalFormat(partten)
                val format = DecimalFormat.getNumberInstance(locale) as DecimalFormat
                format.applyPattern(partten)
                return format.format(value)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return "$value"
        }

...
        val defaultStr=DoubleNationalUtil.getDefaultDoubleStr(doubleValue,"#0.00")
        val defaultStr2=DoubleNationalUtil.getDefaultDoubleStr(doubleValue,"#0.00#",russianLocale)
        //设置整数位分隔符
        val defaultStr3=DoubleNationalUtil.getDefaultDoubleStr(doubleValue,"#,##0.00#")

结果:

String转Double

将String转换为Double,这里很容易抛出异常或结果与预期不一致的情况。比如:

  • 不同的语言间进行转换
  • 当前程序语言和系统语言不一致,使用Double. parseDouble方法

使用Double. parseDouble:

try {
        //chineseStr=1,258.69999
        java.lang.Double.parseDouble(chineseStr)
        //russianStr=1 258,69999
        java.lang.Double.parseDouble(russianStr)
    }catch (e:Exception){
        e.printStackTrace()
    }

这里不管是先转换中文String还是俄文String,结果都会抛出异常。
不管是中文环境下,还是俄文环境下,结果都会抛出异常。
所以不建议大家采用Double. parseDouble方法将String转换为Double

使用NumberFormat:

下面使用NumberFormat转换,查看语言不一致的输出结果。
为方便阅读,命名方式没有按照驼峰命名,请大家忽略。

fun getDouble(value: String, locale: Locale = Locale.CHINA): Double {
            try {
                val format = NumberFormat.getNumberInstance(locale)
                //等同于NumberFormat.getNumberInstance
                //val format = DecimalFormat.getNumberInstance(locale)
                return format.parse(value).toDouble()
            } catch (e: Exception) {
                e.printStackTrace()
            }
            //真实情况请勿返回0.0
            return 0.0
        }
...
        //输出中文格式化字符串
        val chineseStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,Locale.CHINA)
        //中文转中文double
        val chineseStr_chinese = DoubleNationalUtil.getDouble(chineseStr, Locale.CHINA)
        //中文转俄文double
        val chineseStr_russian = DoubleNationalUtil.getDouble(chineseStr, russianLocale)
        //中文转英语double
        val chineseStr_english = DoubleNationalUtil.getDouble(chineseStr, Locale.US)
        //输出俄文格式化字符串
        val russianStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,russianLocale)
        //俄文转中文double
        val russianStr_chinese = DoubleNationalUtil.getDouble(russianStr, Locale.CHINA)
        //俄文转俄文double
        val russianStr_russian = DoubleNationalUtil.getDouble(russianStr, russianLocale)
        //俄文转英语double
        val russianStr_english = DoubleNationalUtil.getDouble(russianStr, Locale.US)

结果:
只有语言相互对应,才能保证输出结果是正确的。

其它

涉及到小数或者金额的计算,请使用BigDecimal类。
喜欢本篇的朋友,不要忘记点赞哟。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容