iPhone en_* sublanguage localization - iphone

I want to localize strings in my iphone app for en_GB and other 'en' sub-languages, but XCode and the iphone refuse to let this happen. I have created a localization of "Localizable.strings" for en_GB and en_US (I tried both hyphens and underscores) for testing purposes, but they just aren't recognized. The only language code that works is simply "en" (displayed as "English" in XCode).
I can't believe this isn't possible, so what am I doing wrong? I'm also hoping to get the typical 'cascading' behaviour where if a string isn't found in the sub-language e.g. "en_GB" then it should be taken from "en" if possible. Help?

When you choose 'English' from the list of languages on the iPhone preferences, that actually means the 'en_US' language.
So until apple update their software with additional sublanguages like "English (British)" etc. we are left with going by the locale region setting, and loading strings manually from another string table.
However, the language and regional locale are separated for a reason: a Spanish user in the UK may want dates/times formatted according to the local customs, but program strings in their native tongue. It would be incorrect to detect the regional locale (UK) and therefore display UK strings.
So basically there is no way to do this currently.

What you're doing should work according to the docs. But it appears that the iPhoneOS implementation is at odds with the documentation. According to Radar 6158876, there's no support for en_GB language, only locale (date formats and the like).

I found the same problem.
BTW, if you look at the iPhone Settings -> General -> International menu, it makes the distinction between language and region quite clear:
Languages:
-English
Region Format:
-United States
-United Kingdom
The localization framework only appears to pay attention to the language, not the region.
I'm tempted to raise an enhancement request for this with Apple, as IMO it is reasonable that a user might want to use British English (for the text) whilst being in the United States (where, say, phone numbers should be in US format).

This can actually be done - check my solution here - iPhone App Localization - English problems?

Create a separate string resource, say UKLocalization.strings, and create localizations for each of your supported languages. For all localizations other than en this file is empty. For en, it contains only the strings that have unique en_GB spelling.
Next, you create a replacement for NSLocalizationString that will first check the UKLocalization table before falling back to the standard localization table.
e.g.:
static NSString* _locTable = nil;
void RTLocalizationInit()
{
_locTable = nil;
NSString* country = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
if ([country isEqual:#"GB"])
{
_locTable = #"UKLocalization";
}
}
NSString* RTLocalizedString(NSString* key, NSString* ignored)
{
NSString* value = nil;
value = [[NSBundle mainBundle] localizedStringForKey:key value:nil table: _locTable];
if (value == key)
{
value = NSLocalizedString(key, #"");
}
return value;
}

I’m not sure in which version of iOS it was introduced, but iOS 7 definitely has a ‘British English’ language preference that will pick up resources from the en_GB.lproj directory. The various hacks floating around the web shouldn’t be necessary unless you’re after a more specialised* dialect.
*see what I did there ;)

Related

Localized date not translating the days

Here is how I am localising days:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
NSString *dayFormat = [NSDateFormatter dateFormatFromTemplate:#"EEEE" options:0 locale:[NSLocale currentLocale]];
[dateFormatter setDateFormat:dayFormat];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSString *day = [dateFormatter stringFromDate:date];
And yet it seems to be returning English days of the week (Monday, Tuesday etc) rather than the device language (which has been set to German in the simulator).
Any idea where I'm going wrong?
Update after doing some research on device I've realised that its actually the region setting, not the language setting which changes the date language. Odd, but I guess its done for a reason.
Thanks
The language of the date is set by the region not the language. This has to be a bug. If I'm in Germany, but an English speaker I don't want to have my dates in German, surely?
Anyway, this is why. You have to change language and region.
I've had the same issue. It wouldn't work on the simulator, but it would on a device. Can you try it ? I did not however solve it, I did not even look more into it as it was working perfectly on the device, which is truly the main target of your app.
Edit:
This comes from Apple's doc:
currentLocale
Returns the logical locale for the current user.
+ (id)currentLocale
Return Value
The logical locale for the current user. The locale is formed from the settings for the current user’s chosen system locale overlaid with any custom settings the user has specified in System Preferences.
Discussion
Settings you get from this locale do not change as a user’s
Preferences are changed so that your operations are consistent.
Typically you perform some operations on the returned object and then
allow it to be disposed of. Moreover, since the returned object may be
cached, you do not need to hold on to it indefinitely. Contrast with
autoupdatingCurrentLocale.
Maybe you can try using:
preferredLanguages
Returns the user's language preference order as an array of strings.
+ (NSArray *)preferredLanguages
Return Value
The user's language preference order as an array of NSString objects, each of which is a canonicalized IETF BCP 47 language identifier.

Cannot load country-specific resource

I am loading text resources in different languages from text files using:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"myfile" ofType:#"txt"];
if(filePath) {
NSString *myText = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
}
Works very well with my language specific resources in English, German etc.
But I do also have some country-specific resources, like an English text for United Kingdom, an English text for USA, a German text for Austria etc.
I would expect these resources to be loaded if the phone is set to the specific country (Region Format), but they don't. The filepath will always be set to the file in the "pure" language folder.
So for example I have myfile.txt in the folders:
en.lproj
en-GB.lproj
en-US.lproj
but it always loads the one in en.lproj
Folders have been created using the localization function in Xcode and are all located in the same folder.
What is a bit odd is that these folders created by Xcode do seperate language and country by a hyphen, but according to the documentation it should be an underscore. I have tried to copy folders (with underscores) into the project manually, but it still doesn't work.
How do I manage to load the country-specific files?
Thanks.....
I created a "dutch-netherlands" translation of Localisations.string and set iPhone language to dutch, but NSLocalizedString always returned the English translation. The solution was to create a "dutch" translation (nl.lproj) instead of "dutch-netherlands" (nl-NL.lproj).
In the settings screen of iOS4 or iOS5, you can select Dutch language but there's no subselection for Dutch-Netherlands or Dutch-Belgium, even though these are available in XCode. It seems to me that XCode offers more translation options than that are actually supported by iOS.
I mean there's no way to make your app use "nl-NL" resources. I guess the same problem goes for German and Austria ("de-DE" and "de-AT").
Note: Your code sample is a complicated way of loading text resources. It's fine if you are loading non-string resources such as images or media files, but for simple strings it's much easier to just use one .strings file per language (containing multiple localized strings) and call NSLocalizedString to get the relevant language version of a given string.
Answer: The iOS setting under "Region Format" does not affect resource loading. Instead it controls how the system performs locale-dependent functionality (such as date/number formatting and parsing). The iOS setting that controls which language version of your localized resources gets loaded is the Language setting (General > International > Language). Currently, it includes only two English variants: "English" and "British English".
I have the same problem with nl-NL.lproj (Netherland) and ca-ES.lproj (Catalan). The solution for me is add the language using Xcode. Go to Xcode Project, and then in Info Tab add the langugages. Automatically will create the folders nl-NL.lproj and ca-ES.lproj.
Then you can try this on Simulator (Xcode version 4.6) and these languages don't word, instead will load English language.
First rename manually the folder nl-NL.lproj to nl.lproj.
Finally go to file {MY_PROJECT}.xcodeproj/project.pbxproj and replace the PATH of the following lines:
C3D2888817108DDA00CE8AC2 /* nl-NL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nl-NL"; path = "nl.lproj/Localizable.strings"; sourceTree = "<group>"; };
C3D2888917108DE500CE8AC2 /* nl-NL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nl-NL"; path = "nl.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
That's all, now I can get these languages settings correctly.

List of Countries using NSLocaleCountryCode

I want to be able to make the app change depending on the users location. Im using the code below:
NSLocale *locale = [NSLocale autoupdatingCurrentLocale];
NSString *countryCode = [locale objectForKey: NSLocaleCountryCode];
NSString *countryName = [locale displayNameForKey: NSLocaleCountryCode value: countryCode];
NSLog(#"countryName %#", countryName);
which works great, but I want to know how the countryName's will be displayed, so I can set up switch case's, which is hard if you dont know how exactly each country is spelt: USA, United States, United States of America, etc. Is there a list of countryCode from Apple, I cant find one.
Also is there a way to make sure the result is in English?
Apple uses the ISO-3166 standard.
"ISO standard ISO-3166" is accurate in most cases, but try selecting "Europe" as a region in iOS settings. You will get a return value of "150". Why "150"? Seems like a region code from here: http://en.wikipedia.org/wiki/UN_M.49. Or from here: http://site.icu-project.org/design/t/territory-region-apis
NSLocale gets its data from CFLocale which in turn gets its data from the
ICU - International Components for Unicode (Apple keep copies here). The file /icuSources/common/uloc.cpp contains almost all the information we usually see returned.
However, /cldrFiles/supplementalData.xml may be the primary source. This file comes from the CLDR - Unicode Common Locale Data Repository.

NSLocale always returns en_US

I want to get current selected iphone language. If using "NSLocale" it returns always the same language. It seems that this is not the one you choose inside iphone settings.
NSLocale * locale = [NSLocale currentLocale];
NSString * localLanguage = [locale objectForKey:NSLocaleLanguageCode];
NSLog (#"Language : %#", localLanguage); // Returns always : "en_US"
How to obtain the current language?
The answer is to check the preferred language :
NSString *preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
This question is old, but the reason is new since upgrading to Xcode 6.1
Due to a bug, the iOS8 simulator's [NSLocale currentLocale] always returns "en_US". It is alright on the device though.
Hence, I recommend doing a test on a real device before bashing your head against a wall in frustration.
Sources: https://stackoverflow.com/a/26510914/3099609
https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html#//apple_ref/doc/uid/TP40001051-CH4-DontLinkElementID_23
Locales encapsulate information about linguistic, cultural, and technological conventions and standards.
Your code returns the "Region Format" information, if set "China", it will return "zh".
Settings | General | International | Region Format
See “NSLocale Class Reference” for more information.
To add to what SteamTrout suggested:
To get your language to respond to changes in iPhone settings you're going to want to use autoupdatingCurrentLocale:
https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/classes/NSLocale_Class/Reference/Reference.html#//apple_ref/occ/clm/NSLocale/autoupdatingCurrentLocale
If you read closely in the currentLocale reference you'll see that this value will not update from the settings panel:
https://developer.apple.com/library/ios/documentation/cocoa/reference/foundation/classes/NSLocale_Class/Reference/Reference.html#//apple_ref/occ/clm/NSLocale/currentLocale
Try [[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleLanguageCode]

Is there any way to get the "Me" card from iPhone Address Book API?

So I'm stumped on this one.
In Mac OS X there is an easy way to get the "Me" card (the owner of the Mac/account) from the built-in address book API.
Has anyone found a way to find out which contact (if it exists) belongs to the owner of the iPhone?
You could use the undocumented user default:
[[NSUserDefaults standardUserDefaults] objectForKey:#"SBFormattedPhoneNumber"];
and then search the address book for the card with that phone number.
Keep in mind that since the User Default is undocumented, Apple could change at any time and you may have trouble getting into the App Store.
Another approach you could take, although it is much more fragile, is to look at the device name. If the user hasn't changed it from the default "User Name's iPhone" AND they are using their real name as an iPhone, you could grab the user name from that. Again, not the best solution by any means, but it does give you something else to try.
The generally accepted answer to this question is to file a Radar with Apple for this feature and to prompt users to choose their card.
Contacts container have a me identifier property on iOS that can be accessed using container.value(forKey: "meIdentifier")
if let containers = try? CNContactStore().containers(matching: nil) {
containers.forEach { container in
if let meIdentifier = container.value(forKey: "meIdentifier") as? String {
print("Contacts:", "meIdentifier", meIdentifier)
}
}
The identifier is a legacy identifier used in the old AddressBook framework. You can still access it in CNContact:
let iOSLegacyIdentifier = contact.value(forKey: "iOSLegacyIdentifier")
There is no such API in the iPhone SDK 2.2.1 and earlier. Please file a request for it at: http://bugreport.apple.com
Edit: [Obsolete answer]
There's no API for getting the "me" card because there is no "me" card. The iPhone's contacts app has no way of marking a card as being "me", and the API reflects this.
I came up with a partial solution to this
you can get the device name as follows
NSString *ownerName = [[UIDevice currentDevice] name];
in English a device is originally called, for example, 'Joe Blogg's iPhone'
the break out the name
NSRange t = [ownerName rangeOfString:#"’s"];
if (t.location != NSNotFound) {
ownerName = [ownerName substringToIndex:t.location];
}
you can then take that name and search the contacts
CNContactStore *contactStore = [CNContactStore new];
NSPredicate *usersNamePredicate = [CNContact predicateForContactsMatchingName:usersName];
NSArray * keysToFetch = #[[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName],CNContactPhoneNumbersKey,CNContactEmailAddressesKey,CNContactSocialProfilesKey, ];
NSArray * matchingContacts = [contactStore unifiedContactsMatchingPredicate:usersNamePredicate keysToFetch:keysToFetch error:nil];
Of course other languages differ in the device name string e.g. 'iPhone Von Johann Schmidt' so more parsing needs to be done for other languages and it only works if the user hasn't changed the name of the device in iTunes to something like "Joes phone' but it gives you a starting point
well... it gives you an array of matching items :) So if there is more than one contact with that array you just have to use pot luck and go with the first one or work thru multiple cards and take what you need from each.
I did say its a partial solution and even though it won't work for all user cases you might find it works for many of your users and reduces a little friction