it-swarm.com.ru

Смена языка на лету, под управлением iOS, программно

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

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

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

exit(0);

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

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

Есть намеки?


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

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

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

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

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

30
gabrielstuff

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

http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html

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

21
Swissdude

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

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

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

 LanguageChange

-anoop

5
anoop4real

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

3
meronix

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

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

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

1
Ryan

Согласно 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()

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

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

    label.text = dynamicLocalizableString(<KEY_IN_STRINGS>)
    

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

 enter image description here

0
gbk

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

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

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
Pincha