Port Languages enum

This commit is contained in:
Matt Nadareski
2026-06-14 13:23:08 -04:00
parent b8601b2b8a
commit 4c6fd26b22
4 changed files with 1975 additions and 0 deletions

View File

@@ -425,6 +425,175 @@ namespace SabreTools.RedumpLib.Test.RedumpInfo
#endregion
#region Language
/// <summary>
/// Check that every Language has a long name provided
/// </summary>
/// <param name="language">Language value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateLanguageTestData))]
public void Language_LongName(Language? language, bool expectNull)
{
var actual = language.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every Language has a short name provided
/// </summary>
/// <param name="language">Language value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateLanguageTestData))]
public void Language_ShortName(Language? language, bool expectNull)
{
var actual = language.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every Language can be mapped from a string
/// </summary>
/// <param name="lang">Language value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateLanguageTestData))]
public void Language_ToLanguage(Language? lang, bool expectNull)
{
string? twoLetterCode = lang.TwoLetterCode();
string? threeLetterCode = lang.ThreeLetterCode();
string? threeLetterCodeAlt = lang.ThreeLetterCodeAlt();
var actualTwoLetterCode = twoLetterCode.ToLanguage();
var actualThreeLetterCode = threeLetterCode.ToLanguage();
var actualThreeLetterCodeAlt = threeLetterCodeAlt.ToLanguage();
if (expectNull)
{
Assert.Null(actualTwoLetterCode);
Assert.Null(actualThreeLetterCode);
Assert.Null(actualThreeLetterCodeAlt);
}
else
{
Assert.Equal(lang, actualThreeLetterCode);
// Not guaranteed to exist
if (twoLetterCode is not null)
Assert.Equal(lang, actualTwoLetterCode);
if (threeLetterCodeAlt is not null)
Assert.Equal(lang, actualThreeLetterCodeAlt);
}
}
/// <summary>
/// Ensure that every Language that has a standard/bibliographic ISO 639-2 code is unique
/// </summary>
[Fact]
public void Language_ThreeLetterCode_NoDuplicates()
{
var fullLanguages = Enum.GetValues<Language>().Cast<Language?>().ToList();
var filteredLanguages = new Dictionary<string, Language?>();
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
var code = language.ThreeLetterCode();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
if (filteredLanguages.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredLanguages[code] = language;
totalCount++;
}
Assert.Equal(totalCount, filteredLanguages.Count);
}
/// <summary>
/// Ensure that every Language that has a terminology ISO 639-2 code is unique
/// </summary>
[Fact]
public void Language_ThreeLetterCodeAlt_NoDuplicates()
{
var fullLanguages = Enum.GetValues<Language>().Cast<Language?>().ToList();
var filteredLanguages = new Dictionary<string, Language?>();
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
var code = language.ThreeLetterCodeAlt();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
if (filteredLanguages.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredLanguages[code] = language;
totalCount++;
}
Assert.Equal(totalCount, filteredLanguages.Count);
}
/// <summary>
/// Ensure that every Language that has an ISO 639-1 code is unique
/// </summary>
[Fact]
public void Language_TwoLetterCode_NoDuplicates()
{
var fullLanguages = Enum.GetValues<Language>().Cast<Language?>().ToList();
var filteredLanguages = new Dictionary<string, Language?>();
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
var code = language.TwoLetterCode();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
if (filteredLanguages.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredLanguages[code] = language;
totalCount++;
}
Assert.Equal(totalCount, filteredLanguages.Count);
}
/// <summary>
/// Generate a test set of Language values
/// </summary>
/// <returns>MemberData-compatible list of Language values</returns>
public static TheoryData<Language?, bool> GenerateLanguageTestData()
{
var testData = new TheoryData<Language?, bool>() { { null, true } };
foreach (Language? language in Enum.GetValues<Language>().Cast<Language?>())
{
testData.Add(language, false);
}
return testData;
}
#endregion
#region Media Type
/// <summary>

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SabreTools.RedumpLib.RedumpInfo.Data;
namespace SabreTools.RedumpLib.RedumpInfo.Converters
{
/// <summary>
/// Serialize Language enum values
/// </summary>
public class LanguageConverter : JsonConverter<Language?[]>
{
public override bool CanRead { get { return true; } }
public override Language?[] ReadJson(JsonReader reader, Type objectType, Language?[]? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
// If we have a value already, don't overwrite it
if (hasExistingValue)
return existingValue ?? [];
// Get the current depth for checking
int currentDepth = reader.Depth;
// Read the array while it exists
List<Language> languages = [];
while (reader.Read() && reader.Depth > currentDepth)
{
if (reader.Value is not string value)
continue;
Language? lang = value.ToLanguage();
if (lang is not null)
languages.Add(lang.Value);
}
return [.. languages];
}
public override void WriteJson(JsonWriter writer, Language?[]? value, JsonSerializer serializer)
{
if (value is null)
return;
JArray array = [];
foreach (var val in value)
{
JToken t = JToken.FromObject(val.ShortName() ?? string.Empty);
array.Add(t);
}
array.WriteTo(writer);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1037,6 +1037,128 @@ namespace SabreTools.RedumpLib.RedumpInfo.Data
#endregion
#region Language
/// <summary>
/// Get the Redump longnames for each known language
/// </summary>
public static string? LongName(this Language language)
=> ((Language?)language).LongName();
/// <summary>
/// Get the Redump longnames for each known language
/// </summary>
public static string? LongName(this Language? language)
=> AttributeHelper<Language?>.GetHumanReadableAttribute(language)?.LongName;
/// <summary>
/// Get the Redump shortnames for each known language
/// </summary>
public static string? ShortName(this Language language)
=> ((Language?)language).ShortName();
/// <summary>
/// Get the Redump shortnames for each known language
/// </summary>
public static string? ShortName(this Language? language)
{
// Some languages need to use the alternate code instead
#pragma warning disable IDE0072 // Add missing cases
return language switch
{
Language.Albanian
or Language.Armenian
or Language.Icelandic
or Language.Macedonian
or Language.Romanian
or Language.Slovak => language.ThreeLetterCodeAlt(),
_ => language.ThreeLetterCode(),
};
#pragma warning restore IDE0072 // Add missing cases
}
/// <summary>
/// Get the Language enum value for a given string
/// </summary>
/// <param name="lang">String value to convert</param>
/// <returns>Language represented by the string, if possible</returns>
public static Language? ToLanguage(this string? lang)
{
// No value means no match
if (lang is null || lang.Length == 0)
return null;
lang = lang.ToLowerInvariant();
var languages = (Language[])Enum.GetValues(typeof(Language));
// Check ISO 639-1 codes
int index = Array.FindIndex(languages, l => lang == l.TwoLetterCode());
if (index > -1)
return languages[index];
// Check standard ISO 639-2 codes
index = Array.FindIndex(languages, l => lang == l.ThreeLetterCode());
if (index > -1)
return languages[index];
// Check alternate ISO 639-2 codes
index = Array.FindIndex(languages, l => lang == l.ThreeLetterCodeAlt());
if (index > -1)
return languages[index];
return null;
}
/// <summary>
/// Get the ISO 639-2 code for each known language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
public static string? ThreeLetterCode(this Language language)
=> (AttributeHelper<Language>.GetHumanReadableAttribute(language) as LanguageAttribute)?.ThreeLetterCode;
/// <summary>
/// Get the ISO 639-2 code for each known language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
public static string? ThreeLetterCode(this Language? language)
=> (AttributeHelper<Language?>.GetHumanReadableAttribute(language) as LanguageAttribute)?.ThreeLetterCode;
/// <summary>
/// Get the ISO 639-2 alternate code for each known language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
public static string? ThreeLetterCodeAlt(this Language language)
=> (AttributeHelper<Language>.GetHumanReadableAttribute(language) as LanguageAttribute)?.ThreeLetterCodeAlt;
/// <summary>
/// Get the ISO 639-2 alternate code for each known language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
public static string? ThreeLetterCodeAlt(this Language? language)
=> (AttributeHelper<Language?>.GetHumanReadableAttribute(language) as LanguageAttribute)?.ThreeLetterCodeAlt;
/// <summary>
/// Get the ISO 639-1 code for each known language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
public static string? TwoLetterCode(this Language language)
=> (AttributeHelper<Language>.GetHumanReadableAttribute(language) as LanguageAttribute)?.TwoLetterCode;
/// <summary>
/// Get the ISO 639-1 code for each known language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
public static string? TwoLetterCode(this Language? language)
=> (AttributeHelper<Language?>.GetHumanReadableAttribute(language) as LanguageAttribute)?.TwoLetterCode;
#endregion
#region Media Type
/// <summary>