Get iOS current language (including country code) - ios

I need to get a string like en_US or es_ES in iOS.
The obvious method is to use [NSLocale currentLocale] and get the language info from there. However, that gives you the "region format" and not the actual device language, which means that if you have the device language in english but the region format as "spain", it'll erroneously report es_ES.
If you need the device language you must do this instead:
[[NSLocale preferredLanguages] objectAtIndex:0]
However, that only gives you the language, so you get something like en or es, without the country code at the end.
How would I get the country code correctly, like Safari does?

Try with this code:
NSString *locale = [[NSLocale currentLocale] localeIdentifier];
NSRange startRange = [locale rangeOfString:#"_"];
NSString *result = [locale stringByReplacingCharactersInRange:NSMakeRange(0, startRange.length+1) withString:[[NSLocale preferredLanguages] objectAtIndex:0]];
DebugLog(#"current locale: %#", result);

Ok, seeing getting the right info out of iOS is probably not possible, here's a hackish solution but which gives the output I needed. It's not complete and it won't give precise output in some cases (like for arabic), but it's the best I've been able to get.
const string lang = [[[NSLocale preferredLanguages] objectAtIndex:0] UTF8String];
// Search the language with country code in the langs map
static std::map<string, string> langMap;
static bool initialized = false;
if(!initialized) {
#define LANG(l, c) langMap.insert(std::make_pair(#l, #c))
LANG(en, en-us); LANG(es, es-es); LANG(fr, fr-fr); LANG(de, de-de);
LANG(ja, ja-jp); LANG(nl, nl-nl); LANG(it, it-it); LANG(pt, pt-br);
LANG(da, da-dk); LANG(fi, fi-fi); LANG(nb, nb-no); LANG(sv, sv-se);
LANG(ko, ko-kr); LANG(ru, ru-ru); LANG(pl, pl-pl); LANG(tr, tr-tr);
LANG(uk, uk-ua); LANG(hr, hr-hr); LANG(cs, cs-cz); LANG(el, el-gr);
LANG(he, he-il); LANG(ro, ro-ro); LANG(sk, sk-sk); LANG(th, th-th);
LANG(ca, ca-es); LANG(hu, hu-hu); LANG(vi, vi-vn);
LANG(zh-Hans, zh-cn); LANG(pt-PT, pt-pt); LANG(id, id); LANG(ms, ms);
LANG(zh-Hant, zh-tw); LANG(en-GB, en-gb); LANG(ar, ar);
#undef LANG
initialized = true;
}
map<string,string>::iterator it = langMap.find(lang);
if( it != langMap.end() ){
return it->second;
}
// Didn't find a country code, default to the lang name
return lang;

Check the below code:
NSArray *langs = [NSLocale preferredLanguages];
for (NSString *lang in langs) {
NSLog(#"%#: %# %#",lang, [NSLocale canonicalLanguageIdentifierFromString:lang], [[[NSLocale alloc] initWithLocaleIdentifier:lang] displayNameForKey:NSLocaleIdentifier value:lang]);
}

Swift
NSLocale.preferredLanguages()[0] as String
Output would be like
en-GB
Source

An alternative would be to reconstruct the locale from the components of the current locale, swapping in the language that you want.
For example, to get the current locale but modified to use the language which UIKit is currently using to display the app:
let languageCode = Bundle.main.preferredLocalizations.first ?? "en"
var components = Locale.components(fromIdentifier: Locale.current.identifier)
components[NSLocale.Key.languageCode.rawValue] = languageCode
let locale = Locale(identifier: Locale.identifier(fromComponents: components))

I am using this at the moment. I have run a few test cases and it seems ok, However I am not convinced that it is a robust solution. I am surprised not to find a clear answer to this simple problem. I would not recommend my solution but I hope it can generate discussion.
NSLocale *locale = [NSLocale currentLocale];
NSString *countryCode = [locale objectForKey: NSLocaleCountryCode];
NSString *language = [[NSLocale preferredLanguages] firstObject];
NSString *myLocale = [NSString stringWithFormat:#"%#_%#",language,countryCode];
NSLocale *userLocale = [[NSLocale alloc] initWithLocaleIdentifier:myLocale];
NSDate* now = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSString *dateFormat = [NSDateFormatter dateFormatFromTemplate:#"E MMM d yyyy HH:mm" options:0 locale:userLocale];

Simply do this:
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString *locale = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode];
NSString *formattedStr = [NSString stringWithFormat:#"%#_%#",language, locale];
NSLog(#"%#", formattedStr); // Display en_US for example

Related

NSLocale.currentLocale in iOS always returns en_US. Does not return the language of iPad

I am using the following code:
NSLocale *locale = NSLocale.currentLocale;
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
NSString *langaugeCode =[locale objectForKey:NSLocaleLanguageCode];
and the language code always comes back to be en_US even if I switch the language of my iPad. Any suggestions of what I might be doing wrong?

NSLocale Language Names In Local Script

I am trying to get Xcode to return a list of iOS language names in a variety of formats. In the code below I have created a dictionary returning a variety of strings that I could get returned from NSLocale.
langInUsersLang - Returns the language name in the users language
langInLang - Returns the language name in the language
NSArray *test = [NSLocale availableLocaleIdentifiers];
NSMutableArray *toBeReturned = [[NSMutableArray alloc] init];
for (int i = 0; i < [test count]; i++) {
NSDictionary *languageDictionary = #{
#"langInUsersLang" :
[[NSLocale currentLocale] displayNameForKey:NSLocaleLanguageCode value:[test objectAtIndex:i]],
#"langInLang" :
[[NSLocale systemLocale] displayNameForKey:NSLocaleLanguageCode value:[test objectAtIndex:i]],
#"regLangInUsersLang" :
[[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[test objectAtIndex:i]],
#"regLangInLang" :
[[NSLocale systemLocale] displayNameForKey:NSLocaleIdentifier value:[test objectAtIndex:i]],
};
[toBeReturned addObject:languageDictionary];
}
return toBeReturned;
Above I was also calling the following, however it seems to return the same content as above, albeit in the reverse order.
#"regLangInUsersLang" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[localIdentifiers objectAtIndex:i]],
#"regLangInLang" : [[NSLocale systemLocale] displayNameForKey:NSLocaleIdentifier value:[localIdentifiers objectAtIndex:i]]
I need to return the language name in the native script too, any ideas how to?
Thanks :-)
Ah it appears this works:
NSLocale *localLocale = [NSLocale localeWithLocaleIdentifier:[test objectAtIndex:i]];
By not using the current or system Locale but rather the with the language in question then it returns its name in the script!

Dynamically create string using return value from blocks

So I'm trying to dynamically build a string and I'd really like all the code which builds this string to live in a block passed as a parameter to the stringWithFormat method. The following code example should demonstrate what I'm trying to achieve:
NSString * deviceLanguage = [NSString stringWithFormat:#"Device Language: %#", ^NSString*(void){
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString *locale = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode];
return [NSString stringWithFormat:#"%#_%#", language, locale];
}];
The expected output of this would be something like...
Device Language: en_GB
However, the output I'm getting from this method actually returns the description of the NSGlobalBlock method, e.g.
Device Language: <__NSGlobalBlock__:0x30a35c>
Is this because i'm not using the correct placeholder in the string, or failing to declare that the block returns an NSString object?
That's because you pass the block itself as argument to stringWithFormat:,
instead of the result of calling the block:
NSString * deviceLanguage = [NSString stringWithFormat:#"Device Language: %#", ^NSString*(void){
NSLocale *locale = [NSLocale currentLocale];
NSString *language = [locale displayNameForKey:NSLocaleIdentifier
value:[locale localeIdentifier]];
return [NSString stringWithFormat:#"%#_%#", language, locale];
}()];
Note that you can achieve a similar result with a "compound statement expression"
instead of a block:
NSString * deviceLanguage = [NSString stringWithFormat:#"Device Language: %#", ({
NSLocale *locale = [NSLocale currentLocale];
NSString *language = [locale displayNameForKey:NSLocaleIdentifier
value:[locale localeIdentifier]];
[NSString stringWithFormat:#"%#_%#", language, locale];
})];
This is a good question!
The problem is in the stringWithFormat:. As it's processing it's format string, when it reaches the %#, it finds the object argument, calls -description on it and adds the object's description to its output. The description in this case is <__NSGlobalBlock__:0x30a35c>
You can get around this by doing:
NSString*(^userLanguage)(void) = ^(void) {
NSLocale *locale = [NSLocale currentLocale];
NSString *language = [locale displayNameForKey:NSLocaleIdentifier
value:[locale localeIdentifier]];
return [NSString stringWithFormat:#"%#_%#", language, locale];
};
NSString *language = userLanguage();
NSString * deviceLanguage = [NSString stringWithFormat:#"Device Language: %#", language];
But of course you don't need the block now, so you can just do this:
NSLocale *locale = [NSLocale currentLocale];
NSString *language = [locale displayNameForKey:NSLocaleIdentifier
value:[locale localeIdentifier]];
NSString *userLanguage = [NSString stringWithFormat:#"%#_%#", language, locale];;
NSString * deviceLanguage = [NSString stringWithFormat:#"Device Language: %#", userLanguage];
Because you are passing the block object to stringWithFormat: so you are printing the description. As in #MartinR Answer.
An easier management to your blocks would be if you recode it like this.
// first you declare a block like this, to retain it or pass it multiple times
typedef NSString* (^MyBlock)(void);
MyBlock block = ^{
NSLocale *locale = [NSLocale currentLocale];
NSString *language = [locale displayNameForKey:NSLocaleIdentifier
value:[locale localeIdentifier]];
// print the local id
return [NSString stringWithFormat:#"%#_%#", language, locale.localeIdentifier];
};
// Now pass it to stringWithFormat:
NSString *deviceLanguage = [NSString stringWithFormat:#"device language: %#", block()];

Get Currency Symbol and Currency Code based on ISOCountryCode

There are similar questions to this one from newbies like me in localization, but I couldn't find one that does the trick for me.
Here is my problem. We can get all the ISO country codes in an NSArray with a statement of the form [NSLocale ISOCountryCodes]. Now, for each and every country of those I would like to print the local currency as well as the currency code used in that country. What would be the appropriate way of doing this?
I did the following that does not work in the sense that I get lines of the form
US United States: (null) ((null))
when instead I would like to get lines of the form
US United States: $ (USD):
myCountryCode = [[NSLocale ISOCountryCodes] objectAtIndex:row];
appLocale = [[NSLocale alloc] initWithLocaleIdentifier: #"en_US"];
identifier = [NSLocale localeIdentifierFromComponents: [NSDictionary
dictionaryWithObject: myCountryCode
forKey: NSLocaleCountryCode]];
myDictionary = [NSLocale componentsFromLocaleIdentifier: identifier];
myCountryName = [appLocale displayNameForKey:NSLocaleCountryCode
value:[myDictionary
objectForKey:NSLocaleCountryCode]];
localCurrencySymbol = [appLocale displayNameForKey:NSLocaleCurrencySymbol
value:[myDictionary objectForKey:NSLocaleCurrencySymbol]];
currencyCode = [appLocale displayNameForKey:NSLocaleCurrencyCode
value: [myDictionary objectForKey:NSLocaleCurrencyCode]];
title = [NSString stringWithFormat:#"%# %#: %# (%#)", myCountryCode, myCountryName, localCurrencySymbol, currencyCode];
[appLocale release];
(Above identifier, myCountryCode, myCountryName, localCurrencySymbol, currencyCode, and title are all NSString pointers. Moreover myDictionary is an NSDictionary pointer and appLocale is an NSLocale pointer). Essentially the above code will be in a pickerview where I want to generate the title of each line on the fly.
Thank you very much for your time. Essentially the question is once we have the ISO country code how can we print (in the application locale) the currency symbol and the currency code for that specific country.
For anyone just wanting to get the currency code from the 3 letter iso code ( commonISOCurrencyCodes ). You can simply do this.
NSString *localeId = #"JPY"; //[[NSLocale commonISOCurrencyCodes] objectAtIndex:1];
NSLocale *locale = [NSLocale currentLocale];
NSString *currency = [locale displayNameForKey:NSLocaleCurrencySymbol
value:localeId];
NSLog(#"locale %# currency %#", localeId, currency);
Prints.
locale JPY currency ¥
Try the following test app and adapt as needed
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
// select an arbitrary locale identifier; you could use your row index but your
// indexing scheme would be different
NSString *localeIdentifier = [[NSLocale availableLocaleIdentifiers] objectAtIndex:72];
// get the selected locale and the application locale
NSLocale *selectedLocale = [[NSLocale alloc] initWithLocaleIdentifier:localeIdentifier];
NSLocale *appLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
// country code and name (in app's locale)
NSString *countryCode = [selectedLocale objectForKey:NSLocaleCountryCode];
NSString *countryName = [appLocale displayNameForKey:NSLocaleCountryCode value:countryCode];
// symbol and currency code
NSString *localCurrencySymbol = [selectedLocale objectForKey:NSLocaleCurrencySymbol];
NSString *currencyCode = [selectedLocale objectForKey:NSLocaleCurrencyCode];
NSString *title = [NSString stringWithFormat:#"%# %#: %# (%#)", countryCode, countryName, localCurrencySymbol, currencyCode];
[appLocale release];
NSLog(#"title = %#",title);
[p release];
}
This logs the following to the console:
2012-06-09 06:01:08.299 Untitled 2[11668:707] title = ES Spain: € (EUR)
I think your problem here is that you are expecting that componentsFromLocaleIdentifer will return information about the locale. Instead, it returns information about the string that is passed in as the identifier. Although you can receive NSLocalCurrencySymbol, it will only be present when the string that you are passing in has an override for the particular currency (which will never happen in your case, since you are only using the standard array). An example of this in the wild would be a user who has set up a FR system, but with a USD currency.
Generally, you shouldn't need to use -displayNameForKey:value: for the NSLocaleCurrencyCode and NSLocaleCurrencySymbol, since they both return international strings, not localized strings. Thus, once you have the preferred local, you should be able to get this information just by using -objectForKey:.
The tricky part, in my testing, is that assuming that the locale in the list is sufficient to create a valid currency code and symbol isn't true, instead you need to have a language and country code. Fortunately, +[NSLocale canonicalLanguageIdentifierFromString:] will provide you the right language, which you can then append to the country code (after a _) to create the country/language string that will appropriately result in the currency information being retrieved.
Here's my revised code:
myCountryCode = [[NSLocale ISOCountryCodes] objectAtIndex:row];
appLocale = [[NSLocale alloc] initWithLocaleIdentifier: #"en_US"];
identifier = [NSLocale localeIdentifierFromComponents: [NSDictionary
dictionaryWithObject: myCountryCode
forKey: NSLocaleCountryCode]];
myDictionary = [NSLocale componentsFromLocaleIdentifier: identifier];
myCountryName = [appLocale displayNameForKey:NSLocaleCountryCode
value:[myDictionaryobjectForKey:NSLocaleCountryCode]];
// Create the currency language combination and then make our locale
NSString *currencyLocaleLanguage= [NSLocale canonicalLanguageIdentifierFromString: myCountryCode];
NSString *countryLanguage= [NSString stringWithFormat: #"%#_%#", myCountryCode, currencyLocaleLanguage];
NSLocale *currencyLocale = [[NSLocale alloc] initWithLocaleIdentifier: countryLanguage];
localCurrencySymbol = [currencyLocale objectForKey:NSLocaleCurrencySymbol];
currencyCode = [currencyLocale objectForKey:NSLocaleCurrencyCode];
title = [NSString stringWithFormat:#"%# %#: %# (%#)", myCountryCode, myCountryName, localCurrencySymbol, currencyCode];
[currencyLocale release];
[appLocale release];

wrong decimal separator with NSDecimalNumber in iOS

I tried to output the description of a decimal number with the correct decimal separator in the following way:
NSString* strValue = #"9.94300";
NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString: strValue];
NSLocale* locale = [NSLocale currentLocale];
NSLog(#"%#", [locale localeIdentifier]);
NSLog(#"%#", [decimalNumber descriptionWithLocale: locale] );
The output is:
de_DE
9.943
The decimal separator should be ',' instead of '.' for this locale. Where is the error? How can I output the correct decimal separator depending on the local?
#TriPhoenix: Yes I'm running iOS 5.
#Vignesh: Thank you very much. The following code works now if I set the iPhone language to German:
NSString* strValue = #"9.94300";
NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString: strValue];
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
NSLog(#"%#", [numberFormatter stringFromNumber: decimalNumber] );
The output is:
9,943
But now I have another problem. If I switch the iPhone language back to English the output is still 9,943 instead of 9.943. Do I make something wrong?
You can use this:
NSLocale *locale = [NSLocale currentLocale];
float r = 50/100;
NSString *str = [NSString stringWithFormat:#"%.2f%%", r];
str = [str stringByReplacingOccurrencesOfString:#"." withString:[locale objectForKey: NSLocaleDecimalSeparator]];
It worked!

Resources