diff --git a/TalerCommon/Helper/CurrencySpecification.swift b/TalerCommon/Helper/CurrencySpecification.swift index 5a6811ee..5ea2b6ac 100644 --- a/TalerCommon/Helper/CurrencySpecification.swift +++ b/TalerCommon/Helper/CurrencySpecification.swift @@ -181,29 +181,46 @@ public struct CurrencyInfo: Sendable { } } + /// Replace whichever currency markers the formatter actually has. + /// + /// `NumberFormatter.currencySymbol` and `.currencyCode` are + /// implicitly-unwrapped optionals, and both are nil when the formatter's + /// locale carries no region -- `de` rather than `de_DE`. `CurrencyFormatter` + /// takes its locale from `Locale.autoupdatingCurrent`, so that happens + /// whenever the app runs with `-AppleLanguages`/`-AppleLocale` set to a bare + /// language, which is what Xcode's "Application Language" scheme option and + /// UI-test runs do. Passing such a value straight to + /// `replacingOccurrences(of:)` traps and takes the app down. + private func replacingCurrencyMarkers(in aString: String, + with replacement: String) -> String { + var result = aString + if let symbol = formatter.currencySymbol, !symbol.isEmpty { + result = result.replacingOccurrences(of: symbol, with: replacement) + } + if let code = formatter.currencyCode, !code.isEmpty { + result = result.replacingOccurrences(of: code, with: replacement) + } + return result + } + func currencyString(_ aString: String, useISO: Bool = false) -> String { if !useISO { if let aSymbol = altUnitSymbol { - let symbolString = aString.replacingOccurrences(of: formatter.currencySymbol, with: aSymbol) - return symbolString.replacingOccurrences(of: formatter.currencyCode, with: aSymbol) + return replacingCurrencyMarkers(in: aString, with: aSymbol) } if let aName = altUnitName0 { let spacedName = formatter.leadingCurrencySymbol ? aName + SPACE : SPACE + aName - let spacedString1 = aString.replacingOccurrences(of: formatter.currencySymbol, with: spacedName) - let spacedString2 = spacedString1.replacingOccurrences(of: formatter.currencyCode, with: spacedName) - let spacedString3 = spacedString2.replacingOccurrences(of: " ", with: " ") // ensure we have only 1 space - return spacedString3 + let spaced = replacingCurrencyMarkers(in: aString, with: spacedName) + return spaced.replacingOccurrences(of: " ", with: " ") // ensure we have only 1 space } } let currency = formatter.currency if !currency.isEmpty { let spacedName = formatter.leadingCurrencySymbol ? currency + SPACE : SPACE + currency - let spacedString1 = aString.replacingOccurrences(of: formatter.currencySymbol, with: spacedName) - let spacedString2 = spacedString1.replacingOccurrences(of: formatter.currencyCode, with: spacedName) - let spacedString3 = spacedString2.replacingOccurrences(of: " ", with: " ") // ensure we have only 1 space - return spacedString3 + let spaced = replacingCurrencyMarkers(in: aString, with: spacedName) + return spaced.replacingOccurrences(of: " ", with: " ") // ensure we have only 1 space } return aString }