Bug 26030 - NSBundle.MainBundle.LocalizedString returns incorrect result on iOS 7 devices
Summary: NSBundle.MainBundle.LocalizedString returns incorrect result on iOS 7 devices
Status: RESOLVED ANSWERED
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: XI 8.6.0
Hardware: PC Mac OS
: Normal normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2015-01-14 15:33 UTC by John Miller [MSFT]
Modified: 2015-01-15 14:42 UTC (History)
4 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Test Case (10.93 KB, application/zip)
2015-01-14 15:33 UTC, John Miller [MSFT]
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description John Miller [MSFT] 2015-01-14 15:33:24 UTC
Created attachment 9354 [details]
Test Case

**Overview:**

   The attached project returns the incorrect localized string when running on iOS 7 devices. On iOS 8 devices it works as expected.

**Steps to Reproduce:**

   1. Make sure you have an iOS 7 device with Language and Region settings for English (US) and US. 
   2. Run the attached sample on that device. 

**Actual Results:**

   The screen shows a label of "Hello GB", which means the English (UK) string is being used "en-GB". 

**Expected Results:**

   The label should show "Hello US", which would mean the English (US) string was being used "en-US". 

**Build Date & Platform:**

   XI 8.6.0.51
   Mono 3.12
   Xcode 6.1.1

**Additional Information:**

   Run the same app on an iOS 8 device it you will see the expected results. 
   Note* This probably won't work on any simulators because of a known Apple issue with simulators. That is why we are testing on devices.
Comment 1 Sebastien Pouliot 2015-01-14 18:08:20 UTC
There's no Xamarin logic between

> NSBundle.MainBundle.LocalizedString("Test", null);

and calling the `localizedStringForKey:value:table:` selector. IOW you'll get the same behaviour using ObjC. 

Since iOS8 (and 7) changed how to set region I guess that your device setting is not done correctly on iOS 7. Please add screenshot on how/where the device is set to it's locale.
Comment 2 John Miller [MSFT] 2015-01-14 18:50:20 UTC
@Sebastien

https://www.dropbox.com/s/xd7mifdf9bm8k0y/iphonedemo.mp4?dl=0
Comment 4 Ian Leatherbury 2015-01-14 20:42:12 UTC
John made an Obj-C sample that demonstrates that the issue does not occur in native code: https://www.dropbox.com/s/ud2pqyzxxhg37mh/LocaleTestXcode.zip?dl=0

Running iOS 7.0.2 on an iPhone 4.

* Running with Language set to English and Region set to United States I receive "Test" as the label shown. This is the expected behaviour.

* Running with Language set to British English and Region set to United Kingdom I receive "Hello GB". This is the expected behaviour.
Comment 5 Sebastien Pouliot 2015-01-14 23:40:48 UTC
@Ian are you sure it the right version of the objc sample ? 

I had to change it's deployment target to deploy to 7.0 (to use it on my iOS 7.1 device) and en-CA, my current locale, does not show me "Hello CA", just "Test" (the default value).

I'll try again tomorrow (more devices, more configs and more caffeine).
Comment 6 Sebastien Pouliot 2015-01-15 10:43:56 UTC
I get the same "Test" on an iOS8 device. Please double-check the Xcode project from comment #4.

From what I could google the lack of "region" support in .lproj files [1] is an iOS "feature". It does not work like you would expect/want it to be (but that's not XI specific).

I'm testing a workaround (based on one for ObjC) but I'd really like to clear this up (so we can be sure we're all talking about the same thing).

[1] http://stackoverflow.com/q/15848566
Comment 8 Ian Leatherbury 2015-01-15 11:46:46 UTC
@Sebastien, John does not have a device running iOS 7, so
the sample was built(and tested by him as well, I believe), on iOS 8. He passed
it along to me as I have an older device.

I have set the region by going to Settings > General > International, and
setting the Language and Region format[1]. I performed the following tests:

In Objc project(Comment #4):
Test 1: Set Language to "English" and Region Format to "United States", objc
project returns "Test"

Test 2: Set Language to "British English" and Region Format to "United Kingdom"
objc project returns "Hello GB".

Objc project works as expected.

In X.iOS project(comment #1):
Test 3: Set Language to "English" and Region Format to "United States", objc
project returns "Hello GB"

Test 4: Set Language to "British English" and Region Format to "United Kingdom"
objc project returns "Hello GB".

X.iOS project is not working as expected. It is worth noting that I get "Hello
GB" every time I run the X.iOS project, I've never gotten another region.

[1] Screenshots of where I am setting the Language and Regions Format at the
images below.
• https://www.dropbox.com/s/f4m9n1z7v8fp94z/photo%201.PNG?dl=0https://www.dropbox.com/s/8hjw0rfp108hp56/photo%202.PNG?dl=0

Let me know what other info you need!
Comment 9 Sebastien Pouliot 2015-01-15 12:17:30 UTC
> Test 1:

That's incorrect. There's an en-US.lproj file that should show you "Hello US", not "Test".

That kind of confirms the lack of iOS (before 8.0) support for regions.


> Test 2: Set Language to "British English"

Ah, here you're changing the *language* and the region. Try it while leaving the Region to "United States" and you still get "Hello GB", which again is incorrect.

IOW the objc app does not show region support (before iOS8) and that match my findings.
Comment 10 Ian Leatherbury 2015-01-15 12:46:47 UTC
Sebastien,

> That's incorrect. There's an en-US.lproj file that should show you "Hello US",
> not "Test".
Yep, it still shows "Test" when I run it with English as the language set. Additionally I tests is with Language set to English and Region Format set to UK, and it still returned "Test"

> Ah, here you're changing the *language* and the region. Try it while leaving
> the Region to "United States" and you still get "Hello GB", which again is
> incorrect.
This is confirmed. I set the Language to British English and Region Format to United States and still received "Hello GB"

So there is no support for Regions in iOS 7? How should one handle localization?
Comment 11 Sebastien Pouliot 2015-01-15 14:42:53 UTC
> So there is no support for Regions in iOS 7?

No. Only Apple can tell why... my best guess is that devices are small (physically and in storage) and processing power) so that feature (available on OSX) was cut out. Recent devices are much more powerful and Apple eventually re-introduced the feature in iOS8.


> How should one handle localization?

There's many ways:

a) use a different toolkit (there are tons of solutions for this problem) that support it this and more capabilities, e.g. cross-platform [1]

[1] https://github.com/rdio/vernacular


b) Apple API let you provide the table you want to use (where most people just provide `null`). That should be usable to support regions.

> NSBundle.MainBundle.LocalizedString ("Test", null, null);

^ that's the (optional) 3rd parameter in the call.


c) Use a similar workaround as other ObjC developers, facing the same situation, are using.

Here's some code, based on an ObjC workaround [2] and updated to handle iOS8, to get this to work like "expected", by cheating/modifying the settings before calling `UIApplicationMain` (when bundle decisions will be made).

This works as it seems _parts_ of iOS 7 already supported regions, it's just not enabled at application startup.


static void Main (string[] args)
{
	// For a device that's set to "English Canada" this would return
	// - "en" before iOS 8; and
	// - "en-CA" since iOS 8
	var language = NSLocale.PreferredLanguages [0];
	string region;
	int n = language.IndexOf ('-');
	if (n == -1) {
		region = language + "_" + NSLocale.CurrentLocale.CountryCode;
	} else {
		region = language.Replace ('-', '_');
		language = language.Substring (0, n);
	}
	using (var key = new NSString ("AppleLanguages"))
	using (var value = NSArray.FromObjects (region, language))
		NSUserDefaults.StandardUserDefaults.SetValueForKey (value, key);

    UIApplication.Main (args, null, "AppDelegate");
}

Note that the code above was only tested on iOS7.1 and 8.0. Also you better do some checks to ensure you can handle the language/region combo before telling iOS to use it.

It's also an hack that Apple could dislike and we do not know why it was not exposed in iOS 7.x (some API might not like that). OTOH it's main advantage is that it does not require other changes, except to Main, which could be a big time saver (if a lot of time was already invested into lozalizing the application this way).

[2] http://stackoverflow.com/q/3308519