Подтвердить что ты не робот

Изменение языка на лету, при запуске iOS, программно

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

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

Ну, после googling, я пытался настроить будильник, а затем заставил приложение выйти с помощью

exit(0);

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

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

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


EDIT, информация из APPLE

В общем, вы не должны изменять iOS (с использованием AppleLanguages ​​pref key) изнутри ваше приложение. Это противоречит базовая модель пользователя iOS для переключения языков в приложении "Настройки" и также использует ключ предпочтения, который не является документально, что означает, что в какой-то момент в будущем ключевое имя может изменения, которые приложение.

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

Что касается вопроса о выходе (0), Apple DTS не может комментировать утверждение приложения обработать. Вы должны связаться [email protected], чтобы получить ответ для этого вопроса.

Ну, я должен выбрать до сих пор.

4b9b3361

Ответ 3

да, у меня была та же проблема, тогда я справился с ней с помощью своего собственного языка в моем 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 - это слово для перевода, а его значение - слово переведено)...

Ответ 4

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

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

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

Ответ 5

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

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

    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
            }
        }
    }
    
  • Добавьте некоторые правила, чтобы убедиться, что компоненты пользовательского интерфейса будут постоянно обновляться:

    protocol Localizable {
        func localizeUI()
    }
    
  • Внедрить их

    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")
        }
    }
    
  • Наследовать любой контроллер u, чтобы он соответствовал динамическим функциям переключателя приложений и реализовал localizeUI() func

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

    LanguageService.service.switchToLanguage(.en)
    
  • Все строки localizabled должны быть установлены как:

    label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
    

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

введите описание изображения здесь

Ответ 6

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

Взгляните на код помощника:

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

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