manual language selection in an iOS-App (iPhone an

2019-01-03 01:07发布

My question:

How can my iPhone-app tell the iOS, that the user did select a language in the apps preferences, that is different from the language set in the general settings?

Other formulation of the same question:

How can i tell the system, that NSLocalizedString (@"text", @"comment"); should not access the systemwide selected language, but the in-app-selected language?

background, example:

Please take this situation as an example: A son of german immigrants is living in the north-east of France next to Luxemburg and Germany. His native language is French, so he did set the user-interfaces language of his iPhone to French (Settings -> General -> International -> Language -> Français). But due to his cultural background and because the region where he is living is bilingual, he also speaks German very well. But he doesn't speak ten words of English. On an iPhone (and iPad as well) he has no chance to select a second language, so the phone only knows that he spreaks french. It has no knowledge of the users skills in other languages.

Now comes my app: I did develop it in English and German (German is my native language and English is standard-language in IT). I did develop it according to all rules and best practices for mulilingual iOS-Apps. "First" Language (default language) of my app is English.

This means:

If somebody has chosen English or German in his Settings, the apps user-interface automatically will use the selected language. The user will not even notice that there are other languages available.

But if he did select any other language (like Chinese, Polish or French) in the general settings, he will get the apps default-language, which, in my case, is English. But for my french-german friend this is not the best choice. He would like to use the existing german version, but there seems to be no way to let the user select this version.

Adding a french translation would solve the problem for our french-german friend, but not for people speaking two other languages (such as italian and german), and I can not support my app with all languages spoken on this planet. Setting the default-language to German is also not optimal, because this would rise the same problem for people speaking french (an native language) and English (as second language).

So I think my app must have the possibility to manually select a language that is different from the pre-selected language. Adding a language-selection to the apps settings-panel ist not the problem. But how can i tell the system, that NSLocalizedString (@"text", @"comment"); should not access the systemwide selected language, but the in-app-selected language?

6条回答
可以哭但决不认输i
2楼-- · 2019-01-03 01:18

Localize-Swift - Swift friendly localization and i18n with in-app language switching

查看更多
等我变得足够好
3楼-- · 2019-01-03 01:18

Its very simple and easy to change language manually. First of all you need to localize your app, then you can use below code to change language manually in your app.

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];
}
查看更多
forever°为你锁心
4楼-- · 2019-01-03 01:19

Here's a ready-to-use and step-by-step guide on how to use Novarg's approach in Swift 3:


Step #1: Implement a language chooser

How to best do this is up to you and depends on the project. But use

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

to get a list of all your supported locales language codes programmatically. Also you can use

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

to present the language name in the apps current language.

As a complete example you could present a popover action sheet after clicking a button like this:

@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)
}

Step #2: Explain user what to do + Change language with restart

You might have noticed that the code in step #1 calls a method named changeToLanguage(langCode:). That's what you should do, too when the user chooses a new language to change to, no matter how you designed your chooser. Here's its implementation, just copy it to your project:

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)
    }
}

This will both ask and inform the user about if he wants to do the change and how to do it. Also it sets the apps language on the next start using:

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

Step #3 (optional): Localize the Strings

You might want to localize the strings like "Close now" by using the NSLocalizedString macro (or any other enhanced method).


Real World Example

I'm using this exact implementation in an app targeted for iOS 10, I can confirm it works for me both on simulator and device. The app is actually open source, so you can find the above code distributed into different classes here.

查看更多
混吃等死
5楼-- · 2019-01-03 01:19

My answer may be a matter of preference since I would disdain manual language selection in the app: you will have to override all system-provided buttons and make sure not to use them. It also adds another layer of complexity and may lead the user to confusion.

However, since this has to be an answer, I think your use case is solved without hacking language selection.

In iOS preferences, you may set additional languages:

iOS language selection preferences

Your example son of immigrants could have set French as main language and German as an additional language.

Then, when your app is localized to English and German, that young man's iPhone would pick German resources.

Would that solve the problem?

查看更多
地球回转人心会变
6楼-- · 2019-01-03 01:27

Simply add the following to the screen with language choice:

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

Then ask them to restart the app and the app will be in other language.

Hope it helps

查看更多
祖国的老花朵
7楼-- · 2019-01-03 01:34

In the meantime I did find a solution for my problem on myself:

I created a new class "LocalizeHelper":


Header 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

For each language you want to support you need a file named Localizable.strings. This works exactly as described in Apples documentation for localization. The only difference: Now you even can use languages like hindi or esperanto, that are not supported by Apple.

To give you an example, here are the first lines of my english and german versions of Localizable.strings:

English

/* 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

/* 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";

To use localizing, you must have some settings-routines in your app, and in the language-selection you call the macro:

LocalizationSetLanguage(selectedLanguage);

After that you must enshure, that everything that was displayed in the old language, gets redrawn in the new language right now (hidden texts must be redrawn as soon as they get visible again).

To have localized texts available for every situation, you NEVER must write fix texts to the objects titles. ALWAYS use the macro LocalizedString(keyword).

don't:

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

do:

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

and have a "nice title" entry in every version of Localizable.strings!

查看更多
登录 后发表回答