Локализация рублей в Swift 5.3, XCode 13 — RUB вместо ₽

Делаю универсальное приложение для iOS, iPadOS, macOS. Приложение работает с кодами валют, приходящими из JSON API: RUB, USD, EUR и другие. Для отображения в приложении я конвертирую их в символы: ₽, $, €. Вроде задача элементарная и частично у меня получилось.

Воспользовался классом Locale.

Теперь внимание: все три символа прекрасно конвертируются в iOS Simulator, но ₽ остаётся как RUB на реальных устройствах — iOS, iPadOS, macOS.

import UIKit

func getSymbol(code: String) -> String? {
    let result = Locale
        .availableIdentifiers
        .map { Locale(identifier: $0) }
        .first { $0.currencyCode == code }

    return result?.currencySymbol
}

print(getSymbol(code: "RUB")!) // ₽

Этот пример для Playground работает и заменяет RUB на ₽. Этот же код не работает на реальном iPhone, iPad, macOS.

В чём может быть проблема? Выглядит как баг библиотеки локализации.


Ответы (2 шт):

Автор решения: schmidt9

Это не баг, а фича. Если выполнить следующий код

for identifier in Locale.availableIdentifiers {
    let locale = Locale(identifier: identifier)
            
    if locale.currencyCode == code {
        print("\(identifier)\t\(locale.currencyCode)\t\(locale.currencySymbol)")
    }

}

то мы получим

ba_RU   Optional("RUB") Optional("RUB")
ru_RU   Optional("RUB") Optional("₽")
cv_RU   Optional("RUB") Optional("RUB")
sah_RU  Optional("RUB") Optional("₽")
ce_RU   Optional("RUB") Optional("₽")
myv_RU  Optional("RUB") Optional("RUB")
tt_RU   Optional("RUB") Optional("₽")
os_RU   Optional("RUB") Optional("₽")
en_RU   Optional("RUB") Optional("RUB")

То бишь для разных локалей символ разный, и в вашем коде он видимо берется для ba_RU.

Вариант 1 - грязный фикс - это исправить ваш код таким образом, но не факт, что он будет работать для других валют

let result = Locale
            .availableIdentifiers
            .map { Locale(identifier: $0) }
            .first { $0.currencyCode == code && $0.currencySymbol != code } // <--

Вариант 2 - использовать напрямую локаль (например текущую локаль юзера, если мы делаем вывод в интерфейс), а не код валюты, так как символ валюты как мы видим для разных локалей разный, и здесь вопрос, для какой локали вы хотите этот символ получить

func getSymbol(locale: Locale) -> String? {
    locale.currencySymbol
}
→ Ссылка
Автор решения: foxwwweb

Решения пробовал разные, решил добавить костыль, что если RUB — то ₽:

let result = Locale
    .availableIdentifiers
    .map { Locale(identifier: $0) }
    .first { $0.currencyCode == currency.rawValue }
        
return result?.currencySymbol == "RUB" ? "₽" : String(result?.currencySymbol ?? "?")
→ Ссылка