Skip to content

Commit

Permalink
Fix TimeSpan as words for Russian and Ukrainian languages
Browse files Browse the repository at this point in the history
  • Loading branch information
hazzik committed Feb 24, 2024
1 parent dfe6226 commit 71f4f83
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,16 @@ namespace Humanizer
public class DefaultFormatter : Humanizer.IFormatter
{
public DefaultFormatter(string localeCode) { }
protected System.Globalization.CultureInfo Culture { get; }
public virtual string DataUnitHumanize(Humanizer.DataUnit dataUnit, double count, bool toSymbol = true) { }
public virtual string DateHumanize(Humanizer.TimeUnit timeUnit, Humanizer.Tense timeUnitTense, int unit) { }
public virtual string DateHumanize_Never() { }
public virtual string DateHumanize_Now() { }
protected virtual string Format(string resourceKey) { }
protected virtual string Format(string resourceKey, int number, bool toWords = false) { }
protected virtual string Format(Humanizer.TimeUnit unit, string resourceKey, int number, bool toWords = false) { }
protected virtual string GetResourceKey(string resourceKey) { }
protected virtual string GetResourceKey(string resourceKey, int number) { }
protected virtual string NumberToWords(Humanizer.TimeUnit unit, int number, System.Globalization.CultureInfo culture) { }
public virtual string TimeSpanHumanize(Humanizer.TimeUnit timeUnit, int unit, bool toWords = false) { }
public virtual string TimeSpanHumanize_Age() { }
public virtual string TimeSpanHumanize_Zero() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,16 @@ namespace Humanizer
public class DefaultFormatter : Humanizer.IFormatter
{
public DefaultFormatter(string localeCode) { }
protected System.Globalization.CultureInfo Culture { get; }
public virtual string DataUnitHumanize(Humanizer.DataUnit dataUnit, double count, bool toSymbol = true) { }
public virtual string DateHumanize(Humanizer.TimeUnit timeUnit, Humanizer.Tense timeUnitTense, int unit) { }
public virtual string DateHumanize_Never() { }
public virtual string DateHumanize_Now() { }
protected virtual string Format(string resourceKey) { }
protected virtual string Format(string resourceKey, int number, bool toWords = false) { }
protected virtual string Format(Humanizer.TimeUnit unit, string resourceKey, int number, bool toWords = false) { }
protected virtual string GetResourceKey(string resourceKey) { }
protected virtual string GetResourceKey(string resourceKey, int number) { }
protected virtual string NumberToWords(Humanizer.TimeUnit unit, int number, System.Globalization.CultureInfo culture) { }
public virtual string TimeSpanHumanize(Humanizer.TimeUnit timeUnit, int unit, bool toWords = false) { }
public virtual string TimeSpanHumanize_Age() { }
public virtual string TimeSpanHumanize_Zero() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public void Hours(int hours, string expected, bool toWords = false) =>
[InlineData(19, "19 минут")]
[InlineData(20, "20 минут")]
[InlineData(21, "21 минута")]
[InlineData(21, "двадцать одна минута", true)]
[InlineData(22, "22 минуты")]
[InlineData(23, "23 минуты")]
[InlineData(24, "24 минуты")]
Expand All @@ -97,6 +98,7 @@ public void Minutes(int minutes, string expected, bool toWords = false) =>
[InlineData(19, "19 секунд")]
[InlineData(20, "20 секунд")]
[InlineData(21, "21 секунда")]
[InlineData(21, "двадцать одна секунда", true)]
[InlineData(22, "22 секунды")]
[InlineData(23, "23 секунды")]
[InlineData(24, "24 секунды")]
Expand All @@ -118,7 +120,9 @@ public void Seconds(int seconds, string expected, bool toWords = false) =>
[InlineData(19, "19 миллисекунд")]
[InlineData(20, "20 миллисекунд")]
[InlineData(21, "21 миллисекунда")]
[InlineData(21, "двадцать одна миллисекунда", true)]
[InlineData(22, "22 миллисекунды")]
[InlineData(22, "двадцать две миллисекунды", true)]
[InlineData(23, "23 миллисекунды")]
[InlineData(24, "24 миллисекунды")]
[InlineData(25, "25 миллисекунд")]
Expand Down
62 changes: 38 additions & 24 deletions src/Humanizer.Tests/Localisation/uk-UA/TimeSpanHumanizeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,51 @@ public class TimeSpanHumanizeTests
{
[Theory]
[Trait("Translation", "Google")]
[InlineData(366, "один рік")]
[InlineData(366, "один рік", true)]
[InlineData(366, "1 рік")]
[InlineData(731, "2 роки")]
[InlineData(1096, "3 роки")]
[InlineData(4018, "11 років")]
public void Years(int days, string expected) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year));
public void Years(int days, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year, toWords: toWords));

[Theory]
[Trait("Translation", "Google")]
[InlineData(31, "один місяць")]
[InlineData(31, "один місяць", true)]
[InlineData(31, "1 місяць")]
[InlineData(61, "2 місяці")]
[InlineData(92, "3 місяці")]
[InlineData(335, "11 місяців")]
public void Months(int days, string expected) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year));
public void Months(int days, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year, toWords: toWords));

[Theory]
[InlineData(7, "один тиждень")]
[InlineData(7, "один тиждень", true)]
[InlineData(7, "1 тиждень")]
[InlineData(14, "2 тижні")]
[InlineData(21, "3 тижні")]
[InlineData(28, "4 тижні")]
[InlineData(35, "5 тижнів")]
[InlineData(77, "11 тижнів")]
public void Weeks(int days, string expected) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize());
[InlineData(147, "двадцять один тиждень", true)]
public void Weeks(int days, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(toWords: toWords));

[Theory]
[InlineData(1, "один день")]
[InlineData(1, "один день", true)]
[InlineData(1, "1 день")]
[InlineData(2, "2 дні")]
[InlineData(3, "3 дні")]
[InlineData(4, "4 дні")]
[InlineData(5, "5 днів")]
[InlineData(6, "6 днів")]
public void Days(int days, string expected) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize());
[InlineData(21, "двадцять один день", true)]
public void Days(int days, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(toWords: toWords, maxUnit: TimeUnit.Day));

[Theory]
[InlineData(1, "одна година")]
[InlineData(1, "одна година", true)]
[InlineData(1, "1 година")]
[InlineData(2, "2 години")]
[InlineData(3, "3 години")]
[InlineData(4, "4 години")]
Expand All @@ -53,13 +60,15 @@ public void Days(int days, string expected) =>
[InlineData(19, "19 годин")]
[InlineData(20, "20 годин")]
[InlineData(21, "21 година")]
[InlineData(21, "двадцять одна година", true)]
[InlineData(22, "22 години")]
[InlineData(23, "23 години")]
public void Hours(int hours, string expected) =>
Assert.Equal(expected, TimeSpan.FromHours(hours).Humanize());
public void Hours(int hours, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromHours(hours).Humanize(toWords: toWords));

[Theory]
[InlineData(1, "одна хвилина")]
[InlineData(1, "одна хвилина",true)]
[InlineData(1, "1 хвилина")]
[InlineData(2, "2 хвилини")]
[InlineData(3, "3 хвилини")]
[InlineData(4, "4 хвилини")]
Expand All @@ -70,16 +79,18 @@ public void Hours(int hours, string expected) =>
[InlineData(19, "19 хвилин")]
[InlineData(20, "20 хвилин")]
[InlineData(21, "21 хвилина")]
[InlineData(21, "двадцять одна хвилина", true)]
[InlineData(22, "22 хвилини")]
[InlineData(23, "23 хвилини")]
[InlineData(24, "24 хвилини")]
[InlineData(25, "25 хвилин")]
[InlineData(40, "40 хвилин")]
public void Minutes(int minutes, string expected) =>
Assert.Equal(expected, TimeSpan.FromMinutes(minutes).Humanize());
public void Minutes(int minutes, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromMinutes(minutes).Humanize(toWords: toWords));

[Theory]
[InlineData(1, "одна секунда")]
[InlineData(1, "одна секунда", true)]
[InlineData(1, "1 секунда")]
[InlineData(2, "2 секунди")]
[InlineData(3, "3 секунди")]
[InlineData(4, "4 секунди")]
Expand All @@ -90,16 +101,18 @@ public void Minutes(int minutes, string expected) =>
[InlineData(19, "19 секунд")]
[InlineData(20, "20 секунд")]
[InlineData(21, "21 секунда")]
[InlineData(21, "двадцять одна секунда", true)]
[InlineData(22, "22 секунди")]
[InlineData(23, "23 секунди")]
[InlineData(24, "24 секунди")]
[InlineData(25, "25 секунд")]
[InlineData(40, "40 секунд")]
public void Seconds(int seconds, string expected) =>
Assert.Equal(expected, TimeSpan.FromSeconds(seconds).Humanize());
public void Seconds(int seconds, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromSeconds(seconds).Humanize(toWords: toWords));

[Theory]
[InlineData(1, "одна мілісекунда")]
[InlineData(1, "одна мілісекунда", true)]
[InlineData(1, "1 мілісекунда")]
[InlineData(2, "2 мілісекунди")]
[InlineData(3, "3 мілісекунди")]
[InlineData(4, "4 мілісекунди")]
Expand All @@ -110,13 +123,14 @@ public void Seconds(int seconds, string expected) =>
[InlineData(19, "19 мілісекунд")]
[InlineData(20, "20 мілісекунд")]
[InlineData(21, "21 мілісекунда")]
[InlineData(21, "двадцять одна мілісекунда", true)]
[InlineData(22, "22 мілісекунди")]
[InlineData(23, "23 мілісекунди")]
[InlineData(24, "24 мілісекунди")]
[InlineData(25, "25 мілісекунд")]
[InlineData(40, "40 мілісекунд")]
public void Milliseconds(int milliseconds, string expected) =>
Assert.Equal(expected, TimeSpan.FromMilliseconds(milliseconds).Humanize());
public void Milliseconds(int milliseconds, string expected, bool toWords = false) =>
Assert.Equal(expected, TimeSpan.FromMilliseconds(milliseconds).Humanize(toWords: toWords));

[Fact]
public void NoTime() =>
Expand Down
2 changes: 1 addition & 1 deletion src/Humanizer.Tests/Localisation/vi/NumberToWordsTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer.Tests.vi;
namespace Humanizer.Tests.Localisation.vi;

[UseCulture("vi")]
public class NumberToWordsTests
Expand Down
66 changes: 33 additions & 33 deletions src/Humanizer/Localisation/Formatters/DefaultFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
/// </summary>
public class DefaultFormatter : IFormatter
{
readonly CultureInfo _culture;
protected CultureInfo Culture { get; }

/// <param name="localeCode">Name of the culture to use.</param>
public DefaultFormatter(string localeCode) =>
_culture = new(localeCode);
Culture = new(localeCode);

public virtual string DateHumanize_Now() =>
GetResourceForDate(TimeUnit.Millisecond, Tense.Past, 0);
Expand Down Expand Up @@ -41,41 +41,41 @@ public virtual string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords
/// <inheritdoc/>
public virtual string TimeSpanHumanize_Age()
{
if (Resources.TryGetResource("TimeSpanHumanize_Age", _culture, out var ageFormat))
return ageFormat;
return "{0}";
}
if (Resources.TryGetResource("TimeSpanHumanize_Age", Culture, out var ageFormat))
return ageFormat;
return "{0}";
}

/// <inheritdoc cref="IFormatter.DataUnitHumanize(DataUnit, double, bool)"/>
public virtual string DataUnitHumanize(DataUnit dataUnit, double count, bool toSymbol = true)
{
var resourceKey = toSymbol ? $"DataUnit_{dataUnit}Symbol" : $"DataUnit_{dataUnit}";
var resourceValue = Format(resourceKey);
var resourceKey = toSymbol ? $"DataUnit_{dataUnit}Symbol" : $"DataUnit_{dataUnit}";
var resourceValue = Format(resourceKey);

if (!toSymbol && count > 1)
resourceValue += 's';
if (!toSymbol && count > 1)
resourceValue += 's';

return resourceValue;
}
return resourceValue;
}

/// <inheritdoc />
public virtual string TimeUnitHumanize(TimeUnit timeUnit)
{
var resourceKey = ResourceKeys.TimeUnitSymbol.GetResourceKey(timeUnit);
return Format(resourceKey);
}
var resourceKey = ResourceKeys.TimeUnitSymbol.GetResourceKey(timeUnit);
return Format(resourceKey);
}

string GetResourceForDate(TimeUnit unit, Tense timeUnitTense, int count)
{
var resourceKey = ResourceKeys.DateHumanize.GetResourceKey(unit, timeUnitTense: timeUnitTense, count: count);
return count == 1 ? Format(resourceKey) : Format(resourceKey, count);
}
var resourceKey = ResourceKeys.DateHumanize.GetResourceKey(unit, timeUnitTense: timeUnitTense, count: count);
return count == 1 ? Format(resourceKey) : Format(unit, resourceKey, count);
}

string GetResourceForTimeSpan(TimeUnit unit, int count, bool toWords = false)
{
var resourceKey = ResourceKeys.TimeSpanHumanize.GetResourceKey(unit, count, toWords);
return count == 1 ? Format(resourceKey + (toWords ? "_Words" : "")) : Format(resourceKey, count, toWords);
}
var resourceKey = ResourceKeys.TimeSpanHumanize.GetResourceKey(unit, count, toWords);
return count == 1 ? Format(resourceKey + (toWords ? "_Words" : "")) : Format(unit, resourceKey, count, toWords);
}

/// <summary>
/// Formats the specified resource key.
Expand All @@ -84,28 +84,28 @@ string GetResourceForTimeSpan(TimeUnit unit, int count, bool toWords = false)
/// <exception cref="ArgumentException">If the resource not exists on the specified culture.</exception>
protected virtual string Format(string resourceKey)
{
var resolvedKey = GetResourceKey(resourceKey);
return Resources.GetResource(resolvedKey, _culture);
}
var resolvedKey = GetResourceKey(resourceKey);
return Resources.GetResource(resolvedKey, Culture);
}

/// <summary>
/// Formats the specified resource key.
/// </summary>
/// <param name="unit"></param>
/// <param name="resourceKey">The resource key.</param>
/// <param name="number">The number.</param>
/// <param name="toWords"></param>
/// <exception cref="ArgumentException">If the resource not exists on the specified culture.</exception>
protected virtual string Format(string resourceKey, int number, bool toWords = false)
protected virtual string Format(TimeUnit unit, string resourceKey, int number, bool toWords = false)
{
var resolvedKey = GetResourceKey(resourceKey, number);
var resourceString = Resources.GetResource(resolvedKey, _culture);
var resolvedKey = GetResourceKey(resourceKey, number);
var resourceString = Resources.GetResource(resolvedKey, Culture);

if (toWords)
{
return string.Format(resourceString, number.ToWords(_culture));
}
return string.Format(resourceString, toWords ? NumberToWords(unit, number, Culture) : number.ToString(Culture));
}

return string.Format(resourceString, number);
}
protected virtual string NumberToWords(TimeUnit unit, int number, CultureInfo culture) =>
number.ToWords(culture);

/// <summary>
/// Override this method if your locale has complex rules around multiple units; e.g. Arabic, Russian
Expand Down
39 changes: 9 additions & 30 deletions src/Humanizer/Localisation/Formatters/IcelandicFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,19 @@
namespace Humanizer;

class IcelandicFormatter() :
DefaultFormatter(LocaleCode)
DefaultFormatter("is")
{
const string LocaleCode = "is";
readonly CultureInfo localCulture = new(LocaleCode);

public override string DataUnitHumanize(DataUnit dataUnit, double count, bool toSymbol = true) =>
base.DataUnitHumanize(dataUnit, count, toSymbol).TrimEnd('s');

protected override string Format(string resourceKey, int number, bool toWords = false)
{
var resourceString = Resources.GetResource(GetResourceKey(resourceKey, number), localCulture);

if (toWords)
{
var unitGender = GetGrammaticalGender(resourceString);
return string.Format(resourceString, number.ToWords(unitGender, localCulture));
}

return string.Format(resourceString, number);
}
protected override string NumberToWords(TimeUnit unit, int number, CultureInfo culture) =>
number.ToWords(GetUnitGender(unit), culture);

static GrammaticalGender GetGrammaticalGender(string resource)
{
if (resource.Contains(" mán") ||
resource.Contains(" dag"))
static GrammaticalGender GetUnitGender(TimeUnit unit) =>
unit switch
{
return GrammaticalGender.Masculine;
}

if (resource.Contains(" ár"))
{
return GrammaticalGender.Neuter;
}

return GrammaticalGender.Feminine;
}
TimeUnit.Day or TimeUnit.Month => GrammaticalGender.Masculine,
TimeUnit.Year => GrammaticalGender.Neuter,
_ => GrammaticalGender.Feminine
};
}
Loading

0 comments on commit 71f4f83

Please sign in to comment.