Files
ContainerController/Example/ContainerControllerSwift/Localization/LocalizationManager.swift
T
2021-11-16 15:08:55 +03:00

274 lines
9.0 KiB
Swift

//
// LocalizationManager.swift
// KeyGroupFramework
//
// Created by Nikita Bondar on 22/11/2018.
// Copyright © 2018 KeyGroup. All rights reserved.
//
import Foundation
extension Notification.Name {
public static let kGTNotificationLanguageChanged = Notification.Name("kGTNotificationLanguageChanged")
}
open class LocalizationManager {
// MARK: - Properties
public private(set) static var locale: Locale = .current
public static var availableLocales: [String] = ["en"]
private static var languageCode: String = ""
private static var regionCode: String = ""
private static var languageBundle: Bundle?
private static var englishLanguageBundle = languageBundle(withCode: "en", andType: "lproj")
private static var nakedLocalizationEnabled: Bool = false
// MARK: - Static part
public static func setNakedLocalization(to value: Bool) {
nakedLocalizationEnabled = value
NotificationCenter.default.post(name: .kGTNotificationLanguageChanged, object: nil)
}
public static func format(key: String, values: [String]) -> String {
if !nakedLocalizationEnabled {
let key = _L(key)
let formatCounter = key.components(separatedBy: "%@").count - 1
if formatCounter != values.count {
return "FORMAT ERROR: \(key) -\(values)"
}
let args: [CVarArg] = values
let formattedString = withVaList(args) { NSString(format: key, arguments: $0) } as String
return formattedString
} else {
return key
}
}
public static func localizedString(_ key: String) -> String {
initLanguageCode()
if !nakedLocalizationEnabled {
let langCode = Locale.current.languageCode ?? "en"
// languageBundle = languageBundle(withCode: languageCode, andType: "lproj")
languageBundle = languageBundle(withCode: langCode, andType: "lproj")
var val = languageBundle?.localizedString(forKey: key, value: key, table: nil)
val = val?.replacingOccurrences(of: "%s", with: "%@")
if val == key {
guard let englishString = englishLanguageBundle?.localizedString(forKey: key,
value: key,
table: nil) else { return key }
return englishString.replacingOccurrences(of: "%s", with: "%@")
}
return val ?? key
} else {
return key
}
}
public static func localizedTitleForCurrency(isoCode: String?) -> String {
if let isoCode = isoCode {
return self.currentLocale().displayName(forKey: .currencyCode, value: isoCode) ?? ""
} else {
return ""
}
}
public static func localizedString(forReplacedKey key: String) -> String {
var replaced = localizedString(key)
replaced = replaced.replacingOccurrences(of: "%s", with: "%@")
return replaced
}
public static func localizedString(forCount count: Float, key: String) -> String {
initLanguageCode()
if !nakedLocalizationEnabled {
let fewKey = localizedString(key.appending("_FEW"))
let manyKey = localizedString(key.appending("S"))
let singleKey = localizedString(key)
let countInt = Int(count)
if languageCode == "ru" {
if count < 1 && count > 0 {
return fewKey
}
let mod100 = countInt % 100
let mod10 = countInt % 10
switch mod10 {
case 1: return mod100 == 11 ? manyKey : singleKey
case 2, 3, 4: return mod100 > 10 && mod100 < 20 ? manyKey : fewKey
case 5, 6, 7, 8, 9, 0: return manyKey
default: return manyKey
}
}
return count <= 1 ? singleKey : manyKey
} else {
return key
}
}
public static func currentLanguageName() -> String {
initLanguageCode()
return languageName(withCode: languageCode)
}
public static func currentLanguageCode() -> String {
initLanguageCode()
return languageCode
}
// class func currentCurrencyCode() -> String {
// return ""
// }
public static func currentRegionCode() -> String {
let locale = Locale.current as NSLocale
if let country = locale.object(forKey: .countryCode) as? String {
return country
}
// writeToLog
return ""
}
public static func languageName(withCode code: String) -> String {
let locale = NSLocale(localeIdentifier: code)
let newCode = languageCode(withLocale: code)
let language = locale.displayName(forKey: .identifier, value: newCode)
return language ?? ""
}
public static func countryName(withCode code: String) -> String {
let locale = self.currentLocale()
if #available(iOS 10.0, *) {
// let name = locale.localizedString(forCurrencyCode: code)
let name = locale.displayName(forKey: .countryCode, value: code)
return name ?? ""
} else {
return ""
}
}
public static func currentLocale() -> NSLocale {
initLanguageCode()
let locale = NSLocale(localeIdentifier: languageCode)
return locale
}
public static func setCurrentLocale(code: String) {
let code = code.lowercased()
guard languageCode != code else { return }
guard availableLocales.contains(code) else { return }
let appleLanguages = code != "sys" ? [code] : []
UserDefaults.standard.set(appleLanguages, forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
languageCode = code
self.regionCode = Locale.current.regionCode ?? ""
languageBundle = languageBundle(withCode: languageCode, andType: "lproj")
reinstallLocale(languageCode: languageCode, regionCode: regionCode, posix: Date.is12HourFormat)
NotificationCenter.default.post(name: .kGTNotificationLanguageChanged, object: nil)
}
// MARK: - Private part
private static func initLanguageCode() {
guard languageCode.isEmpty else { return }
let languageCodeArray = UserDefaults.standard.object(forKey: "AppleLanguages") as? [String] ?? []
var code = languageCodeArray.count == 1 ? languageCodeArray[0] : Locale.preferredLanguages[0]
code = languageCode(withLocale: code)
if !availableLocales.contains(code) {
code = "en"
}
languageCode = code
languageBundle = nil
}
private static func languageCode(withLocale locale: String) -> String {
var code = locale
if let range = code.range(of: "-") {
let idx = NSRange(range, in: code).location
code = String( code[0..<idx] )
}
return code
}
private static func languageBundle(withCode code: String, andType type: String) -> Bundle? {
let path = Bundle.main.path(forResource: code, ofType: type)
return Bundle(path: path ?? "")
}
private static func reinstallLocale(languageCode: String, regionCode: String, posix: Bool = false) {
var identifier = "\(languageCode.lowercased())_\(regionCode.uppercased())"
if posix {
identifier += "_POSIX"
}
locale = Locale(identifier: identifier)
}
}
extension String {
subscript(value: CountableClosedRange<Int>) -> Substring {
return self[index(at: value.lowerBound)...index(at: value.upperBound)]
}
subscript(value: CountableRange<Int>) -> Substring {
return self[index(at: value.lowerBound)..<index(at: value.upperBound)]
}
subscript(value: PartialRangeUpTo<Int>) -> Substring {
return self[..<index(at: value.upperBound)]
}
subscript(value: PartialRangeThrough<Int>) -> Substring {
return self[...index(at: value.upperBound)]
}
subscript(value: PartialRangeFrom<Int>) -> Substring {
return self[index(at: value.lowerBound)...]
}
func index(at offset: Int) -> String.Index {
return index(startIndex, offsetBy: offset)
}
}
extension Date {
static public var is12HourFormat: Bool {
let locale = Locale.current
let formatter = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: locale)
return formatter?.contains("a") ?? false
}
}