in Xcode 4.6.3 I set the "Localization native development region" = en.
I provide my app with Spanish, English and Russian locales.
Unfortunately when I set the Language of the Iphone Simulator to for example "German"
my app uses then the Spanish locale.
I suspect because I have a "Spanish Computer".
But I would like that the customers of my app get English if their locale is not provided by the app.
How can I ensure this?
One thing I noticed. In the "copy bundle resources" tab I see my other Localizable.strings files appear red. But I know that they are being despite red deployed...
Actual language which the app will have has a dynamic nature because it depends on a dynamic list in device settings. iOS will fall down through it and choose the 1st supported language.
Let's consider some app supports "en" and "ru". Despite that fact that "en" is much more preferable language for Portuguese locale, "ru" will be chosen because it situated higher than "en" in device settings on the screenshot below.
I had the same issue with one of my apps. What I did is force en as the second language in AppleLanguages and it's working now. You have to do that before the application starts (main). It's a bit of an ugly hack but works for now (NSLocalizedString is looking at that list to determine the language)... You have to be careful though, because you may get some weird stuff from NSLocale after you do that. For example if you want to use the - displayNameForKey:value: it won't be in sync and you'll have to restart the app so you get the correct result. so what you can do is initialize NSLocale with the first element in the [NSLocale preferredLanguages] (NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:[NSLocale preferredLanguages][0]];) and then it's going to return the expected results.
Here's what you have to do to swap the languages:
// remove what was previously stored in NSUserDefaults (otherwise the previously selected language will be still the first one in the list and your app won't be localized in the language selected in settings)
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; // make the change immediate
// reorder the languages so English is always the default fallback
NSMutableArray *mutableLanguages = [[NSLocale preferredLanguages] mutableCopy];
NSInteger enIndex = NSNotFound;
for (NSString *lang in mutableLanguages) { if ([lang isEqualToString:#"en"]) { enIndex = [mutableLanguages indexOfObject:lang]; break; } }
#try {
if ((enIndex != 0) && (enIndex != 1)) { [mutableLanguages exchangeObjectAtIndex:1 withObjectAtIndex:enIndex]; }
} #catch (NSException *exception) {
}
// save the changes
[[NSUserDefaults standardUserDefaults] setObject:mutableLanguages forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; // to make the change immediate
return UIApplicationMain(argc, argv, nil, NSStringFromClass([YourAppDelegate class]));
Related
I'm pretty stuck with localization inside the app. What is idea:
You choose language inside the app, and depends on which language you choose it save value in NSUserDefaults. Because I didn't find material for this kind of localization, my idea is to make class that will have class method that return string depending on which language is saved in NSUserDefaults. Example:
+(NSString *) helloString
{
NSString *hello = [NSString new];
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"language"] isEqualToString:#"en"]) {
hello = #"Hello";
}
else if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"language"] isEqualToString:#"es"]) {
hello = #"Holla!";
}
return hello;
}
Is this legit way, is there any better solution?
I found solution for my problem. I used LocalizationSystem class, which simulated changed language from system settings.
I added a manual language section inside my iOS App where you can change the language the app should be displayed in. If someone chooses to select a manual language I override the "AppleLanguages" standardUserDefaults like so
NSString *language = [[[NSUserDefaults alloc] initWithSuiteName:kAppGroup] objectForKey:kManualLanguageKey];
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:language, nil]
forKey:#"AppleLanguages"];
Now when the user restarts the app (after terminating it) the App automatically loads the correct LocalizedStrings.
Now here is my problem:
even though this solves my problem of changing the language of the app it does not display numbers etc. correctly. Number/DateFormatter and localizedStringWithFormat depend on the [NSLocale currentLocale]. I know I could just overrite the current lokale as well like so
[[NSUserDefaults standardUserDefaults] setObject:language
forKey:#"AppleLocale"];
but then I can't get the real language/region selected in the system settings once the user decides to disable the manual language. I could store the currentLocale inside my own userDefaults before I override it but then if the user decides (for whatever reason) to change the system language while the manual language in the app is active I won't be able to get this new selected system language.
Is there any way to get the right format of Numbers and Dates without manually changing the locale property of NumberFormatter etc.?
Ok, I found a way to reset the current locale after the user disables the manual language on http://www.thetawelle.de/?p=3800
In main.m if the bool for manual language is false, I reset the current language like this:
NSArray *keysToRemove = #[#"AppleLanguages",#"NSLanguages",#"AppleLocale"];
NSLog( #"RESETTING TO USE SYSTEM LOCALE" );
#try {
for( NSString *currentKey in keysToRemove ) {
if( [defaults objectForKey:currentKey] ) {
[defaults removeObjectForKey:currentKey];
}
}
}
#catch (NSException *exception) {
// NOTHNG TO CATCH HERE
}
#finally {
[defaults synchronize];
}
after that, the current locale and language are back to the language and region selected in the system settings.
I'm trying to share data between my application and a custom keyboard extension. I've turned on App Groups in both the main application target and the custom keyboard target. In my main application, I add an object with the following:
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.mycompany.myapp"];
[userDefaults setObject:someObject forKey:#"KEY"];
Printing out [userDefaults dictionaryRepresentation] in the console reveals that this object has been saved, as does calling [userDefaults objectForKey:#"KEY"].
However, when I try to access this object in the custom keyboard extension:
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.mycompany.myapp"];
NSString *value = [userDefaults objectForKey:#"KEY"];
The value is nil and a call to [userDefaults dictionaryRepresentation] does not reveal the entry that was saved above. I'm on Xcode 6 beta 3. Any ideas?
UPDATE
Fixed in Xcode 6 beta 5
A few probable solutions are:
Your app group is not setup correctly, or you are not using the correct group identifier with initWithSuiteName:
You have not enabled network access for your keyboard. This document states the following when you have network access disabled for your keyboard (default behavior):
No shared container with containing app
It's a bug.
Set
RequestsOpenAccess = YES;
Settings:
NSUserDefaults * usrInfo = [[NSUserDefaults alloc] initWithSuiteName:#"myKeyboard"];
[usrInfo setObject:theme.icon forKey:#"themeName"]; // This is the new data;
[usrInfo synchronize];
keyboardChange:
NSUserDefaults * usrInfo = [[NSUserDefaults alloc] initWithSuiteName:#"myKeyboard"];
[usrInfo synchronize];
NSString * str = [usrInfo objectForKey:#"themeName"];
Then you can change the keyboard , for example ,change its background
I think your suite name has to start with group and match up with the container you made (source).
I found it has to follow this format: group.com.company.appname - I had something else and it didn't work.
Your group name (as set in Xcode) has to start with "group" (e.g." "group.com.myName.MyApp" (I learned this when creating a group capability since the name field is already populated with "group".) and the NSUserDefaults suiteName has to start with another "group", so "group" is there twice, e.g. "group.group.com.myName.MyApp"). I learned this by looking at the app's Library/Preferences directory when it was running in the sim. Nothing would appear there unless I used this scheme.
I still haven't been able to get the watch app to see the common group defaults file.
I'm using this line to change the default language
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:#"it"] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate
and using this line to get the value
NSLocalizedStringFromTable(#"text", #"PA", nil);
Only issue is that I need to restart the app for the changes to be applied.
When i call NSLocalizedStringFromTable it always return from the language before the change
Is it possible to let the user have the option to change the local language without exiting the app?
Here is a sample project i created that demonstrate the issue
https://dl.dropboxusercontent.com/u/13431688/LocalizationTest.zip
As far as I know, it is not possible. What you could do is to have in your Localizable.strings file key-value pairs like
"en.error.general" = "An error occured.";
"fr.error.general" = "...";
then define a macro like:
#define LocalizedString(key, language) [[NSBundle mainBundle] localizedStringForKey:[NSString stringWithFormat:#"%#.%#", language, key] value:key table:nil]
and call it by LocalizedString(#"error.general", #"en");
I have:
int main(int argc, char* argv[])
{
#autoreleasepool
{
// Force use of English.
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en", nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
This forced libraries like HockeySDK to use English only. But localizedDescription of for example NSErrors from iOS are still in the user selected language. When I have a payment error in SKPaymentTransaction I get a Dutch (my current iPhone language) description in a further English (my own code) alert; looks really sloppy.
How can I force all texts in my whole app to be always in English?
Or in general: How can I make sure that iOS always choses a language from a list (which currently only contains "en") that I define?