ручний вибір мови в додатку iOS (iPhone та iPad)


74

Моє запитання:

Як мій додаток для iPhone може сказати iOS, що користувач вибрав мову в налаштуваннях програм, яка відрізняється від мови, встановленої в загальних налаштуваннях?

Інше формулювання того ж питання:

Як я можу сказати системі, яка NSLocalizedString (@"text", @"comment");повинна мати доступ не до загальновибраної мови, а до вибраної в програмі мови?

фон, приклад:

Візьміть цю ситуацію як приклад: син німецьких іммігрантів живе на північному сході Франції поруч із Люксембургом та Німеччиною. Його рідною мовою є французька, тому він встановив для мови інтерфейсу користувача свого iPhone французьку (Налаштування -> Загальні -> Міжнародні -> Мова -> Французька). Але завдяки своєму культурному походженню та тому, що регіон, де він живе, є двомовним, він також дуже добре володіє німецькою мовою. Але він не говорить десять слів англійською. На iPhone (і iPad також) він не має можливості вибрати другу мову, тому телефон знає лише, що він поширює французьку мову. Він не знає навичок користувачів іншими мовами.

Тепер з’являється мій додаток: я розробив його англійською та німецькою мовами (німецька - моя рідна мова, а англійська - стандартна мова в ІТ). Я розробив його відповідно до всіх правил та найкращих практик для мулілінгвальних iOS-додатків. "Першою" мовою (мовою за замовчуванням) мого додатка є англійська.

Це означає:

Якщо хтось вибрав англійську чи німецьку мову у своїх налаштуваннях, користувальницький інтерфейс програм автоматично використовуватиме вибрану мову. Користувач навіть не помітить, що доступні інші мови.

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

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

Тому я думаю, що у мого додатка повинна бути можливість вручну вибрати мову, яка відрізняється від попередньо вибраної мови. Додавання вибору мови на панель налаштувань програм не є проблемою. Але як я можу сказати системі, яка NSLocalizedString (@"text", @"comment");повинна мати доступ не до загальновибраної мови, а до вибраної в програмі мови?


2
Це не широко відома функція, але насправді можна встановити кілька бажаних мов у програмі Налаштування. Ви повинні вибрати їх у зворотному порядку, наприклад, вашому другу потрібно буде переключити свій iPhone на німецьку, а потім на французьку. Це зробить французьку переважною мовою, повернувшись до німецької, якщо вона недоступна, а потім до локалізації базової програми.
Таум

5
ПЕРШИЙ: Це не просто не широко відомо, воно повністю не оформлено документально. ДРУГИЙ: Я, як розробник програми, не маю повноважень повідомляти цю функцію всім 800 мільйонам користувачів iOS. І навіть якби я міг: Навряд чи кожен користувач зробив би це. Отже, це приємне рішення, яке може зробити десяток досвідчених користувачів, але це не рішення, яке я, як розробник, можу записати у свій додаток.
Губерт Шельнаст

3
Немає проблем, людина, рада, що змогла допомогти!
Таум

7
Ааа. Ну. Я не знаю, як це сказати, не завдаючи вам шкоди, але: Ви не допомогли. Це те, що я хотів сказати фразами на кшталт "це повністю недокументовано" і "це не рішення".
Губерт Шельнаст,

Відповіді:


85

Тим часом я все-таки знайшов на собі рішення своєї проблеми:

Я створив новий клас "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. Єдина відмінність: тепер ви навіть можете використовувати такі мови, як хінді чи есперанто, які не підтримуються 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");

і мати запис "хороший заголовок" у кожній версії Localizable.strings!


21
Це здається хорошим рішенням для текстових рядків, що містяться в коді. Але як щодо базової інтернаціоналізації та розкадрувань? Як змусити MainStoryboard.storyboard, наприклад, перейти на нову мову? Ви замінили NSLocalizedString (ключ, коментар) на LocalizedString (ключ), що дозволяє використовувати відповідний пакет. Але це не змушує зміни на розкадруванні. Чи є спосіб змінити, який комплект використовується розкадровкою?
Грегорі Хілл

1
@ HubertSchölnast Не хвилюйтесь, я знайшов проблему. Я не використовую те саме ім'я для файлу localizable.string. Це викликає проблему. тепер проблема вирішена.
MinuMaster

1
Як переконатись, що всі ВК перезавантажені, щоб оновити мітки новою, вибраною мовою? Я використовую '[self viewDidLoad]; [self viewWillAppear: (BOOL) NO]; ' але іноді це не працює, і мова не змінюється, навіть якщо всі мітки встановлені правильними методами. У мене є один файл німецькою мовою та один базовий файл англійською мовою.
Mexx

1
@MaheshNarla: Те, що я описав, НЕ є алгоритмом перекладу. Що вам потрібно, це машина перекладу, і це тема багатьох десятиліть напружених досліджень, які досі не принесли задовільного рішення. Але, очевидно, очевидно, що мобільний телефон 2017 року не зможе забезпечити ресурси для перекладацької машини.
Губерт Шельнаст,

1
@ HubertSchölnast добре, але я щиро дякую за Вашу цінну відповідь.
Бумеш Пурохіт,

40

Просто додайте на екран наступне з вибором мови:

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

Потім попросіть їх перезапустити програму, і програма буде іншою мовою.

Сподіваюся, це допоможе


Я не випробовував вашу пропозицію, але я впевнений, що це не вирішить мою проблему. Здається, це робить одну з двох речей (я не впевнений в одній з них): або вона встановлює глобальний загальносистемний вибір мови на нову мову, або просто зберігає мову selectes у програмах UserDefaults. Зміна загальних налаштувань мови - це погано, оскільки вибір у моїй програмі не повинен впливати на інші програми! Зберігання виділеного в додатках UserDefaults не робить нічого іншого, як зберігати його там. IOS не знатиме, що слід ВИКОРИСТОВУВАТИ обрану мову для цього додатка.
Губерт Шельнаст,

7
@ HubertSchölnast, як ви можете бути впевнені, що це не вирішить вашу проблему, якщо ви не спробували? Крім того, ви не можете змінити "вибір загальносистемної мови" у своєму додатку. І ви впевнені, що це нічого не дасть? Я впевнений, що це змінить мову програми.
Novarg

Я спробував, і це справді працює! Дякую! Але проблема все ще є: програму потрібно повністю вимкнути, а потім перезапустити. недостатньо просто натиснути кнопку головного, щоб перемістити його у фоновий режим. Я хотів би знайти більш зручне рішення.
Губерт Шельнаст,

3
@ HubertSchölnast, якщо ви хочете, щоб програма закрилася після натискання кнопки головного, яку ви можете додати Application does not run in backgroundдо свого info.plistфайлу. Тоді щоразу, коли ви натискаєте кнопку додому у своєму додатку, програма закриватиметься. І позначте відповідь як правильну, щоб заохотити інших користувачів відповідати на ваші запитання в майбутньому.
Novarg

2
+1 за "якщо ви хочете, щоб програма закрилася після натискання кнопки" Домашня сторінка ", ви можете додати, що програма не працює у фоновому режимі до вашого файлу info.plist." - позбавляє вас від примусового закриття програми, щоб перезавантажити всі контролери перегляду
thomers

24

Ось готовий до використання і крок за кроком керівництво про те , як використовувати підхід Novarg в Swift 3 :


Крок No1: Застосуйте інструмент вибору мови

Як найкраще це зробити, залежить від вас і залежить від проекту. Але використовуйте

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

Крок No3 (необов’язково): Локалізуйте рядки

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


Приклад реального світу

Я використовую саме цю реалізацію в додатку, призначеному для iOS 10, я можу підтвердити, що це працює для мене як на симуляторі, так і на пристрої. Додаток насправді є відкритим кодом , тому ви можете знайти вищевказаний код, розподілений по різних класах тут .


Привіт, Як локалізувати динамічний текст, що надходить із сервера, у моїй програмі кожен текст, який я повинен відображати спрощеною китайською мовою, але як відображати текст китайською мовою, який надходить англійською мовою. допоможи мені.
Махеш Нарла,

1
Як ви вже сказали: текст динамічний і надходить із сервера англійською мовою. Звичайним підходом було б сказати серверу, яку мову ви очікуєте у своєму запиті, і відповідальність сервера відповісти спрощеним китайським текстом, якщо ви це вкажете. Зазвичай це робиться із localeнабором десь у запиті. Якщо у вас немає доступу до сервера, можливо, ви захочете скористатися послугою машинного перекладу, як Google Translate, - але результати можуть бути не найкращими ...
Jeehut

Це працює у мене вже досить давно. Яку версію iOS ви використовуєте? Можливо, ви робите це неправильно, я міг би поглянути, якщо ви завантажили частину коду, де ви реалізуєте це ...
Jeehut

Якщо я буду використовувати код виходу (EXIT_SUCCESS) і спробувати завантажити програму в iTunes Connect. Apple відхилить заявку?
Bhavesh Nayi

2
Отже, в основному те, що ви пропонуєте за цими двома контролерами сповіщень, - це перезапустити програму. Не надто корисно
rommex

4

Починаючи з iOS 13, тепер існує спосіб встановити окрему мову для кожної програми, що підтримується самою iOS. У програмі Apple Settings відкрийте налаштування певної програми та виберіть мову в розділі "Мова, яку ви бажаєте". Ви можете вибрати мову, яку підтримує програма.


1
Так, це захоплююче, і якщо хтось надає користувачеві бездоганний досвід, тоді в програмі можна відкрити налаштування, щоб вибрати потрібну мову. developer.apple.com/videos/play/wwdc2019/403
Manish Nahar

3

Моя відповідь може бути питанням переваги, оскільки я б зневажав вибір мови вручну в додатку: вам доведеться замінити всі передбачені системою кнопки та не використовувати їх. Це також додає ще один рівень складності і може призвести користувача до плутанини.

Однак, оскільки це має бути відповіддю, я думаю, що ваш варіант використання вирішено без злому мови.

У налаштуваннях iOS ви можете встановити додаткові мови:

Налаштування вибору мови iOS

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

Тоді, коли ваш додаток буде локалізовано на англійській та німецькій мовах, iPhone цього молодого чоловіка вибере німецькі ресурси.

Це вирішить проблему?



0

Його дуже просто і легко змінити мову вручну. Перш за все, вам потрібно локалізувати програму, а потім ви можете скористатися наведеним нижче кодом для ручної зміни мови у програмі.

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

1
Якщо я зможу це зробити, як ваша відповідь apple прийме мою заявку?
Bhavesh Nayi

Так, Apple не має проблем з цим підходом, оскільки його сфера застосування - лише ваша програма. Я поширював із ним додатки
Spydy

0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.