CultureInfo class

This article provides supplementary remarks to the reference documentation for this API.

The CultureInfo class provides culture-specific information, such as the language, sublanguage, country/region, calendar, and conventions associated with a particular culture. This class also provides access to culture-specific instances of the DateTimeFormatInfo, NumberFormatInfo, CompareInfo, and TextInfo objects. These objects contain the information required for culture-specific operations, such as casing, formatting dates and numbers, and comparing strings. The CultureInfo class is used either directly or indirectly by classes that format, parse, or manipulate culture-specific data, such as String, DateTime, DateTimeOffset, and the numeric types.

Culture names and identifiers

The CultureInfo class specifies a unique name for each culture, based on RFC 4646. The name is a combination of an ISO 639 two-letter or three-letter lowercase culture code associated with a language and an ISO 3166 two-letter uppercase subculture code associated with a country or region. In addition, for apps that are running under Windows 10 or later, culture names that correspond to valid BCP-47 language tags are supported.

Note

When a culture name is passed to a class constructor or a method such as CreateSpecificCulture or CultureInfo, its case is not significant.

The format for the culture name based on RFC 4646 is languagecode2-country/regioncode2, where languagecode2 is the two-letter language code and country/regioncode2 is the two-letter subculture code. Examples include ja-JP for Japanese (Japan) and en-US for English (United States). In cases where a two-letter language code is not available, a three-letter code as defined in ISO 639-3 is used.

Some culture names also specify an ISO 15924 script. For example, Cyrl specifies the Cyrillic script and Latn specifies the Latin script. A culture name that includes a script uses the pattern languagecode2-scripttag-country/regioncode2. An example of this type of culture name is uz-Cyrl-UZ for Uzbek (Cyrillic, Uzbekistan). On Windows operating systems before Windows Vista, a culture name that includes a script uses the pattern languagecode2-country/regioncode2-scripttag, for example, uz-UZ-Cyrl for Uzbek (Cyrillic, Uzbekistan).

A neutral culture is specified by only the two-letter, lowercase language code. For example, fr specifies the neutral culture for French, and de specifies the neutral culture for German.

Note

There are two culture names that contradict this rule. The cultures Chinese (Simplified), named zh-Hans, and Chinese (Traditional), named zh-Hant, are neutral cultures. The culture names represent the current standard and should be used unless you have a reason for using the older names zh-CHS and zh-CHT.

A culture identifier is a standard international numeric abbreviation and has the components necessary to uniquely identify one of the installed cultures. Your application can use predefined culture identifiers or define custom identifiers.

Certain predefined culture names and identifiers are used by this and other classes in the System.Globalization namespace. For detailed culture information for Windows systems, see the Language tag column in the list of language/region names supported by Windows. Culture names follow the standard defined by BCP 47.

The culture names and identifiers represent only a subset of cultures that can be found on a particular computer. Windows versions or service packs can change the available cultures. Applications can add custom cultures using the CultureAndRegionInfoBuilder class. Users can add their own custom cultures using the Microsoft Locale Builder tool. Microsoft Locale Builder is written in managed code using the CultureAndRegionInfoBuilder class.

Several distinct names are closely associated with a culture, notably the names associated with the following class members:

Invariant, neutral, and specific cultures

The cultures are generally grouped into three sets: invariant cultures, neutral cultures, and specific cultures.

An invariant culture is culture-insensitive. Your application specifies the invariant culture by name using an empty string ("") or by its identifier. InvariantCulture defines an instance of the invariant culture. It is associated with the English language but not with any country/region. It is used in almost any method in the Globalization namespace that requires a culture.

A neutral culture is a culture that is associated with a language but not with a country/region. A specific culture is a culture that is associated with a language and a country/region. For example, fr is the neutral name for the French culture, and fr-FR is the name of the specific French (France) culture. Note that Chinese (Simplified) and Chinese (Traditional) are also considered neutral cultures.

Creating an instance of a CompareInfo class for a neutral culture is not recommended because the data it contains is arbitrary. To display and sort data, specify both the language and region. Additionally, the Name property of a CompareInfo object created for a neutral culture returns only the country and does not include the region.

The defined cultures have a hierarchy in which the parent of a specific culture is a neutral culture and the parent of a neutral culture is the invariant culture. The Parent property contains the neutral culture associated with a specific culture. Custom cultures should define the Parent property in conformance with this pattern.

If the resources for a specific culture are not available in the operating system, the resources for the associated neutral culture are used. If the resources for the neutral culture are not available, the resources embedded in the main assembly are used. For more information on the resource fallback process, see Packaging and Deploying Resources.

The list of locales in the Windows API is slightly different from the list of cultures supported by .NET. If interoperability with Windows is required, for example, through the p/invoke mechanism, the application should use a specific culture that's defined for the operating system. Use of the specific culture ensures consistency with the equivalent Windows locale, which is identified with a locale identifier that is the same as LCID.

A DateTimeFormatInfo or a NumberFormatInfo can be created only for the invariant culture or for specific cultures, not for neutral cultures.

If DateTimeFormatInfo.Calendar is the TaiwanCalendar but the Thread.CurrentCulture is not set to zh-TW, then DateTimeFormatInfo.NativeCalendarName, DateTimeFormatInfo.GetEraName, and DateTimeFormatInfo.GetAbbreviatedEraName return an empty string ("").

Custom cultures

On Windows, you can create custom locales. For more information, see Custom locales.

CultureInfo and cultural data

.NET derives its cultural data from a one of a variety of sources, depending on implementation, platform, and version:

  • In all versions of .NET (Core) running on Unix platforms or Windows 10 and later versions, cultural data is provided by the International Components for Unicode (ICU) Library. The specific version of the ICU Library depends on the individual operating system.
  • In all versions of .NET (Core) running on Windows 9 and earlier versions, cultural data is provided by the Windows operating system.
  • In .NET Framework 4 and later versions, 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. In particular, zh-CN, or Chinese (Simplified, China) and zh-TW, or Chinese (Traditional, Taiwan), are available cultures on Windows systems, but they are aliased cultures on Unix systems. "zh-CN" is an alias for the "zh-Hans-CN" culture, and "zh-TW" is an alias for the "zh-Hant-TW" culture. Aliased cultures are not returned by calls to the GetCultures method and may have different property values, including different Parent cultures, than their Windows counterparts. For the zh-CN and zh-TW cultures, these differences include the following:

  • On Windows systems, the parent culture of the "zh-CN" culture is "zh-Hans", and the parent culture of the "zh-TW" culture is "zh-Hant". The parent culture of both these cultures is "zh". On Unix systems, the parents of both cultures are "zh". This means that, if you don't provide culture-specific resources for the "zh-CN" or "zh-TW" cultures but do provide a resources for the neutral "zh-Hans" or "zh-Hant" culture, your application will load the resources for the neutral culture on Windows but not on Unix. On Unix systems, you must explicitly set the thread's CurrentUICulture to either "zh-Hans" or "zh-Hant".

  • On Windows systems, calling CultureInfo.Equals on an instance that represents the "zh-CN" culture and passing it a "zh-Hans-CN" instance returns true. On Unix systems, the method call returns false. This behavior also applies to calling Equals on a "zh-TW" CultureInfo instance and passing it a "zh-Hant-Tw" instance.

Dynamic culture data

Except for the invariant culture, culture data is dynamic. This is true even for the predefined cultures. For example, countries or regions adopt new currencies, change their spellings of words, or change their preferred calendar, and culture definitions change to track this. Custom cultures are subject to change without notice, and any specific culture might be overridden by a custom replacement culture. Also, as discussed below, an individual user can override cultural preferences. Applications should always obtain culture data at run time.

Caution

When saving data, your application should use the invariant culture, a binary format, or a specific culture-independent format. Data saved according to the current values associated with a particular culture, other than the invariant culture, might become unreadable or might change in meaning if that culture changes.

The current culture and current UI culture

Every thread in a .NET application has a current culture and a current UI culture. The current culture determines the formatting conventions for dates, times, numbers, and currency values, the sort order of text, casing conventions, and the ways in which strings are compared. The current UI culture is used to retrieve culture-specific resources at run time.

Note

For information on how the current and current UI culture is determined on a per-thread basis, see the Culture and threads section. For information on how the current and current UI culture is determined on threads executing in a new application domain, and on threads that cross application domain boundaries, see the Culture and application domains section. For information on how the current and current UI culture is determined on threads performing task-based asynchronous operations, see the Culture and task-based asynchronous operations section.

For more detailed information on the current culture, see the CultureInfo.CurrentCulture property. For more detailed information on the current UI culture, see the CultureInfo.CurrentUICulture property topic.

Retrieve the current and current UI cultures

You can get a CultureInfo object that represents the current culture in either of two ways:

The following example retrieves both property values, compares them to show that they are equal, and displays the name of the current culture.

using System;
using System.Globalization;
using System.Threading;

public class CurrentCultureEx
{
    public static void Main()
    {
        CultureInfo culture1 = CultureInfo.CurrentCulture;
        CultureInfo culture2 = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine("The current culture is {0}", culture1.Name);
        Console.WriteLine("The two CultureInfo objects are equal: {0}",
                          culture1 == culture2);
    }
}
// The example displays output like the following:
//     The current culture is en-US
//     The two CultureInfo objects are equal: True

You can get a CultureInfo object that represents the current UI culture in either of two ways:

The following example retrieves both property values, compares them to show that they are equal, and displays the name of the current UI culture.

using System;
using System.Globalization;
using System.Threading;

public class CurrentUIEx
{
    public static void Main()
    {
        CultureInfo uiCulture1 = CultureInfo.CurrentUICulture;
        CultureInfo uiCulture2 = Thread.CurrentThread.CurrentUICulture;
        Console.WriteLine("The current UI culture is {0}", uiCulture1.Name);
        Console.WriteLine("The two CultureInfo objects are equal: {0}",
                          uiCulture1 == uiCulture2);
    }
}
// The example displays output like the following:
//     The current UI culture is en-US
//     The two CultureInfo objects are equal: True

Set the current and current UI cultures

To change the culture and UI culture of a thread, do the following:

  1. Instantiate a CultureInfo object that represents that culture by calling a CultureInfo class constructor and passing it the name of the culture. The CultureInfo(String) constructor instantiates a CultureInfo object that reflects user overrides if the new culture is the same as the current Windows culture. The CultureInfo(String, Boolean) constructor allows you to specify whether the newly instantiated CultureInfo object reflects user overrides if the new culture is the same as the current Windows culture.

  2. Assign the CultureInfo object to the CultureInfo.CurrentCulture or CultureInfo.CurrentUICulture property on .NET Core and .NET Framework 4.6 and later versions.

The following example retrieves the current culture. If it is anything other than the French (France) culture, it changes the current culture to French (France). Otherwise, it changes the current culture to French (Luxembourg).

using System;
using System.Globalization;

public class ChangeEx1
{
    public static void Main()
    {
        CultureInfo current = CultureInfo.CurrentCulture;
        Console.WriteLine("The current culture is {0}", current.Name);
        CultureInfo newCulture;
        if (current.Name.Equals("fr-FR"))
            newCulture = new CultureInfo("fr-LU");
        else
            newCulture = new CultureInfo("fr-FR");

        CultureInfo.CurrentCulture = newCulture;
        Console.WriteLine("The current culture is now {0}",
                          CultureInfo.CurrentCulture.Name);
    }
}
// The example displays output like the following:
//     The current culture is en-US
//     The current culture is now fr-FR

The following example retrieves the current culture. If it is anything other the Slovenian (Slovenia) culture, it changes the current culture to Slovenian (Slovenia). Otherwise, it changes the current culture to Croatian (Croatia).

using System;
using System.Globalization;

public class ChangeUICultureEx
{
    public static void Main()
    {
        CultureInfo current = CultureInfo.CurrentUICulture;
        Console.WriteLine("The current UI culture is {0}", current.Name);
        CultureInfo newUICulture;
        if (current.Name.Equals("sl-SI"))
            newUICulture = new CultureInfo("hr-HR");
        else
            newUICulture = new CultureInfo("sl-SI");

        CultureInfo.CurrentUICulture = newUICulture;
        Console.WriteLine("The current UI culture is now {0}",
                          CultureInfo.CurrentUICulture.Name);
    }
}
// The example displays output like the following:
//     The current UI culture is en-US
//     The current UI culture is now sl-SI

Get all cultures

You can retrieve an array of specific categories of cultures or of all the cultures available on the local computer by calling the GetCultures method. For example, you can retrieve custom cultures, specific cultures, or neutral cultures either alone or in combination.

The following example calls the GetCultures method twice, first with the System.Globalization.CultureTypes enumeration member to retrieve all custom cultures, and then with the System.Globalization.CultureTypes enumeration member to retrieve all replacement cultures.

using System;
using System.Globalization;

public class GetCulturesEx
{
    public static void Main()
    {
        // Get all custom cultures.
        CultureInfo[] custom = CultureInfo.GetCultures(CultureTypes.UserCustomCulture);
        if (custom.Length == 0)
        {
            Console.WriteLine("There are no user-defined custom cultures.");
        }
        else
        {
            Console.WriteLine("Custom cultures:");
            foreach (var culture in custom)
                Console.WriteLine("   {0} -- {1}", culture.Name, culture.DisplayName);
        }
        Console.WriteLine();

        // Get all replacement cultures.
        CultureInfo[] replacements = CultureInfo.GetCultures(CultureTypes.ReplacementCultures);
        if (replacements.Length == 0)
        {
            Console.WriteLine("There are no replacement cultures.");
        }
        else
        {
            Console.WriteLine("Replacement cultures:");
            foreach (var culture in replacements)
                Console.WriteLine("   {0} -- {1}", culture.Name, culture.DisplayName);
        }
        Console.WriteLine();
    }
}
// The example displays output like the following:
//     Custom cultures:
//        x-en-US-sample -- English (United States)
//        fj-FJ -- Boumaa Fijian (Viti)
//
//     There are no replacement cultures.

Culture and threads

When a new application thread is started, its current culture and current UI culture are defined by the current system culture, and not by the current thread culture. The following example illustrates the difference. It sets the current culture and current UI culture of an application thread to the French (France) culture (fr-FR). If the current culture is already fr-FR, the example sets it to the English (United States) culture (en-US). It displays three random numbers as currency values and then creates a new thread, which, in turn, displays three more random numbers as currency values. But as the output from the example shows, the currency values displayed by the new thread do not reflect the formatting conventions of the French (France) culture, unlike the output from the main application thread.

using System;
using System.Globalization;
using System.Threading;

public class DefaultThreadEx
{
    static Random rnd = new Random();

    public static void Main()
    {
        if (Thread.CurrentThread.CurrentCulture.Name != "fr-FR")
        {
            // If current culture is not fr-FR, set culture to fr-FR.
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("fr-FR");
        }
        else
        {
            // Set culture to en-US.
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("en-US");
        }
        ThreadProc();

        Thread worker = new Thread(ThreadProc);
        worker.Name = "WorkerThread";
        worker.Start();
    }

    private static void DisplayThreadInfo()
    {
        Console.WriteLine("\nCurrent Thread Name: '{0}'",
                          Thread.CurrentThread.Name);
        Console.WriteLine("Current Thread Culture/UI Culture: {0}/{1}",
                          Thread.CurrentThread.CurrentCulture.Name,
                          Thread.CurrentThread.CurrentUICulture.Name);
    }

    private static void DisplayValues()
    {
        // Create new thread and display three random numbers.
        Console.WriteLine("Some currency values:");
        for (int ctr = 0; ctr <= 3; ctr++)
            Console.WriteLine("   {0:C2}", rnd.NextDouble() * 10);
    }

    private static void ThreadProc()
    {
        DisplayThreadInfo();
        DisplayValues();
    }
}
// The example displays output similar to the following:
//       Current Thread Name: ''
//       Current Thread Culture/UI Culture: fr-FR/fr-FR
//       Some currency values:
//          8,11 €
//          1,48 €
//          8,99 €
//          9,04 €
//
//       Current Thread Name: 'WorkerThread'
//       Current Thread Culture/UI Culture: en-US/en-US
//       Some currency values:
//          $6.72
//          $6.35
//          $2.90
//          $7.72

You can set the culture and UI culture of all threads in an application domain by assigning a CultureInfo object that represents that culture to the DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture properties. The following example uses these properties to ensure that all threads in the default application domain share the same culture.

using System;
using System.Globalization;
using System.Threading;

public class SetThreadsEx
{
    static Random rnd = new Random();

    public static void Main()
    {
        if (Thread.CurrentThread.CurrentCulture.Name != "fr-FR")
        {
            // If current culture is not fr-FR, set culture to fr-FR.
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture("fr-FR");
        }
        else
        {
            // Set culture to en-US.
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture("en-US");
        }
        ThreadProc();

        Thread worker = new Thread(SetThreadsEx.ThreadProc);
        worker.Name = "WorkerThread";
        worker.Start();
    }

    private static void DisplayThreadInfo()
    {
        Console.WriteLine("\nCurrent Thread Name: '{0}'",
                          Thread.CurrentThread.Name);
        Console.WriteLine("Current Thread Culture/UI Culture: {0}/{1}",
                          Thread.CurrentThread.CurrentCulture.Name,
                          Thread.CurrentThread.CurrentUICulture.Name);
    }

    private static void DisplayValues()
    {
        // Create new thread and display three random numbers.
        Console.WriteLine("Some currency values:");
        for (int ctr = 0; ctr <= 3; ctr++)
            Console.WriteLine("   {0:C2}", rnd.NextDouble() * 10);
    }

    private static void ThreadProc()
    {
        DisplayThreadInfo();
        DisplayValues();
    }
}
// The example displays output similar to the following:
//       Current Thread Name: ''
//       Current Thread Culture/UI Culture: fr-FR/fr-FR
//       Some currency values:
//          6,83 €
//          3,47 €
//          6,07 €
//          1,70 €
//
//       Current Thread Name: 'WorkerThread'
//       Current Thread Culture/UI Culture: fr-FR/fr-FR
//       Some currency values:
//          9,54 €
//          9,50 €
//          0,58 €
//          6,91 €

Warning

Although the DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture properties are static members, they define the default culture and default UI culture only for the application domain that is current at the time these property values are set. For more information, see the next section, Culture and application domains.

When you assign values to the DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture properties, the culture and UI culture of the threads in the application domain also change if they have not explicitly been assigned a culture. However, these threads reflect the new culture settings only while they execute in the current application domain. If these threads execute in another application domain, their culture becomes the default culture defined for that application domain. As a result, we recommend that you always set the culture of the main application thread, and not rely on the DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture properties to change it.

Culture and application domains

DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture are static properties that explicitly define a default culture only for the application domain that is current when the property value is set or retrieved. The following example sets the default culture and default UI culture in the default application domain to French (France), and then uses the AppDomainSetup class and the AppDomainInitializer delegate to set the default culture and UI culture in a new application domain to Russian (Russia). A single thread then executes two methods in each application domain. Note that the thread's culture and UI culture are not explicitly set; they are derived from the default culture and UI culture of the application domain in which the thread is executing. Note also that the DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture properties return the default CultureInfo values of the application domain that is current when the method call is made.

using System;
using System.Globalization;

public class Example
{
    public static void Main()
    {
        // Set the default culture and display the current date in the current application domain.
        Info info1 = new Info();
        SetAppDomainCultures("fr-FR");

        // Create a second application domain.
        AppDomainSetup setup = new AppDomainSetup();
        setup.AppDomainInitializer = SetAppDomainCultures;
        setup.AppDomainInitializerArguments = new string[] { "ru-RU" };
        AppDomain domain = AppDomain.CreateDomain("Domain2", null, setup);
        // Create an Info object in the new application domain.
        Info info2 = (Info)domain.CreateInstanceAndUnwrap(typeof(Example).Assembly.FullName,
                                                           "Info");

        // Execute methods in the two application domains.
        info2.DisplayDate();
        info2.DisplayCultures();

        info1.DisplayDate();
        info1.DisplayCultures();
    }

    public static void SetAppDomainCultures(string[] names)
    {
        SetAppDomainCultures(names[0]);
    }

    public static void SetAppDomainCultures(string name)
    {
        try
        {
            CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(name);
            CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture(name);
        }
        // If an exception occurs, we'll just fall back to the system default.
        catch (CultureNotFoundException)
        {
            return;
        }
        catch (ArgumentException)
        {
            return;
        }
    }
}

public class Info : MarshalByRefObject
{
    public void DisplayDate()
    {
        Console.WriteLine("Today is {0:D}", DateTime.Now);
    }

    public void DisplayCultures()
    {
        Console.WriteLine("Application domain is {0}", AppDomain.CurrentDomain.Id);
        Console.WriteLine("Default Culture: {0}", CultureInfo.DefaultThreadCurrentCulture);
        Console.WriteLine("Default UI Culture: {0}", CultureInfo.DefaultThreadCurrentUICulture);
    }
}
// The example displays the following output:
//       Today is 14 октября 2011 г.
//       Application domain is 2
//       Default Culture: ru-RU
//       Default UI Culture: ru-RU
//       Today is vendredi 14 octobre 2011
//       Application domain is 1
//       Default Culture: fr-FR
//       Default UI Culture: fr-FR

For more information about cultures and application domains, see the "Application Domains and Threads" section in the Application Domains topic.

Culture and task-based asynchronous operations

The task-based asynchronous programming pattern uses Task and Task<TResult> objects to asynchronously execute delegates on thread pool threads. The specific thread on which a particular task runs is not known in advance, but is determined only at runtime.

For apps that target .NET Framework 4.6 or a later version, culture is part of an asynchronous operation's context. In other words, asynchronous operations by default inherit the values of the CurrentCulture and CurrentUICulture properties of the thread from which they are launched. If the current culture or current UI culture differs from the system culture, the current culture crosses thread boundaries and becomes the current culture of the thread pool thread that is executing an asynchronous operation.

The following example provides a simple illustration. The example defines a Func<TResult> delegate, formatDelegate, that returns some numbers formatted as currency values. The example changes the current system culture to either French (France) or, if French (France) is already the current culture, English (United States). It then:

  • Invokes the delegate directly so that it runs synchronously on the main app thread.
  • Creates a task that executes the delegate asynchronously on a thread pool thread.
  • Creates a task that executes the delegate synchronously on the main app thread by calling the Task.RunSynchronously method.

As the output from the example shows, when the current culture is changed to French (France), the current culture of the thread from which tasks are invoked asynchronously becomes the current culture for that asynchronous operation.

using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

public class AsyncCultureEx1
{
    public static void Main()
    {
        decimal[] values = { 163025412.32m, 18905365.59m };
        string formatString = "C2";

        string FormatDelegate()
        {
            string output = $"Formatting using the {CultureInfo.CurrentCulture.Name} " +
            "culture on thread {Thread.CurrentThread.ManagedThreadId}.\n";
            foreach (decimal value in values)
                output += $"{value.ToString(formatString)}   ";

            output += Environment.NewLine;
            return output;
        }

        Console.WriteLine($"The example is running on thread {Thread.CurrentThread.ManagedThreadId}");
        // Make the current culture different from the system culture.
        Console.WriteLine($"The current culture is {CultureInfo.CurrentCulture.Name}");
        if (CultureInfo.CurrentCulture.Name == "fr-FR")
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
        else
            Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

        Console.WriteLine($"Changed the current culture to {CultureInfo.CurrentCulture.Name}.\n");

        // Execute the delegate synchronously.
        Console.WriteLine("Executing the delegate synchronously:");
        Console.WriteLine(FormatDelegate());

        // Call an async delegate to format the values using one format string.
        Console.WriteLine("Executing a task asynchronously:");
        var t1 = Task.Run(FormatDelegate);
        Console.WriteLine(t1.Result);

        Console.WriteLine("Executing a task synchronously:");
        var t2 = new Task<string>(FormatDelegate);
        t2.RunSynchronously();
        Console.WriteLine(t2.Result);
    }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €

DefaultThreadCurrentCulture and DefaultThreadCurrentUICulture are per-app domain properties; that is, they establish a default culture for all threads not explicitly assigned a culture in a specific application domain. However, for apps that target .NET Framework 4.6 or later, the culture of the calling thread remains part of an asynchronous task's context even if the task crosses app domain boundaries.

CultureInfo object serialization

When a CultureInfo object is serialized, all that is actually stored is Name and UseUserOverride. It is successfully deserialized only in an environment where that Name has the same meaning. The following three examples show why this is not always the case:

  • If the CultureTypes property value is CultureTypes.InstalledWin32Cultures, and if that culture was first introduced in a particular version of the Windows operating system, it is not possible to deserialize it on an earlier version of Windows. For example, if a culture was introduced in Windows 10, it cannot be deserialized on Windows 8.

  • If the CultureTypes value is CultureTypes.UserCustomCulture, and the computer on which it is deserialized does not have this user custom culture installed, it is not possible to deserialize it.

  • If the CultureTypes value is CultureTypes.ReplacementCultures, and the computer on which it is deserialized does not have this replacement culture, it deserializes to the same name, but not all of the same characteristics. For example, if en-US is a replacement culture on computer A, but not on computer B, and if a CultureInfo object referring to this culture is serialized on computer A and deserialized on computer B, then none of the custom characteristics of the culture are transmitted. The culture deserializes successfully, but with a different meaning.

Control Panel overrides

The user might choose to override some of the values associated with the current culture of Windows through the regional and language options portion of Control Panel. For example, the user might choose to display the date in a different format or to use a currency other than the default for the culture. In general, your applications should honor these user overrides.

If UseUserOverride is true and the specified culture matches the current culture of Windows, the CultureInfo uses those overrides, including user settings for the properties of the DateTimeFormatInfo instance returned by the DateTimeFormat property, and the properties of the NumberFormatInfo instance returned by the NumberFormat property. If the user settings are incompatible with the culture associated with the CultureInfo, for example, if the selected calendar is not one of the OptionalCalendars, the results of the methods and the values of the properties are undefined.

Alternate sort orders

Some cultures support more than one sort order. For example:

  • The Spanish (Spain) culture has two sort orders: the default international sort order, and the traditional sort order. When you instantiate a CultureInfo object with the es-ES culture name, the international sort order is used. When you instantiate a CultureInfo object with the es-ES-tradnl culture name, the traditional sort order is used.

  • The zh-CN (Chinese (Simplified, PRC)) culture supports two sort orders: by pronunciation (the default) and by stroke count. When you instantiate a CultureInfo object with the zh-CN culture name, the default sort order is used. When you instantiate a CultureInfo object with a local identifier of 0x00020804, strings are sorted by stroke count.

The following table lists the cultures that support alternate sort orders and the identifiers for the default and alternate sort orders.

Culture name Culture Default sort name and identifier Alternate sort name and identifier
es-ES Spanish (Spain) International: 0x00000C0A Traditional: 0x0000040A
zh-TW Chinese (Taiwan) Stroke Count: 0x00000404 Bopomofo: 0x00030404
zh-CN Chinese (PRC) Pronunciation: 0x00000804 Stroke Count: 0x00020804
zh-HK Chinese (Hong Kong SAR) Stroke Count: 0x00000c04 Stroke Count: 0x00020c04
zh-SG Chinese (Singapore) Pronunciation: 0x00001004 Stroke Count: 0x00021004
zh-MO Chinese (Macao SAR) Pronunciation: 0x00001404 Stroke Count: 0x00021404
ja-JP Japanese (Japan) Default: 0x00000411 Unicode: 0x00010411
ko-KR Korean (Korea) Default: 0x00000412 Korean Xwansung - Unicode: 0x00010412
de-DE German (Germany) Dictionary: 0x00000407 Phone Book Sort DIN: 0x00010407
hu-HU Hungarian (Hungary) Default: 0x0000040e Technical Sort: 0x0001040e
ka-GE Georgian (Georgia) Traditional: 0x00000437 Modern Sort: 0x00010437

The current culture and UWP apps

In Universal Windows Platform (UWP) apps, the CurrentCulture and CurrentUICulture properties are read-write, just as they are in .NET Framework and .NET Core apps. However, UWP apps recognize a single culture. The CurrentCulture and CurrentUICulture properties map to the first value in the Windows.ApplicationModel.Resources.Core.ResourceManager.DefaultContext.Languages collection.

In .NET apps, the current culture is a per-thread setting, and the CurrentCulture and CurrentUICulture properties reflect the culture and UI culture of the current thread only. In UWP apps, the current culture maps to the Windows.ApplicationModel.Resources.Core.ResourceManager.DefaultContext.Languages collection, which is a global setting. Setting the CurrentCulture or CurrentUICulture property changes the culture of the entire app; culture cannot be set on a per-thread basis.