Skip to content

Commit

Permalink
Move unit abbreviation strings into culture-specific resource files (#…
Browse files Browse the repository at this point in the history
…1210)

Fixes #1126 
Refs #1238 

A quick draft to move the unit abbreviations into resource files. I also want to move methods for getting culture-specific abbreviations to the individual quantities.

This should:
- Reduce initialization time by removing the MapGeneratedLocalizations methods (TODO)
- Reduce in-memory footprint by only loading a requested culture
- Allow formatting to use methods on the quantities themselves
  - Which should allow for better extensibility as well
- Remove the unit abbreviations cache (deprecate)

### TODOs
- [ ] Test fallback to invariant culture `export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT='1'` #1238 

---------

Co-authored-by: Andreas Gullberg Larsen <[email protected]>
  • Loading branch information
tmilnthorp and angularsen authored Jun 18, 2023
1 parent 4a02911 commit 37ada11
Show file tree
Hide file tree
Showing 282 changed files with 3,901 additions and 4,652 deletions.
23 changes: 2 additions & 21 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private void GenerateStaticConstructor()
if (baseUnits == null)
{
Writer.WL($@"
new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", BaseUnits.Undefined),");
new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", BaseUnits.Undefined, ""{_quantity.Name}""),");
}
else
{
Expand All @@ -152,7 +152,7 @@ private void GenerateStaticConstructor()
}.Where(str => str != null));

Writer.WL($@"
new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", new BaseUnits({baseUnitsCtorArgs})),");
new UnitInfo<{_unitEnumName}>({_unitEnumName}.{unit.SingularName}, ""{unit.PluralName}"", new BaseUnits({baseUnitsCtorArgs}), ""{_quantity.Name}""),");
}
}

Expand Down Expand Up @@ -359,25 +359,6 @@ internal static void RegisterDefaultConversions(UnitConverter unitConverter)
Writer.WL($@"
}}
internal static void MapGeneratedLocalizations(UnitAbbreviationsCache unitAbbreviationsCache)
{{");
foreach(Unit unit in _quantity.Units)
{
foreach(Localization localization in unit.Localization)
{
// All units must have a unit abbreviation, so fallback to "" for units with no abbreviations defined in JSON
var abbreviationParams = localization.Abbreviations.Any() ?
string.Join(", ", localization.Abbreviations.Select(abbr => $@"""{abbr}""")) :
$@"""""";

Writer.WL($@"
unitAbbreviationsCache.PerformAbbreviationMapping({_unitEnumName}.{unit.SingularName}, new CultureInfo(""{localization.Culture}""), false, {unit.AllowAbbreviationLookup.ToString().ToLower()}, new string[]{{{abbreviationParams}}});");
}
}

Writer.WL($@"
}}
/// <summary>
/// Get unit abbreviation string.
/// </summary>
Expand Down
22 changes: 9 additions & 13 deletions CodeGen/Generators/UnitsNetGen/StaticQuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace UnitsNet
/// <summary>
/// Dynamically parse or construct quantities when types are only known at runtime.
/// </summary>
public static partial class Quantity
public partial class Quantity
{
/// <summary>
/// All QuantityInfo instances mapped by quantity name that are present in UnitsNet by default.
Expand Down Expand Up @@ -63,7 +63,7 @@ public static IQuantity FromQuantityInfo(QuantityInfo quantityInfo, QuantityValu
Writer.WL(@"
_ => throw new ArgumentException($""{quantityInfo.Name} is not a supported quantity."")
};
}
}
/// <summary>
/// Try to dynamically construct a quantity.
Expand All @@ -74,26 +74,22 @@ public static IQuantity FromQuantityInfo(QuantityInfo quantityInfo, QuantityValu
/// <returns><c>True</c> if successful with <paramref name=""quantity""/> assigned the value, otherwise <c>false</c>.</returns>
public static bool TryFrom(QuantityValue value, Enum? unit, [NotNullWhen(true)] out IQuantity? quantity)
{
switch (unit)
quantity = unit switch
{");
foreach (var quantity in _quantities)
{
var quantityName = quantity.Name;
var unitTypeName = $"{quantityName}Unit";
var unitValue = unitTypeName.ToCamelCase();
Writer.WL($@"
case {unitTypeName} {unitValue}:
quantity = {quantityName}.From(value, {unitValue});
return true;");
{unitTypeName} {unitValue} => {quantityName}.From(value, {unitValue}),");
}

Writer.WL(@"
default:
{
quantity = default(IQuantity);
return false;
}
}
_ => null
};
return quantity is not null;
}
/// <summary>
Expand Down Expand Up @@ -125,7 +121,7 @@ public static bool TryParse(IFormatProvider? formatProvider, Type quantityType,
Writer.WL(@"
_ => false
};
}
}
internal static IEnumerable<Type> GetQuantityTypes()
{");
Expand Down
51 changes: 51 additions & 0 deletions CodeGen/Generators/UnitsNetGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using CodeGen.Generators.UnitsNetGen;
Expand Down Expand Up @@ -71,6 +72,7 @@ public static void Generate(string rootDir, Quantity[] quantities, QuantityNameT
Log.Information("");
GenerateIQuantityTests(quantities, $"{testProjectDir}/GeneratedCode/IQuantityTests.g.cs");
GenerateStaticQuantity(quantities, $"{outputDir}/Quantity.g.cs");
GenerateResourceFiles(quantities, $"{outputDir}/Resources");

var unitCount = quantities.SelectMany(q => q.Units).Count();
Log.Information("");
Expand Down Expand Up @@ -130,5 +132,54 @@ private static void GenerateStaticQuantity(Quantity[] quantities, string filePat
File.WriteAllText(filePath, content);
Log.Information("✅ Quantity.g.cs");
}

private static void GenerateResourceFiles(Quantity[] quantities, string resourcesDirectory)
{
foreach(var quantity in quantities)
{
var cultures = new HashSet<string>();

foreach(Unit unit in quantity.Units)
{
foreach(Localization localization in unit.Localization)
{
cultures.Add(localization.Culture);
}
}

foreach(var culture in cultures)
{
var fileName = culture.Equals("en-US", System.StringComparison.InvariantCultureIgnoreCase) ?
$"{resourcesDirectory}/{quantity.Name}.restext" :
$"{resourcesDirectory}/{quantity.Name}.{culture}.restext";

using var writer = File.CreateText(fileName);

foreach(Unit unit in quantity.Units)
{
foreach(Localization localization in unit.Localization)
{
if(localization.Culture == culture)
{
if(localization.Abbreviations.Any())
{
writer.Write($"{unit.PluralName}=");

for(int i = 0; i < localization.Abbreviations.Length; i++)
{
writer.Write($"{localization.Abbreviations[i]}");

if(i != localization.Abbreviations.Length - 1)
writer.Write(",");
}

writer.WriteLine();
}
}
}
}
}
}
}
}
}
37 changes: 22 additions & 15 deletions UnitsNet/BaseUnits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,28 @@ public override int GetHashCode()
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();

string GetDefaultAbbreviation<TUnitType>(TUnitType? unitOrNull) where TUnitType : struct, Enum => unitOrNull is { } unit
? UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit)
: "N/A";

sb.AppendFormat("[Length]: {0}, ", GetDefaultAbbreviation(Length));
sb.AppendFormat("[Mass]: {0}, ", GetDefaultAbbreviation(Mass));
sb.AppendFormat("[Time]: {0}, ", GetDefaultAbbreviation(Time));
sb.AppendFormat("[Current]: {0}, ", GetDefaultAbbreviation(Current));
sb.AppendFormat("[Temperature]: {0}, ", GetDefaultAbbreviation(Temperature));
sb.AppendFormat("[Amount]: {0}, ", GetDefaultAbbreviation(Amount));
sb.AppendFormat("[LuminousIntensity]: {0}", GetDefaultAbbreviation(LuminousIntensity));

return sb.ToString();
if(!Equals(Undefined))
{
var sb = new StringBuilder();

string GetDefaultAbbreviation<TUnitType>(TUnitType? unitOrNull) where TUnitType : struct, Enum => unitOrNull is { } unit
? UnitAbbreviationsCache.Default.GetDefaultAbbreviation(unit)
: "N/A";

sb.AppendFormat("[Length]: {0}, ", GetDefaultAbbreviation(Length));
sb.AppendFormat("[Mass]: {0}, ", GetDefaultAbbreviation(Mass));
sb.AppendFormat("[Time]: {0}, ", GetDefaultAbbreviation(Time));
sb.AppendFormat("[Current]: {0}, ", GetDefaultAbbreviation(Current));
sb.AppendFormat("[Temperature]: {0}, ", GetDefaultAbbreviation(Temperature));
sb.AppendFormat("[Amount]: {0}, ", GetDefaultAbbreviation(Amount));
sb.AppendFormat("[LuminousIntensity]: {0}", GetDefaultAbbreviation(LuminousIntensity));

return sb.ToString();
}
else
{
return "Undefined";
}
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions UnitsNet/CustomCode/Quantities/Length.extra.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ public string ToString(IFormatProvider? cultureInfo)
{
cultureInfo = cultureInfo ?? CultureInfo.CurrentCulture;

var footUnit = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(LengthUnit.Foot, cultureInfo);
var inchUnit = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(LengthUnit.Inch, cultureInfo);
var footUnit = Length.GetAbbreviation(LengthUnit.Foot, cultureInfo);
var inchUnit = Length.GetAbbreviation(LengthUnit.Inch, cultureInfo);

// Note that it isn't customary to use fractions - one wouldn't say "I am 5 feet and 4.5 inches".
// So inches are rounded when converting from base units to feet/inches.
Expand Down
4 changes: 2 additions & 2 deletions UnitsNet/CustomCode/Quantities/Mass.extra.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ public string ToString(IFormatProvider? cultureInfo)
{
cultureInfo = cultureInfo ?? CultureInfo.CurrentCulture;

var stoneUnit = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(MassUnit.Stone, cultureInfo);
var poundUnit = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(MassUnit.Pound, cultureInfo);
var stoneUnit = Mass.GetAbbreviation(MassUnit.Stone, cultureInfo);
var poundUnit = Mass.GetAbbreviation(MassUnit.Pound, cultureInfo);

// Note that it isn't customary to use fractions - one wouldn't say "I am 11 stone and 4.5 pounds".
// So pounds are rounded here.
Expand Down
Loading

0 comments on commit 37ada11

Please sign in to comment.