How to force .Net ToString to use given Globalization / CultureInfo - c#

I'm working with a piece of .Net Core 6 library (dll), in which, amongst other functionality, numbers and date/time information is outputted as strings and this code is required to be globalized ie. to be culture-aware. The library is for a document producing system, so in the same application session, users would produce documents for de-CH, de-FR, en-US etc. So basically, We would like to have the library culture-aware, but the culture outputted should have it's "standard" formats, not the ones customised in Operating system level or user level.
For example for decimal values, the library code is using these methods to output stuff:
public string ToString();
public string ToString(string? format);
public string ToString(IFormatProvider? provider)
public string ToString(string? format, IFormatProvider? provider)
So calling these overloads by different users in different computers having different culture-settings, will format the output differently: decimal separator can be comma or point and the thousands grouping delimiter might be non-braking-space or ’ etc.
However, when provider argument is passed explicitly by using an instance of a specific culture for example "fr-CH" (Swiss-French) and with CultureInfo.UseUserOverride == false, I was expecting always the same output string, independent of user and/or computer. So, I was thinking, that .Net would have "built-in" values for all the properties of CultureInfo-instances for all culture-types and with argument useUserOverride: false I could force the formatting to use these "Built-in" values and hence would output always the same string.
But this seems not be the case!
The code:
CultureInfo cultInfo = new CultureInfo(name: "fr-CH", useUserOverride: false) //* fr-CH" is Swiss-French
Decimal myDec = -123456789123456.987654321M;
String output = myDec.ToString(format: "N4", provider: cultInfo); // I was expecting always same output independent, which user is calling it in which computer, but this is not the case!
This example code was compiled with .Net Core 6 as Console application and I run it on a) a Windows 10 Pro PC and b) on a Windows Server 2012R2 and they had having different outputs.
Fr-CH/Windows Pro 10 PC: -123 456 789 123 456,9877
Fr-CH/Windows Server 2012R2: -123'456'789'123'456.9877
The same by setting CultureInfo cultInfo = CultureInfo.InvariantCulture: Then the result look always the same, which is expected.
I also tried setting Thread.CurrentThread.CurrentCulture and Thread.CurrentThread.CurrentUICulture using the cultInfo-variable the above and then calling ToString(format?, provider?), but there was no effect: the results were exactly like without setting the CurrentThread. PS. I actually learned from here, here and here that setting Thread.CurrentThread.CurrentUICulture has nothing to do with formatting (my case), but with translation (resx stuff).
I'm aware of this vaste piece of Microsoft documentation about CultureInfo-class, but I cannot see the tree from the forest there.
My questions are as follows:
Has any version of .Net Core (or any version .Net Framework) a "built-in values" for all CultureInfo-properties at all?
How could I force my code to use always exactly the culture given culture independent of the environment?
This would be very important in order to be able to unit-test the library output in a reliable way.
Likely important this would be, when reading back (utilizing System.Convert(value, provider?) the values outputted by any user in any computer (just knowing the culture they were outputted).

You can set up a custom culture and configure that one according to your needs.
Start from an existing neutral one, like the InvariantCulture.
Make a clone in order to keep that InvariantCulture as-is.
var clone = CultureInfo.InvariantCulture.Clone() as CultureInfo;
Then make the required changes upon that clone - e.g.:
clone.NumberFormat.NumberDecimalSeparator = ",";
clone.NumberFormat.NumberGroupSeparator = ".";
Optionally but advisable, you might want to make a readonly version of it.
var customCulture = CultureInfo.ReadOnly(clone);
Use that culture wherever you need a fixed controlled output - e.g.:
var number = -123456789123456.987654321M;
var formattedNumber = number.ToString("N4", customCulture)); // -123.456.789.123.456,9877
Apply that same culture when parsing the formatted string version back to a decimal - e.g.:
var parsedNumber = decimal.Parse(formattedNumber, customCulture);
If you are interested in how a CultureInfo is set up have a look at the source code.
In short the InvariantCulture is one with fixed settings, whereas the others rely on the operating system and optional user overrides.
From the documentation:
.NET derives its cultural data from a one of a variety of sources,
depending on implementation, platform, and version:
In .NET Framework 3.5 and earlier versions, cultural data is provided by both the Windows operating system and .NET Framework.
In .NET Framework 4 and later versions, cultural data is provided by the Windows operating system.
In all versions of .NET Core running on Windows, cultural data is provided by the Windows operating system.
Because of this, a culture available on a particular .NET
implementation, platform, or version may not be available on a
different .NET implementation, platform, or version.
Some CultureInfo objects differ depending on the underlying platform.

Set it on the thead for it to work and if you don't want it to repeat in every place:
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fr-CH");
.....
Decimal myDec = -123456789123456.987654321M;
String output = myDec.ToString(format: "N4");
Console.WriteLine(output);
when you run the code on other computers, try to check the cultureinfo and see what it will return. I bet it is not setting it to fr-CH:
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
Console.WriteLine(currentCulture.Name);

Related

C# float to string separator

I am new to C # and I am currently having problems with the following. In C #, I have a Pi floating point number and I want to convert it to a string using the ToString() method. But the conversion gives a string result with a comma "3,1415". On another machine, the same gives the string result with the dot "3.1415". What is the reason for this and what should I do to get a dotted string result?
EDIT: The problem is, I can't change the code, but I can install and uninstall .Net frameworks, change my OS settings, etc.
Edit: if you can't change the code. Change the language/localization of the system to one which uses dot as decimal separator. In Control Panel or Settings.
You should look at internationalization and localization in the System.Globalization namespace.
The advice here is to use one CultureInfo specific for parsing numbers or writing numbers to string.
var flt = 232.23f;
var str = flt.ToString(CultureInfo.InvariantCulture); //for example, you can use CultureInfo.CurrentCulture
This allows you to keep the ThreadCulture without change it.
But take a look at this link https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo?view=net-5.0 .Take your time, is dense.
I would just set the current culture at the entry point of your program.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
However I would also check the regional settings and so forth on the machine where the comma separator appears.
It is related with current culture info. You can specify the culture info in ToString method as a parameter like;
var convertedFloat = floatVariable.ToString(new CultureInfo("en-GB"));
Thanks to GSerg, for comment about Windows regional settings. That solves my problem. In the Windows Control panel enter Region and Language. In the Formats tab click Additional Settings and in the Decimal symbol field specify what decimal separator must be used when converting a floating point number to a string.

Define a string based on culture info in C# Silverlight

I need to spell word "Colour" as "Color" in US format and as "Colour" in UK format. My applications works in these 2 countries. I need to change the spellings accordingly. I tried it with the following code :
string sample = "";
if (CultureInfo.CurrentUICulture.Name == "en-US")
sample = "Color";
else
sample = "Colour";
But this somehow doesn't seem to work.
In Silverlight the culture defaults to "en-US" regardless of the country you are actually running in.
You need to explicitly set the culture in the view class with the following code:
this.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);
after having set the thread culture accordingly:
CultureInfo ci = new CultureInfo(GetEnterpriseCultureName(enterpriseCultureId));
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
return ci.Name;
You can also override the currency symbol and number separators in this as well.
Source
Then, once you've done that the simplest solution is to put your strings into a pair of resource files. One just called "Resource.resx" which is the default and another called "Resource.en-US.resx" (for example) if your default is the UK.
Then .NET will automatically select the correct resources based on the the culture of the thread.
The right way to deal with localization and region differences in spelling is to use the Silverlight Resource Manager that was included into the framework for exactly that sort of thing.
The Hierarchical Organization of Resources
To understand how resources are loaded, it is useful to think of them as organized in a hierarchical manner. A localized application can have resource files at three levels:
At the top of the hierarchy are the fallback resources for the default culture, for example, English ("en"). These are the only resources that do not have their own file; they are stored in the main assembly.
At the second level are the resources for any region-neutral cultures. A region-neutral culture is associated with a language but not a region. For example, French ("fr") is a region-neutral culture.
At the bottom of the hierarchy are the resources for any specific cultures. A specific culture is associated with a language and a region. For example, French Canadian ("fr-CA") is a specific culture.

WinRT - How To Get the correct DateTimeFormatter based on the user culture

I've some trouble finding the right DateTimeFormatter for the user.
When converting a date to a string for example with
.ToString("D");
always the en-US culture is used in WinRT.
I found out that there are new globalization apis which should be used.
for example
var langs = Windows.System.UserProfile.GlobalizationPreferences.Languages;
var homeregion = Windows.System.UserProfile.GlobalizationPreferences.HomeGeographicRegion;
Windows.Globalization.DateTimeFormatting.DateTimeFormatter dtf = new DateTimeFormatter(homeregion);
but the result of HomeGeographicRegion is not in the format with a new DateTimeformatter requires
I also tried this
var formatter = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter(Windows.Globalization.DateTimeFormatting.YearFormat.Default,
Windows.Globalization.DateTimeFormatting.MonthFormat.Abbreviated,
Windows.Globalization.DateTimeFormatting.DayFormat.Default,
Windows.Globalization.DateTimeFormatting.DayOfWeekFormat.Default);
string result = formatter.Format(Date);
but that also only returns date string in en-Us format.
Can anyour tell me what is the correct way to get a DateTimeFormatter according to the users culture (which is also automatically used for resource localizations via uid)?
The single argument DateTimeFormatter constructor takes a template (something like "month.abbreviated day dayofweek"). Providing a region to this will fail with an invalid argument.
For Windows Store applications, the DateTimeFormatters constructed without a languages parameter, will be equivalent to if the DateTimeFormatter had been constructed by providing the value of the Windows.Globalization.ApplicationLanguages.Languages property. For desktop applications, the default is the user locale.
Note that the application languages are resolved from the user languages (which you can query at Windows.System.UserProfile.GlobalizationPreferences.Languages) and the declared application manifest languages (which you can query at Windows.Globalization.ApplicationLanguages.ManifestLanguages).
Finally, the ResolvedLanguage property will let you see exactly what language is being used internally by the DateTimeFormatter.
In my experience, when people get results that they didn't expect, it is generally because the application only supports a single language, in which case, the application language will always be that no matter what the user preference is. Otherwise, verify that the language you expect is at the top of your user language list.

Specify a culture in string conversion explicitly

I have a loop in which I call ToString() on the int variable i.
for (int i = 1; i <= n; i++)
{
#Html.ActionLink(#i.ToString(), "Index")
}
Resharper tells me that I should:
Specify a culture in string conversion explicitly.
Why is this?
How many ways are there to convert an int into a string?
I would have thought only 1.
There are different methods of grouping symbols, like 1000; 1 000 and 1'000.
Besides there are different digits used for numbers in different countries
Chinese numerals
You can see different numbers in Control Panel -> region and language -> Formats -> additional settings -> Standart digits. (Win 7)
Generally, it's a good practice to always specify explicitly whether you want to use the current culture or if the data your processing is culture invariant. The problem is that if you are processing data which is only processed by your software and not presented to the user in any way (for example database identifiers), then you might run into problems if the data is different on different machines.
For example a database identifier may be serialized on a machine with some culture and deserialized on a machine with a different culture, and in that case it might be different! If you specify explicitly that the string you're processing is culture-invariant, then it will always be the same, regardless of what culture is set on the machine.
You can read more about this topic on MSDN code analysis documentation:
CA1305: Specify IFormatProvider

WinRT apps and Regional settings. The correct way to format dates and numbers based on the user's regional settings?

I'm having some problems in Windows 8 Metro apps (XAML & C#) regarding the user's regional settings. It seems that the apps won't respect user's regional settings, so even if your Windows 8 is set to display dates and times in Finnish format, the apps will still display them using US-formatting. But this is such a big problem that there must be something I'm missing?
To test this I started by creating a WPF-application. The application just prints out the CurrentCulture and the formatted DateTime.Now:
private void Culture_Loaded_1(object sender, RoutedEventArgs e)
{
this.Culture.Text = System.Globalization.CultureInfo.CurrentCulture.DisplayName;
}
private void Date_Loaded_1(object sender, RoutedEventArgs e)
{
this.Date.Text = DateTime.Now.ToString();
}
Here's my default regional settings:
When run, the app displayed the date in Finnish format:
Then I changed the regional settings to US:
And when the app was run again, the culture and formatting changed:
This is as I expected everything to work and this is also how I expected WinRT apps to work.
So as a next step, I created a WinRT (XAML & C#) app with the same code and reverted the regional settings back to Finnish. The problem:
Even when I've defined through regional settings that the formatting should be "Finnish", the WinRT app displays the datetime with US-formatting. I then modified the app's project file and made fi-FI the default language:
This change also modified the app's culture:
Strange. I changed the Default Language back to its default value and the formatting was restored to US. I then created folders "Strings - fi-FI" inside the project and added an empty "Resources.resw" to the project. This empty file seems to be enough, as I was now getting the Finnish formatting:
As soon as I remove the empty resource file, the formattings reverts back to US:
Very strange.
This leads to few questions, but the main one I think is: Is it intentional that the WinRT-apps don't follow the user's regional settings like the WPF apps do?
It's been a while, but the question is not fully answered, so let me share my little research. Depechie is mostly right, but he provided only a link and wasn't really sure.
Yes, this unexpected change is intentional. We shouldn't use CultureInfo anymore as it contains legacy codes and Microsoft want us to use Windows.Globalization APIs instead.
To obtain current region we can use:
GeographicRegion userRegion = new GeographicRegion();
string regionCode = userRegion.CodeTwoLetter;
But as I noticed it contains only region information, there's no language code. To obtain language we can use:
string langRegionCode = Windows.Globalization.Language.CurrentInputMethodLanguageTag; // depends on keyboard settings
List<string> langs = Windows.System.UserProfile.GlobalizationPreferences.Languages; // all user languages, like in languages control panel
List<string> applicationlangs = Windows.Globalization.ApplicationLanguages.Languages; // application languages (user languages resolved against languages declared as supported by application)
They return BCP47 language tags in format language-REGION like "en-US" if language has dialects or just language like "pl" if the language doesn't have major dialects.
We can also set one primary language which will override all the rest:
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "en-US";
(This is a persisted setting and is supposed to be used at user request)
There is also new API for date, time and numbers:
Windows.Globalization.DateTimeFormatting.DateTimeFormatter dtf = new DateTimeFormatter("longdate", new[] { "en-US" }, "US", CalendarIdentifiers.Gregorian, ClockIdentifiers.TwentyFourHour);
string longDate = dtf.Format(DateTime.Now);
Windows.Globalization.NumberFormatting.DecimalFormatter deciamlFormatter = new DecimalFormatter(new string[] { "PL" }, "PL");
double d1 = (double)deciamlFormatter.ParseDouble("2,5"); // ParseDouble returns double?, not double
There's really a lot more in Windows.Globalization APIs, but I think that this gives us the general idea. For further reading:
date & time formatting sample:
http://code.msdn.microsoft.com/windowsapps/Date-and-time-formatting-2361f348/sourcecode?fileId=52070&pathId=561085805
number formatting & parsing sample:
http://code.msdn.microsoft.com/windowsapps/Number-formatting-and-bb10ba3d/sourcecode?fileId=52249&pathId=1462911094
there's also a nice article titled "How to use patterns to format dates and times" on msdn, but I can add only 2 links
You can also find some topics about the issue on windows 8 dev center forum with some Microsoft employee answers, but they mainly send you to the documentation.
It is intentional. Microsoft is moving away from forcing applications to be in the language of the OS. Instead, each application uses information declared by the application (manifest languages, observable at Windows.Globalization.ApplicationLanguages.ManifestLanguages) and declared by the user (user languages, observable at Windows.System.UserProfile.GlobalizationPreferences.Languages) to determine how to display resources and globalized dates and times. This set of languages is called the application languages (observable at Windows.Globalization.ApplicationLanguages.Languages). The behavior you are seeing is because you are fiddling with the user languages and the manifest languages and you will get different application languages.
Could it be we now need to query other classes? Like the example given here: http://code.msdn.microsoft.com/windowsapps/Globalization-preferences-6654eb36/sourcecode?fileId=52104&pathId=236099476
This post still seems to be relevant even though it was asked two years ago.
I just came across it as I was looking for an answer to about the same thing.
I also wanted to display dates in the regional format in my WP8.1 WinRT app.
The information posted here helps, but it was a bit hard to piece it together.
This is what I came up with and it seems to work for me as the answer I needed:
using Windows.Globalization;
using Windows.Globalization.DateTimeFormatting;
private string FormatDate(int year, int month, int day)
{
GeographicRegion userRegion = new GeographicRegion();
string regionCode = userRegion.CodeTwoLetter;
var formatter = new DateTimeFormatter("year month day", new[] { regionCode });
DateTime dateToFormat = new DateTime(year, month, day);
var formattedDate = formatter.Format(dateToFormat);
return formattedDate;
}

Categories

Resources