A few of you have asked how to create something like es-US.  (Spanish in the US).  Here's a start of how you might do something like that.  This code has many faults and concerns, so you should look at how these work for your application. 

  • I didn't include any error handling and made assumptions that might fail, cause strange data, or even throw exceptions in some cases.
  • This grabs formatting and preference type information from one locale and strings from the other.  Its quite likely that this combination might not be appropriate in many cases.
  • This is an EXAMPLE.  It could generate gibberish for your example.  Hopefully in that case at least this'll get you started.

To run this you need the .Net Framework v2.0 (Whidbey) Beta 2.  Save this as something like CombineCultures.cs and then do "csc /r:sysglobl.dll CombineCultures.cs" to compile it.  Run it on the command line, passing a culture to use for the target language and then the culture to use for the target location.  "CombineCultures es-ES en-US" for example.

It'll ask you for the native country name in this language and the native currency name.  It'll then create a culture using the language from the first culture and the region name from the 2nd culture, so something like es-US.  This program will go ahead and Register the custom culture, use Unregister() to remove it.

Hope this is interesting, and remember, it might make gibberish and have tons of errors, so feel free to use this as a starting place, but make sure you address the actual cases for your situation.

using System;
using System.Globalization;

public class CombineCultures
{
    static void Main(string[] args)
    {
        if (args.Length != 2)
        {
            Console.WriteLine("Usage: CombineCultures [language culture] [location culture]");
            return;
        }

        String strLanguage = args[0];
        String strLocation = args[1];

        CultureInfo ciLanguage = new CultureInfo(strLanguage);
        CultureInfo ciLocation = new CultureInfo(strLocation);
        RegionInfo riLanguage = new RegionInfo(strLanguage);
        RegionInfo riLocation = new RegionInfo(strLocation);

        String strNewCulture = ciLanguage.TwoLetterISOLanguageName + "-" + riLocation.TwoLetterISORegionName;
        Console.WriteLine("Creating " + strNewCulture);
        Console.WriteLine("WARNING: Resulting Culture will be a bunch of mixed up bad guesses!!!");
        Console.WriteLine();

        // Try to use a template (won't help much)
        CultureAndRegionInfoBuilder cb = new CultureAndRegionInfoBuilder(strNewCulture, CultureAndRegionModifiers.None);
        cb.LoadDataFromRegionInfo(riLocation);
        cb.LoadDataFromCultureInfo(ciLocation);

        // Fix stuff
        cb.IetfLanguageTag = strNewCulture;
        cb.Parent = ciLocation;

        // Need to fix the language names
        cb.ThreeLetterISOLanguageName = ciLanguage.ThreeLetterISOLanguageName;
        cb.TwoLetterISOLanguageName = ciLanguage.TwoLetterISOLanguageName;
        cb.ThreeLetterWindowsLanguageName = ciLanguage.ThreeLetterWindowsLanguageName;
       
        // Need the Region Name in the current Language
        Console.Write("Native Region Name: ");
        cb.RegionNativeName = Console.ReadLine();
        Console.WriteLine(cb.RegionNativeName);

        // Fix the culture name(s)
        string strEnglishLanguageName = ciLanguage.EnglishName.Substring(0,ciLanguage.EnglishName.IndexOf("(")-1);
        string strNativeLanguageName = ciLanguage.NativeName.Substring(0,ciLanguage.NativeName.IndexOf("(")-1); ;
        cb.CultureEnglishName = strEnglishLanguageName + " (" + cb.RegionEnglishName + ")";
        cb.CultureNativeName = strNativeLanguageName + " (" + cb.RegionNativeName + ")";
        Console.WriteLine("English Culture Name: " + cb.CultureEnglishName);
        Console.WriteLine("Native Culture Name: " + cb.CultureNativeName);

        // Currency Name
        Console.Write("Native Currency Name: ");
        cb.CurrencyNativeName = Console.ReadLine();
        Console.WriteLine(cb.CurrencyNativeName);

        // Numbers
        cb.NumberFormat.PositiveInfinitySymbol = ciLanguage.NumberFormat.PositiveInfinitySymbol;
        cb.NumberFormat.NegativeInfinitySymbol = ciLanguage.NumberFormat.NegativeInfinitySymbol;
        cb.NumberFormat.NaNSymbol = ciLanguage.NumberFormat.NaNSymbol;

        // Copy Calendar Strings
        DateTimeFormatInfo dtfi = cb.GregorianDateTimeFormat;
        DateTimeFormatInfo dtfiLanguage = ciLanguage.DateTimeFormat;
        dtfiLanguage.Calendar = new GregorianCalendar();

        // Copy calendar data from that gregorian localized calendar
        dtfi.AbbreviatedDayNames = dtfiLanguage.AbbreviatedDayNames;
        dtfi.AbbreviatedMonthGenitiveNames = dtfiLanguage.AbbreviatedMonthGenitiveNames;
        dtfi.AbbreviatedMonthNames = dtfiLanguage.AbbreviatedMonthNames;
        dtfi.DayNames = dtfiLanguage.DayNames;
        dtfi.MonthGenitiveNames = dtfiLanguage.MonthGenitiveNames;
        dtfi.MonthNames = dtfiLanguage.MonthNames;
        dtfi.ShortestDayNames = dtfiLanguage.ShortestDayNames;

        // Warnings
        Console.WriteLine();
        Console.WriteLine("WARNING: Code pages were left as is for the " + strLocation + " culture.");
        Console.WriteLine("         That's probably good for data portability with others in the locale,");
        Console.WriteLine("         however these code pages may not store all characters of the target");
        Console.WriteLine("         language.  USE UNICODE ! :-)");
        Console.WriteLine("WARNING: We used " + strLocation + " Native Digits");
        Console.WriteLine("WARNING: We made assumptions about calendar strings and other strings and");
        Console.WriteLine("         they could all be very wrong!");

        // Save it
        cb.Save(strNewCulture + ".ldml");
        cb.Register();       
    }  
}