Home > Articles > Programming > Windows Programming

This chapter is from the book

Combining Cultures

One of the common reasons for wanting to create a custom culture is to create a combination of language and region in which the language and the region are known but have not yet been paired. The benefit of creating such a combined culture is that you can refer to a language and region that is important to your target market but that is not defined in the .NET Framework or operating system. Table 11.1 shows some example combinations, with "es-US" (Spanish (United States)) being one of the most requested. The CultureAndRegionInfoBuilderHelper class (included with the source code for this book) performs the drudgery of combining two cultures and can be used like this:

CultureAndRegionInfoBuilder builder =
    CultureAndRegionInfoBuilderHelper.
    CreateCultureAndRegionInfoBuilder(
    new CultureInfo("es-ES"), new RegionInfo("en-US"));

builder.Register();

The CultureAndRegionInfoBuilderHelper.CreateCultureAndRegion InfoBuilder method creates a new CultureAndRegionInfoBuilder from a "language"CultureInfo ("es-ES") and a "region"RegionInfo ("en-US"). The new object is then used either to Register the culture or to Save the culture. The Create-CultureAndRegionInfoBuilder has various overloads to accept variations on the same theme.

The process of "splicing together" two cultures is not as straightforward as you might think. Table 11.6 shows the CultureAndRegionInfoBuilder properties, and the source of their values and their actual values using the Spanish (United States) example.

Table 11.6. CultureAndRegionInfoBuilder Properties and Values for the Spanish (United States) Culture

The new culture is a combination of the language and the region, but many of the names used in the culture need to be localized. Whereas the new culture uses the calendar for the region, the names of the days and months of that calendar must be in the specified language (i.e., Spanish), and not the language from which the calendar has come (i.e., English). The LoadDataFromRegionInfo method is very helpful in this scenario, but the LoadDataFromCultureInfo is less so. The CultureAndRegionInfoBuilderHelper.CreateCultureAndRegionInfoBuilder method is shown here:

public static CultureAndRegionInfoBuilder
    CreateCultureAndRegionInfoBuilder(
    CultureInfo languageCultureInfo,
    RegionInfo regionInfo,
    string cultureName)
{
    if (cultureName == null || cultureName == String.Empty)
        // the culture name is blank so construct a default name
        cultureName =
            languageCultureInfo.TwoLetterISOLanguageName + "-" +
            regionInfo.TwoLetterISORegionName;

    CultureInfo languageNeutralCultureInfo =
        GetNeutralCulture(languageCultureInfo);

    CultureInfo regionCultureInfo = new CultureInfo(regionInfo.Name);

    CultureAndRegionInfoBuilder builder =
        new CultureAndRegionInfoBuilder(
        cultureName, CultureAndRegionModifiers.None);

    builder.LoadDataFromCultureInfo(regionCultureInfo);
    builder.LoadDataFromRegionInfo(regionInfo);

    builder.Parent = languageNeutralCultureInfo;

    builder.CompareInfo = languageCultureInfo.CompareInfo;
    builder.TextInfo = languageCultureInfo.TextInfo;

    builder.IetfLanguageTag = cultureName;

    builder.RegionNativeName = GetNativeRegionName(
        regionInfo, languageCultureInfo);

    builder.CultureEnglishName =
        languageNeutralCultureInfo.EnglishName + " (" +
        regionInfo.EnglishName + ")";

    builder.CultureNativeName =
        languageNeutralCultureInfo.NativeName + " (" +
        builder.RegionNativeName + ")";

    builder.CurrencyNativeName = GetNativeCurrencyName(
        regionInfo, languageCultureInfo);

    // copy the native month and day names
    DateTimeFormatInfo builderDtfi =
        builder.GregorianDateTimeFormat;

    DateTimeFormatInfo languageDtfi =
        languageCultureInfo.DateTimeFormat;

    builderDtfi.AbbreviatedDayNames =
        languageDtfi.AbbreviatedDayNames;

    builderDtfi.AbbreviatedMonthGenitiveNames =
        languageDtfi.AbbreviatedMonthGenitiveNames;

    builderDtfi.AbbreviatedMonthNames =
        languageDtfi.AbbreviatedMonthNames;

    builderDtfi.DayNames = languageDtfi.DayNames;

    builderDtfi.MonthGenitiveNames = languageDtfi.MonthGenitiveNames;

    builderDtfi.MonthNames = languageDtfi.MonthNames;

    builderDtfi.ShortestDayNames = languageDtfi.ShortestDayNames;

    builder.KeyboardLayoutId =
        languageNeutralCultureInfo.KeyboardLayoutId;

    builder.ThreeLetterISOLanguageName =
        languageNeutralCultureInfo.ThreeLetterISOLanguageName;

    builder.ThreeLetterWindowsLanguageName =
        languageNeutralCultureInfo.ThreeLetterWindowsLanguageName;

    builder.TwoLetterISOLanguageName =
        languageNeutralCultureInfo.TwoLetterISOLanguageName;

    return builder;
}

Two methods, GetNativeRegionName and GetNativeCurrencyName, make an attempt to get the native versions of the region name and currency name, respectively. They both work by changing the CurrentCulture to the language for which a native name is required (i.e., Spanish) and then getting the property. If the appropriate .NET Framework Language Pack is installed, the correct native name will be returned; otherwise, the native name will be the English name and you will need to manually update these values before registering or saving the culture. The GetNativeCurrencyName method is shown here (the GetNativeRegionName is identical, except for the name of the property and the fact that it attempts to get the region's DisplayName because DisplayName is localized).

protected static string GetNativeCurrencyName(
    RegionInfo regionInfo, CultureInfo languageCultureInfo)
{
    string nativeName;
    CultureInfo oldCultureInfo =
        Thread.CurrentThread.CurrentUICulture;
    try
    {
        // attempt to change the UI culture
        Thread.CurrentThread.CurrentUICulture = languageCultureInfo;
        // get the new name (if a corresponding language pack is
        // installed then this yields a true native name)
        nativeName = regionInfo.CurrencyNativeName;
    }
    catch (Exception)
    {
        // it was not possible to change the UI culture
        nativeName = regionInfo.CurrencyNativeName;
    }
    finally
    {
        Thread.CurrentThread.CurrentUICulture = oldCultureInfo;
    }
    return nativeName;
}
  • + Share This
  • 🔖 Save To Your Account