Смена языка на лету, под управлением iOS, программно - PullRequest
30 голосов
/ 24 февраля 2011

Я часами копил и гуглил.И я сейчас в отчаянии.Я хотел бы изменить язык моего приложения внутри приложения не только на языке по умолчанию.

Из того, что я пробовал, я застрял, как и все, на этапе перезагрузки.Это означает, что яблоки заставляет вас перезапустить приложение вручную.Это означает, что вы должны выйти из приложения и затем запустить его снова.

Ну, после поиска в Google, я пытался настроить будильник, а затем заставлял приложение завершать работу с

exit(0);

Мой плохой, Apple, кажется, не нравится это и мешает разработчику использовать его ... Я предполагаю, что я не указываю в правильном направлении.

Наконец, несмотря на все проблемы, я мог встретиться, я хотел быобсудить это.

Любые подсказки?


РЕДАКТИРОВАТЬ, информация от APPLE

В общем, вы не должны изменятьЯзык системы iOS (с помощью преф-ключа AppleLanguages) из вашего приложения.Это противоречит базовой пользовательской модели iOS для переключения языков в приложении «Настройки», а также использует ключ настройки, который не документирован, а это означает, что в какой-то момент в будущем имя ключа может измениться, что приведет к поломке вашего приложения.

Если вы хотите переключать языки в своем приложении, вы можете сделать это, вручную загрузив файлы ресурсов в вашем комплекте.Вы можете использовать NSBundle: pathForResource: ofType: inDirectory: forLocalization: для этой цели, но имейте в виду, что ваше приложение будет отвечать за всю загрузку локализованных данных.

Что касается вопроса выхода (0), Apple DTS не может комментировать процесс утверждения приложения.Вы должны связаться с appreview@apple.com, чтобы получить ответ на этот вопрос.

Что ж, пока мне нужно выбирать.

Ответы [ 6 ]

21 голосов
/ 03 сентября 2011

Это довольно старый вопрос, но я просто боролся с той же проблемой и нашел это решение:

http://aggressive -mediocrity.blogspot.com / 2010/03 / заказ локализация-система-для-your.html

Что делает именно то, что вам нужно (и может быть полезно для тех, кто с той же проблемой:)

5 голосов
/ 22 апреля 2014

Ниже приведена хорошая реализация наличия собственного языка из приложения в приложении.

ручной выбор языка в iOS-приложении (iPhone и iPad)

Попытка SWIFT-версии найти здесь

3 голосов
/ 25 февраля 2011

да, у меня была такая же проблема, затем я справился с этим с помощью своего собственного языкового параметра в моем prefFile, где я установил переменную для языкового параметра:

// write a new value in file and set the var
- (void)changeLangInPrefFile:(NSString *)newLanguage {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
    NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
    //here add elements to data file and write data to file
    [data setObject:newLanguage forKey:@"language"];
    [data writeToFile:path atomically:YES];
    [data release];

// NSString *chosenLang; <- declared in .h file
    if (chosenLang != nil){
        [chosenLang release];
        chosenLang = nil;
    }
    chosenLang = [[NSString alloc] initWithString:(@"%@",newLanguage)];

}

// read the language from file and set the var:
- (void)readFromFileInBundleDocuments {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"myPreference.plist"];
    NSMutableDictionary *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile:path];

    NSString *chosenLangTemp = [savedStock objectForKey:@"language"];
    NSLog (@"read in file: %@", chosenLangTemp);
    if (chosenLang != nil){
        [chosenLang release];
        chosenLang = nil;
    }
    chosenLang = [[NSString alloc] initWithString:(@"%@",chosenLangTemp)];
    [savedStock release];
}

тогда я загружаю все содержимое из разных файлов в зависимости от языка например, я могу загрузить «an_image_eng.png» или «an_image_ita.png», или иметь 2 разных файла .xib и для загрузки текста я использую разные словарные файлы, по одному для каждого языка, со всеми переведенными словами / выражениями, я просто загружаю выбранное и читаю в нем правильное выражение для каждого загружаемого текста (код для его загрузки). аналогичен методу, который я написал в этом примере, вы можете просто расположить его так, чтобы он читал правильное слово для каждого выражения: просто посмотрите на значение для objectForKey в правом файле словаря, где objectForKey - слово для перевода, а его значение - слово переведено) ...

1 голос
/ 25 февраля 2011

Обычно язык, который видит пользователь, определяется настройкой локали, которая является общесистемной настройкой.Только пользователь может изменить его, и когда он это сделает, SpringBoard и все работающие приложения на устройстве должны быть перезапущены.Обойти это невозможно, потому что все системные приложения и платформы предполагают, что языковой стандарт не меняется после запуска.Для Apple было бы очень сложно изменить приложения и платформы, чтобы они не требовали перезапуска.

Я предполагаю, что вы либо хотите полностью изменить язык интерфейса вашего приложения независимо от настройки локали системы, либо хотите использовать настройку локали системы по умолчанию, но позволить пользователю переопределить его только дляваше приложение.

Вы можете получить текущую локаль и проверить ее различные значения, используя +[NSLocale currentLocale].Чтобы отобразить пользовательский интерфейс вашего приложения на языке, который не зависит от языкового стандарта системы, вам необходимо полностью исключить использование NSLocalizedString() и использовать какое-либо собственное пользовательское состояние, чтобы определить, какие строки отображать пользователю икак изменить интерфейс в соответствии с языком вашего приложения.От вас будет зависеть состояние языка вашего приложения и, соответственно, изменение его пользовательского интерфейса.

0 голосов
/ 07 декабря 2018

Это старый вопрос, но я разрабатывал помощника, который уведомляет меня, когда язык меняется на лету.

Посмотрите на код помощника:

import Foundation

class LocalizableLanguage {

    // MARK: Constants

    fileprivate static let APPLE_LANGUAGE_KEY = "AppleLanguages"

    /// Notification Name to observe when language change
    static let ApplicationDidChangeLanguage = Notification.Name("ApplicationDidChangeLanguage")

    // MARK: Properties

    /// An array with all available languages as String
    static var availableLanguages: [String]? = {
        return UserDefaults.standard.object(forKey: APPLE_LANGUAGE_KEY) as? [String]
    }()

    /// The first element of available languages that is the current language
    static var currentLanguageCode: String? = {
        return availableLanguages?.first
    }()

    /// The current language code with just 2 characters
    static var currentShortLanguageCode: String? = {
        guard let currentLanguageCode = currentLanguageCode else {
            return nil
        }

        let strIndex = currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2)
        return currentLanguageCode.substring(to: strIndex)
    }()

    // MARK: Handle functions

    /// This accepts the short language code or full language code
    /// Setting this will send a notification with name "ApplicationDidChangeLanguage", that can be observed in order to refresh your localizable strings
    class func setLanguage(withCode langCode: String) {

        let matchedLangCode = availableLanguages?.filter {
            $0.contains(langCode)
        }.first

        guard let fullLangCode = matchedLangCode else {
            return
        }

        var reOrderedArray = availableLanguages?.filter {
            $0.contains(langCode) == false
        }

        reOrderedArray?.insert(fullLangCode, at: 0)

        guard let langArray = reOrderedArray else {
            return
        }

        UserDefaults.standard.set(langArray, forKey: APPLE_LANGUAGE_KEY)
        UserDefaults.standard.synchronize()

        LocalizableLanguage.refreshAppBundle()

        NotificationCenter.default.post(name: ApplicationDidChangeLanguage, object: fullLangCode)
    }
}

// MARK: Refresh Bundle Helper

private extension LocalizableLanguage {

    class func refreshAppBundle() {
        MethodSwizzleGivenClassName(cls: Bundle.self, originalSelector: #selector(Bundle.localizedString(forKey:value:table:)), overrideSelector: #selector(Bundle.specialLocalizedStringForKey(_:value:table:)))
    }

    class func MethodSwizzleGivenClassName(cls: AnyClass, originalSelector: Selector, overrideSelector: Selector) {
        let origMethod: Method = class_getInstanceMethod(cls, originalSelector);
        let overrideMethod: Method = class_getInstanceMethod(cls, overrideSelector);
        if (class_addMethod(cls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
            class_replaceMethod(cls, overrideSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, overrideMethod);
        }
    }
}

extension Bundle {

    func specialLocalizedStringForKey(_ key: String, value: String?, table tableName: String?) -> String {

        let availableLanguages = UserDefaults.standard.object(forKey: LocalizableLanguage.APPLE_LANGUAGE_KEY) as? [String]
        let currentLanguageCode = availableLanguages?.first ?? "en-US"
        let currentShortLanguageCode = currentLanguageCode.substring(to: currentLanguageCode.index(currentLanguageCode.startIndex, offsetBy: 2))

        let path =
                Bundle.main.path(forResource: currentLanguageCode, ofType: "lproj") ??
                Bundle.main.path(forResource: currentShortLanguageCode, ofType: "lproj") ??
                Bundle.main.path(forResource: "Base", ofType: "lproj")

        guard
            self == Bundle.main,
            let bundlePath = path,
            let bundle = Bundle(path: bundlePath)
        else {
            return self.specialLocalizedStringForKey(key, value: value, table: tableName)
        }

        return bundle.specialLocalizedStringForKey(key, value: value, table: tableName)
    }
}

Вам просто нужно скопировать этот код и вставить в свой проект.

Затем вы просто реализуете слушателя следующим образом:

NotificationCenter.default.addObserver(forName: LocalizableLanguage.ApplicationDidChangeLanguage, object: nil, queue: nil) { notification in
            guard let langCode = notification.object as? String else {
                return
            }
            self.accountStore.languageCode.value = langCode
        } 

Обратите внимание, что эта строка self.accountStore.languageCode.value = langCode - это то, что мне нужно обновить, когда язык приложения изменился, тогда я могу легко изменить все строки моих ViewModels, чтобы немедленно изменить язык на пользователя.

Чтобы сменить язык, вы можете просто позвонить:

LocalizableLanguage.setLanguage(withCode: "en")

Другой помощник, который может быть вам приятен:

import Foundation

extension String {

    var localized: String {
        return NSLocalizedString(self, comment: "")
    }

}

Так что, если у вас есть в локализуемых файлах что-то вроде этого:

main.view.title = "Title test";

Вы можете просто позвонить:

"main.view.title".localized

И ваша строка переведена.

0 голосов
/ 21 марта 2017

Согласно Apple guidelines это не очень хорошая идея, чтобы программно менять язык в приложении, но если у вас нет возможности изменить запрашиваемое поведение, вы можете сделать что-то вроде следующего:

  1. Подготовьте некоторые службы для управления вашим языком даже после перезапуска приложения

    enum LanguageName: String {
        case undefined
        case en
        case es
    }
    
    let DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey = "DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey"
    
    func dynamicLocalizableString(_ key: String) -> String {
        return LanguageService.service.dynamicLocalizedString(key)
    }
    
    class LanguageService {
    
        private struct Defaults {
            static let keyAppleLanguage = "AppleLanguages"
            static let keyCurrentLanguage = "KeyCurrentLanguage"
        }
    
        static let service:LanguageService = LanguageService()
    
        var languageCode: String {
            get {
                return language.rawValue
            }
        }
    
        var currentLanguage:LanguageName {
            get {
                var currentLanguage = UserDefaults.standard.object(forKey: Defaults.keyCurrentLanguage)
                if let currentLanguage = currentLanguage as? String {
                    UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
                    UserDefaults.standard.synchronize()
                } else {
                    if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
                        currentLanguage = languages.first
                    }
                }
                if let currentLanguage = currentLanguage as? String, 
                    let lang = LanguageName(rawValue: currentLanguage) {
                    return lang
                }
                return LanguageName.undefined
            }
        }
    
        func switchToLanguage(_ lang:LanguageName) {
            language = lang
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: DynamicLanguageServiceDidDetectLanguageSwitchNotificationKey), object: nil)
        }
    
        private var localeBundle:Bundle?
    
        fileprivate var language: LanguageName = LanguageName.en {
            didSet {
                let currentLanguage = language.rawValue
    
                UserDefaults.standard.set([currentLanguage], forKey:Defaults.keyAppleLanguage)
                UserDefaults.standard.setValue(currentLanguage, forKey:Defaults.keyCurrentLanguage)
                UserDefaults.standard.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.standard.object(forKey: Defaults.keyCurrentLanguage)
            if let currentLanguage = currentLanguage as? String {
                UserDefaults.standard.set([currentLanguage], forKey: Defaults.keyAppleLanguage)
                UserDefaults.standard.synchronize()
            } else {
                if let languages = UserDefaults.standard.object(forKey: Defaults.keyAppleLanguage) as? [String] {
                    currentLanguage = languages.first
                }
            }
    
            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
            }
        }
    }
    
  2. Добавьте некоторые правила, чтобы гарантировать постоянное обновление компонентов пользовательского интерфейса:

    protocol Localizable {
        func localizeUI()
    }
    
  3. Выполнить их

    class LocalizableViewController: UIViewController {
    
        // 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")
        }
    }
    
  4. Унаследуйте любой контроллер, который вы хотите согласовать с функциональностью динамического переключения приложений, и реализуйте localizeUI() func

    final class WelcomeTableViewController: LoadableTableViewController
    
  5. При необходимости переключите язык:

    LanguageService.service.switchToLanguage(.en)
    
  6. Вся локализуемая строка должна быть установлена ​​как:

    label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
    

Примечание: не забудьте добавить Localizable.strings с теми же кодами, что и в LanguageName

enter image description here

...