2

I want to create an iPhone application with English and Arabic language. I checked the Internationalization document for language switcher, however to take that into effect I have to manually go and change the iPhone setting. I don't want to do that. So what I am planning is on home screen I will have two button as English and Arabic. If user click Arabic, I will have arabic text and if user select English, app will be in english.

Any idea/ suggestion how to get this done?

Note: I don't want to manually go and change the language.


Edit 1

As per @Jano, I have done below.

Created new project. Added Arabic in Localization. Now I have two storyboard and two InfoPlist.strings file.

Added Localization.h and .m file as shown in answer.

Directory structure is MyProject-ar.lproj & MyProject-en.lproj

Content of Plist are "myButton01" = "Back"; & "myButton01" = "ظهر";

First View Controller have two button as English and Arabic. Called action on those button.

- (IBAction)pressedEnglish:(id)sender {
    [Localization sharedInstance].fallbackLanguage = @"ar";
    [Localization sharedInstance].preferredLanguage = @"en";
    NSLog(@"pressed english");
}

- (IBAction)pressedArabic:(id)sender {
    [Localization sharedInstance].fallbackLanguage = @"en";
    [Localization sharedInstance].preferredLanguage = @"ar";
    NSLog(@"pressed arabic");
}

In second view controller, I added one button and gave name as myButton. Now in viewDidLoad, I have

[self.myButton setTitle:localize(@"myButton01") forState:UIControlStateNormal];

I hope this should be working, however when I run the project, I see button as myButton01

Any reason why this is happening?


Edit 2

I got Edit 1 problem. I renamed InfoPlist.strings to Localizable.strings and it worked. But but but, I am still getting Arabic text irrespective of whatever button I press.

When finding reason, I found that it was because of below statement that we have in Localization.m

static Localization *shared = nil;
dispatch_once(&pred, ^{
    shared = [[Localization alloc] init];
    shared.fallbackLanguage = @"en";
    shared.preferredLanguage = @"ar";

The problem is at last two lines. As we have set Arabic as preferredLanguage, I am always seeing the arabic text.

What changes will I need to do so that I can have it as changeable as per button pressed.

Jano
  • 62,815
  • 21
  • 164
  • 192
Fahim Parkar
  • 30,974
  • 45
  • 160
  • 276

1 Answers1

5

You want to set the language of the app from the app UI ignoring the user preference on the device. This is unusual, but here you go...

First write all your language strings on a directory structure like this:

i18n/en.lproj/Localizable.strings
i18n/ar.lproj/Localizable.strings

Create an additional directory with the corresponding two letter code for each additional language supported.

If the files are recognized as i18n resources, they will be presented like this: enter image description here

Files will have a key=value with the following format:

"button.back" = "ظهر";

In your code, replace any localizable string with the key. Example:

[self.stateBtn setTitle:localize(@"button.back") forState:UIControlStateNormal];

Usually you would use NSLocalizedString(@"key",@"fallback") but since you want to ignore iPhone settings, I wrote a localize(@"key") macro above that will have the following implementation:

Localization.h

#ifndef localize
#define localize(key) [[Localization sharedInstance] localizedStringForKey:key]
#endif

@interface Localization : NSObject

@property (nonatomic, retain) NSBundle* fallbackBundle;
@property (nonatomic, retain) NSBundle* preferredBundle;

@property (nonatomic, copy) NSString* fallbackLanguage;
@property (nonatomic, copy) NSString* preferredLanguage;

-(NSString*) localizedStringForKey:(NSString*)key;

-(NSString*) pathForFilename:(NSString*)filename type:(NSString*)type;

+(Localization*)sharedInstance;

@end

Localization.m

#import "Localization.h"

@implementation Localization

+(Localization *)sharedInstance
{
    static dispatch_once_t pred;
    static Localization *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[Localization alloc] init];
        [shared setPreferred:@"en" fallback:@"ar"];
    });
    return shared;
}

-(void) setPreferred:(NSString*)preferred fallback:(NSString*)fallback 
{
    self.fallbackLanguage = fallback;
    self.preferredLanguage = preferred;
    NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:self.fallbackLanguage];
    self.fallbackBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];
    bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:self.preferredLanguage];
    self.preferredBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];
}

-(NSString*) pathForFilename:(NSString*)filename type:(NSString*)type
{
    NSString *path = [self.preferredBundle pathForResource:filename ofType:type inDirectory:nil forLocalization:self.preferredLanguage];
    if (!path) path = [self.fallbackBundle pathForResource:filename ofType:type inDirectory:nil forLocalization:self.fallbackLanguage];
    if (!path) NSLog(@"Missing file: %@.%@", filename, type);
    return path;
}

-(NSString*) localizedStringForKey:(NSString*)key
{
    NSString* result = nil;
    if (_preferredBundle!=nil) {
        result = [_preferredBundle localizedStringForKey:key value:nil table:nil];
    }
    if (result == nil) {
        result = [_fallbackBundle localizedStringForKey:key value:nil table:nil];
    }
    if (result == nil) {
        result = key;
    }
    return result;
}

@end

This will use lookup the key strings in the arabic file, and if the key is missing, it will look in the arabic file. If you want it the other way, do the following from your button handlers:

[[Localization sharedInstance] setPreferred:@"ar" fallback:@"en"];

Sample project at Github.

If localisation doesn't work

If localisation doesn't work, use the plutil command line tool to verify the format of the file. It should output: Localizable.strings: OK. Example:

$ plutil -lint Localizable.strings
Localizable.strings: OK

This format is described in Internationalization Programming Topics > Localizing String Resources. You can optionally add // single-line or /* multi-line */ comments. For non latin languages it’s recommended to encode Localized.strings in UTF-16. You can convert between encodings in the inspector pane of XCode.

If it still doesn't work, check that you are copying the Localizable.strings file in the Copy files phase of your target. Note that when you add Localizable.strings files there, sometimes they appear in red, keep doing it until a file appears in black, then delete the red ones (hacky I know, blame Xcode).

copy files

Jano
  • 62,815
  • 21
  • 164
  • 192
  • thanks for your answer... I think you are going to save me... :) – Fahim Parkar Jan 13 '13 at 08:42
  • uh, you are right, the files should be called Localization.h and Localization.m, not Localize.h and Localize.m. – Jano Jan 15 '13 at 07:32
  • I have updated the question. Could you please take a look at it? – Fahim Parkar Jan 15 '13 at 08:38
  • Ohh, got problem. I renamed `InfoPlist.strings` to `Localizable.strings`, but whenever I click any button, English or arabic, I see arabic text only. – Fahim Parkar Jan 15 '13 at 08:55
  • I edited the answer and added a sample project in github: https://github.com/j4n0/14301523 I think you'll fix your problem copy pasting the new Localization.m – Jano Jan 15 '13 at 09:21
  • Exactly what I wanted. :) I will add your link in answer so that other person can use it. Thanks a ton again :) :) – Fahim Parkar Jan 15 '13 at 09:24
  • Can you take a look at my [`one more question`](http://stackoverflow.com/questions/14304459/is-there-rtl-support-in-iphone-application-stoaryboard). Thanks in advance – Fahim Parkar Jan 15 '13 at 09:29