Menu

Language Changer: in-app language selection in iOS

Alan Chung By Alan Chung

Many apps on the iOS app store have the ability to change the language independently of the language setting of the device, but this is not catered for in the standard iOS localisation features. Carry on reading to find out how to change the language of your app independently of the device setting.

Setting up

Apple provides features for internationalisation of strings, storyboards and image files within your app, but the one we will focus on is strings (and images indirectly).

Localised strings can be found in the Localisable.strings file. This won’t be in your project by default, so go ahead and add it by navigating to File > New > File… (⌘N). Then pick Strings File from the Resources section. Be sure to name it Localizable.strings as this is the standard place where the operating system looks for your translations.

Strings file dialog in XCode

You’ll then need to select the new Localizable.strings file in the navigator on the left, then click the Localize… button on the right hand panel of Xcode. Then add the languages you want to cater for.

Language Localisation dialog in XCode

Usually we would use the NSLocalizedString() macro in the place of an ordinary string when we want to localise a particular string. When running the app, this displays the appropriate string using the language setting of the device if the string has a value in the Localizable.strings file. However we want to show the string for the language the user has selected within the app rather than for the language setting of the device, so we need to write our own version of NSLocalizedString(). But before we do that, we need a way to keep track of which language the user has selected.

The model

The Locale class in the example project is a model for this. Properties include a name to display, a language code and a country code for the flag images which we’ll look at soon.

#import <Foundation/Foundation.h>

@interface Locale : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *languageCode;
@property (nonatomic, copy) NSString *countryCode;

- (id)initWithLanguageCode:(NSString *)languageCode countryCode:(NSString *)countryCode name:(NSString *)name;

@end

The manager

In the LanguageManager class we have our method that acts as a modified version of NSLocalizedString(). It looks in the relevant .lproj folder of your project for a Localisable.strings file, then gets the translated string for the given key. There’s also a macro in Constants.h to mimic the syntax for usage of NSLocalizedString(). I’ve very creatively called it CustomLocalisedString().

- (NSString *)getTranslationForKey:(NSString *)key {
    
    // Get the language code.
    NSString *languageCode = [[[NSUserDefaults standardUserDefaults] stringForKey:DEFAULTS_KEY_LANGUAGE_CODE] lowercaseString];

    // Get the relevant language bundle.
    NSString *bundlePath = [[NSBundle mainBundle] pathForResource:languageCode ofType:@"lproj"];
    NSBundle *languageBundle = [NSBundle bundleWithPath:bundlePath];
    
    // Get the translated string using the language bundle.
    NSString *translatedString = [languageBundle localizedStringForKey:key value:@"" table:nil];
    
    if (translatedString.length < 1) {
        
        // There is no localizable strings file for the selected language.
        translatedString = NSLocalizedStringWithDefaultValue(key, nil, [NSBundle mainBundle], key, key);
    }
    
    return translatedString;
}

The LanguageManager class also handles the selection of a different Locale. To persist between launches, the example app uses the NSUserDefaults, but this can be done using another method if you prefer.

- (void)setLanguageWithLocale:(Locale *)locale {
    
    [[NSUserDefaults standardUserDefaults] setObject:locale.languageCode forKey:DEFAULTS_KEY_LANGUAGE_CODE];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

Putting it together

Finally we need a way to refresh the strings that are on display when the user picks a different Locale. (Note: this doesn’t need to be done for every view controller in your app, just the ones where the locale can change during the lifetime of the view controller.) See the -setupLocalisableElements method in the ViewController class. You might notice that this also changes the image being displayed. In this example we are using the countryCode property on the selected Locale to get the name of the appropriate image file.

- (void)setupLocalisableElements {
    
    self.title = CustomLocalisedString(@"Title", @"The string to display in the navigation bar.");
    
    self.textView.text = CustomLocalisedString(@"Text", @"The string to display in the text view.");
    self.textView.contentOffset = CGPointZero;
    
    // Flag images are named after the country code of the Locale.
    UIImage *flagImage = [UIImage imageNamed:[[LanguageManager sharedLanguageManager] getSelectedLocale].countryCode];
    [self.flagImageView setImage:flagImage];
}

The example project also selects a Locale automatically on first launch depending on the language setting of the device. This is done in the -application:didFinishLaunchingWithOptions: method in the AppDelegate.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    LanguageManager *languageManager = [LanguageManager sharedLanguageManager];
    
    // Check whether the language code has already been set.
    if (![userDefaults stringForKey:DEFAULTS_KEY_LANGUAGE_CODE]) {
        
        NSLog(@"No language set - trying to find the right setting for the device locale.");
        NSLocale *currentLocale = [NSLocale currentLocale];
        
        // Iterate through available localisations to find the matching one for the device locale.
        for (Locale *localisation in languageManager.availableLocales) {
            if ([localisation.languageCode caseInsensitiveCompare:[currentLocale objectForKey:NSLocaleLanguageCode]] == NSOrderedSame) {
                [languageManager setLanguageWithLocale:localisation];
                break;
            }
        }
        
        // If the device locale doesn't match any of the available ones, just pick the first one.
        if (![userDefaults stringForKey:DEFAULTS_KEY_LANGUAGE_CODE]) {
            NSLog(@"Couldn't find the right localisation - using default.");
            [languageManager setLanguageWithLocale:languageManager.availableLocales[0]];
        }
    }
    else {
        NSLog(@"The language has already been set :)");
    }
    return YES;
}

There you have it, a way to change the locale of your app without having to change your device setting. Take a look on GitHub for the full source code of the example project. Now go forth and have a bash at changing languages in your own app!

Categories

Tutorials
comments powered by Disqus

We're Hiring - C#.NET Web Developer

By Sophie Hardbattle

We're looking for a new web developer to join the Eden Agency team.

Read more

We're Hiring - Unity Game Developer

By Sophie Hardbattle

Eden Agency are looking for another member of the development team.

Read more

Yorkshire Virtual Reality Meetup

By Adam Taglietti

We're hosting the Yorkshire VR event. Meet up with other enthusiasts and try the latest technology including the Microsoft HoloLens and the Oculus Rift with Touch controllers.

Read more

Top AR/VR Developers

By Adam Taglietti

Clutch has named us one of the top AR/VR developers.

Read more

Introducing PowerUp

By Sophie Hardbattle

Introducing PowerUp -- our new virtual reality solution for events.

Read more