首页 > 解决方案 > 根据选择的货币代码格式化货币,而不考虑设备的区域设置 (Swift)

问题描述

我正在尝试根据用户选择的货币格式化货币。如果未选择货币,则使用设备的当前区域设置进行格式化。但是,我遇到了问题:

我正在使用数字格式化程序将双精度格式化为货币字符串。

let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    formatter.currencySymbol = ""
        
    if currencyCode != nil {
        formatter.currencyCode = currencyCode
    }
        
    let amount = Double(amt/100) + Double(amt%100)/100
    return formatter.string(from: NSNumber(value: amount))
}

currencyCode 基本上是用户选择的货币。但是,如果说用户选择 EURO,则格式与 USD 几乎相同,这意味着它不尊重所选货币。我知道我们不可能从 currencyCode 中创建一个区域设置,因为欧元在 26 个不同的国家/地区使用,因此不可能得出正确的区域设置。

此外,由于我使用的格式基本上填充小数位,然后 ONES、Tenths 等以及某些货币不支持小数位,例如 PKR(巴基斯坦卢比),那么我该如何满足呢?

所以我的问题是,无论选择哪种设备区域设置,如何正确格式化货币。如果我的设备语言环境是美元并且我创建了一个欧元列表,我希望列表中的所有付款都采用欧元格式。因此,如果以美元计价为 3,403.23 美元,以欧元计价,则应为 3 403,23 欧元。

关于我应该如何进行格式化的任何建议?谢谢!

标签: iosswiftlocalecurrencynumberformatter

解决方案


简而言之

与货币相关的语言环境设置有两种:

  • 取决于货币:这些与货币价值有关,仅取决于货币,并且在您使用该货币的任何地方都保持有效。这只是ISO 4217定义的国际 ISO 代码和小数位数。
  • 文化背景:这些取决于与用户的语言和国家相关的使用和实践,不是直接与货币有关。通常,它是货币代码或符号相对于值的位置,以及小数点和千位分隔符。

幸运的是,Swift 很好地发挥了作用。这里有一些代码,允许您调整与货币相关的设置,而无需触及对用户很重要的文化设置。我还将解释为什么您不应该更改所有本地设置。

编码

这里是演示代码,带有几种代表性货币:

let value: Double = 1345.23
for mycur in ["USD", "TND", "EUR", "JPY" ] {
    let myformatter = NumberFormatter()
    myformatter.numberStyle = .currencyISOCode
    let newLocale = "\(Locale.current.identifier)@currency=\(mycur)" // this is it!
    myformatter.locale = Locale(identifier:newLocale)
    print ("currency:\(mycur): min:\(myformatter.minimumFractionDigits) max:\(myformatter.maximumFractionDigits)" 
    print ("result: \(myformatter.string(from: value as NSNumber) ?? "xxx")")
}

对于代表性演示,我使用过:

  • 美元和欧元,与大多数货币一样,可以分为 100 个子单位(美分),
  • TND (突尼斯第纳尔),与少数其他第纳尔货币一样,可以分为 1000 个子单位(毫米),
  • JPY (日元),在过去可以分成如此小的价值的子单位(sens),以至于日本政府决定不再使用它们。这就是日元金额不再有小数的原因。

结果

对于用户而言,将受益于最小惊讶原则,并看到小数和千位分隔符以及他/她习惯的定位。

在我当前的语言环境中(在我的语言中,货币代码在右边,小数用逗号分隔,千位用硬空格分隔)结果将是:

cur:USD: min:2 max:2 result: 1  345,23 USD
cur:TND: min:3 max:3 result: 1  345,230 TND
cur:EUR: min:2 max:2 result: 1  345,23 EUR
cur:JPY: min:0 max:0 result: 1  345 JPY

但是,如果您通常在英语环境中工作,例如在美国文化中,您会得到:

cur:USD: min:2 max:2 result: USD 1,345.23
cur:TND: min:3 max:3 result: TND 1,345.230
cur:EUR: min:2 max:2 result: EUR 1,345.23
cur:JPY: min:0 max:0 result: JPY 1,345

这个怎么运作:

代码的诀窍是创建一个新的语言环境,只需更改货币设置,但保持所有其他国家和语言相关参数不变。

    let newLocale = "\(Locale.current.identifier)@currency=\(mycur)" // this is it!
    myformatter.locale = Locale(identifier:newLocale)

为什么你不应该完全实现你想要的

如果您开始调整定位以采用货币来源国家/地区的语言的做法,您可能会激怒那些不再在他们期望的地方看到货币代码的用户。幸运的是,它不会造成真正的混乱。

示例:欧元是具有不同文化的国家的货币。因此,关于货币或货币符号定位的规则被定义为取决于金额出现的文本的语言。 官方参考

现在,如果您开始采用另一种语言或国家/地区的千位和小数分隔符,因为它是货币的母国,这将造成真正的混乱,尤其是对于较小的金额。此外,这并不总是可能的。

示例:在加拿大,讲法语的加拿大人使用逗号小数分隔符书写相同的货币金额,而讲英语的加拿大人使用点小数分隔符书写相同的货币金额。这清楚地表明,决定使用分隔符的不是货币,而是用户的语言。

因此,您应该尊重用户在这方面的设置,并且只调整特定于货币的设置。


推荐阅读