jeffijoe / messageformat.net

ICU MessageFormat implementation for .NET.
MIT License
161 stars 23 forks source link

Supporting choice format #19

Closed NightOwl888 closed 1 year ago

NightOwl888 commented 5 years ago

I am porting some code over to .NET from Java and it uses the JDK's MessageFormat class to format some strings. I am having 2 issues with compatibility here as far as support goes:

  1. messageformat.net doesn't support the choice format
  2. The args parameter that MessageFormat expects is an object array (which is what I have), and MessageFormatter expects a dictionary.

Inputs

Pattern: "{0,choice,0#|1#{1}|2#{1} to {2}}" Args: object[] args = new object[] { 2, "Any", "Hex Escape" };

Attempt

I think I have worked around the second issue by converting the object array to a dictionary like so:

object[] args = new object[] { 2, "Any", "Hex Escape" };
var dic = new Dictionary<string, object>();
for (int j = 0; j < args.Length; j++)
{
    dic.Add(j.ToString(), args[j]);
}

But that causes MessageFormatter to throw:

format.FormatMessage(pattern, dic)' threw an exception of type 'Jeffijoe.MessageFormat.FormatterNotFoundException' Message: "Format 'choice' could not be resolved.\r\nLine 1, position 1\r\nSource literal: '0,choice,0#|1#{1}|2#{1} to {2}'" Source: "Jeffijoe.MessageFormat" StackTrace: " at Jeffijoe.MessageFormat.Formatting.FormatterLibrary.GetFormatter(FormatterRequest request)\r\n at Jeffijoe.MessageFormat.MessageFormatter.FormatMessage(String pattern, IDictionary`2 args)"

Questions

  1. First of all, is there a workaround for this with messageformat.net?
  2. Is this something that can be supported by messageformat.net, or should I be considering porting MessageFormat from the JDK?
  3. If it can be supported, is this something you would be willing to support?
jeffijoe commented 5 years ago

MessageFormat.NET only supports plural and select formatters, I'm not really sure what choice is for?

NightOwl888 commented 5 years ago

Sorry, I guess I should have included a link to MessageFormat.

For more sophisticated patterns, you can use a ChoiceFormat to produce correct forms for singular and plural:

 MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
 double[] filelimits = {0,1,2};
 String[] filepart = {"no files","one file","{0,number} files"};
 ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
 form.setFormatByArgumentIndex(0, fileform);

 int fileCount = 1273;
 String diskName = "MyDisk";
 Object[] testArgs = {new Long(fileCount), diskName};

 System.out.println(form.format(testArgs));

Do note that ICU's MessageFormat also supports choice (even though they recommend not to use it, but hey, it is their code I am porting and they are using it).

jeffijoe commented 5 years ago

So that example seems like it should be using plural instead.

NightOwl888 commented 5 years ago

Well, I can't change the original strings because they are in resource files. If it is simple enough, I could try using Replace("choice", "plural") on the string though (a little hokey).

pattern = pattern.Replace("choice", "plural");

But I did make an attempt to do that before contacting you, and I got the following error:

'format.FormatMessage(pattern, dic)' threw an exception of type 'Jeffijoe.MessageFormat.MessageFormatterException' Data: {System.Collections.ListDictionaryInternal} HResult: -2146233088 HelpLink: null InnerException: null Message: "'other' option not found in pattern." Source: "Jeffijoe.MessageFormat" StackTrace: " at Jeffijoe.MessageFormat.Formatting.Formatters.PluralFormatter.Pluralize(String locale, ParsedArguments arguments, Double n, Double offset)\r\n at Jeffijoe.MessageFormat.Formatting.Formatters.PluralFormatter.Format(String locale, FormatterRequest request, IDictionary2 args, Object value, IMessageFormatter messageFormatter)\r\n at Jeffijoe.MessageFormat.MessageFormatter.FormatMessage(String pattern, IDictionary2 args)" TargetSite: {System.String Pluralize(System.String, Jeffijoe.MessageFormat.Formatting.ParsedArguments, Double, Double)}

So, that isn't enough to get it to work by itself. I'd like to try to avoid converting the format in the resource file, as that is something I would have to do every time I upgrade the resource file (I don't maintain the files myself). I'd also like to avoid having 2 separate string conversions to make it work.

jeffijoe commented 5 years ago

The other is for when there's no specific match.

If ICU doesn't recommend using choice, then maybe we shouldn't include it, but not sure.

I don't have the bandwidth to do it right now though. :/

NightOwl888 commented 5 years ago

Would you accept a PR? I found a couple of tests that I could port to at least define the expected behavior:

Unfortunately, it looks like all of the other tests are comparing ICU's choice format against the JDK's ChoiceFormat class to ensure the behavior matches, but I think these 2 will be enough to ensure it works the way I need it to.

jeffijoe commented 5 years ago

Yeah, I’ll accept a PR. I just moved from Denmark to the US and left my Windows PC behind, so I’m not sure whether I can publish a new version on my Mac? When I tried it the last time, the code signing failed.

NightOwl888 commented 5 years ago

How did you do the last build? I suppose I could build it and send it to you.

Perhaps its time you sign up for a free Azure DevOps account so you can do the deployment build on Windows. The build can be controlled using a .yml file in your repo.

Also, MyGet (another free service) can be used to stage the NuGet package(s) so you can test them before pushing to NuGet.org (which can be done from a button in their control panel).

jeffijoe commented 5 years ago

The last build I did on my Windows machine. There's AppVeyor set up, but not to do code signing and pushing of NuGet packages. Pushing to NuGet is such a pain. ☹️

jeffijoe commented 1 year ago

Closing since choice is discouraged over select by the ICU documentation.