Ik maak een ios-app op XCODE 6.3 door Swift.
En mijn app heeft de taalkeuzefunctie zoals de afbeelding hieronder
Ik heb al een storyboard voor mijn lokale taal.
Maar ik kan niet vinden hoe ik de lokalisatie programmatisch uit de app kan wijzigen met de knop.
Iedereen weet hoe het moet
Antwoord 1, autoriteit 100%
Hier is een manier om het direct te wijzigen met Swift, voeg een extensiefunctie toe aan String:
extension String {
func localized(lang:String) ->String {
let path = NSBundle.mainBundle().pathForResource(lang, ofType: "lproj")
let bundle = NSBundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}}
Swift 4:
extension String {
func localized(_ lang:String) ->String {
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}}
in de veronderstelling dat je de normale Localizable.strings hebt ingesteld met lang_id.lproj (bijv. en.lproj, de.lproj etc.), kun je dit overal gebruiken waar je maar wilt:
var val = "MY_LOCALIZED_STRING".localized("de")
Antwoord 2, autoriteit 31%
Hiermee kunt u de taal wijzigendoor een UserDefaults
-sleutelbij te werken.
Dit is gebaseerd op het geweldige antwoord van @dijipiji. Dit is een Swift 3-versie.
extension String {
var localized: String {
if let _ = UserDefaults.standard.string(forKey: "i18n_language") {} else {
// we set a default, just in case
UserDefaults.standard.set("fr", forKey: "i18n_language")
UserDefaults.standard.synchronize()
}
let lang = UserDefaults.standard.string(forKey: "i18n_language")
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
Gebruik
Voeg gewoon .localized
toe aan je string, als zodanig:
"MyString".localized
, waarbij MyString
een sleutel is in het bestand Localizable.strings
.
De taal wijzigen
UserDefaults.standard.set("en", forKey: "i18n_language")
Antwoord 3, autoriteit 19%
Swift 4.2
In mijn geval, als de gebruiker de taalinstelling wijzigt, moet ik tijdens runtime 2 dingen bijwerken.
1. Localizable.strings
2. Storyboard-lokalisatie
Ik maak de @John Pang-code sneller
BundleExtension.swift
import UIKit
private var bundleKey: UInt8 = 0
final class BundleExtension: Bundle {
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
return (objc_getAssociatedObject(self, &bundleKey) as? Bundle)?.localizedString(forKey: key, value: value, table: tableName) ?? super.localizedString(forKey: key, value: value, table: tableName)
}
}
extension Bundle {
static let once: Void = { object_setClass(Bundle.main, type(of: BundleExtension())) }()
static func set(language: Language) {
Bundle.once
let isLanguageRTL = Locale.characterDirection(forLanguage: language.code) == .rightToLeft
UIView.appearance().semanticContentAttribute = isLanguageRTL == true ? .forceRightToLeft : .forceLeftToRight
UserDefaults.standard.set(isLanguageRTL, forKey: "AppleTe zxtDirection")
UserDefaults.standard.set(isLanguageRTL, forKey: "NSForceRightToLeftWritingDirection")
UserDefaults.standard.set([language.code], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
guard let path = Bundle.main.path(forResource: language.code, ofType: "lproj") else {
log(.error, "Failed to get a bundle path.")
return
}
objc_setAssociatedObject(Bundle.main, &bundleKey, Bundle(path: path), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
Taal.swift
import Foundation
enum Language: Equatable {
case english(English)
case chinese(Chinese)
case korean
case japanese
enum English {
case us
case uk
case australian
case canadian
case indian
}
enum Chinese {
case simplified
case traditional
case hongKong
}
}
extension Language {
var code: String {
switch self {
case .english(let english):
switch english {
case .us: return "en"
case .uk: return "en-GB"
case .australian: return "en-AU"
case .canadian: return "en-CA"
case .indian: return "en-IN"
}
case .chinese(let chinese):
switch chinese {
case .simplified: return "zh-Hans"
case .traditional: return "zh-Hant"
case .hongKong: return "zh-HK"
}
case .korean: return "ko"
case .japanese: return "ja"
}
}
var name: String {
switch self {
case .english(let english):
switch english {
case .us: return "English"
case .uk: return "English (UK)"
case .australian: return "English (Australia)"
case .canadian: return "English (Canada)"
case .indian: return "English (India)"
}
case .chinese(let chinese):
switch chinese {
case .simplified: return "简体中文"
case .traditional: return "繁體中文"
case .hongKong: return "繁體中文 (香港)"
}
case .korean: return "한국어"
case .japanese: return "日本語"
}
}
}
extension Language {
init?(languageCode: String?) {
guard let languageCode = languageCode else { return nil }
switch languageCode {
case "en", "en-US": self = .english(.us)
case "en-GB": self = .english(.uk)
case "en-AU": self = .english(.australian)
case "en-CA": self = .english(.canadian)
case "en-IN": self = .english(.indian)
case "zh-Hans": self = .chinese(.simplified)
case "zh-Hant": self = .chinese(.traditional)
case "zh-HK": self = .chinese(.hongKong)
case "ko": self = .korean
case "ja": self = .japanese
default: return nil
}
}
}
Zo gebruiken
var language: [Language] = [.korean, .english(.us), .english(.uk), .english(.australian), .english(.canadian), .english(.indian),
.chinese(.simplified), .chinese(.traditional), .chinese(.hongKong),
.japanese]
Bundle.set(language: languages[indexPath.row].language)
“Locale.current.languageCode” retourneert altijd de taal van de systeeminstelling.
We moeten dus “Locale.preferredLanguages.first” gebruiken. De retourwaarde ziet er echter uit als “ko-US”. Dit is het probleem ! Dus heb ik de LocaleManagergemaakt om alleen de taalcode te krijgen.
LocaleManager.swift
import Foundation
struct LocaleManager {
/// "ko-US" → "ko"
static var languageCode: String? {
guard var splits = Locale.preferredLanguages.first?.split(separator: "-"), let first = splits.first else { return nil }
guard 1 < splits.count else { return String(first) }
splits.removeLast()
return String(splits.joined(separator: "-"))
}
static var language: Language? {
return Language(languageCode: languageCode)
}
}
Zo gebruiken
guard let languageCode = LocaleManager.languageCode, let title = RemoteConfiguration.shared.logIn?.main?.title?[languageCode] else {
return NSLocalizedString("Welcome!", comment: "")
}
return title
Antwoord 4, autoriteit 16%
Bruikbare code in Swift 4:
extension Bundle {
private static var bundle: Bundle!
public static func localizedBundle() -> Bundle! {
if bundle == nil {
let appLang = UserDefaults.standard.string(forKey: "app_lang") ?? "ru"
let path = Bundle.main.path(forResource: appLang, ofType: "lproj")
bundle = Bundle(path: path!)
}
return bundle;
}
public static func setLanguage(lang: String) {
UserDefaults.standard.set(lang, forKey: "app_lang")
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
bundle = Bundle(path: path!)
}
}
en
extension String {
func localized() -> String {
return NSLocalizedString(self, tableName: nil, bundle: Bundle.localizedBundle(), value: "", comment: "")
}
func localizeWithFormat(arguments: CVarArg...) -> String{
return String(format: self.localized(), arguments: arguments)
}
}
bel:
let localizedString = "enter".localized()
stel een nieuwe landinstelling in (bijvoorbeeld “ru”):
Bundle.setLanguage(lang: "ru")
Antwoord 5, autoriteit 11%
Jeremy’s antwoord (hier) werkt ook goed op Swift 4 (ik heb net getest met een eenvoudige app en ik heb van taal veranderd gebruikt in de initiële weergavecontroller).
Hier is de Swift-versie van hetzelfde stuk code (om de een of andere reden geven mijn teamgenoten de voorkeur aan Swift-only dan gemengd met Objective-C, dus ik heb het vertaald):
import UIKit
private var kBundleKey: UInt8 = 0
class BundleEx: Bundle {
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
if let bundle = objc_getAssociatedObject(self, &kBundleKey) {
return (bundle as! Bundle).localizedString(forKey: key, value: value, table: tableName)
}
return super.localizedString(forKey: key, value: value, table: tableName)
}
}
extension Bundle {
static let once: Void = {
object_setClass(Bundle.main, type(of: BundleEx()))
}()
class func setLanguage(_ language: String?) {
Bundle.once
let isLanguageRTL = Bundle.isLanguageRTL(language)
if (isLanguageRTL) {
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
UserDefaults.standard.set(isLanguageRTL, forKey: "AppleTextDirection")
UserDefaults.standard.set(isLanguageRTL, forKey: "NSForceRightToLeftWritingDirection")
UserDefaults.standard.synchronize()
let value = (language != nil ? Bundle.init(path: (Bundle.main.path(forResource: language, ofType: "lproj"))!) : nil)
objc_setAssociatedObject(Bundle.main, &kBundleKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
class func isLanguageRTL(_ languageCode: String?) -> Bool {
return (languageCode != nil && Locale.characterDirection(forLanguage: languageCode!) == .rightToLeft)
}
}
6, Autoriteit 10%
Na het doorbrengen van enkele dagen heb ik daadwerkelijk de oplossing gevonden. Noodzaak van herlancering, vrij elegant: http://www.factorialComplexity.com/blog/2015/01/28/How-to-change-localisation-Internal-in-your-ios-Application.html Controleer de methode # 2.
Het vereist niet om alle titels en teksten handmatig opnieuw in te stellen, negeert u gewoon de lokalisatie voor de categorie Aangepaste NSBUNDLE. Werkt op zowel OBJ-C en SWIFT-projecten (na wat afstemming) als een charme.
Ik had wat twijfels als het door Apple wordt goedgekeurd, maar het daadwerkelijk deed.
Antwoord 7
Nieuwste Swift-syntaxis:
import Foundation
extension String {
func localized(lang:String) ->String {
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
Antwoord 8
Dit is een uitgebreide John Pang’s oplossing, als je systeemstrings onmiddellijk moet vertalen (Terug, Annuleren, Klaar.. .):
private var kBundleKey: UInt8 = 0
class BundleEx: Bundle {
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
if let bundle = objc_getAssociatedObject(self, &kBundleKey) {
return (bundle as! Bundle).localizedString(forKey: key, value: value, table: tableName)
}
return super.localizedString(forKey: key, value: value, table: tableName)
}
}
private var kBundleUIKitKey: UInt8 = 0
class BundleUIKitEx: Bundle {
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
if let bundle = objc_getAssociatedObject(self, &kBundleUIKitKey) {
return (bundle as! Bundle).localizedString(forKey: key, value: value, table: tableName)
}
return super.localizedString(forKey: key, value: value, table: tableName)
}
}
extension Bundle {
static let once: Void = {
object_setClass(Bundle.main, type(of: BundleEx()))
object_setClass(Bundle(identifier:"com.apple.UIKit"), type(of: BundleUIKitEx()))
}()
class func setLanguage(_ language: String?) {
Bundle.once
let isLanguageRTL = Bundle.isLanguageRTL(language)
if (isLanguageRTL) {
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
UserDefaults.standard.set(isLanguageRTL, forKey: "AppleTextDirection")
UserDefaults.standard.set(isLanguageRTL, forKey: "NSForceRightToLeftWritingDirection")
UserDefaults.standard.synchronize()
let value = (language != nil ? Bundle.init(path: (Bundle.main.path(forResource: language, ofType: "lproj"))!) : nil)
objc_setAssociatedObject(Bundle.main, &kBundleKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if let uiKitBundle = Bundle(identifier: "com.apple.UIKit") {
var valueUIKit: Bundle? = nil
if let lang = language,
let path = uiKitBundle.path(forResource: lang, ofType: "lproj") {
valueUIKit = Bundle(path: path)
}
objc_setAssociatedObject(uiKitBundle, &kBundleUIKitKey, valueUIKit, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
class func isLanguageRTL(_ languageCode: String?) -> Bool {
return (languageCode != nil && Locale.characterDirection(forLanguage: languageCode!) == .rightToLeft)
}
}
Als je systeemstrings wilt vertalen, moet je hetzelfde doen voor UIKit Bundle.
Antwoord 9
Voor lokalisatie tijdens runtime kan een van de volgende bibliotheken worden gebruikt:
Localize_Swift
of
LanguageManager-iOS
Als je de lokalisatie wilt wijzigen volgens de aanbevelingen van Apple, kun je de beschrijving vinden in het antwoord van @Muhammad Asyraf.
Antwoord 10
Geoptimaliseerde code voor mr.boyfox.
Stel de code system language
in op de sleutel i18n_language in StandardUserDefaults.
extension String {
var localized: String {
if let _ = UserDefaults.standard.string(forKey: "i18n_language") {} else {
// we set a default, just in case
let lang = Bundle.main.preferredLocalizations.first ?? "en"
UserDefaults.standard.set(lang, forKey: "i18n_language")
UserDefaults.standard.synchronize()
}
let lang = UserDefaults.standard.string(forKey: "i18n_language")
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
Antwoord 11
Als u SwiftUI gebruikt. In de praktijk is het overschrijven van Bundle
onbetrouwbaar gebleken.
Hierdoor kunt u de gebruikte taal op een betrouwbare manier overschrijven. U hoeft alleen uw door de app ondersteunde talen in te stellen op SupportedLanguageCode
.
(mogelijk moet u opnieuw laden als u de huidige weergave onmiddellijk wilt lokaliseren)
import SwiftUI
class Language {
static let shared = Language()
static let overrideKey = "override.language.code"
var currentBundle: Bundle!
init() {
loadCurrentBundle()
}
func loadCurrentBundle() {
let path = Bundle.main.path(forResource: current.rawValue, ofType: "lproj")!
currentBundle = Bundle(path: path)!
}
enum SupportedLanguageCode: String, Equatable, CaseIterable {
case en
case ar
case de
case es
case fr
case hi
case it
case ja
case ko
case nl
case ru
case th
case tr
case vi
case pt_BR = "pt-BR"
case zh_Hans = "zh-Hans"
case zh_Hant = "zh-Hant"
}
func set(language: Language.SupportedLanguageCode) {
UserDefaults.standard.set(language.rawValue, forKey: type(of: self).overrideKey)
loadCurrentBundle()
}
var current: Language.SupportedLanguageCode {
let code = UserDefaults.standard.string(forKey: type(of: self).overrideKey) ?? Locale.current.languageCode!
guard let language = Language.SupportedLanguageCode(rawValue: code) else {
fatalError("failed to load language")
}
return language
}
}
extension String {
var localized: String {
Language.shared.currentBundle.localizedString(forKey: self, value: nil, table: nil)
}
}
Je laadt gewoon de benodigde bundel op en specificeert die bundel wanneer je lokaliseert in de overschreven localized
.