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

Мой вопрос:

Как мое iPhone-приложение может сказать iOS, что пользователь выбрал язык в настройках приложений, который отличается от языка, заданного в общих настройках?

Другая формулировка того же вопроса:

Как я могу сообщить системе, что NSLocalizedString (@"text", @"comment"); не должен иметь доступ к системному выбранному языку, но язык, выбранный в приложении?

фон, пример:

Пожалуйста, примите эту ситуацию в качестве примера: Сын немецких иммигрантов живет на северо-востоке Франции рядом с Люксембургом и Германией. Его родной язык - французский, поэтому он установил язык интерфейса пользователя своего iPhone на французский (Настройки → Общие → Международный → Язык → Français). Но из-за его культурного фона и потому, что регион, где он живет, двуязычен, он также очень хорошо говорит по-немецки. Но он не говорит по-английски десять слов. На iPhone (и iPad также) у него нет шансов выбрать второй язык, поэтому телефон знает только, что он пьет французский. Он не знает навыков пользователей на других языках.

Теперь приходит мое приложение: я разработал его на английском и немецком языках (немецкий - это мой родной язык, а английский - стандартный язык в ИТ). Я разработал его в соответствии со всеми правилами и рекомендациями для многоязычных iOS-приложений. "Первый" язык (язык по умолчанию) моего приложения - английский.

Это означает:

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

Но если он вообще выбирает какой-либо другой язык (например, китайский, польский или французский) в общих настройках, он получит язык по умолчанию для приложений, который, в моем случае, является английским. Но для моего франко-немецкого друга это не лучший выбор. Он хотел бы использовать существующую немецкую версию, но, похоже, нет способа разрешить пользователю выбирать эту версию.

Добавление перевода на французский язык решит проблему для нашего франко-немецкого друга, но не для людей, говорящих на двух других языках (например, итальянском и немецком), и я не могу поддерживать свое приложение со всеми языками, на которых говорят на этой планете. Установка языка по умолчанию на немецкий язык также не является оптимальной, потому что это вызовет ту же проблему для людей, говорящих на французском (родной язык) и английском (как на втором языке).

Поэтому я думаю, что у моего приложения должна быть возможность вручную выбрать язык, отличный от предварительно выбранного языка. Добавление выбора языка в панель настроек приложений не является проблемой. Но как я могу сказать системе, что NSLocalizedString (@"text", @"comment"); не должен иметь доступ к системному выбранному языку, но язык, выбранный в приложении?

Ответ 1

Тем временем я нашел решение для своей проблемы на себе:

Я создал новый класс "LocalizeHelper":


Заголовок LocalizeHelper.h

//LocalizeHelper.h

#import <Foundation/Foundation.h>

// some macros (optional, but makes life easy)

// Use "LocalizedString(key)" the same way you would use "NSLocalizedString(key,comment)"
#define LocalizedString(key) [[LocalizeHelper sharedLocalSystem] localizedStringForKey:(key)]

// "language" can be (for american english): "en", "en-US", "english". Analogous for other languages.
#define LocalizationSetLanguage(language) [[LocalizeHelper sharedLocalSystem] setLanguage:(language)]

@interface LocalizeHelper : NSObject

// a singleton:
+ (LocalizeHelper*) sharedLocalSystem;

// this gets the string localized:
- (NSString*) localizedStringForKey:(NSString*) key;

//set a new language:
- (void) setLanguage:(NSString*) lang;              

@end

iMplementation LocalizeHelper.m

// LocalizeHelper.m
#import "LocalizeHelper.h"

// Singleton
static LocalizeHelper* SingleLocalSystem = nil;

// my Bundle (not the main bundle!)
static NSBundle* myBundle = nil;


@implementation LocalizeHelper


//-------------------------------------------------------------
// allways return the same singleton
//-------------------------------------------------------------
+ (LocalizeHelper*) sharedLocalSystem {
    // lazy instantiation
    if (SingleLocalSystem == nil) {
        SingleLocalSystem = [[LocalizeHelper alloc] init];
    }
    return SingleLocalSystem;
}


//-------------------------------------------------------------
// initiating
//-------------------------------------------------------------
- (id) init {
    self = [super init];
    if (self) {
        // use systems main bundle as default bundle
        myBundle = [NSBundle mainBundle];
    }
    return self;
}


//-------------------------------------------------------------
// translate a string
//-------------------------------------------------------------
// you can use this macro:
// LocalizedString(@"Text");
- (NSString*) localizedStringForKey:(NSString*) key {
    // this is almost exactly what is done when calling the macro NSLocalizedString(@"Text",@"comment")
    // the difference is: here we do not use the systems main bundle, but a bundle
    // we selected manually before (see "setLanguage")
    return [myBundle localizedStringForKey:key value:@"" table:nil];
}


//-------------------------------------------------------------
// set a new language
//-------------------------------------------------------------
// you can use this macro:
// LocalizationSetLanguage(@"German") or LocalizationSetLanguage(@"de");
- (void) setLanguage:(NSString*) lang {

    // path to this languages bundle
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
    if (path == nil) {
        // there is no bundle for that language
        // use main bundle instead
        myBundle = [NSBundle mainBundle];
    } else {

        // use this bundle as my bundle from now on:
        myBundle = [NSBundle bundleWithPath:path];

        // to be absolutely shure (this is probably unnecessary):
        if (myBundle == nil) {
            myBundle = [NSBundle mainBundle];
        }
    }
}


@end

Для каждого языка, который вы хотите поддерживать, вам нужен файл с именем Localizable.strings. Это работает точно так, как описано в документации на яблоки для локализации. Единственное отличие: теперь вы даже можете использовать такие языки, как хинди или эсперанто, которые не поддерживаются Apple.

Чтобы привести вам пример, вот первые строки моих английских и немецких версий Localizable.strings:

английский

/* English - English */

/* for debugging */
"languageOfBundle" = "English - English";

/* Header-Title of the Table displaying all lists and projects */
"summary" = "Summary";

/* Section-Titles in table "summary" */
"help" = "Help";
"lists" = "Lists";
"projects" = "Projects";
"listTemplates" = "List Templates";
"projectTemplates" = "Project Templates";

Немецкий

/* German - Deutsch */

/* for debugging */
"languageOfBundle" = "German - Deutsch";

/* Header-Title of the Table displaying all lists and projects */
"summary" = "Überblick";

/* Section-Titles in table "summary" */
"help" = "Hilfe";
"lists" = "Listen";
"projects" = "Projekte";
"listTemplates" = "Vorlagen für Listen";
"projectTemplates" = "Vorlagen für Projekte";

Чтобы использовать локализацию, вы должны иметь некоторые настройки-процедуры в своем приложении, а в выборе языка вы вызываете макрос:

LocalizationSetLanguage(selectedLanguage);

После этого вы должны убедиться, что все, что было отображено на старом языке, теперь перерисовывается на новом языке (скрытые тексты должны быть перерисованы, как только они снова станут видимыми).

Чтобы локализованные тексты были доступны для каждой ситуации, НИКОГДА не нужно писать тексты исправлений в названия объектов. ВСЕГДА используйте макрос LocalizedString(keyword).

не

cell.textLabel.text = @"nice title";

сделать:

cell.textLabel.text = LocalizedString(@"nice title");

и иметь запись "nice title" в каждой версии Localizable.strings!

Ответ 2

Просто добавьте следующее на экран с выбором языка:

    NSString *tempValue = //user chosen language. Can be picker view/button/segmented control/whatever. Just get the text out of it
    NSString *currentLanguage = @"";
    if ([tempValue rangeOfString:NSLocalizedString(@"English", nil)].location != NSNotFound) {
        currentLanguage = @"en";
    } else if ([tempValue rangeOfString:NSLocalizedString(@"German", nil)].location != NSNotFound) {
        currentLanguage = @"de";
    } else if ([tempValue rangeOfString:NSLocalizedString(@"Russian", nil)].location != NSNotFound) {
        currentLanguage = @"ru";
    }
    [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:currentLanguage, nil] forKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults]synchronize];

Затем попросите перезапустить приложение, и приложение будет на другом языке.

Надеюсь, что это поможет

Ответ 3

Вот готовое к использованию и пошаговое руководство о том, как использовать подход Novarg в Swift 3:


Шаг № 1: Реализация выбора языка

Как лучше всего это сделать, зависит от вас и от проекта. Но используйте

Bundle.main.localizations.filter({ $0 != "Base" }) // => ["en", "de", "tr"]

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

Locale.current.localizedString(forLanguageCode: "en") // replace "en" with your variable

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

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

@IBOutlet var changeLanguageButton: UIButton!

@IBAction func didPressChangeLanguageButton() {
    let message = "Change language of this app including its content."
    let sheetCtrl = UIAlertController(title: "Choose language", message: message, preferredStyle: .actionSheet)

    for languageCode in Bundle.main.localizations.filter({ $0 != "Base" }) {
        let langName = Locale.current.localizedString(forLanguageCode: languageCode)
        let action = UIAlertAction(title: langName, style: .default) { _ in
            self.changeToLanguage(languageCode) // see step #2
        }
        sheetCtrl.addAction(action)
    }

    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
    sheetCtrl.addAction(cancelAction)

    sheetCtrl.popoverPresentationController?.sourceView = self.view
    sheetCtrl.popoverPresentationController?.sourceRect = self.changeLanguageButton.frame
    present(sheetCtrl, animated: true, completion: nil)
}

Шаг № 2: Объясните пользователю, что делать + Смена языка при перезапуске

Вы могли заметить, что код на шаге # 1 вызывает метод с именем changeToLanguage(langCode:). Это то, что вы должны делать, тоже, когда пользователь выбирает новый язык для изменения, независимо от того, как вы его создали. Вот его реализация, просто скопируйте его в свой проект:

private func changeToLanguage(_ langCode: String) {
    if Bundle.main.preferredLocalizations.first != langCode {
        let message = "In order to change the language, the App must be closed and reopened by you."
        let confirmAlertCtrl = UIAlertController(title: "App restart required", message: message, preferredStyle: .alert)

        let confirmAction = UIAlertAction(title: "Close now", style: .destructive) { _ in
            UserDefaults.standard.set([langCode], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
            exit(EXIT_SUCCESS)
        }
        confirmAlertCtrl.addAction(confirmAction)

        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        confirmAlertCtrl.addAction(cancelAction)

        present(confirmAlertCtrl, animated: true, completion: nil)
    }
}

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

UserDefaults.standard.set([langCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize() // required on real device

Шаг № 3 (необязательно): локализация строк

Возможно, вы захотите локализовать строки, такие как "Закрыть сейчас", используя макрос NSLocalizedString (или любой другой улучшенный метод).


Пример из реального мира

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

Ответ 4

Мой ответ может быть вопросом предпочтения, поскольку я буду пренебрегать выбором языка вручную в приложении: вам придется переопределить все системные кнопки и не использовать их. Он также добавляет еще один уровень сложности и может привести пользователя к путанице.

Однако, поскольку это должен быть ответ, я думаю, что ваш прецедент разрешен без взлома выбора языка.

В настройках iOS вы можете установить дополнительные языки:

предпочтения выбора языка iOS

Ваш пример, сын иммигрантов, мог бы поставить французский как основной язык и немецкий как дополнительный язык.

Затем, когда ваше приложение локализовано на английском и немецком, этот молодой человек iPhone будет выбирать немецкие ресурсы.

Помогло ли это решить проблему?

Ответ 5

Localize-Swift - Быстрая дружественная локализация и i18n с переключением языков в приложении

Ответ 6

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

UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"App restart required", @"App restart required") message:NSLocalizedString(@"In order to change the language, the App must be closed and reopened by you.", @"In order to change the language, the App must be closed and reopened by you.") preferredStyle:UIAlertControllerStyleActionSheet];

    [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {


        [self dismissViewControllerAnimated:YES completion:^{


        }];
    }]];

    [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Restart", @"Restart") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {

        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"ar", nil] forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults]synchronize];

        exit(EXIT_SUCCESS);


    }]];


    [self presentViewController:actionSheet animated:YES completion:nil];
}

Ответ 7

У меня была похожая проблема в моем текущем рабочем проекте. Я нашел простой способ сделать это, и мне пришлось объяснить это своим партнерам, чтобы обсудить решение. Я сделал простое приложение с 2 кнопками (английский || испанский), чтобы выбрать язык внутри приложения, код находится в Objective-C (поскольку наше текущее приложение в Objective-C), вот код: https://bitbucket.org/gastonmontes/gmlanguageselectiondemo

Ответ 8

Начиная с iOS 13, теперь есть способ установить индивидуальный язык для каждого приложения, поддерживаемый самой iOS. В приложении Apple Settings откройте настройки конкретного приложения и выберите язык в разделе "Предпочитаемый язык". Вы можете выбрать один из языков, которые поддерживает приложение.