Bug 41292 - NSString.LocalizedFormat does not return the correct string
Summary: NSString.LocalizedFormat does not return the correct string
Status: IN_PROGRESS
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll (show other bugs)
Version: XI 9.8 (tvOS / C7)
Hardware: PC Mac OS
: Normal normal
Target Milestone: Future Cycle (TBD)
Assignee: Manuel de la Peña
URL:
Depends on:
Blocks:
 
Reported: 2016-05-24 16:04 UTC by John Miller [MSFT]
Modified: 2017-05-18 21:39 UTC (History)
6 users (show)

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


Attachments
Sample Project (7.62 KB, application/zip)
2016-05-24 16:04 UTC, John Miller [MSFT]
Details

Description John Miller [MSFT] 2016-05-24 16:04:22 UTC
Created attachment 16089 [details]
Sample Project

**Overview:**

   When trying to use NSString.LocalizedFormat for returning the correct string for localized plurals, it does not return the correct string. It instead returns the format string itself, like when nothing is found.

**Steps to Reproduce:**

   1. Run the attached app on an iOS simulator

   Note* It seems there is another bug in XI because an exception is thrown when trying to execute the method:
   "System.EntryPointNotFoundException: xamarin_localized_string_format_1"

   That will need to worked around/fixed before being able to reproduce the above issue.

**Build Date & Platform:**

=== Xamarin Studio Enterprise ===

Version 6.1 (build 817)
Installation UUID: e01c3049-a2d2-4e0a-aad8-afe6fb627c4d
Runtime:
	Mono 4.4.0 (mono-4.4.0-branch/fcf7a6d) (64-bit)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 404000148

=== NuGet ===

Version: 3.3.0.0

=== Xamarin.Profiler ===

Not Installed

=== Xamarin.Android ===

Version: 6.1.99.224 (Xamarin Enterprise)
Android SDK: /Users/johnmiller/Library/Developer/Xamarin/android-sdk-macosx
	Supported Android versions:
		2.3   (API level 10)
		4.0.3 (API level 15)
		4.1   (API level 16)
		4.2   (API level 17)
		4.4   (API level 19)
		5.0   (API level 21)
		5.1   (API level 22)
		6.0   (API level 23)

SDK Tools Version: 25.1.1
SDK Platform Tools Version: 23.1
SDK Build Tools Version: 23.0.2

Java SDK: /usr
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Xamarin Android Player ===

Version: 0.6.5
Location: /Applications/Xamarin Android Player.app

=== Apple Developer Tools ===

Xcode 7.3 (10183.3)
Build 7D175

=== Xamarin.iOS ===

Version: 9.8.0.318 (Xamarin Enterprise)
Hash: efefc1e
Branch: cycle7
Build date: 2016-05-13 17:19:05-0400

=== Xamarin.Mac ===

Version: 2.9.0.719 (Xamarin Enterprise)

=== Xamarin Inspector ===

Version: 0.8.1.0
Hash: 95792d1
Branch: master
Build date: Thu May 12 22:20:04 UTC 2016

=== Build Information ===

Release ID: 601000817
Git revision: 2335763551f9db8296b08542035977b899b7f3b7
Build date: 2016-04-25 10:45:36-04
Xamarin addins: 7f8c9ab2a981143a87fbd5adbde3f5890a838fde
Build lane: monodevelop-lion-cycle8-preview

=== Operating System ===

Mac OS X 10.11.2

=== Enabled user installed addins ===

Xamarin Inspector 0.8.1.0

**Additional Information:**

   This article helps explain the native behavior expected: http://maniak-dobrii.com/understanding-ios-internationalization/. Scroll to the section title "How -[NSString initWithFormat:locale:arguments] picks correct string from .stringsdict?". 

   It is thought that there is an issue with XIs implementation because of the string->NSString and back again conversion. The data that is carried inside NSString is lost, which causes the native code to not be able to find the correct value (per the article).
Comment 1 John Miller [MSFT] 2016-05-24 16:05:36 UTC
It's also unclear why the implementation has a limit of 9 parameters. 

https://github.com/xamarin/xamarin-macios/blob/2edb2ae4f5bb371a7006731987717c01f8725420/src/Foundation/NSString.cs#L311
Comment 2 Sebastien Pouliot 2016-05-24 18:25:21 UTC
There seems to be several things wrong, or at least incorrect, here.

First the call used by XI is not identical to

> -[NSString initWithFormat:locale:arguments]

It maps to `localizedStringWithFormat` which does _not_ take a NSLocale argument. In retrospect that might not have been the best choice as this is a variadic method

> + (instancetype)localizedStringWithFormat:(NSString *)format, ...

and that cause ABI issues - which is the answer for comment #1


The nice thing is that we should be able to use

> - (instancetype)initWithFormat:(NSString *)format arguments:(va_list)argList

which is not variadic and remove comment #1 limitation (and a lot of other code).


Notes:

1. the current managed code (see LocalizedFormat) lacks null checks so it's NRE prone and that needs to be fixed (and unit tested);

2. the missing symbols error might be because it's not compiled part of libxamarin, I assume is works with C6 otherwise the bug report would be different.

3. I'm not sure this covers the original question of the bug, i.e. why it returns the "format string itself".
Comment 3 Ruben Buniatyan 2016-05-25 17:32:53 UTC
After filing this bug to the support, I spent almost a week on this issue and eventually managed to make it work.

The problem with Xamarin's current implementation consists of two different problems:

1. The NSBundle.LocalizedString() method returns System.String. To be able to use .stringsdict files, this method *must* return NSString instead of System.String as the returned NSString instance is actually an instance of __NSLocalizedString which contains the data from the .stringsdict file (that's why currently it returns the format string itself as underlying formattind data is gone). Then we can pass it to localizedStringWithFormat: or initWithFormat:arguments: methods to format the final string according to the .stringsdict.

2. The current NSString.LocalizedFormat() method should be rewritten as its current implementation is completely useless. As under the hood it calls localizedStringWithFormat: which is a variadic function, there's a problem with passing arguments. NSString.LocalizedFormat() displays argument's pointer value instead of the object's value the pointer points to. For example, calling NSString.LocalizedFormat("%d", 2) displays something like "2,099,595,808" (0x7D254A20) which is the IntPtr arg's value passed to the one of the xamarin_localized_string_format_n methods you currently use.

Thus, I replaced the call to localizedStringWithFormat: with non-variadic initWithFormat:arguments: (or initWithFormat:locale:arguments:) eliminating the argument number limitation and making it work by directly calling 
localizedStringForKey:value:table: to obtain the required NSString instance to pass it to initWithFormat:locale:arguments: method.
Comment 4 CraigD 2016-06-09 19:06:22 UTC
D'oh - I just spent ages trying to get stringsdict working this before finding this bug report. Agree 100% with Ruben's comment #3. Will add sample and docs when fixed.
Comment 5 Sebastien Pouliot 2016-06-22 15:24:02 UTC
More complex than I thought originally, moving to C8
Comment 7 Ruben Buniatyan 2016-12-09 17:02:02 UTC
Any update on this, please?
Comment 8 Ruben Buniatyan 2017-05-18 21:39:13 UTC
What's the status of this issue?

Note You need to log in before you can comment on or make changes to this bug.