初步意见:
这里的主要问题是,我需要一个国际化的解决方案,它依赖于内置的iOS/macOS功能。因此,我不能使用任何基于给定的十进制分隔符的解决方案,因为它会根据用户的区域设置而改变,也不能使用硬编码字符串。这就是为什么我只能对Roman的解决方案投赞成票,因为它不能完全满足需求,而不能接受它。
let value = 12345.678 // value to spell
currency: String? = nil // ISO currency if you want one specific
1.初始化格式化程序:
let formatter = NumberFormatter() // formatter with user's default locale
formatter.locale = Locale.init(identifier: "en_US.UTF-8") // for demo only
如果我使用地区所在国的货币,这很好用。但是如果我使用另一种货币,格式化程序的行为对于某些格式样式可能是错误的。所以,诀窍是使用
特定于货币的区域设置
:
if currency != nil {
let newLocale = "\(formatter.locale.identifier)@currency=\(currency!)"
formatter.locale = Locale(identifier:newLocale)
}
有趣的
Foundation
其特点是,当我更改货币时,特定于货币的区域设置(即货币代码、货币符号和位数)会自动更改:
formatter.numberStyle = .currency
let decimals = max(formatter.minimumFractionDigits, formatter.maximumFractionDigits)
2.以正确的语法形式获取货币全名
decimals
,但我想要全名
spelledCurrency
货币。问题是有些语言是中性的。对于某些语言,你必须写出1个单位的单数,但从2开始是复数。对于某些语言,小数在单数和复数之间的选择很重要。幸运的是,
NumberFormatter
.currencyPlural
. 这种奇怪的样式用数字来写数字,但是根据特定语言的规则来写普通货币。它的魔力:
问题是我只想要货币的名称:没有空格,没有分隔符,没有数字。我希望它适用于日语(语言环境
"ja_JP.UTF-8"
)还有。幸运的是,我们可以很容易地过滤字符串:
formatter.numberStyle = .currencyPlural
var spelledCurrency : String
if let plural = formatter.string(from: value as NSNumber) {
// Keep only the currency spelling: trim spaces, punctuation and digits
// ASSUMPTION: decimal and thousand separators belong to punctuation
var filter = CharacterSet()
filter.formUnion(.punctuationCharacters)
filter.formUnion(.decimalDigits)
filter.formUnion(.whitespaces)
spelledCurrency = plural.trimmingCharacters(in: filter)
}
else {
// Fall back - worst case: the ISO code XXX for no currency is used
spelledCurrency = formatter.currencyCode ?? (formatter.internationalCurrencySymbol ?? "XXX")
}
我用了
value
定义为
Double
. 但你很容易适应
Decimal
类型,whcih对货币更为稳健。诀窍是
wholePart
以货币单位表示,以及
wholeDecimal
,即,以整数表示的部分,对应于货币的子单元数(这是已知的,由于货币的小数数):
let decimalPart = value.truncatingRemainder(dividingBy: 1)
let wholePart = value - decimalPart
let wholeDecimals = floor(decimalPart * pow(10.0, Double(decimals)))
4详细说明部件
这里我们用的是本地人
.spellOut
formatter.numberStyle = .spellOut
let wholeString = formatter.string(from: wholePart as NSNumber) ?? "???"
let decimalString = formatter.string(from: wholeDecimals as NSNumber) ?? "???"
5总装
我们现在必须组装部件。但我们不应该说20美元5.7美元,而是应该正式地说20美元57美分。我决定更换
"and"
// ASSUMPTION: currency is always between whole and fractional
// TO BE VERIFIED: Ok for Right to left writing?
var spelled : String
if (decimals>0 && wholeDecimals != 0) {
spelled = "\(wholeString) \(spelledCurrency) \(decimalString)"
} else {
spelled = "\(wholeString) \(spelledCurrency)"
}
print (">>>>> \(spelled)")
以下是一些语言的测试结果:
en, USD: twelve thousand three hundred forty-five US dollars sixty-seven
fr, EUR: douze mille trois cent quarante-cinq euros soixante-sept
de, EUR: zwölfÂtausendÂdreiÂhundertÂfünfÂundÂvierzig Euro siebenÂundÂsechzig
nl, EUR: twaalfÂduizendÂdrieÂhonderdÂvijfÂenÂveertig euro zevenÂenÂzestig
it, EUR: dodiciÂmilaÂtreÂcentoÂquarantaÂcinque euro sessantaÂsette
es, EUR: doce mil trescientos cuarenta y cinco euros sesenta y siete
ja, JPY: ä¸ä¸äºåä¸ç¾ååäº å
fr, TND: douze mille trois cent quarante-cinq dinars tunisiens six cent soixante-dix-sept
最后一个测试显示的舍入错误
双倍
,所以最好使用
十进制的
.