codememo

Swift를 사용하여 로케일을 프로그래밍 방식으로 변경하는 방법

tipmemo 2023. 8. 31. 23:55
반응형

Swift를 사용하여 로케일을 프로그래밍 방식으로 변경하는 방법

저는 스위프트사의 XCODE 6.3에서 ios app을 만들고 있습니다.그리고 내 앱은 아래 이미지와 같이 언어 선택 기능을 가질 것입니다.

enter image description here

저는 이미 제 지역 언어를 위한 스토리보드를 가지고 있습니다.하지만 버튼으로 앱에서 프로그래밍 방식으로 현지화를 변경하는 방법을 찾을 수 없습니다.

할 줄 아는 사람.

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(예: en.lproj, de.lproj 등)를 사용하여 일반 Localizable.string을 설정했다고 가정하면 필요한 장소에서 이를 사용할 수 있습니다.

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

이를 통해 를 업데이트하는 만으로 언어를 변경할 수 있습니다.

이것은 @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".localized,MyString ▁in의 이 되는 것.Localizable.stringsjava.

언어 변경

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

스위프트 4.2

저의 경우 사용자가 언어 설정을 변경하면 런타임에 2가지를 업데이트해야 합니다.

localizable.strings

Localizable strings

스토리보드 현지화

Storyboard localization

나는 @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);
    }
}

언어.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
        }
    }
}

이렇게 사용

var languages: [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)

Select language screen


"Local.current.languageCode"는 항상 시스템 설정 언어를 반환합니다.그래서 우리는 "로칼레.선호 언어"를 사용해야 합니다.첫째."그러나 반환 값은 "ko-US"처럼 보입니다.이건 문제야! 그래서 언어 코드만 받기 위해 로케일 매니저를 만들었습니다.

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

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

Xcode 15의 새로운 방식

문자열 카탈로그를 사용하여 앱 현지화

앱을 현지화하는 것은 앱을 보다 포괄적으로 만들고 다른 언어로 사용자가 사용할 수 있도록 하는 데 큰 부분을 차지합니다.

이전에는 앱을 현지화하려면 문자열과 문자열을 직접 관리해야 했습니다.수작업이 많아 현지화된 콘텐츠가 누락되는 경우가 많았습니다.

애플은 Xcode 15와 String Catalogs를 시작으로 개발자들이 우리 앱을 더 쉽게 현지화할 수 있도록 했습니다.

공식 문서

예제 소스

출처

enter image description here

제레미의 답변(여기)은 스위프트 4에서도 잘 작동합니다(나는 방금 간단한 앱으로 테스트했고 초기 뷰 컨트롤러에서 사용되는 언어를 변경했습니다).

다음은 동일한 코드의 Swift 버전입니다(어떤 이유로, 제 팀원들은 Objective-C와 혼합되는 것보다 Swift-only를 선호하여 번역했습니다).

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

}

며칠을 보낸 후에 저는 실제로 해결책을 찾았습니다.다시 시작할 필요가 없습니다. 꽤 우아합니다. http://www.factorialcomplexity.com/blog/2015/01/28/how-to-change-localization-internally-in-your-ios-application.html , 방법 #2를 확인하십시오.모든 제목과 텍스트를 수동으로 다시 설정할 필요는 없으며 사용자 지정 NSBundle 범주의 현지화를 재정의하기만 하면 됩니다.Obj-C와 Swift 프로젝트 모두에서 매력적으로 작동합니다.사과로 승인이 날지 의문이 들었지만, 실제로 승인이 되었습니다.

스위프트 4

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

스위프트 3

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

출처: 여기

다음은 Apple이 언어 변경에 대해 언급한 내용입니다.

일반적으로 애플리케이션 내에서 iOS 시스템 언어(Apple Languages Pref 키 사용)를 변경해서는 안 됩니다.이는 설정 앱에서 언어를 전환하는 기본 iOS 사용자 모델에 위배되며 문서화되지 않은 기본 설정 키를 사용하므로 나중에 키 이름이 변경되어 응용 프로그램이 손상될 수 있습니다.

따라서 사용자는 아래에서 찾을 수 있는 앱의 일반 설정 페이지로 이동해야 합니다.

설정 -> [your_app_name] -> 기본 설정 언어

앱에서 직접 애플리케이션 설정을 열기 위해 다음 코드를 사용할 수 있습니다.

스위프트의 경우;

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];

힌트: 설정 페이지로 이동하기 전에 팝업을 표시하고 사용자가 설정 페이지로 전환한 후 수행해야 할 작업이 응용 프로그램에 더 적합한 사용자 환경이라고 말하는 것이 좋습니다.

화이트 이글로 연결된 솔루션은 실제로 언어를 즉시 전환할 수 있습니다.여기 게시물입니다.

저는 거기에 있는 샘플 코드를 메모리의 언어를 즉시 변경하는 단일 .h/.m로 단순화했습니다.스위프트 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에서 이를 호출하려면 Bridging Header에 다음이 있는지 확인하십시오.

#import "NSBundle+Language.h"

코드에서 다음을 호출합니다.

Bundle.setLanguage("es")

주의할 사항:

  • 언어 선택기 등을 표시하기 위한 샘플 코드를 포함하지 않았습니다.원래 연결된 게시물에 일부가 포함되어 있습니다.

  • 저는 이 코드를 지속적으로 변경하지 않기 위해 변경했습니다.다음 번에 앱을 실행할 때에도 사용자가 선호하는 언어를 사용하려고 시도합니다.(한 가지 예외는 오른쪽에서 왼쪽으로 가는 언어입니다. 아래 참조)

  • 보기가 로드되기 전에 언제든지 이 작업을 수행할 수 있으며 새 문자열이 적용됩니다.그러나 이미 로드된 보기를 변경해야 하는 경우 원래 게시물에 나와 있는 대로 rootViewController를 다시 초기화할 수 있습니다.

  • 오른쪽에서 왼쪽으로 이동하는 언어의 경우 이 방법을 사용하면 해당 언어에 대해 NSUserDefaults에서 두 가지 내부 영구 기본 설정을 설정할 수 있습니다.종료 시를 다시할 수 .Bundle.setLanguage(Locale.preferredLanguages.first!)

우선 - 이것은 나쁜 생각이고.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")
    }
}

▁from에서 모든 컨트롤러를 합니다.LocalizableViewController을 구현합니다.localizeUI()

그고대에 에.NSLocalizedString사용하다dynamicLocalizableString예:예:

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

언어 전환하기

LanguageService.service.switchToLanguage(.en)

참고 - 위젯이나 다른 앱 파트를 동적으로 현지화하려면 추가 단계와 로직 수정이 필요합니다.

String 확장자를 가진 제 솔루션이 여기 있습니다.@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: "")
  }
}
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: "")
}
}

앱에서 앱별 언어 설정을 지원하는 방법:
https://.apple.com/news/ 웹사이트://developer.apple.com/news/ ?id=u2cfuj88

ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

앱 내 언어 선택기를 시스템 전체에서 지원하므로 iOS 13 또는 macOS Catalina 이상을 지원하는 경우 더 이상 앱 내에서 언어를 선택할 수 있는 방법을 제공할 필요가 없습니다.현재 이러한 UI를 제공하는 경우 고객의 혼란과 잠재적인 시스템 충돌을 방지하기 위해 해당 UI를 제거해야 합니다.

사람들에게 언어 선택을 위한 시스템 설정을 안내하려면 앱의 사용자 정의 UI를 iOS의 설정 앱으로 바로 시작되는 흐름으로 바꿀 수 있습니다.

iOS에서 다음을 추가합니다.

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

macOS에서 사람들에게 시스템 기본 설정 > 언어 & 지역으로 안내하여 앱의 언어별 설정을 추가합니다.

여기 스위프트 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: "")

보이폭스 씨를 위한 최적화된 코드.

세트system languageStandardUserDefaults의 i18n_language 키로 코드를 지정합니다.

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

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

시스템 문자열을 즉시 변환해야 하는 경우(뒤로, 취소, 완료...), 확장된 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에서도 동일하게 변환해야 합니다.

런타임 중 현지화를 위해 다음 라이브러리 중 하나를 사용할 수 있습니다.현지화_Swift 또는 LanguageManager-iOS

Apple 권장 사항에 따라 현지화를 변경하려면 @Muhammad Asyraf의 답변에서 설명을 찾을 수 있습니다.

Swift를 사용하는 경우UI. 실제로, 재정의Bundle신뢰할 수 없는 것으로 판명되었습니다.

이렇게 하면 사용된 언어를 즉시 신뢰성 있게 재정의할 수 있습니다.앱 지원 언어를 다음으로 설정하기만 하면 됩니다.SupportedLanguageCode.

(현재 보기를 즉시 현지화하려면 다시 로드해야 할 수도 있습니다.)

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

필요한 번들을 로드하고 재정의된 번들을 현지화할 때 해당 번들을 지정하는 것입니다.localized.

언급URL : https://stackoverflow.com/questions/29985614/how-can-i-change-locale-programmatically-with-swift

반응형