25

I am localizing my iOS app, and in the Simulator it runs correctly in my chosen language every time.

When testing on my iPhone 5, it only detects the language properly the first time the app runs. Every other time I recompile and run my app on the device, it detects "en" as the language, even though I am testing with Español ("es") selected.

I detect the language using:

[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]

I've also used:

[[NSLocale preferredLanguages] objectAtIndex:0]

Same result.

If I kill the app after the first run, and restart it on the device, it continues to detect the language properly.

But if I kill the app and then recompile/restart via Xcode after the initial run, it will load with "en" (English) detected instead.

After that, killing and re-starting the app continuously detects as English unless I delete the app completely, and recompile/reinstall/run the app via Xcode. The cycle then repeats... subsequent rebuild/restart without first deleting the app from the device results in misdetection.

All other apps on my device display with Spanish language the entire time. The entire UI shows in Spanish.

UPDATE: I've now tested on my iPad (3rd gen) also running iOS 6, and am experiencing the same behavior.

UPDATE 2:

In didFinishLaunchingWithOptions, I have this code to detect language: (language is an NSString*):

language = [[NSLocale preferredLanguages] objectAtIndex:0];

Followed by this debugging statement, to compare the value I'm getting, as well as a slightly different way of detecting it, just for debugging:

NSLog(@"Detected language: %@ / %@", language, [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]);

The output shows as "Detected language: es / es" when the app works properly in Spanish mode, and then shows as "Detected language: en / en" when it doesn't. Still no idea why it decides to load as English sometimes...

UPDATE 4: I appreciate everybody's answers, and I've tried the various suggestions. Unfortunately I was unable to award the +100 bounty as none of the suggestions seemed to fix the issue. If someone does ultimate find a solution that works for me, I will award another +50 bounty to them at that time.

UPDATE 5: I have updated from Xcode 4.5 to 4.5.2, and experiencing this same issue.

UPDATE 6: I have now created a new test project from scratch, and it works perfectly fine! Obviously something must be wrong in the way my project is laid out, or perhaps in one of the data files. I guess my next journey will be to re-create the project from scratch, copying file data over one by one...

UPDATE 7 (MONTHS LATER): Sadly, I am again facing this issue after temporarily resolving it (seemingly) by painstakingly recreating my project. On first load, the language is correctly rendered, but on subsequent loads, it reverts back to English.

SOLVED See my final solution below. Thanks for the help everyone. I may dole out some of the bounty since it will go to waste anyway.

Community
  • 1
  • 1
Mason G. Zhwiti
  • 6,444
  • 11
  • 61
  • 97
  • 2
    I don't really have too much to share with you, but did you play with -AppleLanguage "(English") run parameter maybe? (See http://dl.dropbox.com/u/15994997/Screenshots/2h.png , that's in Manage Schemes section). I doubt it's the issue but that's the only thing that comes to mind assuming that your code is fine. Also, I bet you did that, but try just printing the language with NSLog right after getting it from the locale and verify that it's not the rest of your code. – Mayjak Nov 29 '12 at 16:59
  • Have you tested this in iOS 5? Maybe this is an iOS 6 bug ? – Lefteris Nov 29 '12 at 17:04
  • @Estarriol I'll give your -AppleLanguage thing a shot this afternoon. I am printing out with NSLog right after I get the values, and it reports it as "es" the first time when it works, and then "en" when it's failing. If I exit out of the app, everything else in iOS continues to be in Spanish as expected. – Mason G. Zhwiti Nov 29 '12 at 20:46
  • @Lefteris I have not tested it in iOS 5, but the app has some iOS 6 only features enabled, so it would be a bit of work. It seems that if it was a bug in iOS 6, it would be affecting a lot of people. Localizing apps is surely quite popular? – Mason G. Zhwiti Nov 29 '12 at 20:47
  • @MasonG.Zhwiti : where you put the code [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0] ? – Rahul Patel Nov 30 '12 at 09:32
  • Do you only have problems with the calls you mention, or also with your XIB file localizations and with NSLocalizedString? – fishinear Nov 30 '12 at 13:37
  • @fishinear I have problems with everything including localized storyboards and NSLocalizedString. That's why I don't think the issue has to do with when I'm looking up preferredLocalozations. – Mason G. Zhwiti Nov 30 '12 at 15:05
  • @Estarriol -AppleLanguages does work, but I'm worried that whatever is causing my problem (when I don't use that) will effect the app once distributed in the app store. It doesn't seem like that compile flag should be required? – Mason G. Zhwiti Dec 03 '12 at 22:01
  • This is interesting. Some piece of code might be setting the language through `[[NSUserDefaults standardUserDefaults] setObject: ... forKey:@"AppleLanguages"]`. Did you check your code (and any third party code you use) for that? Try removing things from the app one by one to eliminate possibilities - don't load any view after the first view. If the problem persists, remove first view as well and create a new view. Remove all dependencies. Remove everything from `didFinish` app delegate method. – maroux Jul 08 '13 at 13:32
  • @Mar0ux Thanks for the reply. No calls to set the AppleLanguages key anywhere in the project, including 3rd party source. I am reading the value of that key (as well as a few other ways of getting the system language) in applicationDidFinishLaunching, and that is where it is incorrectly reported as English on the 2nd (and on) loads of the app on my device. So whatever is happening, it's happening almost immediately, before my views are loaded (which are done in Storyboards). The problem does not happen in the simulator. Every launch works fine there! – Mason G. Zhwiti Jul 09 '13 at 04:19

5 Answers5

8

I have FINALLY solved this problem after many months! Thanks to all for the help (I also had some good back and forth with an Apple developer via the dev channels).

TL;DR: I was accidentally syncing language preferences (among many other unexpected things) between devices, using my app's iCloud key value store (via MKiCloudSync)! Read on...

I am using a third-party class called MKiCloudSync, which helps with syncing [NSUserDefaults standardUserDefaults] to my app's iCloud key value store. My intention when I began using it was to let it handle some user favorites syncing in the background.

However, not understanding how standardUserDefaults works, what I didn't realize is that there are a lot of other things being written into standardUserDefaults other than just my own custom app settings!

So what was happening was this:

  1. Start the app up for the first time. Fresh standardUserDefaults in place, and the internal "AppleLanguages" key that stores the ordered list of language preferences is correct based on the current device choices.

  2. App displays properly in the designated language.

  3. In the background, MKiCloudSync syncs ALL standardUserDefaults to iCloud. Conversely, if you had run this app elsewhere, say with an English set device, that device would have also synced it's language settings up to iCloud. So now this current running app is actually having it's language preferences overwritten.

  4. BOOM ... next time the app is run, no matter what you have selected on the device, it's whatever was pulled down from iCloud that will be used as the default language!

What I plan to do to solve the issue with my next app update:

  1. Use a forked version of MKiCloudSync that allows for syncing only whitelisted key names.

  2. Add code that will do a one-time cleanup, first cleaning out the iCloud keystore for my app, then (based on this SO answer), calling this code to reset the user defaults:

    NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];

In my testing so far, this sort of solves the issue... unfortunately, the user would have to restart the app for the language fix to kick in. However, my guess is most users are not experiencing this issue, as they are unlikely to be using multiple devices with different default languages.

Community
  • 1
  • 1
Mason G. Zhwiti
  • 6,444
  • 11
  • 61
  • 97
  • Did you try cleaning your build folder? cmd+option+shift+k – Danra Jul 07 '13 at 21:00
  • This just saved my life. Anyway, these are the keys that should be ignored: [[NSMutableSet setWithObjects:@"AppleICUForce24HourTime", @"AppleITunesStoreItemKinds", @"AppleKeyboards", @"AppleKeyboardsExpanded", @"AppleLanguages", @"AppleLocale", @"NSInterfaceStyle", @"NSLanguages", @"OpenUDID", @"StatsDone", @"UIDisableLegacyTextView", nil] retain]; – Andrei Radulescu Jun 06 '14 at 16:06
  • @AndreiRadulescu You probably will be better off whitelisting the keys you want to sync, as Apple could introduce new keys in the future that might cause you some other unexpected problems... – Mason G. Zhwiti Jun 06 '14 at 19:06
  • @MasonG.Zhwiti You're right! Your answer is be the general case. The thing is I have over 100 keys saved for my game, and most of them are dinamically generated. I don't want to lose any in the process. Cheers! – Andrei Radulescu Jun 06 '14 at 22:24
3

In settings->general->international, there is an option to set the location language etc. if you set that to the language you are trying to test it will work if your code is correct.

OnkaPlonka
  • 1,232
  • 10
  • 19
  • Yeah, that is how I've been trying to test it. That's how I select the language I'm testing (Spanish in this case). As described in my question, this only works the first time. Once I run my code a second time, it's as if it resets the language, even though the rest of the phone interface makes it obvious I'm still correctly set to Spanish. – Mason G. Zhwiti Dec 10 '12 at 20:57
  • I will give this a try and report back. It looks like I was on Xcode 4.5, and the latest release is 4.5.2. – Mason G. Zhwiti Dec 11 '12 at 20:36
  • OK, I updated the Xcode 4.5.2, and I have the same problem. :-( – Mason G. Zhwiti Dec 12 '12 at 04:45
  • Xcode must have stuffed up your project then it's happened to me many times – OnkaPlonka Dec 15 '12 at 09:28
2

I tested your steps on my iPhone 5 without issues. This leads me to think there's something else at play here: most probably there's something interferring with the way in which you're reading the locale value.

The steps I'd recommend you take to help you debug this issue are:

  1. Post the complete code of the method in which you're obtaining the preferred language value. Make sure the method is executed each time the app is run.
  2. Make sure the code you post includes the location of the NSLog directive you're using to test for the language setting.
  3. Are you storing the preferred language somewhere else after the first run?
diegoreymendez
  • 1,997
  • 18
  • 20
  • Even things in my code that don't explicitly use the value I detect as the preferred language revert back to English. For example, I have localized Storyboard files and Localizable.strings files that work in Spanish on initial load. Then subsequently when it switches to English, even those things revert. – Mason G. Zhwiti Nov 30 '12 at 05:10
2

Try with following code:

LocalizationSystem.h===

#import <Foundation/Foundation.h>

#define AMLocalizedString(key, comment) \
[[LocalizationSystem sharedLocalSystem] localizedStringForKey:(key) value:(comment)]

#define LocalizationSetLanguage(language) \
[[LocalizationSystem sharedLocalSystem] setLanguage:(language)]

#define LocalizationGetLanguage \
[[LocalizationSystem sharedLocalSystem] getLanguage]

#define LocalizationReset \
[[LocalizationSystem sharedLocalSystem] resetLocalization]

@interface LocalizationSystem : NSObject {
    NSString *language;
}


+ (LocalizationSystem *)sharedLocalSystem;

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

//sets the language
- (void) setLanguage:(NSString*) language;

//gets the current language
- (NSString*) getLanguage;

//resets this system.
- (void) resetLocalization;

@end

LocalizationSystem.m===

#import "LocalizationSystem.h"

@implementation LocalizationSystem

//Singleton instance
static LocalizationSystem *_sharedLocalSystem = nil;

//Current application bundle to get the languages.
static NSBundle *bundle = nil;

+ (LocalizationSystem *)sharedLocalSystem{
    @synchronized([LocalizationSystem class])
    {
        if (!_sharedLocalSystem){
            [[self alloc] init];
        }
        return _sharedLocalSystem;
    }
    // to avoid compiler warning
    return nil;
}

+(id)alloc{
    @synchronized([LocalizationSystem class])
    {
        NSAssert(_sharedLocalSystem == nil, @"Attempted to allocate a second instance of a singleton.");
        _sharedLocalSystem = [super alloc];
        return _sharedLocalSystem;
    }
    // to avoid compiler warning
    return nil;
}

- (id)init{
    if ((self = [super init])) 
    {
        //empty.
        bundle = [NSBundle mainBundle];
    }
    return self;
}

// Gets the current localized string as in NSLocalizedString.

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment{
    return [bundle localizedStringForKey:key value:comment table:nil];
}

// If this function is not called it will use the default OS language.
// If the language does not exists y returns the default OS language.
- (void) setLanguage:(NSString*) l{
    NSLog(@"preferredLang: %@", l);

    NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];


    if (path == nil)
        //in case the language does not exists
        [self resetLocalization];
    else
        bundle = [[NSBundle bundleWithPath:path] retain];

    [[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:l, nil] forKey:@"AppleLanguages"];
}

// Just gets the current setted up language.
// returns "es","fr",...
//
// example call:
// NSString * currentL = LocalizationGetLanguage;
- (NSString*) getLanguage{

    NSArray* languages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];

    NSString *preferredLang = [languages objectAtIndex:0];

    return preferredLang;
}

// Resets the localization system, so it uses the OS default language.
//
// example call:
// LocalizationReset;
- (void) resetLocalization{
    bundle = [NSBundle mainBundle];
}


@end

This code works perfectly as you mentioned. It worked for me and that game is live now app store, if you want to check(HueShapes).

Aniket Kote
  • 541
  • 3
  • 9
  • I appreciate your response, but unless I'm missing something, ultimately you're calling `[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"]` to get the language, which is one of several calls I've already tried, and I still experience the same problem... it works the first time (comes back with "es") and then on subsequent runs it fails (returns "en" instead, even though I never changed the language back on the phone). – Mason G. Zhwiti Dec 10 '12 at 20:59
  • are you changing language in app or from settings of the device, if from the app then can you please share code of localization. – Aniket Kote Dec 24 '12 at 05:03
  • From the settings of the device. – Mason G. Zhwiti Dec 28 '12 at 19:46
0

Do you by chance use NSUserDefaults to save something language related?

Look into your Simulator App directory -> Library -> Preferences -> <YourAppBundleName>.plist

See: How to force NSLocalizedString to use a specific language for description of the NSUserDefaults method of setting a language.

Perhaps you just save your language and thus detection just returns the saved value.

Community
  • 1
  • 1
Dunkelstern
  • 1,624
  • 12
  • 18