Formatting and cultures
editFormat with the invariant culture
ToString() uses the current culture: 1234.5 becomes 1234,5 in French. Values written to files, logs, URLs or protocols must be formatted the same way everywhere:
// Numbers, dates and any IFormattable, formatted with the invariant culture
Assert.Equal("1234.5", 1234.5.ToStringInvariant());
Assert.Equal("1,234.50", 1234.5m.ToStringInvariant("N2"));
Assert.Equal("2024-03-15", new DateOnly(2024, 3, 15).ToStringInvariant("yyyy-MM-dd"));
Assert.Equal("00:30:00", TimeSpan.FromMinutes(30).ToStringInvariant());ToStringInvariant exists for the numeric types (int, long, double, decimal, Half and the others), for DateTime, DateOnly and TimeOnly, and for any IFormattable, with or without a format string.
Append invariant values
var sb = new StringBuilder();
// The values are formatted with the invariant culture, whatever the current culture
sb.AppendInvariant(1234.5).Append(' ');
sb.AppendInvariant($"{0.25:P0} of {1000:N0}").Append(' ');
sb.AppendFormatInvariant("{0:F2}", 3.14159);
Assert.Equal("1234.5 25 % of 1,000 3.14", sb.ToString());- AppendInvariant appends a number, a nullable number, an
IFormattableor an object, formatted with the invariant culture. - With an interpolated string, AppendInvariant formats every hole with the invariant culture.
- AppendFormatInvariant is
AppendFormatwith the invariant culture.
Run code with another culture
// Run code with another current culture, then restore the previous one
var formatted = CultureInfoUtilities.UseCulture("fr-FR", () => 1234.5.ToString("N1", CultureInfo.CurrentCulture));
Assert.Equal("1\u202f234,5", formatted);
// Compare cultures by language
var canadianFrench = CultureInfo.GetCultureInfo("fr-CA");
Assert.Equal("fr", canadianFrench.GetNeutralCulture().Name);
Assert.True(canadianFrench.NeutralEquals(CultureInfo.GetCultureInfo("fr-FR")));- CultureInfoUtilities.UseCulture sets the current culture and the current UI culture of the thread, runs the delegate, and restores the previous cultures, even when the delegate throws. It takes a culture or a culture name, and an
Actionor aFunc<T>. - The delegate is synchronous. With an
asynclambda, the cultures are restored at the firstawaitthat does not complete synchronously. - SetCurrentThreadCulture sets both cultures without restoring them.
- GetCulture returns a culture from its name or its LCID, or
nullwhen it does not exist. A number is always read as an LCID. - GetNeutralCulture returns the neutral culture (
frforfr-CA), and NeutralEquals tells whether two cultures share the same neutral culture.
Named cultures are not available when the application runs in invariant globalization mode.
Ordinals
Assert.Equal("1st", NumberExtensions.ToEnglishOrdinal(1, CultureInfo.InvariantCulture));
Assert.Equal("22nd", NumberExtensions.ToEnglishOrdinal(22, CultureInfo.InvariantCulture));
Assert.Equal("13th", NumberExtensions.ToEnglishOrdinal(13, CultureInfo.InvariantCulture));
Assert.Equal("1er", NumberExtensions.ToFrenchOrdinal(1, CultureInfo.InvariantCulture));
Assert.Equal("2e", NumberExtensions.ToFrenchOrdinal(2, CultureInfo.InvariantCulture));ToEnglishOrdinal and ToFrenchOrdinal add the English or French ordinal suffix to a positive number. Zero and negative numbers are returned without a suffix. The format provider formats the number; without it, the current culture is used.
Country flags
var region = new RegionInfo("US");
string flag = Country.GetUnicodeFlag(region);
Assert.Equal("\U0001F1FA\U0001F1F8", flag); // US flag emoji
string flag2 = Country.GetUnicodeFlag("FR");
Assert.Equal("\U0001F1EB\U0001F1F7", flag2); // FR flag emojiCountry.GetUnicodeFlag returns the flag emoji of a country, from a RegionInfo or a two-letter ISO region code. The emoji is made of two regional indicator symbols: how it is displayed depends on the fonts of the system.
The region code must be made of two ASCII letters, uppercase or lowercase: GetUnicodeFlag throws an ArgumentException otherwise. This includes regions whose TwoLetterISORegionName is a numeric UN M.49 code, such as 419 for es-419. Use Country.TryGetUnicodeFlag when the code comes from user input or from an arbitrary RegionInfo:
Assert.True(Country.TryGetUnicodeFlag("FR", out var flag));
Assert.Equal("\U0001F1EB\U0001F1F7", flag); // FR flag emoji
// "419" (Latin America) is a numeric UN M.49 code, not a country code
Assert.False(Country.TryGetUnicodeFlag("419", out _));