Skip to content

Instantly share code, notes, and snippets.

@DisappearPing
Created April 21, 2020 03:50
Show Gist options
  • Save DisappearPing/43bbbbd601b3599ecec110d0a4529011 to your computer and use it in GitHub Desktop.
Save DisappearPing/43bbbbd601b3599ecec110d0a4529011 to your computer and use it in GitHub Desktop.
In-app change language to lokalize temp gist
//
// Bundle-AppLocale.swift
// Radiant Tap Essentials
//
// Copyright © 2016 Aleksandar Vacić, Radiant Tap
// MIT License · http://choosealicense.com/licenses/mit/
//
import Foundation
import Lokalise
/*
Credits:
https://www.factorialcomplexity.com/blog/2015/01/28/how-to-change-localization-internally-in-your-ios-application.html
*/
class MyLocale {
enum Language: String {
case bySystem = "bySystem"
case en = "en"
case tw = "zh-Hant"
case cn = "zh-Hans"
case ja = "ja"
case ms = "ms"
func getName() -> String {
switch self {
case .bySystem:
return NSLocalizedString("MyLocale_bySystem", comment: "")
case .tw:
return "繁體中文"
case .ja:
return "日本語"
case .cn:
return "简体中文"
case .en:
return "English"
case .ms:
return "Bahasa Melayu"
}
}
}
static var mDeviceLocaldID: String = ""
static var mCurrentLocaleID: String? = nil
static var mLocale: Locale = Locale.current
static var mUserDefault = UserDefaults.standard
static func initialize(locale: Locale, userDefault: UserDefaults) {
print("mLocale = \(mLocale)")
print("mUserDefault = \(mUserDefault)")
mLocale = locale
mUserDefault = userDefault
mDeviceLocaldID = mLocale.identifier
print("mDeviceLocaldID = \(mDeviceLocaldID)")
// NSLocale.swizzle(selector: #selector(getter: NSLocale.current))
mCurrentLocaleID = mUserDefault.object(forKey: "savedLocaleID") as? String
if mCurrentLocaleID == nil {
mCurrentLocaleID = Language.bySystem.rawValue
// Bundle.clearInAppOverrides()
} else {
// Bundle.enforceLanguage(code: currentLocale.identifier)
let lokalizedLocal = self.identifyIDToLokalizeSupportID(currentLocale.identifier)
Lokalise.shared.setLocalizationLocale(lokalizedLocal, makeDefault: false) { (error) in
print("Lokalise = \(error?.localizedDescription)")
}
}
}
@objc static var currentLocale: Locale {
if let id = mCurrentLocaleID, mCurrentLocaleID != Language.bySystem.rawValue {
return Locale(identifier: id)
}
return Locale(identifier: mDeviceLocaldID)
}
static func getCurrentLanguage() -> Language {
var id: String
id = mCurrentLocaleID!
if id == Language.bySystem.rawValue {
return .bySystem
} else if id.contains("Hant") || id.contains("zh_TW") || id.contains("zh_HK") {
return .tw
} else if id.hasPrefix("zh") {
return .cn
} else if id.hasPrefix("ja") {
return .ja
} else if id.contains("ms") {
return .ms
} else {
return .en
}
}
static func getCurrentLanguageName() -> String {
return getCurrentLanguage().getName()
}
static func setCurrentLocale(lang: Language){
let id = lang.rawValue
mCurrentLocaleID = id
let userDefault = UserDefaults.standard
userDefault.set(id, forKey: "savedLocaleID")
print("mCurrentLocaleID = \(mCurrentLocaleID)")
print("currentLocale.identifier = \(currentLocale.identifier)")
print("Locale.current = \(Locale.current)")
print("Locale.current.languageCode = \(Locale.current.languageCode)")
// if currentLocale.identifier.contains("TW") {
// Lokalise.shared.setLocalizationLocale(Locale(identifier: "zh-Hant"), makeDefault: false) { (error) in
// print("Lokalise = \(error?.localizedDescription)")
// }
// } else {
let lokalizedLocal = self.identifyIDToLokalizeSupportID(currentLocale.identifier)
Lokalise.shared.setLocalizationLocale(lokalizedLocal, makeDefault: false) { (error) in
print("Lokalise = \(error?.localizedDescription)")
}
// }
// Bundle.enforceLanguage(code: currentLocale.identifier)
AppDelegate.getDelegate()?.setupCategoryActions()
MyApi.shared.updatePushToken()
EventTracker.languageSetting(eventProperties: nil, userProperties: ["language": MyLocale.currentLocale.identifier]).fire()
}
static func identifyIDToLokalizeSupportID(_ id: String) -> Locale {
if id.contains("Hant") || id.contains("zh_TW") || id.contains("zh_HK") {
return Locale(identifier: Language.tw.rawValue)
} else if id.hasPrefix("zh") {
return Locale(identifier: Language.cn.rawValue)
} else if id.hasPrefix("ja") {
return Locale(identifier: Language.ja.rawValue)
} else if id.contains("ms") {
return Locale(identifier: Language.ms.rawValue)
} else {
return Locale(identifier: Language.en.rawValue)
}
}
}
/// Custom subclass to enable on-the-fly Bundle.main language change
public final class LocalizedBundle: Bundle {
/// Overrides system method and enforces usage of particular .lproj translation bundle
override public func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
if let bundle = Bundle.main.localizedBundle {
return bundle.localizedString(forKey: key, value: value, table: tableName)
}
return super.localizedString(forKey: key, value: value, table: tableName)
}
}
public extension Bundle {
private struct AssociatedKeys {
static var b = "LocalizedMainBundle"
}
fileprivate var localizedBundle: Bundle? {
get {
// warning: Make sure this object you are fetching really exists
return objc_getAssociatedObject(self, &AssociatedKeys.b) as? Bundle
}
}
static func mapIDToLproj(id: String) -> String {
if id.contains("Hant") || id.contains("zh_TW") || id.contains("zh_HK") {
return "zh-Hant"
} else if id.hasPrefix("zh") {
return "zh-Hans"
} else if id.hasPrefix("ja") {
return "ja"
} else if id.contains("ms") {
return "ms"
} else {
return "Base"
}
}
/// Loads the translations for the given language code.
///
/// - Parameter code: two-letter ISO 639-1 language code
public static func enforceLanguage(code: String) {
let lproj = mapIDToLproj(id: code)
guard let path = Bundle.main.path(forResource: lproj, ofType: "lproj") else { return }
guard let bundle = Bundle(path: path) else { return }
// prepare translated bundle for chosen language and
// save it as property of the Bundle.main
objc_setAssociatedObject(Bundle.main, &AssociatedKeys.b, bundle, .OBJC_ASSOCIATION_RETAIN)
// now override class of the main bundle (only once during the app lifetime)
// this way, `localizedString(forKey:value:table)` method in our subclass above will actually be called
DispatchQueue.once(token: AssociatedKeys.b) {
object_setClass(Bundle.main, LocalizedBundle.self)
}
}
/// Removes the custom bundle
public static func clearInAppOverrides() {
objc_setAssociatedObject(Bundle.main, &AssociatedKeys.b, nil, .OBJC_ASSOCIATION_RETAIN)
}
}
extension NSLocale {
static func swizzle(selector: Selector) {
let originalSelector = selector
let swizzledSelector = #selector(getter: MyLocale.currentLocale)
let originalMethod = class_getClassMethod(self, originalSelector)
let swizzledMethod = class_getClassMethod(self, swizzledSelector)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
public extension DispatchQueue {
private static var _onceTracker = [String]()
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: String, block:() -> Void) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
if _onceTracker.contains(token) {
return
}
_onceTracker.append(token)
block()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment