Skip to content

Commit 0d14d67

Browse files
committed
Re-implement Support multiple abbreviations (#658)
Merge conflict had to be ported from PowerShell to C#. UnitAbbreviationsCache now gets single entries with string arrays instead of multiple entries with single item arrays. This is an improvement, but should be functionally equivalent.
1 parent b1c30e0 commit 0d14d67

File tree

4 files changed

+240
-271
lines changed

4 files changed

+240
-271
lines changed

CodeGen/Generators/UnitsNetGenerator.cs

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ private static void AddPrefixUnits(Quantity quantity)
151151

152152
unitsToAdd.Add(new Unit
153153
{
154-
SingularName = $"{prefix}{StringExtensions.ToCamelCase(unit.SingularName)}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter"
155-
PluralName = $"{prefix}{StringExtensions.ToCamelCase(unit.PluralName)}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter"
154+
SingularName = $"{prefix}{unit.SingularName.ToCamelCase()}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter"
155+
PluralName = $"{prefix}{unit.PluralName.ToCamelCase()}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter"
156156
BaseUnits = null, // Can we determine this somehow?
157157
FromBaseToUnitFunc = $"({unit.FromBaseToUnitFunc}) / {prefixInfo.Factor}",
158158
FromUnitToBaseFunc = $"({unit.FromUnitToBaseFunc}) * {prefixInfo.Factor}",
@@ -166,60 +166,95 @@ private static void AddPrefixUnits(Quantity quantity)
166166

167167
private static Localization[] GetLocalizationForPrefixUnit(Unit unit, int prefixIndex, PrefixInfo prefixInfo, string quantityName)
168168
{
169-
return unit.Localization.Select(loc =>
169+
string[] GetUnitAbbreviationsForPrefix(Localization loc)
170170
{
171-
EnsureValidAbbreviationsWithPrefixes(quantityName, loc, unit);
172-
173-
// "k" + "m" => "km"
174-
// Correct count is ensured earlier
171+
// If no custom abbreviations are specified, prepend the default prefix to each unit abbreviation: kilo ("k") + meter ("m") => kilometer ("km")
175172
if (loc.AbbreviationsWithPrefixes == null || !loc.AbbreviationsWithPrefixes.Any())
176173
{
177-
var prefixedAbbreviation = $"{prefixInfo.Abbreviation}{loc.Abbreviations.First()}";
178-
return new Localization
179-
{
180-
Culture = loc.Culture,
181-
Abbreviations = new[] {prefixedAbbreviation},
182-
};
174+
string prefix = prefixInfo.Abbreviation;
175+
return loc.Abbreviations.Select(unitAbbreviation => $"{prefix}{unitAbbreviation}").ToArray();
183176
}
184177

185178
/*
186-
Example: For languages where you can't simply prepend "k" for kilo prefix, the prefix abbreviations must be explicitly defined
187-
with AbbreviationsWithPrefixes. This is an array of string|string[] so if there are two items in `Abbreviations` then
188-
there should be sub-arrays each of length 2.
189-
"Prefixes": [ "Nano", "Micro", "Milli" ],
190-
"Localization": [
191-
{
192-
"Culture": "en-US",
193-
"Abbreviations": [ "s", "sec", "secs", "second", "seconds" ]
194-
},
195-
{
196-
"Culture": "ru-RU",
197-
"Abbreviations": [ "с", "сек" ],
198-
"AbbreviationsWithPrefixes": [ ["нс", "нсек"], ["мкс", "мксек"], ["мс", "мсек"] ]
199-
}
179+
Prepend prefix to all abbreviations of a unit.
180+
Some languages, like Russian, you can't simply prepend "k" for kilo prefix, so the prefix abbreviations must be explicitly defined
181+
with AbbreviationsWithPrefixes.
182+
183+
Example 1 - Torque.Newtonmeter has only a single abbreviation in Russian, so AbbreviationsWithPrefixes is an array of strings mapped to each prefix
184+
185+
{
186+
"SingularName": "NewtonMeter",
187+
"PluralName": "NewtonMeters",
188+
"FromUnitToBaseFunc": "x",
189+
"FromBaseToUnitFunc": "x",
190+
"Prefixes": [ "Kilo", "Mega" ],
191+
"Localization": [
192+
{
193+
"Culture": "en-US",
194+
"Abbreviations": [ "N·m" ]
195+
},
196+
{
197+
"Culture": "ru-RU",
198+
"Abbreviations": [ "Н·м" ],
199+
"AbbreviationsWithPrefixes": [ "кН·м", "МН·м" ]
200+
}
201+
]
202+
},
203+
204+
Example 2 - Duration.Second has 3 prefixes and 2 abbreviations in Russian, so AbbreviationsWithPrefixes is an array of 3 items where each
205+
represents the unit abbreviations for that prefix - typically a variant of those in "Abbreviations", but the counts don't have to match.
206+
207+
{
208+
"SingularName": "Second",
209+
"PluralName": "Seconds",
210+
"BaseUnits": {
211+
"T": "Second"
212+
},
213+
"FromUnitToBaseFunc": "x",
214+
"FromBaseToUnitFunc": "x",
215+
"Prefixes": [ "Nano", "Micro", "Milli" ],
216+
"Localization": [
217+
{
218+
"Culture": "en-US",
219+
"Abbreviations": [ "s", "sec", "secs", "second", "seconds" ]
220+
},
221+
{
222+
"Culture": "ru-RU",
223+
"Abbreviations": [ "с", "сек" ],
224+
"AbbreviationsWithPrefixes": [ ["нс", "нсек"], ["мкс", "мксек"], ["мс", "мсек"] ]
225+
}
226+
]
227+
}
200228
*/
201-
string[] abbreviationsWithPrefixes;
202-
switch (loc.AbbreviationsWithPrefixes[prefixIndex].Type)
229+
230+
EnsureValidAbbreviationsWithPrefixes(loc, unit, quantityName);
231+
JToken abbreviationsForPrefix = loc.AbbreviationsWithPrefixes[prefixIndex];
232+
switch (abbreviationsForPrefix.Type)
203233
{
204234
case JTokenType.Array:
205-
abbreviationsWithPrefixes = loc.AbbreviationsWithPrefixes[prefixIndex].ToObject<string[]>();
206-
break;
235+
return abbreviationsForPrefix.ToObject<string[]>();
207236
case JTokenType.String:
208-
abbreviationsWithPrefixes = new[] {loc.AbbreviationsWithPrefixes[prefixIndex].ToObject<string>()};
209-
break;
237+
return new[] {abbreviationsForPrefix.ToObject<string>()};
210238
default:
211239
throw new NotSupportedException("Expect AbbreviationsWithPrefixes to be an array of strings or string arrays.");
212240
}
241+
}
242+
243+
Localization WithPrefixes(Localization loc)
244+
{
245+
string[] unitAbbreviationsForPrefix = GetUnitAbbreviationsForPrefix(loc);
213246

214247
return new Localization
215248
{
216249
Culture = loc.Culture,
217-
Abbreviations = abbreviationsWithPrefixes
250+
Abbreviations = unitAbbreviationsForPrefix,
218251
};
219-
}).ToArray();
252+
}
253+
254+
return unit.Localization.Select(WithPrefixes).ToArray();
220255
}
221256

222-
private static void EnsureValidAbbreviationsWithPrefixes(string quantityName, Localization localization, Unit unit)
257+
private static void EnsureValidAbbreviationsWithPrefixes(Localization localization, Unit unit, string quantityName)
223258
{
224259
if (localization.AbbreviationsWithPrefixes.Length > 0 &&
225260
localization.AbbreviationsWithPrefixes.Length != unit.Prefixes.Length)

CodeGen/Generators/UnitsNetWrcGenerator.cs

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
using System.IO;
77
using System.Linq;
88
using System.Text;
9-
using CodeGen.Generators.UnitsNetGen;
109
using CodeGen.Helpers;
1110
using CodeGen.JsonTypes;
1211
using Newtonsoft.Json;
12+
using Newtonsoft.Json.Linq;
1313
using Serilog;
1414
using QuantityGenerator = CodeGen.Generators.UnitsNetWrcGen.QuantityGenerator;
1515
using QuantityTypeGenerator = CodeGen.Generators.UnitsNetWrcGen.QuantityTypeGenerator;
@@ -134,8 +134,8 @@ private static void AddPrefixUnits(Quantity quantity)
134134

135135
unitsToAdd.Add(new Unit
136136
{
137-
SingularName = $"{prefix}{StringExtensions.ToCamelCase(unit.SingularName)}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter"
138-
PluralName = $"{prefix}{StringExtensions.ToCamelCase(unit.PluralName)}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter"
137+
SingularName = $"{prefix}{unit.SingularName.ToCamelCase()}", // "Kilo" + "NewtonPerMeter" => "KilonewtonPerMeter"
138+
PluralName = $"{prefix}{unit.PluralName.ToCamelCase()}", // "Kilo" + "NewtonsPerMeter" => "KilonewtonsPerMeter"
139139
BaseUnits = null, // Can we determine this somehow?
140140
FromBaseToUnitFunc = $"({unit.FromBaseToUnitFunc}) / {prefixInfo.Factor}",
141141
FromUnitToBaseFunc = $"({unit.FromUnitToBaseFunc}) * {prefixInfo.Factor}",
@@ -149,25 +149,95 @@ private static void AddPrefixUnits(Quantity quantity)
149149

150150
private static Localization[] GetLocalizationForPrefixUnit(Unit unit, int prefixIndex, PrefixInfo prefixInfo, string quantityName)
151151
{
152-
return unit.Localization.Select(loc =>
152+
string[] GetUnitAbbreviationsForPrefix(Localization loc)
153153
{
154-
EnsureValidAbbreviationsWithPrefixes(quantityName, loc, unit);
154+
// If no custom abbreviations are specified, prepend the default prefix to each unit abbreviation: kilo ("k") + meter ("m") => kilometer ("km")
155+
if (loc.AbbreviationsWithPrefixes == null || !loc.AbbreviationsWithPrefixes.Any())
156+
{
157+
string prefix = prefixInfo.Abbreviation;
158+
return loc.Abbreviations.Select(unitAbbreviation => $"{prefix}{unitAbbreviation}").ToArray();
159+
}
160+
161+
/*
162+
Prepend prefix to all abbreviations of a unit.
163+
Some languages, like Russian, you can't simply prepend "k" for kilo prefix, so the prefix abbreviations must be explicitly defined
164+
with AbbreviationsWithPrefixes.
165+
166+
Example 1 - Torque.Newtonmeter has only a single abbreviation in Russian, so AbbreviationsWithPrefixes is an array of strings mapped to each prefix
167+
168+
{
169+
"SingularName": "NewtonMeter",
170+
"PluralName": "NewtonMeters",
171+
"FromUnitToBaseFunc": "x",
172+
"FromBaseToUnitFunc": "x",
173+
"Prefixes": [ "Kilo", "Mega" ],
174+
"Localization": [
175+
{
176+
"Culture": "en-US",
177+
"Abbreviations": [ "N·m" ]
178+
},
179+
{
180+
"Culture": "ru-RU",
181+
"Abbreviations": [ "Н·м" ],
182+
"AbbreviationsWithPrefixes": [ "кН·м", "МН·м" ]
183+
}
184+
]
185+
},
155186
156-
// "k" + "m" => "km"
157-
// Correct count is ensured earlier
158-
var abbrev = loc.AbbreviationsWithPrefixes.Any()
159-
? loc.AbbreviationsWithPrefixes[prefixIndex]
160-
: $"{prefixInfo.Abbreviation}{loc.Abbreviations.First()}";
187+
Example 2 - Duration.Second has 3 prefixes and 2 abbreviations in Russian, so AbbreviationsWithPrefixes is an array of 3 items where each
188+
represents the unit abbreviations for that prefix - typically a variant of those in "Abbreviations", but the counts don't have to match.
189+
190+
{
191+
"SingularName": "Second",
192+
"PluralName": "Seconds",
193+
"BaseUnits": {
194+
"T": "Second"
195+
},
196+
"FromUnitToBaseFunc": "x",
197+
"FromBaseToUnitFunc": "x",
198+
"Prefixes": [ "Nano", "Micro", "Milli" ],
199+
"Localization": [
200+
{
201+
"Culture": "en-US",
202+
"Abbreviations": [ "s", "sec", "secs", "second", "seconds" ]
203+
},
204+
{
205+
"Culture": "ru-RU",
206+
"Abbreviations": [ "с", "сек" ],
207+
"AbbreviationsWithPrefixes": [ ["нс", "нсек"], ["мкс", "мксек"], ["мс", "мсек"] ]
208+
}
209+
]
210+
}
211+
*/
212+
213+
EnsureValidAbbreviationsWithPrefixes(loc, unit, quantityName);
214+
JToken abbreviationsForPrefix = loc.AbbreviationsWithPrefixes[prefixIndex];
215+
switch (abbreviationsForPrefix.Type)
216+
{
217+
case JTokenType.Array:
218+
return abbreviationsForPrefix.ToObject<string[]>();
219+
case JTokenType.String:
220+
return new[] {abbreviationsForPrefix.ToObject<string>()};
221+
default:
222+
throw new NotSupportedException("Expect AbbreviationsWithPrefixes to be an array of strings or string arrays.");
223+
}
224+
}
225+
226+
Localization WithPrefixes(Localization loc)
227+
{
228+
string[] unitAbbreviationsForPrefix = GetUnitAbbreviationsForPrefix(loc);
161229

162230
return new Localization
163231
{
164232
Culture = loc.Culture,
165-
Abbreviations = new[]{abbrev},
233+
Abbreviations = unitAbbreviationsForPrefix,
166234
};
167-
}).ToArray();
235+
}
236+
237+
return unit.Localization.Select(WithPrefixes).ToArray();
168238
}
169239

170-
private static void EnsureValidAbbreviationsWithPrefixes(string quantityName, Localization localization, Unit unit)
240+
private static void EnsureValidAbbreviationsWithPrefixes(Localization localization, Unit unit, string quantityName)
171241
{
172242
if (localization.AbbreviationsWithPrefixes.Length > 0 &&
173243
localization.AbbreviationsWithPrefixes.Length != unit.Prefixes.Length)

0 commit comments

Comments
 (0)