Get currency symbols from currency code with swift

ε祈祈猫儿з 提交于 2019-12-03 05:47:22

问题


How can I get the currency symbols for the corresponding currency code with Swift (macOS).

Example:

  • EUR = €1.00
  • USD = $1.00
  • CAD = $1.00
  • GBP = £1.00

My code:

var formatter = NSNumberFormatter()
formatter.currencySymbol = getSymbol(currencyCode)
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
let number = NSNumber(double: (amount as NSString).doubleValue)
let amountWithSymbol = formatter.stringFromNumber(number)!

getSymbol(_ currencyCode: String) -> String

or.. is there a better way?


回答1:


A bit late but this is a solution I used to get the $ instead of US$ etc. for currency symbol.

/*
 * Bear in mind not every currency have a corresponding symbol. 
 *
 * EXAMPLE TABLE
 *
 * currency code | Country & Currency | Currency Symbol
 *
 *      BGN      |   Bulgarian lev    |      лв   
 *      HRK      |   Croatian Kuna    |      kn
 *      CZK      |   Czech  Koruna    |      Kč
 *      EUR      |       EU Euro      |      €
 *      USD      |     US Dollar      |      $
 *      GBP      |   British Pound    |      £
 */

func getSymbol(forCurrencyCode code: String) -> String? {
   let locale = NSLocale(localeIdentifier: code)
   return locale.displayNameForKey(NSLocaleCurrencySymbol, value: code)
}

Basically this creates NSLocale from your currency code and grabs the display attribute for the currency. In cases where the result matches the currency code for example SEK it will create new country specific locale by removing the last character from the currency code and appending "_en" to form SE_en. Then it will try to get the currency symbol again.

Swift 3 & 4

func getSymbol(forCurrencyCode code: String) -> String? {
    let locale = NSLocale(localeIdentifier: code)
    if locale.displayName(forKey: .currencySymbol, value: code) == code {
        let newlocale = NSLocale(localeIdentifier: code.dropLast() + "_en")
        return newlocale.displayName(forKey: .currencySymbol, value: code)
    }
    return locale.displayName(forKey: .currencySymbol, value: code)
}



回答2:


The proper way to do this is to let the frameworks provide the information for you.

You can retrieve that information using an obscure class method on NSLocale called localeIdentifierFromComponents(). That method will take a dictionary that defines various attributes of your locale, and then returns an identifier you can use to actually construct an NSLocale instance. Once you have the NSLocale, you can ask it for its CurrencySymbol, like this:

let currencyCode = "CAD"

let localeComponents = [NSLocaleCurrencyCode: currencyCode]
let localeIdentifier = NSLocale.localeIdentifierFromComponents(localeComponents)
let locale = NSLocale(localeIdentifier: localeIdentifier)
let currencySymbol = locale.objectForKey(NSLocaleCurrencySymbol) as! String
// currencySymbol is "CA$"



回答3:


The answer may be late but hopefully this helps clarify the root cause.

Problem

  • Currency code does not imply locale and region

The reason why CAD becomes CA$ is probably because NSLocale looks up the first matching currency code, and for CAD, these are the matching localeIdentifiers in order of NSLocale.availableLocaleIdentifiers

1. Optional("CA$") Optional("CA") iu_CA
2. Optional("$") Optional("CA") fr_CA
3. Optional("$") Optional("CA") en_CA

iu_CA is Inuktitut but I'm not sure why it's listed as CA$, but I hope the point is clear.

Similarly in CNY (Chinese Yuan):

1. Optional("CN¥") Optional("CN") en_CN
2. Optional("¥") Optional("CN") yue_CN
3. Optional("¥") Optional("CN") bo_CN
4. Optional("¥") Optional("CN") zh_CN
5. Optional("¥") Optional("CN") ug_CN
6. Optional("¥") Optional("CN") ii_CN

The reason for showing CN¥ when en_CN is probably because JPY also uses ¥.

In CHF (Switzerland Franc), they do not have a one-letter symbol:

1. Optional("CHF") Optional("LI") gsw_LI
2. Optional("CHF") Optional("CH") de_CH
...
9. Optional("CHF") Optional("CH") en_CH
10. Optional("CHF") Optional("CH") it_CH

Solution

Many apps vary, but this is the steps I took that I am happy with for my application:

  1. Find matching locale candidates using currency code lookup from all locale identifiers
  2. Pick the shortest symbol from the candidates
  3. Store the symbol somewhere so that it doesn't have to be computed each time

Implementation

func getSymbolForCurrencyCode(code: String) -> String {
    var candidates: [String] = []
    let locales: [String] = NSLocale.availableLocaleIdentifiers
    for localeID in locales {
        guard let symbol = findMatchingSymbol(localeID: localeID, currencyCode: code) else {
            continue
        }
        if symbol.count == 1 {
            return symbol
        }
        candidates.append(symbol)
    }
    let sorted = sortAscByLength(list: candidates)
    if sorted.count < 1 {
        return ""
    }
    return sorted[0]
}

func findMatchingSymbol(localeID: String, currencyCode: String) -> String? {
    let locale = Locale(identifier: localeID as String)
    guard let code = locale.currencyCode else {
        return nil
    }
    if code != currencyCode {
        return nil
    }
    guard let symbol = locale.currencySymbol else {
        return nil
    }
    return symbol
}

func sortAscByLength(list: [String]) -> [String] {
    return list.sorted(by: { $0.count < $1.count })
}

Usage

let usd = getSymbolForCurrencyCode(code: "USD")
let jpy = getSymbolForCurrencyCode(code: "JPY")
let cny = getSymbolForCurrencyCode(code: "CNY")
let cad = getSymbolForCurrencyCode(code: "CAD")
let uah = getSymbolForCurrencyCode(code: "UAH")
let krw = getSymbolForCurrencyCode(code: "KRW")
let zar = getSymbolForCurrencyCode(code: "ZAR")
let chf = getSymbolForCurrencyCode(code: "CHF")
let all = [usd, jpy, cny, cad, uah, krw, zar, chf]

(lldb) po all
▿ 8 elements
  - 0 : "$"
  - 1 : "¥"
  - 2 : "¥"
  - 3 : "$"
  - 4 : "₴"
  - 5 : "₩"
  - 6 : "R"
  - 7 : "CHF"

Problems

  1. Instinctively, I see that the one letter symbol approach can show an incorrect symbol if there are more than one distinct symbols for currency code, but I haven't seen such case.
  2. Computing this each time is heavy lifting so when a user sets their currency setting, it's wise to store the computed result and use that result upon each lookup



回答4:


An imperfect solution I found to get $ instead of US$ or CA$ was to attempt to match the user's current locale to the currency code first. This will work for situations where you're building a mobile app and an API is sending you currency code based on the settings in that user's account. For us the business case is that 99% of users have the same currency code set in their account on the backend (USD, CAD, EUR, etc.), where we're getting the information from, as they do on their mobile app where we're displaying currency the way a user would expect to see it (i.e. $50.56 instead of US$ 50.56).

Objective-C

- (NSLocale *)localeFromCurrencyCode:(NSString *)currencyCode {
    NSLocale *locale = [NSLocale currentLocale];
    if (![locale.currencyCode isEqualToString:currencyCode]) {
        NSDictionary *localeInfo = @{NSLocaleCurrencyCode:currencyCode};
        locale = [[NSLocale alloc] initWithLocaleIdentifier:[NSLocale localeIdentifierFromComponents:localeInfo]];
    }
    return locale;
}

Swift

func locale(from currencyCode: String) -> Locale {
    var locale = Locale.current
    if (locale.currencyCode != currencyCode) {
        let identifier = NSLocale.localeIdentifier(fromComponents: [NSLocale.Key.currencyCode.rawValue: currencyCode])
        locale = NSLocale(localeIdentifier: identifier) as Locale
    }
    return locale;
}



回答5:


I combined and improved all the suggestion here to have a drop-in(copy/paste) solution for the future readers(you).

It has its own local cache, case insensitive, and have an extension method to provide chaining for String. Swift 4/5 ready.

How to use:

"USD".currencySymbol //returns "$"
//OR
Currency.shared.findSymbol(currencyCode: "TRY") //returns "₺"

Tests:

    XCTAssertEqual("$", "USD".currencySymbol)
    XCTAssertEqual("₺", "TRY".currencySymbol)
    XCTAssertEqual("€", "EUR".currencySymbol)
    XCTAssertEqual("", "ASDF".currencySymbol)

Code:

class Currency {
    static let shared: Currency = Currency()

    private var cache: [String:String] = [:]

    func findSymbol(currencyCode:String) -> String {
        if let hit = cache[currencyCode] { return hit }
        guard currencyCode.count < 4 else { return "" }

        let symbol = findSymbolBy(currencyCode)
        cache[currencyCode] = symbol

        return symbol
    }

    private func findSymbolBy(_ currencyCode: String) -> String {
        var candidates: [String] = []
        let locales = NSLocale.availableLocaleIdentifiers

        for localeId in locales {
            guard let symbol = findSymbolBy(localeId, currencyCode) else { continue }
            if symbol.count == 1 { return symbol }
            candidates.append(symbol)
        }

        return candidates.sorted(by: { $0.count < $1.count }).first ?? ""
    }

    private func findSymbolBy(_ localeId: String, _ currencyCode: String) -> String? {
        let locale = Locale(identifier: localeId)
        return currencyCode.caseInsensitiveCompare(locale.currencyCode ?? "") == .orderedSame
            ? locale.currencySymbol : nil
    }
}

extension String {
    var currencySymbol: String { return Currency.shared.findSymbol(currencyCode: self) }
}



回答6:


SWIFT4 
//converting USD to $a and GBP to £ 
viewDidLoad() 
{ 
 print(getSymbolForCurrencyCode(code: "USD")!) // prints $ 
 print(getSymbolForCurrencyCode(code: "GBP")!) //prints £ 
}

func getSymbolForCurrencyCode(code: String) -> String? 
{ 
  let locale = NSLocale(localeIdentifier: code)
  return locale.displayName(forKey: NSLocale.Key.currencySymbol, value: code) 
}



回答7:


You can try this:

let formatter = NSNumberFormatter()

for locale in NSLocale.availableLocaleIdentifiers() {
    formatter.locale = NSLocale(localeIdentifier: locale)
    print("\(formatter.currencyCode) =  \(formatter.currencySymbol)")
}



回答8:


Swift 4 Version of Pancho's answer, As the String.characters is deprecated now.

We can simply apply dropLast() on String.

func getCurrencySymbol(from currencyCode: String) -> String? {

    let locale = NSLocale(localeIdentifier: currencyCode)
    if locale.displayName(forKey: .currencySymbol, value: currencyCode) == currencyCode {
        let newlocale = NSLocale(localeIdentifier: currencyCode.dropLast() + "_en")
        return newlocale.displayName(forKey: .currencySymbol, value: currencyCode)
    }
    return locale.displayName(forKey: .currencySymbol, value: currencyCode)
}



回答9:


Swift 4.2

// Currency Codes
Locale.isoCurrencyCodes

// Results ⬇︎
// ["ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK", "AON", "AOR", "ARA", "ARL", "ARM", "ARP", "ARS", "ATS", "AUD", "AWG", "AZM", "AZN", "BAD", "BAM", "BAN", "BBD", "BDT", "BEC", "BEF", "BEL", "BGL", "BGM", "BGN", "BGO", "BHD", "BIF", "BMD", "BND", "BOB", "BOL", "BOP", "BOV", "BRB", "BRC", "BRE", "BRL", "BRN", "BRR", "BRZ", "BSD", "BTN", "BUK", "BWP", "BYB", "BYN", "BYR", "BZD", "CAD", "CDF", "CHE", "CHF", "CHW", "CLE", "CLF", "CLP", "CNH", "CNX", "CNY", "COP", "COU", "CRC", "CSD", "CSK", "CUC", "CUP", "CVE", "CYP", "CZK", "DDM", "DEM", "DJF", "DKK", "DOP", "DZD", "ECS", "ECV", "EEK", "EGP", "EQE", "ERN", "ESA", "ESB", "ESP", "ETB", "EUR", "FIM", "FJD", "FKP", "FRF", "GBP", "GEK", "GEL", "GHC", "GHS", "GIP", "GMD", "GNF", "GNS", "GQE", "GRD", "GTQ", "GWE", "GWP", "GYD", "HKD", "HNL", "HRD", "HRK", "HTG", "HUF", "IDR", "IEP", "ILP", "ILR", "ILS", "INR", "IQD", "IRR", "ISJ", "ISK", "ITL", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRH", "KRO", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LSM", "LTL", "LTT", "LUC", "LUF", "LUL", "LVL", "LVR", "LYD", "MAD", "MAF", "MCF", "MDC", "MDL", "MGA", "MGF", "MKD", "MKN", "MLF", "MMK", "MNT", "MOP", "MRO", "MRU", "MTL", "MTP", "MUR", "MVP", "MVR", "MWK", "MXN", "MXP", "MXV", "MYR", "MZE", "MZM", "MZN", "NAD", "NGN", "NIC", "NIO", "NLG", "NOK", "NPR", "NZD", "OMR", "PAB", "PEI", "PEN", "PES", "PGK", "PHP", "PKR", "PLN", "PLZ", "PTE", "PYG", "QAR", "RHD", "ROL", "RON", "RSD", "RUB", "RUR", "RWF", "SAR", "SBD", "SCR", "SDD", "SDG", "SDP", "SEK", "SGD", "SHP", "SIT", "SKK", "SLL", "SOS", "SRD", "SRG", "SSP", "STD", "STN", "SUR", "SVC", "SYP", "SZL", "THB", "TJR", "TJS", "TMM", "TMT", "TND", "TOP", "TPE", "TRL", "TRY", "TTD", "TWD", "TZS", "UAH", "UAK", "UGS", "UGX", "USD", "USN", "USS", "UYI", "UYP", "UYU", "UZS", "VEB", "VEF", "VND", "VNN", "VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XEU", "XFO", "XFU", "XOF", "XPD", "XPF", "XPT", "XRE", "XSU", "XTS", "XUA", "XXX", "YDD", "YER", "YUD", "YUM", "YUN", "YUR", "ZAL", "ZAR", "ZMK", "ZMW", "ZRN", "ZRZ", "ZWD", "ZWL", "ZWR"]


// Validation
let currencyCode = "USD"
let isValid = 0 < Locale.isoCurrencyCodes.filter { $0 == currencyCode }.count


来源:https://stackoverflow.com/questions/31999748/get-currency-symbols-from-currency-code-with-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!