Conversation
…ble digits number
|
Hey @jeffijoe could you please update the .NET SDK to 5.0.* in github actions? |
| </ItemGroup> | ||
|
|
||
| <PropertyGroup> | ||
| <PluralLanguagesMetadataExcludeLocales>si da is mk ceb fil tl lv prg bs hr sh sr fr dsb hsb lt</PluralLanguagesMetadataExcludeLocales> |
There was a problem hiding this comment.
These languages contain constructs that this generator cannot parse yet
There was a problem hiding this comment.
da is Danish? Damn, sucks to be me 😂
There was a problem hiding this comment.
Eventually, support for these should be there, but I figured that it would be even harder to review with more changes 😅
| <LangVersion>latest</LangVersion> | ||
| <Nullable>enable</Nullable> | ||
| <TargetFramework>netstandard1.1</TargetFramework> | ||
| <TargetFramework>netstandard2.0</TargetFramework> |
There was a problem hiding this comment.
Was forced to update to netstandard2.0 to consume the source generator. Feel free to evaluate whether this is worth it or not
There was a problem hiding this comment.
What are we losing by doing this?
There was a problem hiding this comment.
The .NET Standard page documents this, but in layman's terms:
- .NET Framework 4.5 through 4.6 (4.5 and 4.5.1 have reached EoL).
- .NET Core 1.x (which has reached EoL).
- Mono 4.5 through 5.3
- All flavors of Xamarin and UWP would have to be upgraded slightly (probably not an issue for most)
For now keeping a single .NET Standard 2.0 target is the best option for supporting both .NET Framework and .NET Core without adding all of the dependencies that .NET Standard 1.x had.
You may also wish to consider the tradeoffs of supporting earlier versions of .NET Framework and improved API support for .NET Standard 2.1 which you could gain at the price of multi-targeting and keeping conditional sections to gracefully degrade for .NET Framework, both of which add complexity. But IMO, abandoning .NET Standard 1.x at this stage is a good decision either way.
There was a problem hiding this comment.
So TL;DR is that upgrading to 2.0 is what we want to do either way? 😄
There was a problem hiding this comment.
Maybe. If multi-targeting .NET Framework 4.5 and .NET Standard 2.1, there isn't a huge gap that .NET Standard 2.0 fills (basically .NET Core 2.x and some older mobile platforms). It depends on how high your tolerance for setting up a more complicated build and for dealing with conditional compilation sections is 😉.
There was a problem hiding this comment.
I don't mind removing support for EoL targets.
src/Jeffijoe.MessageFormat/Formatting/Formatters/PluralFormatter.cs
Outdated
Show resolved
Hide resolved
|
|
||
| var numberSpan = ConsumeCharacters(numbersCount); | ||
|
|
||
| var number = int.Parse(new string(numberSpan.ToArray())); |
There was a problem hiding this comment.
Didn't find span-based overload in netstandard 2.0 :(
There was a problem hiding this comment.
numberSpan.ToString() might be more efficient than ToArray() + new string()?
There was a problem hiding this comment.
Didn't know you could do that, thanks
Isn't that controlled in the workflow file in the branch? |
|
Thanks, that worked! Wasn't sure of how the security model works with forks, but seems that I can change SDKs freely. |
|
This is amazing! I'm going to need some time to play with it and review, but wow! 🤩 |
|
Rather than passing "Locale" as a string in .NET, it would be more intuitive (and more compatible) to pass in a Also, it is quite non-intuitive to attach I am not saying you have to make these changes, but this is the way Microsoft would do it if they add support for message formatting. |
|
Accepting CultureInfo in method argument sounds reasonable, but would not satisfy my use-case. My situation is that multiple users are being served by one server. These users can have different languages configured for their personal account. Basically, I have a language code, and need to format a string for that language code. I imagine that going from a language code to a CultureInfo may be a bit awkward. |
|
I agree with your point that it would have been easier to pass the locale to the formatting method, though. Probably, that sounds like a separate discussion thread, and the parsing logic for CLDR rules will be the same regardless of whether the locale-specific pluralization data is needed in the constructor or in the formatting method. |
There isn't a lot of pluralization data and doesn't need to be updated at runtime. It could be made as a default set of pluralization rules that is cached. But the fact that you are serving multiple users per server is the point. If this is a webserver, each user can pass their culture in the URL or somewhere else in the request envelope and it can then be made "the" culture of the current thread. CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(userCulture);This is done at the beginning of each request, so when using the
I haven't analyzed this in detail to see how big the change would be. But most users will be using the built-in I believe you could use Agreed it probably doesn't change the parsing of the file, but maybe change how it is dealt with at the top level after it is parsed. I just noticed it was being passed in to a constructor and wanted to point out that isn't the .NET way because .NET is already aware of its current culture at the top level. It would be best if |
src/Jeffijoe.MessageFormat.MetadataGenerator/Plural/Parsing/InvalidCharacterException.cs
Outdated
Show resolved
Hide resolved
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>netcoreapp3.1</TargetFramework> | ||
| <TargetFramework>net5.0</TargetFramework> |
There was a problem hiding this comment.
I would suggest multi-targeting the tests. This adds enough complexity that it may differ on .NET Framework vs .NET Core vs .NET 5.
There was a problem hiding this comment.
Yes, just change the test projects from using
<TargetFramework>net5.0</TargetFramework>to
<TargetFrameworks>net5.0;netcoreapp3.1;net48</TargetFrameworks>TIP: Make sure you pay attention to the
sin the name or you might be scratching your head for awhile.
In Visual Studio 2019, it will then show you all of the tests on each of the target frameworks and you can run them all or select the ones you want to run.
At the command line, it is a bit more complicated because AFAIK you have to run a dotnet test on each target framework as a separate command.
I believe it will work just to install the .NET 5.0 SDK (or at least you can always use the .NET 5 SDK as the entry point and all of the options it supports, but you may have to install other SDKs such as .NET Core 3.1 before installing .NET 5.0).
Working Example
My setup is more complicated than yours because I test on multiple operating systems and use build assets to transfer the same binaries from the build agent to the test agents (you could just run dotnet test which would build from source on each OS, but I prefer to test the binaries I ship). I am also using Azure DevOps rather than GitHub Actions, but you should be able to set it up similarly there, too if you want to.
In my case, I do dotnet publish (in combination with properly setting the <IsPublishable> property to true for test projects and false for all of the others) to make the tests runnable on each OS and I specifically set up a build agent to test 1 target framework. But you are welcome to analyze the templates here and here and use what you need (from anywhere in the project) to get it working.
| namespace Jeffijoe.MessageFormat.MetadataGenerator | ||
| { | ||
| [Generator] | ||
| public class PluralLanguagesGenerator : ISourceGenerator |
There was a problem hiding this comment.
Brilliant use of a source code generator!
…r formatting errors
…utely the same check
|
The more I think about it, the more your point about WIll not do any changes in this PR, but the SourceGenerator should work if somewhere in the future the |
Move AST to a separate folder, move classes into separate files. Use parametrized expression for documentation file. Apply simplification for ParseRuleContent. Remove 'and' calculation out of loop. Simplify number construction.
src/Jeffijoe.MessageFormat.MetadataGenerator/Plural/Parsing/RuleParser.cs
Show resolved
Hide resolved
Yeah that sounds good! You may be able to use the compiler to run the generated class?
Could you give me an example of what you mean here? |
The generated rules are in tests assembly already, because MessageFormat assembly already contains the generated code when referenced. Added tests. Made sure they work by commenting out the line which uses generated metadata and ensuring the tests fail (except EN, of course).
I mean that string.Format creates(allocates) a new string, was hesitant to use it for that reason. I wonder if there is an easy enough mathematical method (divide the initial number by something. multiply by something) to calculate these variables. If there is no other reasonable way - well, that's better than nothing :) |
|
@kostya9 oh I completely misunderstood what the
EDIT2: I completely forgot about the We currently convert to If we change from int count = BitConverter.GetBytes(decimal.GetBits(value)[3])[2]; |
|
@jeffijoe Finally found some time to work on this :) I'm exploring adding string manipulation for fractional numbers to support all the rules properly (trailing zeroes and stuff). |
Yes, see this: https://messageformat.github.io/messageformat/guide/#plural-offset |
|
Added support for all rules except exponent (you cannot express exponent in any way except passing string with exponent directly) Thanks @pointnet , did something like you proposed |
| <LangVersion>latest</LangVersion> | ||
| <Nullable>enable</Nullable> | ||
| <TargetFramework>netstandard1.1</TargetFramework> | ||
| <TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks> |
There was a problem hiding this comment.
Added net5.0 to target frameworks to allocate less when parsing PluralContext on projects that support it
src/Jeffijoe.MessageFormat.MetadataGenerator/Plural/Parsing/RuleParser.cs
Show resolved
Hide resolved
...ijoe.MessageFormat.MetadataGenerator/Plural/SourceGeneration/PluralRulesMetadataGenerator.cs
Outdated
Show resolved
Hide resolved
|
Amazing work @kostya9, and shoutout to @pointnet and @NightOwl888 for the input! Much appreciated! This looks good to merge, unless you have some final cleanup you want to do first? |
|
Thanks! Made a final pass through the code - made the plural metadata internal. Everything else looks good to me, feel free to merge |
|
Would you happen to have R# or Rider? Maybe running a code cleanup before merge would be good hygiene 😄 |
|
I think there's a |


Fully generated file with rules is here https://gist.github.com/kostya9/467b404cddfbaae2fa63b5e6c6bfb584
I used the specification here https://www.unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules to guide me.
I implemented a source generator that
Feel free to start review either from tests, or from the source generator entrypoint PluralLanguagesGenerator.cs.
For variables, I implemented only
Locales with other variables were excluded
For right side, the generator understands both ranges (11..14) and individual numbers (15).
For left side, the generator understands both plain variables(i), and module operations (i % 10).
Contributes to #22
UPD:
Added support for all variables except exponents