在不了解文化的情况下将月份名称从一种语言转换为另一种语言?

问题描述

如何在不知道“agosto”是西班牙语还是意大利语的情况下将月份名称为“agosto”的字符串转换为英文翻译“August”?

我知道我用西班牙语获得了月份数字 8 的月份名称

Dim SpanishMonthName as String = Globalization.CultureInfo.GetCultureInfo("ES").DateTimeFormat.GetMonthName(8)

但是我怎样才能得到字符串“August”(英语中的第 8 个月名称)作为西班牙语或意大利语月份名称“agosto”的翻译?

解决方法

在各种 CultureInfo 内有月份名称(请参阅 someCulture.DateTimeFormat.MonthNames),因此您可以:

var italian = CultureInfo.GetCultureInfo("it-IT");
var spanish = CultureInfo.GetCultureInfo("es-ES");
var english = CultureInfo.GetCultureInfo("en-US");

string month = "agosto";

var italianMonthNames = italian.DateTimeFormat.MonthNames;
var spanishMonthNames = spanish.DateTimeFormat.MonthNames;

int ix = Array.FindIndex(italianMonthNames,x => StringComparer.OrdinalIgnoreCase.Equals(x,month));
if (ix == -1)
{
    ix = Array.FindIndex(spanishMonthNames,month));
}

// ix is 0 based,while months are 1 based
string englishMonth = ix != -1 ? english.DateTimeFormat.GetMonthName(ix + 1) : null;

您甚至可以尝试将一些委托给 .NET DateTime.ParseExact

var italian = CultureInfo.GetCultureInfo("it-IT");
var spanish = CultureInfo.GetCultureInfo("es-ES");
var english = CultureInfo.GetCultureInfo("en-US");

string month = "agosto";
string englishMonth = null;
DateTime dt;

if (DateTime.TryParseExact(month,"MMMM",italian,out dt) || DateTime.TryParseExact(month,spanish,out dt))
{
    englishMonth = dt.ToString("MMMM",english);
}

一般来说,至少有一个月在两种语言中具有不同的含义:listopad(十月或十一月,参见here)。完整列表是Hlakola、listopad、Mopitlo、Nhlangula、Nyakanga、Phupu

第一个使用 Dictionary<> 收集月份名称的版本:

public class MonthNameFinder
{
    private readonly IReadOnlyDictionary<string,int> MonthToNumber;

    public MonthNameFinder(params string[] cultures)
    {
        MonthToNumber = BuildDictionary(cultures.Select(x => CultureInfo.GetCultureInfo(x)));
    }

    public MonthNameFinder(params CultureInfo[] cultureInfos)
    {
        MonthToNumber = BuildDictionary(cultureInfos);
    }

    public MonthNameFinder(CultureTypes cultureTypes = CultureTypes.AllCultures)
    {
        MonthToNumber = BuildDictionary(CultureInfo.GetCultures(cultureTypes));
    }

    private static IReadOnlyDictionary<string,int> BuildDictionary(IEnumerable<CultureInfo> cultureInfos)
    {
        // Note that the comparer will always be wrong,sadly. Each culture has its comparer
        var dict = new Dictionary<string,int>(StringComparer.InvariantCultureIgnoreCase);

        foreach (var culture in cultureInfos)
        {
            var monthNames = culture.DateTimeFormat.MonthNames;

            for (int i = 0; i < monthNames.Length; i++)
            {
                string monthName = monthNames[i];

                int other;

                if (!dict.TryGetValue(monthName,out other))
                {
                    dict[monthName] = i + 1;
                }
                else if (other != i + 1)
                {
                    Debug.WriteLine($"Repeated month {monthName}: {i + 1} in {culture.Name} ({culture.DisplayName})");
                }
            }
        }

        return dict;
    }

    public int? GetMonthNumber(string monthName)
    {
        int monthNumber;

        if (MonthToNumber.TryGetValue(monthName,out monthNumber))
        {
            return monthNumber;
        }

        return null;
    }
}

像这样使用:

var mnf = new MonthNameFinder();
int? n = mnf.GetMonthNumber("agosto");

if (n != null)
{
    string name = new DateTime(1,n.Value,1).ToString("MMMM",CultureInfo.GetCultureInfo("en-US"));
}

(注意你应该缓存 mnf...它的构建成本可能相当高)​​

嗯...我不喜欢它...我有点 OC...而且我知道月份名称中存在一些冲突的简单事实困扰着我。

这是第二个版本,使用 ILookup<> 并保存甚至 CultureName,以便可以发现月份名称的语言。这 GetMonthNumbers(monthName) 现在返回一个 (int MonthNumber,string CultureName)[],一个匿名值类型的数组。可以明明拿第一个过得开心,也可以检查一下,看看有没有多个不同的MonthNumber

public class MonthNameFinder
{
    private readonly ILookup<string,(int MonthNumber,string CultureName)> MonthToNumber;

    public MonthNameFinder(params string[] cultures)
    {
        MonthToNumber = BuildLookup(cultures.Select(x => CultureInfo.GetCultureInfo(x)));
    }

    public MonthNameFinder(params CultureInfo[] cultureInfos)
    {
        MonthToNumber = BuildLookup(cultureInfos);
    }

    public MonthNameFinder(CultureTypes cultureTypes = CultureTypes.AllCultures)
    {
        MonthToNumber = BuildLookup(CultureInfo.GetCultures(cultureTypes));
    }

    private static ILookup<string,string CultureName)> BuildLookup(IEnumerable<CultureInfo> cultureInfos)
    {
        // Note that the comparer will always be wrong,sadly. Each culture has its comparer
        var lst = new List<(string Name,int MonthNumber,string CultureName)>();

        foreach (var culture in cultureInfos)
        {
            var monthNames = culture.DateTimeFormat.MonthNames;

            for (int i = 0; i < monthNames.Length; i++)
            {
                string monthName = monthNames[i];
                lst.Add((monthName,i + 1,culture.Name));
            }
        }

        return lst.OrderBy(x => x.Name)
            .ThenBy(x => x.MonthNumber)
            .ToLookup(x => x.Name,x => (x.MonthNumber,x.CultureName),StringComparer.InvariantCultureIgnoreCase);
    }

    public (int MonthNumber,string CultureName)[] GetMonthNumbers(string monthName)
    {
        return MonthToNumber[monthName].ToArray();
    }
}

像这样使用:

// This is an array of (MonthNumber,CultureName)
var mnf = new MonthNameFinder();

var numbers = mnf.GetMonthNumbers("agosto");

if (numbers.Length != 0)
{
    string monthName = new DateTime(1,numbers[0].MonthNumber,CultureInfo.GetCultureInfo("en-US"));
}

(即使在这里你也应该缓存 mnf...构建起来可能相当昂贵)

请注意,有许多相似的文化,因此 numbers 会很大(例如,仅对于意大利语就有 5 种文化,搜索 agosto 会返回 52 种不同的文化,月 agosto

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...