NumberFormatter 字符串作为次要货币单位而不是区域设置的主要单位(例如 99p 不是 £0.99)

NumberFormatter string as minor currency unit not major with locale (e.g 99p not £0.99)

我们使用此函数将 SKProduct 转换为消耗品包的每件商品的本地化价格:

static func pricePerUnit(_ product: SKProduct, quantity: NSDecimalNumber) -> String? {
    let numberFormatter = NumberFormatter()
    numberFormatter.numberStyle = .currency
    numberFormatter.locale = product.priceLocale

    let pricePerItem = product.price.dividing(by: quantity)

    guard let formattedPricePerItem = numberFormatter.string(from: pricePerItem) else {
        return nil
    }

    return "\(formattedPricePerItem) each"
}

例如,10 件商品售价 9.99 英镑对于英国用户为 0.99 英镑,对于美国用户为 0.99 美元。

理想情况下,如果金额小于一个货币单位(又名一美元),我们希望它以次要货币单位(例如美分、便士等)显示。

我找不到这个或其他任何答案的 NumberFormatter 样式。这可以使用 NumberFormatter 完成吗?

遗憾的是,这无法使用 NumberFormatter 完成。 NumberFormatter使用Locale获取货币格式,包括小数位数、正码、负码、货币符号等

(参见 Number Format Patterns in Unicode

但是,小货币的格式在 Unicode 中没有标准化(尽管一些必要的数据是,例如乘数)并且它不存在于 iOS Locale 数据中。

NumberFormatter 可能不直接支持分和便士的格式,但 NumberFormatter 足够灵活,您可以根据自己的意愿进行配置。例如,可以创建一个包含 2 NumberFormatter 的辅助结构:一个以美元和英镑的形式格式化,一个以分和便士的形式格式化。

struct CurrencyFormatter {
    private static let defaultFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        return formatter
    }()

    private static let alternativeFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.multiplier = 100
        return formatter
    }()

    static var alternativeCurrencySymbols = [
        "USD": "c",
        "GBP": "p"
    ]

    static func string(from number: NSNumber, locale: Locale) -> String? {
        var formatter = defaultFormatter

        if number.isLessThan(1),
            let currencyCode = locale.currencyCode,
            let alternativeCurrencySymbol = alternativeCurrencySymbols[currencyCode]
        {
            formatter = alternativeFormatter
            formatter.positiveSuffix = alternativeCurrencySymbol
            formatter.negativeSuffix = alternativeCurrencySymbol
        }

        formatter.locale = locale
        return formatter.string(from: number)
    }
}

let number = NSNumber(value: 0.7)
let locale = Locale(identifier: "en_GB")
if let str = CurrencyFormatter.string(from: number, locale: locale) {
    print(str) // result: 70p
}

该解决方案具有 built-in 故障保护机制。如果您没有为货币定义替代符号,它将回退到默认货币格式。您可以通过将语言环境更改为 fr_FR 来测试它,结果变为 0,70 €.