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

Add french localisation for NumberToWords #190

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

namespace Humanizer.Tests
{
public class DateTimeHumanizePrecisionStrategyTests
public class DateTimeHumanizePrecisionStrategyTests : AmbientCulture
{
public DateTimeHumanizePrecisionStrategyTests() : base("en-US") { }

private const double defaultPrecision = .75;

[Theory]
Expand Down
2 changes: 2 additions & 0 deletions src/Humanizer.Tests/Humanizer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
<Compile Include="Localisation\fr-BE\DateHumanizeTests.cs" />
<Compile Include="Localisation\fr-BE\TimeSpanHumanizeTests.cs" />
<Compile Include="Localisation\fr\DateHumanizeTests.cs" />
<Compile Include="Localisation\fr\NumberToOrdinalWordsTests.cs" />
<Compile Include="Localisation\fr\NumberToWordsTests.cs" />
<Compile Include="Localisation\fr\TimeSpanHumanizeTests.cs" />
<Compile Include="Localisation\invariant\NumberToWordsTests.cs" />
<Compile Include="Localisation\invariant\ToQuantityTests.cs" />
Expand Down
59 changes: 59 additions & 0 deletions src/Humanizer.Tests/Localisation/fr/NumberToOrdinalWordsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Xunit;
using Xunit.Extensions;

namespace Humanizer.Tests.Localisation.fr
{
public class NumberToOrdinalWordsTests : AmbientCulture
{
public NumberToOrdinalWordsTests() : base("fr-FR") { }

[Theory]
[InlineData(0, "zéroième")]
[InlineData(1, "premier")]
[InlineData(2, "deuxième")]
[InlineData(3, "troisième")]
[InlineData(4, "quatrième")]
[InlineData(5, "cinquième")]
[InlineData(6, "sixième")]
[InlineData(7, "septième")]
[InlineData(8, "huitième")]
[InlineData(9, "neuvième")]
[InlineData(10, "dixième")]
[InlineData(11, "onzième")]
[InlineData(12, "douzième")]
[InlineData(13, "treizième")]
[InlineData(14, "quatorzième")]
[InlineData(15, "quinzième")]
[InlineData(16, "seizième")]
[InlineData(17, "dix-septième")]
[InlineData(18, "dix-huitième")]
[InlineData(19, "dix-neuvième")]
[InlineData(20, "vingtième")]
[InlineData(21, "vingt et unième")]
[InlineData(22, "vingt-deuxième")]
[InlineData(30, "trentième")]
[InlineData(40, "quarantième")]
[InlineData(50, "cinquantième")]
[InlineData(60, "soixantième")]
[InlineData(70, "soixante-dixième")]
[InlineData(80, "quatre-vingtième")]
[InlineData(90, "quatre-vingt-dixième")]
[InlineData(95, "quatre-vingt-quinzième")]
[InlineData(96, "quatre-vingt-seizième")]
[InlineData(100, "centième")]
[InlineData(120, "cent vingtième")]
[InlineData(121, "cent vingt et unième")]
[InlineData(1000, "millième")]
[InlineData(1001, "mille unième")]
[InlineData(1021, "mille vingt et unième")]
[InlineData(10000, "dix millième")]
[InlineData(10121, "dix mille cent vingt et unième")]
[InlineData(100000, "cent millième")]
[InlineData(1000000, "millionième")]
[InlineData(1000000000, "milliardième")]
public void ToOrdinalWords(int number, string words)
{
Assert.Equal(words, number.ToOrdinalWords());
}
}
}
62 changes: 62 additions & 0 deletions src/Humanizer.Tests/Localisation/fr/NumberToWordsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Xunit;
using Xunit.Extensions;

namespace Humanizer.Tests.Localisation.fr
{
public class NumberToWordsTests : AmbientCulture
{
public NumberToWordsTests() : base("fr-FR") { }

[Theory]
[InlineData(0, "zéro")]
[InlineData(1, "un")]
[InlineData(10, "dix")]
[InlineData(11, "onze")]
[InlineData(15, "quinze")]
[InlineData(17, "dix-sept")]
[InlineData(25, "vingt-cinq")]
[InlineData(31, "trente et un")]
[InlineData(71, "soixante et onze")]
[InlineData(81, "quatre-vingt-un")]
[InlineData(122, "cent vingt-deux")]
[InlineData(3501, "trois mille cinq cent un")]
[InlineData(100, "cent")]
[InlineData(1000, "mille")]
[InlineData(100000, "cent mille")]
[InlineData(1000000, "un million")]
[InlineData(10000000, "dix millions")]
[InlineData(100000000, "cent millions")]
[InlineData(1000000000, "un milliard")]
[InlineData(111, "cent onze")]
[InlineData(1111, "mille cent onze")]
[InlineData(111111, "cent onze mille cent onze")]
[InlineData(1111111, "un million cent onze mille cent onze")]
[InlineData(11111111, "onze millions cent onze mille cent onze")]
[InlineData(111111111, "cent onze millions cent onze mille cent onze")]
[InlineData(1111111111, "un milliard cent onze millions cent onze mille cent onze")]
[InlineData(123, "cent vingt-trois")]
[InlineData(1234, "mille deux cent trente-quatre")]
[InlineData(12345, "douze mille trois cent quarante-cinq")]
[InlineData(123456, "cent vingt-trois mille quatre cent cinquante-six")]
[InlineData(1234567, "un million deux cent trente-quatre mille cinq cent soixante-sept")]
[InlineData(12345678, "douze millions trois cent quarante-cinq mille six cent soixante-dix-huit")]
[InlineData(123456789, "cent vingt-trois millions quatre cent cinquante-six mille sept cent quatre-vingt-neuf")]
[InlineData(1234567890, "un milliard deux cent trente-quatre millions cinq cent soixante-sept mille huit cent quatre-vingt-dix")]
[InlineData(1234567899, "un milliard deux cent trente-quatre millions cinq cent soixante-sept mille huit cent quatre-vingt-dix-neuf")]
[InlineData(223, "deux cent vingt-trois")]
[InlineData(2234, "deux mille deux cent trente-quatre")]
[InlineData(22345, "vingt-deux mille trois cent quarante-cinq")]
[InlineData(223456, "deux cent vingt-trois mille quatre cent cinquante-six")]
[InlineData(2234567, "deux millions deux cent trente-quatre mille cinq cent soixante-sept")]
[InlineData(22345678, "vingt-deux millions trois cent quarante-cinq mille six cent soixante-dix-huit")]
[InlineData(223456789, "deux cent vingt-trois millions quatre cent cinquante-six mille sept cent quatre-vingt-neuf")]
[InlineData(2147483646, "deux milliards cent quarante-sept millions quatre cent quatre-vingt-trois mille six cent quarante-six")]
[InlineData(1999, "mille neuf cent quatre-vingt-dix-neuf")]
[InlineData(2014, "deux mille quatorze")]
[InlineData(2048, "deux mille quarante-huit")]
public void ToWordsFrench(int number, string expected)
{
Assert.Equal(expected, number.ToWords());
}
}
}
4 changes: 3 additions & 1 deletion src/Humanizer.Tests/NumberToOrdinalWordsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

namespace Humanizer.Tests
{
public class NumberToOrdinalWordsTests
public class NumberToOrdinalWordsTests : AmbientCulture
{
public NumberToOrdinalWordsTests(): base("en-US") { }

[Theory]
[InlineData(0, "zeroth")]
[InlineData(1, "first")]
Expand Down
1 change: 1 addition & 0 deletions src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<Compile Include="Configuration\Configurator.cs" />
<Compile Include="DateHumanizeExtensions.cs" />
<Compile Include="Localisation\NumberToWords\EnglishNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\FrenchNumberToWordsConverter.cs" />
<Compile Include="Localisation\NumberToWords\INumberToWordsConverter.cs" />
<Compile Include="Localisation\Formatters\CzechSlovakPolishFormatter.cs" />
<Compile Include="DateTimeHumanizeStrategy\DefaultDateTimeHumanizeStrategy.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;

namespace Humanizer.Localisation.NumberToWords
{
internal class FrenchNumberToWordsConverter : INumberToWordsConverter
{
private static readonly string[] UnitsMap = { "zéro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf" };
private static readonly string[] TensMap = { "zéro", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante-dix", "quatre-vingt", "quatre-vingt-dix"};

private static readonly Dictionary<int,string> NumberExceptions = new Dictionary<int, string>
{
{71, "soixante et onze"},
{81, "quatre-vingt-un"},
{91, "quatre-vingt-onze"}
};

public string Convert(int number)
{
if (number == 0)
return UnitsMap[0];
if (number < 0)
return string.Format("moins {0}", Convert(Math.Abs(number)));

var parts = new List<string>();

if ((number / 1000000000) > 0)
{
parts.Add(string.Format("{0} milliard{1}",
Convert(number/1000000000),
number/1000000000 == 1 ? "" : "s"));

number %= 1000000000;
}

if ((number / 1000000) > 0)
{
parts.Add(string.Format("{0} million{1}",
Convert(number/1000000),
number/1000000 == 1 ? "" : "s"));

number %= 1000000;
}

if ((number / 1000) > 0)
{
parts.Add(number/1000 == 1
? string.Format("mille")
: string.Format("{0} mille", Convert(number/1000)));

number %= 1000;
}

if ((number / 100) > 0)
{
parts.Add(number < 200 ? "cent" : string.Format("{0} cent", Convert(number/100)));
number %= 100;
}

if (number > 0)
{
if (NumberExceptions.ContainsKey(number))
parts.Add(NumberExceptions[number]);
else if (number < 20)
parts.Add(UnitsMap[number]);
else
{
string lastPart;
if (number >= 70 && (number < 80 || number >= 90))
{
int baseNumber = number < 80 ? 60 : 80;
lastPart = string.Format("{0}-{1}", TensMap[baseNumber/10], Convert(number - baseNumber));
}
else
{
lastPart = TensMap[number/10];
if ((number%10) > 0)
{
if ((number - 1) % 10 == 0)
lastPart += " et un";
else
lastPart += string.Format("-{0}", UnitsMap[number%10]);
}
}
parts.Add(lastPart);
}
}

return string.Join(" ", parts.ToArray());
}

public string ConvertToOrdinal(int number)
{
if (number == 1)
return "premier";

var convertedNumber = Convert(number);

if (convertedNumber.EndsWith("s") && !convertedNumber.EndsWith("trois"))
convertedNumber = convertedNumber.TrimEnd('s');
else if (convertedNumber.EndsWith("cinq"))
convertedNumber += "u";
else if (convertedNumber.EndsWith("neuf"))
convertedNumber = convertedNumber.TrimEnd('f') + "v";

if (convertedNumber.StartsWith("un "))
{
convertedNumber = convertedNumber.Remove(0, 3);
}

convertedNumber = convertedNumber.TrimEnd('e');
convertedNumber += "ième";
return convertedNumber;
}
}
}
3 changes: 2 additions & 1 deletion src/Humanizer/NumberToWordsExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static class NumberToWordsExtension {
{ "ar", () => new ArabicNumberToWordsConverter() },
{ "fa", () => new FarsiNumberToWordsConverter() },
{ "es", () => new SpanishNumberToWordsConverter() },
{ "pl", () => new PolishNumberToWordsConverter() }
{ "pl", () => new PolishNumberToWordsConverter() },
{ "fr", () => new FrenchNumberToWordsConverter() }
};

/// <summary>
Expand Down