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

Posted

技术标签:

【中文标题】在不了解文化的情况下将月份名称从一种语言转换为另一种语言?【英文标题】:Convert month name from one language to another without knowing the Culture? 【发布时间】:2021-04-09 16:04:50 【问题描述】:

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

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

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

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

【问题讨论】:

一旦有了数字,您就知道如何从中获取“八月”,所以您真正的问题是,当您不知道月份名称是什么语言时,如何从月份名称中获取月份编号在,对吧?你有它可能来自的文化列表吗? @IanMercer 不幸的是,我没有这样的文化列表。至少不包括所有可能的语言。-) var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures); 【参考方案1】:

在各种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, x => StringComparer.OrdinalIgnoreCase.Equals(x, 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, 0, out dt) || DateTime.TryParseExact(month, "MMMM", spanish, 0, 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&lt;&gt; 并保存CultureName,以便可以发现月份名称的语言。这 GetMonthNumbers(monthName) 现在返回一个 (int MonthNumber, string CultureName)[],一个匿名值类型数组。可以明明白白拿第一个就开心的过日子,也可以查一下有没有多个不同的MonthNumbers。

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, (int MonthNumber, 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, 1).ToString("MMMM", CultureInfo.GetCultureInfo("en-US"));

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

请注意,有许多相似的文化,所以numbers 会很大(例如,仅意大利就有 5 种文化,搜索 agosto 会返回 52 种不同文化的月份 agosto.

【讨论】:

哇,Array.FindIndex 两次听起来效率低下。为什么不只是Array.Find() 你可能是指Array.IndexOf.... 使用Array.Find 如果我寻找 Agosto 它将返回 Agosto ...@987654344 @ 不支持忽略大小写比较器。如果需要高性能的解决方案,可以将月份名称缓存在字典中。但它变得更加复杂。 好的。但是为什么不将结果缓存到本地呢? @Charlieface 第二个 Array.FindIndex 在第一个失败时完成,并且在 spanish 月份名称上完成,而第一个在 italian上完成> 月份名称 @PeterCo 是的,这是可能的,但正如我在代码中所写的那样,存在一些名称冲突......例如 listopad。碰撞的完整列表是:Hlakola、listopad、Mopitlo、Nhlangula、Nyakanga、Phupu

以上是关于在不了解文化的情况下将月份名称从一种语言转换为另一种语言?的主要内容,如果未能解决你的问题,请参考以下文章

将字符串从一种编码转换为另一种编码

无法从一种迭代器类型转换为另一种,但两者完全相同

通过重新解释原始字节从一种类型的 numpy 数组转换为另一种类型

通过命令行Pandoc 来转换文件

使用linux工具将行从一种格式转换为另一种格式[关闭]

CSS动画