Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testable initialization of Gu.Localizalization.Culture #85

Merged
merged 8 commits into from
Sep 24, 2018
13 changes: 13 additions & 0 deletions Gu.Localization.Tests/Internals/CultureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,18 @@ public void TryGetRegion(string cultureName, string regionName)
Assert.AreEqual(true, Culture.TryGetRegion(culture, out var region));
Assert.AreEqual(regionName, region.TwoLetterISORegionName);
}

[TestCase]
public void TryGetInvalidCulture()
{
Assert.AreEqual(false, Culture.TryGet("foo", out var match));
}

[TestCase]
public void TryGetRegionForFakeCulture()
{
var fakeCulture = new CultureInfo("foo");
Assert.AreEqual(false, Culture.TryGetRegion(fakeCulture, out var region));
}
}
}
95 changes: 46 additions & 49 deletions Gu.Localization/Internals/Culture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,18 @@ namespace Gu.Localization
/// <summary> Utility class for <see cref="CultureInfo"/> </summary>
internal static class Culture
{
internal static readonly IReadOnlyList<CultureInfo> AllCultures =
internal static IReadOnlyList<CultureInfo> AllCultures =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't be readonly here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not necessary for functionality to be settable. But it makes testing TryGet possible.

CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(x => !IsInvariant(x))
.ToArray();
.Where(x => !IsInvariant(x))
.ToArray();

internal static readonly IReadOnlyDictionary<CultureInfo, RegionInfo> CultureRegionMap =
AllCultures
.Where(x => !x.IsNeutralCulture)
.ToDictionary(
x => x,
x => new RegionInfo(x.Name),
CultureInfoComparer.ByName);

internal static readonly IEnumerable<RegionInfo> AllRegions = CultureRegionMap.Values;

private static readonly Dictionary<string, CultureInfo> TwoLetterISOLanguageNameCultureMap =
AllCultures.Where(x => x.Name == x.TwoLetterISOLanguageName)
.ToDictionary(
x => x.TwoLetterISOLanguageName,
x => x,
StringComparer.OrdinalIgnoreCase);

private static readonly Dictionary<string, CultureInfo> NameCultureMap =
AllCultures.ToDictionary(
x => x.Name,
x => x,
StringComparer.OrdinalIgnoreCase);

private static readonly Dictionary<string, RegionInfo> NameRegionMap =
AllRegions
.ToDictionary(
x => x.Name,
x => x,
StringComparer.OrdinalIgnoreCase);

private static readonly Dictionary<CultureInfo, RegionInfo> NeutralCultureRegionMap =
AllCultures
.Where(x => x.IsNeutralCulture)
.Select(x => CreateSpecificCultureOrDefault(x)?.Name)
.Where(x => x != null && NameCultureMap.ContainsKey(x))
.Select(x => NameCultureMap[x])
.Distinct(CultureInfoComparer.ByTwoLetterIsoLanguageName)
.ToDictionary(
x => x.Parent,
x => CultureRegionMap[x],
CultureInfoComparer.ByTwoLetterIsoLanguageName);
internal static IReadOnlyList<RegionInfo> AllRegions
{
get
{
return AllCultures.Select(x => TryGetRegion(x, out var region) ? region : null).Where(x => x != null).ToList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LINQ in the getter is potentially expensive here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be the first time at least. The RegionInfo is cached by Microsoft, so it might not be that bad. Are you up for profiling this?

}
}

internal static bool TryGet(string name, out CultureInfo culture)
{
Expand All @@ -63,31 +29,62 @@ internal static bool TryGet(string name, out CultureInfo culture)
return false;
}

return NameCultureMap.TryGetValue(name, out culture) ||
TwoLetterISOLanguageNameCultureMap.TryGetValue(name, out culture);
culture = AllCultures.SingleOrDefault(c => NameEquals(c, name));
return culture != null;
}

internal static bool TryGetRegion(CultureInfo culture, out RegionInfo region)
{
if (culture == null)
if (culture == null || culture.IsInvariant())
{
region = null;
return false;
}

if (culture.IsNeutralCulture)
{
return NeutralCultureRegionMap.TryGetValue(culture, out region);
try
{
culture = CultureInfo.CreateSpecificCulture(culture.Name);
if (culture.IsNeutralCulture)
{
// See https://github.com/GuOrg/Gu.Localization/issues/82
region = null;
return false;
}
}
catch (CultureNotFoundException)
{
// This is only possible if the given culture is a mock culture.
region = null;
return false;
}
}

return NameRegionMap.TryGetValue(culture.Name, out region);
try
{
region = new RegionInfo(culture.Name);
return true;
}
catch (ArgumentException)
{
// Odd that this exception is not a CultureNotFoundException. But that's what Microsoft decided to throw
// https://referencesource.microsoft.com/#mscorlib/system/globalization/regioninfo.cs,86
region = null;
return false;
}
}

internal static bool NameEquals(CultureInfo first, CultureInfo other)
{
return CultureInfoComparer.ByName.Equals(first, other);
}

internal static bool NameEquals(CultureInfo culture, string name)
{
return StringComparer.OrdinalIgnoreCase.Equals(culture.Name, name);
}

internal static bool TwoLetterIsoLanguageNameEquals(CultureInfo first, CultureInfo other)
{
return CultureInfoComparer.ByTwoLetterIsoLanguageName.Equals(first, other);
Expand Down