如何使用Swift以编程方式更改语言环境


71

我正在使用Swift在XCODE 6.3上制作ios应用。而且我的应用程序将具有选择语言功能,如下图所示

在此处输入图片说明

我已经有了使用当地语言的故事板。但是我无法找到如何通过按钮以编程方式从应用程序更改本地化。

任何人都知道该怎么做



哦,这非常有帮助,谢谢!
Varis Darasirikul 2015年

如果您能解决问题,请在此处发布答案
MBH 2016年

对于“实时”更改语言,您可以使用cocoapods.org/pods/L10n-swift
Adrian Bobrowski

<p>您可以改变全局NSLocale.current </ P> medium.com/@konradpiekos93/...
康拉德Piękoś

Answers:


83

这是一种使用Swift快速更改它的方法,将扩展函数添加到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: "")
}}

斯威夫特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: "")
}}

然后假设您已使用lang_id.lproj设置了常规Localizable.strings(例如en.lproj,de.lproj等),则可以在需要的任何地方使用它:

var val = "MY_LOCALIZED_STRING".localized("de")

5
迅捷的3版本:func localized(lang:String)-> String {let path = Bundle.main.path(forResource:lang,ofType:“ lproj”)let bundle = Bundle(path:path!)返回NSLocalizedString(self, tableName:nil,bundle:bundle !,值:“”,注释:“”))}
Medhi,2016年

恭喜,这是迄今为止我遇到的最好的解决方案。我刚刚进行了修改,以便能够动态更改语言。我将其发布为答案。
Alexandre G.

4
但是,当您更改语言时,如何重新加载情节提要?目前,当我更改语言时,带有.localized()的字符串有效,但情节提要不会反映所选的语言。
Hardik Shekhat

嗨,哈迪克,在这种情况下,您的故事板标签需要从代码中设置,并且每次您的语言更改时都应运行代码
dijipiji

7
这个答案不能回答问题,这是关于使用当地语言的故事板的。它只是翻译字符串的糖。
rommex

30

这允许仅通过更新密钥更改语言UserDefaults

这是基于@dijipiji的出色答案。这是Swift 3版本。

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: "")
    }
}

用法

只需添加 .localized到您的字符串中,如下所示:

"MyString".localizedMyString作为Localizable.strings文件中的密钥。

改变语言

UserDefaults.standard.set("en", forKey: "i18n_language")

7
但是当您更改语言时如何重新加载情节提要?目前,当我更改语言时,带有.localized()的字符串可以工作,但情节提要完全不改变。
Hardik Shekhat

我遇到了类似的问题。我的解决方法只是在名为“ Settings”的视图控制器中更改语言,然后关闭该视图,瞧!我所有的标签和按钮都会即时更改语言。我正在努力优化它,如果我找到一种方法,我将发布解决方案。
菲利普·博尔赫斯

1
是的@PhilipBorges也是关于我的应用程序发生的事情,尽管我在视图控制器中还有其他一些字符串可以更改语言。我只是调用了一个updateStrings()方法,在其中我重置了这些字符串。Hardik,如果您希望在更改语言时加载整个其他故事板,那么我认为这不是正确的解决方案。出于好奇,为什么首先需要加载另一个故事板?似乎很麻烦。
Alexandre G.

1
您不应该使用syncnize()。苹果文档从字面上指出,到处都是(至少对于iOS 10和11而言):developer.apple.com/documentation/foundation/userdefaults/…–
NeverwinterMoon,

14

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!)
    }
}

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)
    }
}

呼叫:

let localizedString = "enter".localized()

设置新的语言环境(例如“ ru”):

Bundle.setLanguage(lang: "ru")

更改语言时,此方法会即时更改字符串吗?
阿卜杜哈菲兹19'Mar

是的,如果您只想在运行时在应用程序内部更改应用程序的语言,则与设备操作系统语言
无关

嗨,我无法即时进行这项工作。如果重新加载视图,则文本将更新,但对于当前控制器,文本将保持不变。任何建议如何解决此问题?
dandepeched '19

如果要查看文本元素将自动更新,则应更改设备语言,如果要即时更改它们,则应手动更改它们(使用新的语言环境更新所有文本,移动所有ui元素,使用文本将其转换为通用功能)在控制器中,并且当区域设置更改时,将传递给控制器​​通知并再次调用该通用函数)
mr.boyfox

您如何处理语言变化?
凯琳

12

斯威夫特4.2

就我而言,如果用户更改语言设置,则必须在运行时更新2件事。

1. Localizable.strings

可本地化的字符串

2.故事板的本地化

故事板本地化

我让@John Pang代码更加快捷

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);
    }
}

语言速记

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
        }
    }
}

这样使用

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 ”将始终返回系统设置语言。因此,我们必须使用“ Locale.preferredLanguages.first ”。但是,返回值看起来像“ ko-US”。这是问题!因此,我使LocaleManager仅获得语言代码。

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)
    }
}

这样使用

guard let languageCode = LocaleManager.languageCode, let title = RemoteConfiguration.shared.logIn?.main?.title?[languageCode] else {
      return NSLocalizedString("Welcome!", comment: "")
}
return title

太好了,太感谢你了
帕雷什。P

11

杰里米的答案(在这里)在Swift 4上也很好用(我刚刚用一个简单的应用程序进行了测试,并且更改了初始视图控制器中使用的语言)。

这是同一段代码的Swift版本(出于某些原因,我的队友更喜欢仅Swift而不是与Objective-C混合,所以我翻译了它):

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)
    }

}

工作正常,但是当您更改英文版本时会崩溃。我使用目标C代码,在两种情况下均能完美工作。
Maulik shah

10

花了几天后,我实际上找到了解决方案。不需要重新启动,非常优雅:http : //www.factorialcomplexity.com/blog/2015/01/28/how-to-change-localization-internally-in-your-ios-application.html,检查方法2。不需要手动重新建立所有标题和文本,只需覆盖自定义NSBundle类别的本地化即可。像魅力一样,在Obj-C和Swift项目(经过一些调整)上均可工作。我怀疑它是否会被苹果公司批准,但实际上确实如此。


您可以在此处添加一些链接或Swift变体的代码吗?
拉维·索索迪亚

@whiteagle是否也可用于更改项目的语言方向?就是说,如果我想更改为RTL语言,它现在会像假定的那样翻转所有UI元素和约束吗?
royherma

@royherma,不确定,但是应该更改,因为它会更改整个本地化设置。
whiteagle

@rPragma,我使用从快速代码调用的Obj-C类别定义来做到这一点。
whiteagle

太好了,谢谢!我实际上找到了一个不同的解决方案,当您只想在所有UI元素加载之前(快速)在应用程序上强制进行本地化时,因此,如果有人跳到这里并正在寻找类似的东西,请查看stackoverflow.com/a / 38429383/1511877
royherma

5

斯威夫特4

UserDefaults.standard.set(["es", "de", "it"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

迅捷3

NSUserDefaults.standardUserDefaults().setObject(["es", "de", "it"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()

资料来源:这里


1
谢谢!为我工作,以强制在开始时设置应用程序语言,并将这些行添加到AppDelegate中。
Vitaliy

但是问题在于重新启动时会更改应用程序的语言。除非语言不会改变。在您所指的答案中对此进行了讨论。stackoverflow.com/a/1670524/10505343
Wimukthi Rajapaksha

3

whiteagle链接的解决方案实际上可以实时切换语言。这是帖子。

我将其中的示例代码简化为单个.h / .m,它将在内存中即时更改语言。我已经展示了如何从Swift 3调用它。

标头:

//
//  NSBundle+Language.h
//  ios_language_manager
//
//  Created by Maxim Bilan on 1/10/15.
//  Copyright (c) 2015 Maxim Bilan. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSBundle (Language)

+ (void)setLanguage:(NSString *)language;

@end

实现方式:

//
//  NSBundle+Language.m
//  ios_language_manager
//
//  Created by Maxim Bilan on 1/10/15.
//  Copyright (c) 2015 Maxim Bilan. All rights reserved.
//

#import "NSBundle+Language.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>

static const char kBundleKey = 0;

@interface BundleEx : NSBundle

@end

@implementation BundleEx

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle *bundle = objc_getAssociatedObject(self, &kBundleKey);
    if (bundle) {
        return [bundle localizedStringForKey:key value:value table:tableName];
    }
    else {
        return [super localizedStringForKey:key value:value table:tableName];
    }
}

@end

@implementation NSBundle (Language)

+ (void)setLanguage:(NSString *)language
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        object_setClass([NSBundle mainBundle], [BundleEx class]);
    });

    BOOL isLanguageRTL = [self isLanguageRTL:language];
    if (isLanguageRTL) {
        if ([[[UIView alloc] init] respondsToSelector:@selector(setSemanticContentAttribute:)]) {
            [[UIView appearance] setSemanticContentAttribute:
             UISemanticContentAttributeForceRightToLeft];
        }
    }else {
        if ([[[UIView alloc] init] respondsToSelector:@selector(setSemanticContentAttribute:)]) {
            [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
        }
    }
    [[NSUserDefaults standardUserDefaults] setBool:isLanguageRTL forKey:@"AppleTextDirection"];
    [[NSUserDefaults standardUserDefaults] setBool:isLanguageRTL forKey:@"NSForceRightToLeftWritingDirection"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    id value = language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil;
    objc_setAssociatedObject([NSBundle mainBundle], &kBundleKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

+ (BOOL)isLanguageRTL:(NSString *)languageCode
{
    return ([NSLocale characterDirectionForLanguage:languageCode] == NSLocaleLanguageDirectionRightToLeft);
}


@end

要从Swift调用此方法,请确保您的桥接头具有:

#import "NSBundle+Language.h"

然后从您的代码中调用:

Bundle.setLanguage("es")

注意事项:

  • 我没有提供任何示例代码来显示语言选择器或其他内容。原始链接文章确实包含一些内容。

  • 我更改了此代码以不永久更改任何内容。下次运行该应用程序时,它仍将尝试使用用户的首选语言。(一个例外是从右到左语言,请参见下文)

  • 您可以在加载视图之前随时执行此操作,新字符串将生效。但是,如果您需要更改已加载的视图,则可能需要按照原始文章所述重新初始化rootViewController。

  • 这应该适用于从右到左的语言,但是它在NSUserDefaults中为这些语言设置了两个内部持久性首选项。您可能希望通过在应用退出时将语言设置回用户的默认值来撤消该操作:Bundle.setLanguage(Locale.preferredLanguages.first!)


看起来不错,除了我正在使用NSLocalizedStringWithDefaultValue并且没有将我的本地化文本添加到情节提要中:(
Jules

我在这里发布了Swift-lover的Swift版本。感谢Jeremy简化了代码。

2

首先-这是个坏主意Apple建议您使用iOS选择的语言进行本地化。

但是,如果您真的需要它,可以为此目的提供一些小服务

enum LanguageName: String {
    case undefined
    case en
    case es
    case fr
    case uk
    case ru
    case de
    case pt
}

let DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey = "DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey"


func dynamicLocalizableString(_ key: String) -> String {
    return LanguageService.service.dynamicLocalizedString(key)
}

class LanguageService {

    private struct Defaults {
        static let keyCurrentLanguage = "KeyCurrentLanguage"
    }

    static let service:LanguageService = LanguageService()

    var languageCode: String {
        get {
            return language.rawValue
        }
    }

    var currentLanguage:LanguageName {
        get {
            var currentLanguage = UserDefaults.roxy.object(forKey: Defaults.keyCurrentLanguage)
            if currentLanguage == nil {
                currentLanguage = Locale.preferredLanguages[0]

            }
            if var currentLanguage = currentLanguage as? String, 
                let lang = LanguageName(rawValue: currentLanguage.truncatedBy(by:2)) {
                return lang
            }
            return LanguageName.en
        }
    }

    var defaultLanguageForLearning:LanguageName {
        get {
            var language: LanguageName = .es
            if currentLanguage == language {
                language = .en
            }
            return language
        }
    }

    func switchToLanguage(_ lang:LanguageName) {
        language = lang
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
    }

    func clearLanguages() {
        UserDefaults.roxy.setValue(nil, forKey:Defaults.keyCurrentLanguage)
        print(UserDefaults.roxy.synchronize())
    }

    private var localeBundle:Bundle?

    fileprivate var language: LanguageName = LanguageName.en {
        didSet {
            let currentLanguage = language.rawValue
            UserDefaults.roxy.setValue(currentLanguage, forKey:Defaults.keyCurrentLanguage)
            UserDefaults.roxy.synchronize()

            setLocaleWithLanguage(currentLanguage)            
        }
    }

    // MARK: - LifeCycle

    private init() {
        prepareDefaultLocaleBundle()
    }

    //MARK: - Private

    fileprivate func dynamicLocalizedString(_ key: String) -> String {
        var localizedString = key
        if let bundle = localeBundle {
            localizedString = NSLocalizedString(key, bundle: bundle, comment: "")
        } else {
            localizedString = NSLocalizedString(key, comment: "")
        }
        return localizedString
    }

    private func prepareDefaultLocaleBundle() {
        var currentLanguage = UserDefaults.roxy.object(forKey: Defaults.keyCurrentLanguage)
        if currentLanguage == nil {
            currentLanguage = Locale.preferredLanguages[0]
        }

        if let currentLanguage = currentLanguage as? String {
            updateCurrentLanguageWithName(currentLanguage)
        }
    }

    private func updateCurrentLanguageWithName(_ languageName: String) {
        if let lang = LanguageName(rawValue: languageName) {
            language = lang
        }
    }

    private func setLocaleWithLanguage(_ selectedLanguage: String) {
        if let pathSelected = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
            let bundleSelected = Bundle(path: pathSelected)  {
            localeBundle = bundleSelected
        } else if let pathDefault = Bundle.main.path(forResource: LanguageName.en.rawValue, ofType: "lproj"),
            let bundleDefault = Bundle(path: pathDefault) {
            localeBundle = bundleDefault
        }
    }
}

然后将rootViewControllerClass设置为:

import Foundation

protocol Localizable {
    func localizeUI()
}

class LocalizableViewController: UIViewController, Localizable {

    // MARK: - LifeCycle

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(self.localizeUI), name: NSNotification.Name(rawValue:DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        localizeUI()
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

extension LocalizableViewController: Localizable {
    // MARK: - Localizable

    func localizeUI() {
        fatalError("Must Override to provide inApp localization functionality")
    }
}

比继承每个控制器LocalizableViewController并实现localizeUI()

而不是像这样NSLocalizedString使用dynamicLocalizableString

func localizeOnceUI() {
    label.text = dynamicLocalizableString("keyFrom<"Localizable.strings">")
}

切换语言:

LanguageService.service.switchToLanguage(.en)

另请注意-如果要动态本地化小部件或其他应用程序部件,则需要其他步骤和逻辑修改。


1
嗨!您能否指出您提到的Apple推荐(使用OS语言而不是自定义切换器)?我认为,如果您声明这样的内容,则至少应始终提供到源的链接。
NeverwinterMoon,

2

这是我对字符串扩展的解决方案。@Das答案提高了安全性。

extension String {
  var localized: String {
    guard let path = Bundle.main.path(forResource:    Locale.current.regionCode?.lowercased(), ofType: "lproj"), let bundle = Bundle(path: path) else {
      return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }

    return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")
  }
}

2

这是Swift 4的更新答案

let language = "es" // replace with Locale code
guard let path = Bundle.main.path(forResource: language, ofType: "lproj") else {
  return self
}
guard let bundle = Bundle(path: path) else {
  return self
}
return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")

1
class ViewController: UIViewController {

@IBOutlet weak var resetOutlet: MyButton! {
    didSet {
        resetOutlet.setTitle("RESET".localized().uppercased(), for: .normal)
    }
}`
}

extension String {

func localized(tableName: String = "Localizable") -> String {
    if let languageCode = Locale.current.languageCode, let preferredLanguagesFirst = Locale.preferredLanguages.first?.prefix(2)  {
        if languageCode != preferredLanguagesFirst {
            if let path = Bundle.main.path(forResource: "en", ofType: "lproj") {
                let bundle = Bundle.init(path: path)
                return NSLocalizedString(self, tableName: tableName, bundle: bundle!, value: self, comment: "")
            }
        }
        }
    return NSLocalizedString(self, tableName: tableName, value: self, comment: "")
}
}

0

最新的Swift语法:

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: "")
    }
}

0

如果您需要立即转换系统字符串(返回,取消,完成...),则这是John Pang的解决方案的扩展:

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)
    }

}

如果要转换系统字符串,则必须对UIKit Bundle做同样的事情。


0

这就是苹果公司关于更改语言的说法;

通常,您不应在应用程序内更改iOS系统语言(通过使用AppleLanguages首选项键)。这违反了在设置应用程序中切换语言的基本iOS用户模型,并且还使用了未记录的首选项键,这意味着将来某个时候,键名可能会更改,这会破坏您的应用程序。

因此,建议您将用户导航到应用程序的常规设置页面,页面可在

设置-> [您的应用名称]->首选语言

为了直接打开应用程序设置从您的应用程序,您可以使用以下代码段;

对于斯威夫特;

let settingsURL = URL(string: UIApplication.openSettingsURLString)!
UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)

对于Obj-C;

NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil];

提示: 在导航到设置页面之前,最好弹出对话框并说出用户切换到设置页面后应该执行的操作,这对于您的应用程序来说是更好的用户体验。


0

如何在您的应用程序中支持每个应用程序的语言设置:https :
//developer.apple.com/news/?id=u2cfuj88

如何从应用程序中的自定义语言选择器过渡到

到系统范围内对应用程序内语言选择器的支持,如果您支持iOS 13或macOS Catalina或更高版本,则不再需要提供一种在应用程序中选择语言的方法。如果当前提供这样的UI,则应将其删除,以免引起客户混乱以及与系统的潜在冲突。

如果您想引导人们进行系统设置以进行语言选择,则可以使用直接在iOS上的“设置”应用程序中启动的流程来替换应用程序的自定义UI。

在iOS上,添加以下内容:

UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)

在macOS上,将用户引导至“系统偏好设置”>“语言和区域”以为您的应用添加每种语言的设置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.