From 3447c7188110bb1b26ece197926ebaa80b675062 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Wed, 1 Apr 2020 11:29:25 -0400 Subject: [PATCH 001/126] Update code signing script --- scripts/SignClient.json => SignClient.json | 0 azure-pipelines.yml | 206 ++++++++++++--------- scripts/Sign-Package.ps1 | 27 --- 3 files changed, 119 insertions(+), 114 deletions(-) rename scripts/SignClient.json => SignClient.json (100%) delete mode 100644 scripts/Sign-Package.ps1 diff --git a/scripts/SignClient.json b/SignClient.json similarity index 100% rename from scripts/SignClient.json rename to SignClient.json diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 317a02be1..22550f76d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,94 +6,126 @@ pr: - master - rel/* -pool: - vmImage: windows-2019 - variables: BuildConfiguration: Release DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true -steps: -- task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.x' - inputs: - version: 3.x - -- task: UseDotNet@2 - displayName: 'Use .NET Core Runtime 2.1.x' - inputs: - version: 2.1.x - packageType: runtime - - -- task: DotNetCoreCLI@2 - inputs: - command: custom - custom: tool - arguments: install --tool-path . nbgv - displayName: Install NBGV tool - -- script: nbgv cloud - displayName: Set Version - -- task: DotNetCoreCLI@2 - inputs: - command: build - projects: .\src\Humanizer.sln - arguments: -c $(BuildConfiguration) - displayName: Build - -- task: NuGetToolInstaller@1 - -- pwsh: | - mkdir $(Build.ArtifactStagingDirectory)\Packages - $version = .\nbgv get-version -f json | ConvertFrom-Json - $nuspecs = gci .\NuSpecs\*.nuspec - foreach ($item in $nuspecs) { - nuget pack $($item.FullName) ` - -outputdirectory $(Build.ArtifactStagingDirectory)\Packages ` - -basepath $(System.DefaultWorkingDirectory)\src\ ` - -NoPackageAnalysis ` - -Properties "version=$($version.NuGetPackageVersion);RepositoryType=git;RepositoryCommit=$($version.GitCommitId);RepositoryUrl=https://github.com/Humanizr/Humanizer" - } - displayName: Create packages - -- task: DotNetCoreCLI@2 - inputs: - command: test - projects: .\src\Humanizer.Tests\Humanizer.Tests.csproj - arguments: -c $(BuildConfiguration) --collect:"XPlat code coverage" -s $(System.DefaultWorkingDirectory)/src/CodeCoverage.runsettings -- RunConfiguration.DisableAppDomain=true - displayName: Run Tests - -- task: DotNetCoreCLI@2 - inputs: - command: custom - custom: tool - arguments: install --tool-path . dotnet-reportgenerator-globaltool - displayName: Install ReportGenerator tool - -- script: reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/Rx.NET/Source/coverlet/reports -reporttypes:"Cobertura" - displayName: Create reports - -- task: PublishCodeCoverageResults@1 - displayName: 'Publish code coverage' - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: $(Build.SourcesDirectory)/Rx.NET/Source/coverlet/reports/Cobertura.xml - -- task: PowerShell@2 - displayName: Authenticode Sign artifacts - inputs: - filePath: scripts/Sign-Package.ps1 - env: - SignClientUser: $(SignClientUser) - SignClientSecret: $(SignClientSecret) - ArtifactDirectory: $(Build.ArtifactStagingDirectory)\Packages - condition: and(succeeded(), not(eq(variables['build.reason'], 'PullRequest')), not(eq(variables['SignClientSecret'], '')), not(eq(variables['SignClientUser'], ''))) - -- task: PublishBuildArtifacts@1 - displayName: Publish Package Artifacts - inputs: - pathToPublish: $(Build.ArtifactStagingDirectory)\Packages - artifactType: container - artifactName: Packages +stages: +- stage: Build + jobs: + - job: Build + pool: + vmImage: windows-latest + + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core SDK 3.x' + inputs: + version: 3.x + + - task: UseDotNet@2 + displayName: 'Use .NET Core Runtime 2.1.x' + inputs: + version: 2.1.x + packageType: runtime + + + - task: DotNetCoreCLI@2 + inputs: + command: custom + custom: tool + arguments: install --tool-path . nbgv + displayName: Install NBGV tool + + - script: nbgv cloud + displayName: Set Version + + - task: DotNetCoreCLI@2 + inputs: + command: build + projects: .\src\Humanizer.sln + arguments: -c $(BuildConfiguration) + displayName: Build + + - task: NuGetToolInstaller@1 + + - pwsh: | + mkdir $(Build.ArtifactStagingDirectory)\Packages + $version = .\nbgv get-version -f json | ConvertFrom-Json + $nuspecs = gci .\NuSpecs\*.nuspec + foreach ($item in $nuspecs) { + nuget pack $($item.FullName) ` + -outputdirectory $(Build.ArtifactStagingDirectory)\Packages ` + -basepath $(System.DefaultWorkingDirectory)\src\ ` + -NoPackageAnalysis ` + -Properties "version=$($version.NuGetPackageVersion);RepositoryType=git;RepositoryCommit=$($version.GitCommitId);RepositoryUrl=https://github.com/Humanizr/Humanizer" + } + displayName: Create packages + + - task: DotNetCoreCLI@2 + inputs: + command: test + projects: .\src\Humanizer.Tests\Humanizer.Tests.csproj + arguments: -c $(BuildConfiguration) --collect:"XPlat code coverage" -s $(System.DefaultWorkingDirectory)/src/CodeCoverage.runsettings -- RunConfiguration.DisableAppDomain=true + displayName: Run Tests + + - task: DotNetCoreCLI@2 + inputs: + command: custom + custom: tool + arguments: install --tool-path . dotnet-reportgenerator-globaltool + displayName: Install ReportGenerator tool + + - script: reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/Rx.NET/Source/coverlet/reports -reporttypes:"Cobertura" + displayName: Create reports + + - task: PublishCodeCoverageResults@1 + displayName: 'Publish code coverage' + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: $(Build.SourcesDirectory)/Rx.NET/Source/coverlet/reports/Cobertura.xml + + - publish: $(Build.ArtifactStagingDirectory)\Packages + displayName: Publish build packages + artifact: BuildPackages + + - publish: SignClient.json + displayName: Publish signing config + artifact: config + +- stage: CodeSign + condition: and(succeeded('Build'), not(eq(variables['build.reason'], 'PullRequest'))) + jobs: + - deployment: CodeSign + displayName: Code Signing + pool: + vmImage: windows-latest + environment: Code Sign + variables: + - group: SignClient Credentials + strategy: + runOnce: + deploy: + steps: + - task: DotNetCoreCLI@2 + inputs: + command: custom + custom: tool + arguments: install --tool-path . SignClient + displayName: Install SignTool tool + + - pwsh: | + .\SignClient "Sign" ` + --baseDirectory "$(Pipeline.Workspace)\BuildPackages" ` + --input "**/*.nupkg" ` + --config "$(Pipeline.Workspace)\SignClient.json" ` + --user "$(SignClientUser)" ` + --secret "$(SignClientSecret)" ` + --name "Humanizer" ` + --description "Humanizer" ` + --descriptionUrl "https://github.com/Humanizr/Humanizer" + displayName: Sign packages + + - publish: $(Pipeline.Workspace)/BuildPackages + displayName: Publish Signed Packages + artifact: SignedPackages \ No newline at end of file diff --git a/scripts/Sign-Package.ps1 b/scripts/Sign-Package.ps1 deleted file mode 100644 index b724c0c2e..000000000 --- a/scripts/Sign-Package.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -$currentDirectory = split-path $MyInvocation.MyCommand.Definition - -# See if we have the ClientSecret available -if([string]::IsNullOrEmpty($env:SignClientSecret)){ - Write-Host "Client Secret not found, not signing packages" - return; -} - -dotnet tool install --tool-path . SignClient - -# Setup Variables we need to pass into the sign client tool - -$appSettings = "$currentDirectory\SignClient.json" - -$nupgks = gci $Env:ArtifactDirectory\*.nupkg | Select -ExpandProperty FullName - -foreach ($nupkg in $nupgks){ - Write-Host "Submitting $nupkg for signing" - - .\SignClient 'sign' -c $appSettings -i $nupkg -r $env:SignClientUser -s $env:SignClientSecret -n 'Humanizer' -d 'Humanizer' -u 'https://github.com/Humanizr/Humanizer' - if ($LASTEXITCODE -ne 0) { - exit 1 - } - Write-Host "Finished signing $nupkg" -} - -Write-Host "Sign-package complete" \ No newline at end of file From c01eeb2560727f5705c015d8caad98e9f2446b78 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Wed, 1 Apr 2020 11:37:33 -0400 Subject: [PATCH 002/126] fix path --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 22550f76d..db4f4c96d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -118,7 +118,7 @@ stages: .\SignClient "Sign" ` --baseDirectory "$(Pipeline.Workspace)\BuildPackages" ` --input "**/*.nupkg" ` - --config "$(Pipeline.Workspace)\SignClient.json" ` + --config "$(Pipeline.Workspace)\config\SignClient.json" ` --user "$(SignClientUser)" ` --secret "$(SignClientSecret)" ` --name "Humanizer" ` From dec30d5af17e4a030dacc93ed30ba998accd9e45 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Wed, 1 Apr 2020 11:40:31 -0400 Subject: [PATCH 003/126] add coverage workaround --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index db4f4c96d..d7e0e41a7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -66,7 +66,7 @@ stages: inputs: command: test projects: .\src\Humanizer.Tests\Humanizer.Tests.csproj - arguments: -c $(BuildConfiguration) --collect:"XPlat code coverage" -s $(System.DefaultWorkingDirectory)/src/CodeCoverage.runsettings -- RunConfiguration.DisableAppDomain=true + arguments: -c $(BuildConfiguration) --collect:"XPlat code coverage" -s $(System.DefaultWorkingDirectory)/src/CodeCoverage.runsettings /t:rebuild /p:ContinuousIntegrationBuild=false -- RunConfiguration.DisableAppDomain=true displayName: Run Tests - task: DotNetCoreCLI@2 From 4bca2d0313f40560c3ccfa3b24688ce59f0f7c3c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2020 05:58:59 +0000 Subject: [PATCH 004/126] Bump DiffPlex from 1.6.0 to 1.6.1 Bumps [DiffPlex](https://github.com/mmanela/diffplex) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/mmanela/diffplex/releases) - [Commits](https://github.com/mmanela/diffplex/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index c249880ed..892cb0c9a 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -17,7 +17,7 @@ - + From 76aeade6abef4ecb9b067ea6ff08ee0fd5bd73c4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2020 05:57:25 +0000 Subject: [PATCH 005/126] Bump coverlet.collector from 1.2.0 to 1.2.1 Bumps [coverlet.collector](https://github.com/tonerdo/coverlet) from 1.2.0 to 1.2.1. - [Release notes](https://github.com/tonerdo/coverlet/releases) - [Commits](https://github.com/tonerdo/coverlet/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 892cb0c9a..e1fe68a8c 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -8,7 +8,7 @@ $(DefineConstants);NETFX_CORE - + From 2751f415bd3e618c8c6fbe972bb61498c745fce2 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Thu, 9 Apr 2020 10:45:24 -0400 Subject: [PATCH 006/126] Update sln --- src/Humanizer.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 524773fff..3271244c4 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -82,7 +82,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution - Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{f886a8da-3efc-4a89-91dd-06faf13da172}*SharedItemsImports = 4 Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{fdec244b-f07e-4a5e-bb80-fbc6ac77a9aa}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution From 7bf6e6da25adfb378a200481e28d073bffe4525a Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Thu, 9 Apr 2020 12:14:18 -0400 Subject: [PATCH 007/126] Update version.json --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index aa3d17880..755a199ed 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "2.7", + "version": "2.8", "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N From 50803020290c1dfdd8449bf87ab80554ecedf4c4 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Thu, 9 Apr 2020 17:01:40 -0400 Subject: [PATCH 008/126] Use latest coverlet with support for deterministic builds --- NuGet.config | 8 ++++++++ azure-pipelines.yml | 2 +- src/Directory.Build.targets | 9 +++++++++ src/Humanizer.Tests/Humanizer.Tests.csproj | 5 ++++- 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 NuGet.config diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 000000000..baca44cbf --- /dev/null +++ b/NuGet.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d7e0e41a7..db4f4c96d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -66,7 +66,7 @@ stages: inputs: command: test projects: .\src\Humanizer.Tests\Humanizer.Tests.csproj - arguments: -c $(BuildConfiguration) --collect:"XPlat code coverage" -s $(System.DefaultWorkingDirectory)/src/CodeCoverage.runsettings /t:rebuild /p:ContinuousIntegrationBuild=false -- RunConfiguration.DisableAppDomain=true + arguments: -c $(BuildConfiguration) --collect:"XPlat code coverage" -s $(System.DefaultWorkingDirectory)/src/CodeCoverage.runsettings -- RunConfiguration.DisableAppDomain=true displayName: Run Tests - task: DotNetCoreCLI@2 diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 1543cb15b..6760c3c20 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -7,4 +7,13 @@ https://github.com/dotnet/sourcelink/issues/572 --> + + + + <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/> + + diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index e1fe68a8c..40eb826cc 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -8,7 +8,10 @@ $(DefineConstants);NETFX_CORE - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 501a6ce6484cea619c653132a9654cf007899cbd Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Thu, 9 Apr 2020 20:04:25 -0400 Subject: [PATCH 009/126] fixup --- src/Directory.build.props | 1 - src/Humanizer.Tests/Humanizer.Tests.csproj | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index b4bde1785..3764ace9c 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -13,7 +13,6 @@ true - true diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 40eb826cc..09c726582 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -8,10 +8,7 @@ $(DefineConstants);NETFX_CORE - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + From 64860f437bb1c2213ffa6e9c3c584464d2668912 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Sat, 18 Apr 2020 09:46:28 -0400 Subject: [PATCH 010/126] Latest nightly --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 09c726582..158e95418 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -8,7 +8,7 @@ $(DefineConstants);NETFX_CORE - + From 98302d4f24279ed6d3ff579eba0ed275f8687655 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2020 05:58:27 +0000 Subject: [PATCH 011/126] Bump Microsoft.NET.Test.Sdk from 16.5.0 to 16.6.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.5.0 to 16.6.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.5.0...v16.6.0) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 158e95418..fb9eabcd4 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 58afe4199bdf561f902ca1d61c06c7954d14746b Mon Sep 17 00:00:00 2001 From: Tigran Topchyan Date: Sun, 26 Apr 2020 02:07:06 +0400 Subject: [PATCH 012/126] Added Armenian language support --- .../Humanizer.Tests.Shared.projitems | 4 + .../Localisation/hy/DateHumanizeTests.cs | 143 ++++++++++ .../Localisation/hy/NumberToWordsTests.cs | 162 ++++++++++++ .../Localisation/hy/OrdinalizeTests.cs | 42 +++ .../Localisation/hy/TimeSpanHumanizeTests.cs | 154 +++++++++++ .../Configuration/FormatterRegistry.cs | 1 + .../NumberToWordsConverterRegistry.cs | 1 + .../Configuration/OrdinalizerRegistry.cs | 1 + .../ArmenianNumberToWordsConverter.cs | 183 +++++++++++++ .../Ordinalizers/ArmenianOrdinalizer.cs | 19 ++ src/Humanizer/Properties/Resources.hy.resx | 246 ++++++++++++++++++ 11 files changed, 956 insertions(+) create mode 100644 src/Humanizer.Tests.Shared/Localisation/hy/DateHumanizeTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/hy/NumberToWordsTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/hy/OrdinalizeTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/hy/TimeSpanHumanizeTests.cs create mode 100644 src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs create mode 100644 src/Humanizer/Localisation/Ordinalizers/ArmenianOrdinalizer.cs create mode 100644 src/Humanizer/Properties/Resources.hy.resx diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index 2730edc2c..c39bb923a 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -87,6 +87,10 @@ + + + + diff --git a/src/Humanizer.Tests.Shared/Localisation/hy/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/hy/DateHumanizeTests.cs new file mode 100644 index 000000000..903330831 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/hy/DateHumanizeTests.cs @@ -0,0 +1,143 @@ +using Humanizer.Localisation; +using Xunit; + +namespace Humanizer.Tests.Localisation.hy +{ + [UseCulture("hy")] + public class DateHumanizeTests + { + + [Theory] + [InlineData(1, "մեկ վայրկյան առաջ")] + [InlineData(2, "2 վայրկյան առաջ")] + [InlineData(3, "3 վայրկյան առաջ")] + [InlineData(4, "4 վայրկյան առաջ")] + [InlineData(11, "11 վայրկյան առաջ")] + [InlineData(21, "21 վայրկյան առաջ")] + [InlineData(24, "24 վայրկյան առաջ")] + [InlineData(40, "40 վայրկյան առաջ")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(1, "մեկ վայրկյանից")] + [InlineData(2, "2 վայրկյանից")] + [InlineData(11, "11 վայրկյանից")] + [InlineData(20, "20 վայրկյանից")] + [InlineData(40, "40 վայրկյանից")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(1, "մեկ րոպե առաջ")] + [InlineData(2, "2 րոպե առաջ")] + [InlineData(10, "10 րոպե առաջ")] + [InlineData(25, "25 րոպե առաջ")] + [InlineData(40, "40 րոպե առաջ")] + [InlineData(60, "մեկ ժամ առաջ")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "մեկ րոպեից")] + [InlineData(2, "2 րոպեից")] + [InlineData(19, "19 րոպեից")] + [InlineData(25, "25 րոպեից")] + [InlineData(40, "40 րոպեից")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(1, "մեկ ժամ առաջ")] + [InlineData(2, "2 ժամ առաջ")] + [InlineData(19, "19 ժամ առաջ")] + [InlineData(20, "20 ժամ առաջ")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "մեկ ժամից")] + [InlineData(5, "5 ժամից")] + [InlineData(23, "23 ժամից")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(1, "երեկ")] + [InlineData(2, "2 օր առաջ")] + [InlineData(25, "25 օր առաջ")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "վաղը")] + [InlineData(2, "2 օրից")] + [InlineData(25, "25 օրից")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(1, "մեկ ամիս առաջ")] + [InlineData(11, "11 ամիս առաջ")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "մեկ ամսից")] + [InlineData(11, "11 ամսից")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(1, "մեկ տարի առաջ")] + [InlineData(2, "2 տարի առաջ")] + [InlineData(21, "21 տարի առաջ")] + [InlineData(111, "111 տարի առաջ")] + [InlineData(121, "121 տարի առաջ")] + [InlineData(222, "222 տարի առաջ")] + [InlineData(325, "325 տարի առաջ")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "մեկ տարուց")] + [InlineData(2, "2 տարուց")] + [InlineData(21, "21 տարուց")] + [InlineData(111, "111 տարուց")] + [InlineData(121, "121 տարուց")] + [InlineData(222, "222 տարուց")] + [InlineData(325, "325 տարուց")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Fact] + public void Now() + { + DateHumanize.Verify("հիմա", 0, TimeUnit.Day, Tense.Past); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/hy/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/hy/NumberToWordsTests.cs new file mode 100644 index 000000000..5815fcdb7 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/hy/NumberToWordsTests.cs @@ -0,0 +1,162 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.hy +{ + [UseCulture("hy")] + public class NumberToWordsTests + { + [Theory] + [InlineData(0, "զրո")] + [InlineData(1, "մեկ")] + [InlineData(10, "տաս")] + [InlineData(11, "տասնմեկ")] + [InlineData(12, "տասներկու")] + [InlineData(13, "տասներեք")] + [InlineData(14, "տասնչորս")] + [InlineData(15, "տասնհինգ")] + [InlineData(16, "տասնվեց")] + [InlineData(17, "տասնյոթ")] + [InlineData(18, "տասնութ")] + [InlineData(19, "տասնինը")] + [InlineData(20, "քսան")] + [InlineData(30, "երեսուն")] + [InlineData(40, "քառասուն")] + [InlineData(50, "հիսուն")] + [InlineData(60, "վաթսուն")] + [InlineData(70, "յոթանասուն")] + [InlineData(80, "ութսուն")] + [InlineData(90, "իննսուն")] + [InlineData(100, "հարյուր")] + [InlineData(200, "երկու հարյուր")] + [InlineData(300, "երեք հարյուր")] + [InlineData(400, "չորս հարյուր")] + [InlineData(500, "հինգ հարյուր")] + [InlineData(600, "վեց հարյուր")] + [InlineData(700, "յոթ հարյուր")] + [InlineData(800, "ութ հարյուր")] + [InlineData(900, "ինը հարյուր")] + [InlineData(1000, "հազար")] + [InlineData(2000, "երկու հազար")] + [InlineData(3000, "երեք հազար")] + [InlineData(4000, "չորս հազար")] + [InlineData(5000, "հինգ հազար")] + [InlineData(10000, "տաս հազար")] + [InlineData(100000, "հարյուր հազար")] + [InlineData(1000000, "մեկ միլիոն")] + [InlineData(2000000, "երկու միլիոն")] + [InlineData(10000000, "տաս միլիոն")] + [InlineData(100000000, "հարյուր միլիոն")] + [InlineData(1000000000, "մեկ միլիարդ")] + [InlineData(2000000000, "երկու միլիարդ")] + [InlineData(3000000000, "երեք միլիարդ")] + [InlineData(4000000000, "չորս միլիարդ")] + [InlineData(122, "հարյուր քսաներկու")] + [InlineData(3501, "երեք հազար հինգ հարյուր մեկ")] + [InlineData(111, "հարյուր տասնմեկ")] + [InlineData(1112, "հազար հարյուր տասներկու")] + [InlineData(11213, "տասնմեկ հազար երկու հարյուր տասներեք")] + [InlineData(121314, "հարյուր քսանմեկ հազար երեք հարյուր տասնչորս")] + [InlineData(2132415, "երկու միլիոն հարյուր երեսուներկու հազար չորս հարյուր տասնհինգ")] + [InlineData(12345516, "տասներկու միլիոն երեք հարյուր քառասունհինգ հազար հինգ հարյուր տասնվեց")] + [InlineData(751633617, "յոթ հարյուր հիսունմեկ միլիոն վեց հարյուր երեսուներեք հազար վեց հարյուր տասնյոթ")] + [InlineData(1111111118, "մեկ միլիարդ հարյուր տասնմեկ միլիոն հարյուր տասնմեկ հազար հարյուր տասնութ")] + [InlineData(4111111118, "չորս միլիարդ հարյուր տասնմեկ միլիոն հարյուր տասնմեկ հազար հարյուր տասնութ")] + [InlineData(-751633617, "մինուս յոթ հարյուր հիսունմեկ միլիոն վեց հարյուր երեսուներեք հազար վեց հարյուր տասնյոթ")] + [InlineData(999999999999, "ինը հարյուր իննսունինը միլիարդ ինը հարյուր իննսունինը միլիոն ինը հարյուր իննսունինը հազար ինը հարյուր իննսունինը")] + [InlineData(1_000_000_000_000, "մեկ տրիլիոն")] + [InlineData(3_000_000_000_000, "երեք տրիլիոն")] + [InlineData(5_000_000_000_000, "հինգ տրիլիոն")] + [InlineData(999_000_000_000_000, "ինը հարյուր իննսունինը տրիլիոն")] + [InlineData( + long.MaxValue, + "ինը քվինտիլիոն " + + "երկու հարյուր քսաներեք կվադրիլիոն " + + "երեք հարյուր յոթանասուներկու տրիլիոն " + + "երեսունվեց միլիարդ " + + "ութ հարյուր հիսունչորս միլիոն " + + "յոթ հարյուր յոթանասունհինգ հազար " + + "ութ հարյուր յոթ")] + [InlineData( + long.MinValue, + "մինուս ինը քվինտիլիոն " + + "երկու հարյուր քսաներեք կվադրիլիոն " + + "երեք հարյուր յոթանասուներկու տրիլիոն " + + "երեսունվեց միլիարդ " + + "ութ հարյուր հիսունչորս միլիոն " + + "յոթ հարյուր յոթանասունհինգ հազար " + + "ութ հարյուր ութ")] + public void ToWords(long number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(0, "զրոյական")] + [InlineData(1, "առաջին")] + [InlineData(2, "երկրորդ")] + [InlineData(3, "երրորդ")] + [InlineData(10, "տասերորդ")] + [InlineData(11, "տասնմեկերորդ")] + [InlineData(12, "տասներկուերորդ")] + [InlineData(13, "տասներեքերորդ")] + [InlineData(14, "տասնչորսերորդ")] + [InlineData(15, "տասնհինգերորդ")] + [InlineData(16, "տասնվեցերորդ")] + [InlineData(17, "տասնյոթերորդ")] + [InlineData(18, "տասնութերորդ")] + [InlineData(19, "տասնինըերորդ")] + [InlineData(20, "քսաներորդ")] + [InlineData(30, "երեսուներորդ")] + [InlineData(40, "քառասուներորդ")] + [InlineData(50, "հիսուներորդ")] + [InlineData(60, "վաթսուներորդ")] + [InlineData(70, "յոթանասուներորդ")] + [InlineData(80, "ութսուներորդ")] + [InlineData(90, "իննսուներորդ")] + [InlineData(100, "հարյուրերորդ")] + [InlineData(200, "երկու հարյուրերորդ")] + [InlineData(300, "երեք հարյուրերորդ")] + [InlineData(400, "չորս հարյուրերորդ")] + [InlineData(500, "հինգ հարյուրերորդ")] + [InlineData(600, "վեց հարյուրերորդ")] + [InlineData(700, "յոթ հարյուրերորդ")] + [InlineData(800, "ութ հարյուրերորդ")] + [InlineData(900, "ինը հարյուրերորդ")] + [InlineData(1000, "հազարերորդ")] + [InlineData(2000, "երկու հազարերորդ")] + [InlineData(3000, "երեք հազարերորդ")] + [InlineData(4000, "չորս հազարերորդ")] + [InlineData(5000, "հինգ հազարերորդ")] + [InlineData(10000, "տաս հազարերորդ")] + [InlineData(21000, "քսանմեկ հազարերորդ")] + [InlineData(100000, "հարյուր հազարերորդ")] + [InlineData(101000, "հարյուր մեկ հազարերորդ")] + [InlineData(1000000, "մեկ միլիոներորդ")] + [InlineData(121000, "հարյուր քսանմեկ հազարերորդ")] + [InlineData(200000, "երկու հարյուր հազարերորդ")] + [InlineData(2000000, "երկու միլիոներորդ")] + [InlineData(10000000, "տաս միլիոներորդ")] + [InlineData(21000000, "քսանմեկ միլիոներորդ")] + [InlineData(100000000, "հարյուր միլիոներորդ")] + [InlineData(230000000, "երկու հարյուր երեսուն միլիոներորդ")] + [InlineData(1000000000, "մեկ միլիարդերորդ")] + [InlineData(2000000000, "երկու միլիարդերորդ")] + [InlineData(122, "հարյուր քսաներկուերորդ")] + [InlineData(3501, "երեք հազար հինգ հարյուր մեկերորդ")] + [InlineData(111, "հարյուր տասնմեկերորդ")] + [InlineData(1112, "հազար հարյուր տասներկուերորդ")] + [InlineData(11213, "տասնմեկ հազար երկու հարյուր տասներեքերորդ")] + [InlineData(121314, "հարյուր քսանմեկ հազար երեք հարյուր տասնչորսերորդ")] + [InlineData(2132415, "երկու միլիոն հարյուր երեսուներկու հազար չորս հարյուր տասնհինգերորդ")] + [InlineData(12345516, "տասներկու միլիոն երեք հարյուր քառասունհինգ հազար հինգ հարյուր տասնվեցերորդ")] + [InlineData(751633617, "յոթ հարյուր հիսունմեկ միլիոն վեց հարյուր երեսուներեք հազար վեց հարյուր տասնյոթերորդ")] + [InlineData(1111111118, "մեկ միլիարդ հարյուր տասնմեկ միլիոն հարյուր տասնմեկ հազար հարյուր տասնութերորդ")] + [InlineData(1111111000, "մեկ միլիարդ հարյուր տասնմեկ միլիոն հարյուր տասնմեկ հազարերորդ")] + [InlineData(1234567000, "մեկ միլիարդ երկու հարյուր երեսունչորս միլիոն հինգ հարյուր վաթսունյոթ հազարերորդ")] + [InlineData(-751633617, "մինուս յոթ հարյուր հիսունմեկ միլիոն վեց հարյուր երեսուներեք հազար վեց հարյուր տասնյոթերորդ")] + public void ToOrdinalWords(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/hy/OrdinalizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/hy/OrdinalizeTests.cs new file mode 100644 index 000000000..8f93d03ce --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/hy/OrdinalizeTests.cs @@ -0,0 +1,42 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.hy +{ + [UseCulture("hy")] + public class OrdinalizeTests + { + + [Theory] + [InlineData("0", "0-րդ")] + [InlineData("1", "1-ին")] + [InlineData("2", "2-րդ")] + [InlineData("103", "103-րդ")] + [InlineData("1001", "1001-րդ")] + public void OrdinalizeStringMasculine(string number, string ordinalized) + { + Assert.Equal(number.Ordinalize(GrammaticalGender.Masculine), ordinalized); + } + + [Theory] + [InlineData("0", "0-րդ")] + [InlineData("1", "1-ին")] + [InlineData("2", "2-րդ")] + [InlineData("103", "103-րդ")] + [InlineData("1001", "1001-րդ")] + public void OrdinalizeStringFeminine(string number, string ordinalized) + { + Assert.Equal(number.Ordinalize(GrammaticalGender.Feminine), ordinalized); + } + + [Theory] + [InlineData("0", "0-րդ")] + [InlineData("1", "1-ին")] + [InlineData("2", "2-րդ")] + [InlineData("103", "103-րդ")] + [InlineData("1001", "1001-րդ")] + public void OrdinalizeStringNeuter(string number, string ordinalized) + { + Assert.Equal(number.Ordinalize(GrammaticalGender.Neuter), ordinalized); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/hy/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/hy/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..617f88d81 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/hy/TimeSpanHumanizeTests.cs @@ -0,0 +1,154 @@ +using System; +using Xunit; + +namespace Humanizer.Tests.Localisation.hy +{ + [UseCulture("hy")] + public class TimeSpanHumanizeTests + { + + [Theory] + [Trait("Translation", "Native speaker")] + [InlineData(366, "մեկ տարի")] + [InlineData(731, "2 տարի")] + [InlineData(1096, "3 տարի")] + [InlineData(4018, "11 տարի")] + public void Years(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [Trait("Translation", "Native speaker")] + [InlineData(31, "մեկ ամիս")] + [InlineData(61, "2 ամիս")] + [InlineData(92, "3 ամիս")] + [InlineData(335, "11 ամիս")] + public void Months(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [InlineData(7, "մեկ շաբաթ")] + [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()); + } + + [Theory] + [InlineData(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()); + } + + [Theory] + [InlineData(1, "մեկ ժամ")] + [InlineData(2, "2 ժամ")] + [InlineData(3, "3 ժամ")] + [InlineData(4, "4 ժամ")] + [InlineData(5, "5 ժամ")] + [InlineData(6, "6 ժամ")] + [InlineData(10, "10 ժամ")] + [InlineData(11, "11 ժամ")] + [InlineData(19, "19 ժամ")] + [InlineData(20, "20 ժամ")] + [InlineData(21, "21 ժամ")] + [InlineData(22, "22 ժամ")] + [InlineData(23, "23 ժամ")] + public void Hours(int hours, string expected) + { + Assert.Equal(expected, TimeSpan.FromHours(hours).Humanize()); + } + + [Theory] + [InlineData(1, "մեկ րոպե")] + [InlineData(2, "2 րոպե")] + [InlineData(3, "3 րոպե")] + [InlineData(4, "4 րոպե")] + [InlineData(5, "5 րոպե")] + [InlineData(6, "6 րոպե")] + [InlineData(10, "10 րոպե")] + [InlineData(11, "11 րոպե")] + [InlineData(19, "19 րոպե")] + [InlineData(20, "20 րոպե")] + [InlineData(21, "21 րոպե")] + [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()); + } + + [Theory] + [InlineData(1, "մեկ վայրկյան")] + [InlineData(2, "2 վայրկյան")] + [InlineData(3, "3 վայրկյան")] + [InlineData(4, "4 վայրկյան")] + [InlineData(5, "5 վայրկյան")] + [InlineData(6, "6 վայրկյան")] + [InlineData(10, "10 վայրկյան")] + [InlineData(11, "11 վայրկյան")] + [InlineData(19, "19 վայրկյան")] + [InlineData(20, "20 վայրկյան")] + [InlineData(21, "21 վայրկյան")] + [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()); + } + + [Theory] + [InlineData(1, "մեկ միլիվայրկյան")] + [InlineData(2, "2 միլիվայրկյան")] + [InlineData(3, "3 միլիվայրկյան")] + [InlineData(4, "4 միլիվայրկյան")] + [InlineData(5, "5 միլիվայրկյան")] + [InlineData(6, "6 միլիվայրկյան")] + [InlineData(10, "10 միլիվայրկյան")] + [InlineData(11, "11 միլիվայրկյան")] + [InlineData(19, "19 միլիվայրկյան")] + [InlineData(20, "20 միլիվայրկյան")] + [InlineData(21, "21 միլիվայրկյան")] + [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()); + } + + [Fact] + public void NoTime() + { + Assert.Equal("0 միլիվայրկյան", TimeSpan.Zero.Humanize()); + } + + [Fact] + public void NoTimeToWords() + { + // This one doesn't make a lot of sense but ... w/e + Assert.Equal("ժամանակը բացակայում է", TimeSpan.Zero.Humanize(toWords: true)); + } + } +} diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index 719b82758..2b048cfdb 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -34,6 +34,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterDefaultFormatter("fa"); RegisterDefaultFormatter("fi-FI"); RegisterDefaultFormatter("hu"); + RegisterDefaultFormatter("hy"); RegisterDefaultFormatter("id"); RegisterDefaultFormatter("ja"); Register("mt", new MalteseFormatter("mt")); diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index 4fead6259..5e1603d76 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -43,6 +43,7 @@ public NumberToWordsConverterRegistry() Register("vi", new VietnameseNumberToWordsConverter()); Register("zh-CN", new ChineseNumberToWordsConverter()); Register("bg", new BulgarianNumberToWordsConverter()); + Register("hy", new ArmenianNumberToWordsConverter()); } } } diff --git a/src/Humanizer/Configuration/OrdinalizerRegistry.cs b/src/Humanizer/Configuration/OrdinalizerRegistry.cs index cee400fb7..ba3224c68 100644 --- a/src/Humanizer/Configuration/OrdinalizerRegistry.cs +++ b/src/Humanizer/Configuration/OrdinalizerRegistry.cs @@ -17,6 +17,7 @@ public OrdinalizerRegistry() : base(new DefaultOrdinalizer()) Register("ru", new RussianOrdinalizer()); Register("tr", new TurkishOrdinalizer()); Register("uk", new UkrainianOrdinalizer()); + Register("hy", new ArmenianOrdinalizer()); } } } diff --git a/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs new file mode 100644 index 000000000..14ccb0006 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class ArmenianNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private static readonly string[] UnitsMap = { "զրո", "մեկ", "երկու", "երեք", "չորս", "հինգ", "վեց", "յոթ", "ութ", "ինը", "տաս", "տասնմեկ", "տասներկու", "տասներեք", "տասնչորս", "տասնհինգ", "տասնվեց", "տասնյոթ", "տասնութ", "տասնինը" }; + private static readonly string[] TensMap = { "զրո", "տաս", "քսան", "երեսուն", "քառասուն", "հիսուն", "վաթսուն", "յոթանասուն", "ութսուն", "իննսուն" }; + + private static readonly Dictionary OrdinalExceptions = new Dictionary + { + {0, "զրոյական"}, + {1, "առաջին"}, + {2, "երկրորդ"}, + {3, "երրորդ"}, + {4, "չորրորդ"} + }; + + public override string Convert(long number) + { + return Convert(number, false); + } + + public override string ConvertToOrdinal(int number) + { + if (ExceptionNumbersToWords(number, out var exceptionString)) + { + return exceptionString; + } + + return Convert(number, true); + } + + private string Convert(long number, bool isOrdinal) + { + if (number == 0) + { + return GetUnitValue(0, isOrdinal); + } + + if (number == long.MinValue) + { + return "մինուս ինը քվինտիլիոն " + + "երկու հարյուր քսաներեք կվադրիլիոն " + + "երեք հարյուր յոթանասուներկու տրիլիոն " + + "երեսունվեց միլիարդ " + + "ութ հարյուր հիսունչորս միլիոն " + + "յոթ հարյուր յոթանասունհինգ հազար " + + "ութ հարյուր ութ"; + } + + if (number < 0) + { + return string.Format("մինուս {0}", Convert(-number, isOrdinal)); + } + + var parts = new List(); + + if ((number / 1000000000000000000) > 0) + { + parts.Add(string.Format("{0} քվինտիլիոն", Convert(number / 1000000000000000000))); + number %= 1000000000000000000; + } + + if ((number / 1000000000000000) > 0) + { + parts.Add(string.Format("{0} կվադրիլիոն", Convert(number / 1000000000000000))); + number %= 1000000000000000; + } + + if ((number / 1000000000000) > 0) + + { + parts.Add(string.Format("{0} տրիլիոն", Convert(number / 1000000000000))); + number %= 1000000000000; + } + + if ((number / 1000000000) > 0) + { + parts.Add(string.Format("{0} միլիարդ", Convert(number / 1000000000))); + number %= 1000000000; + } + + if ((number / 1000000) > 0) + { + parts.Add(string.Format("{0} միլիոն", Convert(number / 1000000))); + number %= 1000000; + } + + if ((number / 1000) > 0) + { + if ((number / 1000) == 1) + { + parts.Add("հազար"); + } + else + { + parts.Add(string.Format("{0} հազար", Convert(number / 1000))); + } + + number %= 1000; + } + + if ((number / 100) > 0) + { + if ((number / 100) == 1) + { + parts.Add("հարյուր"); + } + else + { + parts.Add(string.Format("{0} հարյուր", Convert(number / 100))); + } + + number %= 100; + } + + if (number > 0) + { + if (number < 20) + { + parts.Add(GetUnitValue(number, isOrdinal)); + } + else + { + var lastPart = TensMap[number / 10]; + if ((number % 10) > 0) + { + lastPart += string.Format("{0}", GetUnitValue(number % 10, isOrdinal)); + } + else if (isOrdinal) + { + lastPart += "երորդ"; + } + + parts.Add(lastPart); + } + } + else if (isOrdinal) + { + parts[parts.Count - 1] += "երորդ"; + } + + var toWords = string.Join(" ", parts.ToArray()); + + //if (isOrdinal) + //{ + // toWords = RemoveOnePrefix(toWords); + //} + + return toWords; + } + + private static string GetUnitValue(long number, bool isOrdinal) + { + if (isOrdinal) + { + return UnitsMap[number] + "երորդ"; + } + else + { + return UnitsMap[number]; + } + } + + private static string RemoveOnePrefix(string toWords) + { + // one hundred => hundredth + if (toWords.IndexOf("մեկ", StringComparison.Ordinal) == 0) + { + toWords = toWords.Remove(0, 4); + } + + return toWords; + } + + private static bool ExceptionNumbersToWords(long number, out string words) + { + return OrdinalExceptions.TryGetValue(number, out words); + } + } +} diff --git a/src/Humanizer/Localisation/Ordinalizers/ArmenianOrdinalizer.cs b/src/Humanizer/Localisation/Ordinalizers/ArmenianOrdinalizer.cs new file mode 100644 index 000000000..6417deb84 --- /dev/null +++ b/src/Humanizer/Localisation/Ordinalizers/ArmenianOrdinalizer.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Humanizer.Localisation.Ordinalizers +{ + internal class ArmenianOrdinalizer : DefaultOrdinalizer + { + public override string Convert(int number, string numberString) + { + if (number == 1 || number == -1) + { + return numberString + "-ին"; + } + + return numberString + "-րդ"; + } + } +} diff --git a/src/Humanizer/Properties/Resources.hy.resx b/src/Humanizer/Properties/Resources.hy.resx new file mode 100644 index 000000000..39b0cc98e --- /dev/null +++ b/src/Humanizer/Properties/Resources.hy.resx @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + մեկ վայրկյան առաջ + + + մեկ րոպե առաջ + + + մեկ ժամ առաջ + + + երեկ + + + մեկ ամիս առաջ + + + մեկ տարի առաջ + + + {0} ժամ առաջ + + + {0} րոպե առաջ + + + {0} ամիս առաջ + + + {0} վայրկյան առաջ + + + {0} տարի առաջ + + + {0} օր առաջ + + + հիմա + + + {0} օր + + + {0} ժամ + + + {0} միլիվայրկյան + + + {0} րոպե + + + {0} վայրկյան + + + {0} շաբաթ + + + մեկ օր + + + մեկ ժամ + + + մեկ միլիվայրկյան + + + մեկ րոպե + + + մեկ վայրկյան + + + մեկ շաբաթ + + + ժամանակը բացակայում է + + + {0} օրից + + + {0} ժամից + + + {0} րոպեից + + + {0} ամսից + + + {0} վայրկյանից + + + {0} տարուց + + + վաղը + + + մեկ ժամից + + + մեկ րոպեից + + + մեկ ամսից + + + մեկ վայրկյանից + + + մեկ տարուց + + + {0} ամիս + + + {0} տարի + + + մեկ ամիս + + + մեկ տարի + + \ No newline at end of file From 2614180eed57f468432cd5e6823d0171bb52f95f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2020 06:16:16 +0000 Subject: [PATCH 013/126] Bump Microsoft.NET.Test.Sdk from 16.6.0 to 16.6.1 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.6.0 to 16.6.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.6.0...v16.6.1) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index fb9eabcd4..f6df35056 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 9889bf1395590faaf4d3b0c598d3993a3b0b1ab6 Mon Sep 17 00:00:00 2001 From: Tigran Topchyan Date: Tue, 28 Apr 2020 09:24:42 +0400 Subject: [PATCH 014/126] Added NuSpec for Armenian language --- NuSpecs/Humanizer.Core.hy.nuspec | 25 +++++++++++++++++++++++++ src/Humanizer.sln | 1 + 2 files changed, 26 insertions(+) create mode 100644 NuSpecs/Humanizer.Core.hy.nuspec diff --git a/NuSpecs/Humanizer.Core.hy.nuspec b/NuSpecs/Humanizer.Core.hy.nuspec new file mode 100644 index 000000000..fa29282ea --- /dev/null +++ b/NuSpecs/Humanizer.Core.hy.nuspec @@ -0,0 +1,25 @@ + + + + Humanizer.Core.ru + $version$ + Humanizer Locale (ru) + Mehdi Khalili, Claire Novotny + https://github.com/Humanizr/Humanizer + logo.png + false + Humanizer Locale Armenian (hy) + Copyright (c) .NET Foundation and Contributors + MIT + + ru + + + + + + + + + + \ No newline at end of file diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 3271244c4..c633f5497 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -52,6 +52,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ..\NuSpecs\Humanizer.Core.he.nuspec = ..\NuSpecs\Humanizer.Core.he.nuspec ..\NuSpecs\Humanizer.Core.hr.nuspec = ..\NuSpecs\Humanizer.Core.hr.nuspec ..\NuSpecs\Humanizer.Core.hu.nuspec = ..\NuSpecs\Humanizer.Core.hu.nuspec + ..\NuSpecs\Humanizer.Core.hy.nuspec = ..\NuSpecs\Humanizer.Core.hy.nuspec ..\NuSpecs\Humanizer.Core.id.nuspec = ..\NuSpecs\Humanizer.Core.id.nuspec ..\NuSpecs\Humanizer.Core.it.nuspec = ..\NuSpecs\Humanizer.Core.it.nuspec ..\NuSpecs\Humanizer.Core.ja.nuspec = ..\NuSpecs\Humanizer.Core.ja.nuspec From 91aab71f2fc6c29f217ddd6320811937b408e4e7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2020 05:54:18 +0000 Subject: [PATCH 015/126] Bump Nerdbank.GitVersioning from 3.1.74 to 3.1.91 Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.1.74 to 3.1.91. - [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases) - [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/commits) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 3764ace9c..19afa1025 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -7,7 +7,7 @@ - + From 78901fedacf937ac1f56bcd2e841c1aefa294e29 Mon Sep 17 00:00:00 2001 From: Tigran Topchyan Date: Wed, 29 Apr 2020 18:39:18 +0400 Subject: [PATCH 016/126] Update NuSpecs/Humanizer.Core.hy.nuspec Co-Authored-By: Claire Novotny --- NuSpecs/Humanizer.Core.hy.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NuSpecs/Humanizer.Core.hy.nuspec b/NuSpecs/Humanizer.Core.hy.nuspec index fa29282ea..7f97e698e 100644 --- a/NuSpecs/Humanizer.Core.hy.nuspec +++ b/NuSpecs/Humanizer.Core.hy.nuspec @@ -3,7 +3,7 @@ Humanizer.Core.ru $version$ - Humanizer Locale (ru) + Humanizer Locale (hy) Mehdi Khalili, Claire Novotny https://github.com/Humanizr/Humanizer logo.png @@ -22,4 +22,4 @@ - \ No newline at end of file + From e3913584170eeec04bd220a29be36b5476d3aca2 Mon Sep 17 00:00:00 2001 From: Tigran Topchyan Date: Wed, 29 Apr 2020 18:39:26 +0400 Subject: [PATCH 017/126] Update NuSpecs/Humanizer.Core.hy.nuspec Co-Authored-By: Claire Novotny --- NuSpecs/Humanizer.Core.hy.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuSpecs/Humanizer.Core.hy.nuspec b/NuSpecs/Humanizer.Core.hy.nuspec index 7f97e698e..0e3bdce76 100644 --- a/NuSpecs/Humanizer.Core.hy.nuspec +++ b/NuSpecs/Humanizer.Core.hy.nuspec @@ -1,7 +1,7 @@  - Humanizer.Core.ru + Humanizer.Core.hy $version$ Humanizer Locale (hy) Mehdi Khalili, Claire Novotny From 27c6fdc1eb85d1941f04e98b1ba685878675049e Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Sat, 2 May 2020 11:56:07 -0400 Subject: [PATCH 018/126] Add Armenian language support --- NuSpecs/Humanizer.nuspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index c03b6ddae..1c1824d20 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -29,6 +29,7 @@ + @@ -60,4 +61,4 @@ - \ No newline at end of file + From 2b8ae7bf1355af29a676970fd9587fe99773acdb Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Mon, 11 May 2020 10:55:59 -0400 Subject: [PATCH 019/126] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1b611dc0..76b18e3f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,11 +10,11 @@ to clarify expected behavior in our community. For more information see the [.NET Foundation Code of Conduct](http://www.dotnetfoundation.org/code-of-conduct). ### Getting started -This project uses C# 7 language features and SDK-style projects, so you'll need any edition of [Visual Studio 2017](https://www.visualstudio.com/downloads/download-visual-studio-vs) to open and compile the project. The free [Community Edition](https://go.microsoft.com/fwlink/?LinkId=532606&clcid=0x409) will work. +This project uses C# 8 language features and SDK-style projects, so you'll need any edition of [Visual Studio 2019](https://www.visualstudio.com/downloads/download-visual-studio-vs) to open and compile the project. The free [Community Edition](https://go.microsoft.com/fwlink/?LinkId=532606&clcid=0x409) will work. ### Contribution guideline This project uses [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html) for pull requests. -So if you want to contribute, fork the repo, preferably create a local branch, based off of the `dev` branch, to avoid conflicts with other activities, fix an issue, run build.cmd from the root of the project, and send a PR if all is green. +So if you want to contribute, fork the repo, preferably create a local branch, based off of the `master` branch, to avoid conflicts with other activities, fix an issue, run build.cmd from the root of the project, and send a PR if all is green. Pull requests are code reviewed. Here is a checklist you should tick through before submitting a pull request: From cb8b757893545ba8a76f309693e3f00c6159e51c Mon Sep 17 00:00:00 2001 From: Douwe Kerstens Date: Mon, 11 May 2020 16:57:23 +0200 Subject: [PATCH 020/126] Added support for converting big numbers to Dutch --- .../Localisation/nl/NumberToWordsTests.cs | 15 ++++++++++++- .../DutchNumberToWordsConverter.cs | 21 +++++++++---------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/nl/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/nl/NumberToWordsTests.cs index 4df6a1caf..69e58d247 100644 --- a/src/Humanizer.Tests.Shared/Localisation/nl/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/nl/NumberToWordsTests.cs @@ -46,7 +46,20 @@ public class NumberToWordsTests [InlineData(415618, "vierhonderdvijftienduizend zeshonderdachttien")] [InlineData(16415618, "zestien miljoen vierhonderdvijftienduizend zeshonderdachttien")] [InlineData(322, "driehonderdtweeëntwintig")] - public void ToWords(int number, string expected) + public void IntToWords(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(100_000_000_000L, "honderd miljard")] + [InlineData(1_000_000_000_000L, "een biljoen")] + [InlineData(100_000_000_000_000L, "honderd biljoen")] + [InlineData(1_000_000_000_000_000L, "een biljard")] + [InlineData(100_000_000_000_000_000L, "honderd biljard")] + [InlineData(1_000_000_000_000_000_000L, "een triljoen")] + [InlineData(9_223_372_036_854_775_807L, "negen triljoen tweehonderddrieëntwintig biljard driehonderdtweeënzeventig biljoen zesendertig miljard achthonderdvierenvijftig miljoen zevenhonderdvijfenzeventigduizend achthonderdzeven")] + public void LongToWords(long number, string expected) { Assert.Equal(expected, number.ToWords()); } diff --git a/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs index 75b89f828..67b3a15df 100644 --- a/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/DutchNumberToWordsConverter.cs @@ -17,7 +17,7 @@ internal class DutchNumberToWordsConverter : GenderlessNumberToWordsConverter private class Fact { - public int Value { get; set; } + public long Value { get; set; } public string Name { get; set; } public string Prefix { get; set; } public string Postfix { get; set; } @@ -26,19 +26,18 @@ private class Fact private static readonly Fact[] Hunderds = { - new Fact {Value = 1000000000, Name = "miljard", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, - new Fact {Value = 1000000, Name = "miljoen", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, - new Fact {Value = 1000, Name = "duizend", Prefix = "", Postfix = " ", DisplayOneUnit = false}, - new Fact {Value = 100, Name = "honderd", Prefix = "", Postfix = "", DisplayOneUnit = false} + new Fact {Value = 1_000_000_000_000_000_000L, Name = "triljoen", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, + new Fact {Value = 1_000_000_000_000_000L, Name = "biljard", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, + new Fact {Value = 1_000_000_000_000L, Name = "biljoen", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, + new Fact {Value = 1000000000, Name = "miljard", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, + new Fact {Value = 1000000, Name = "miljoen", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, + new Fact {Value = 1000, Name = "duizend", Prefix = "", Postfix = " ", DisplayOneUnit = false}, + new Fact {Value = 100, Name = "honderd", Prefix = "", Postfix = "", DisplayOneUnit = false} }; public override string Convert(long input) { - if (input > Int32.MaxValue || input < Int32.MinValue) - { - throw new NotImplementedException(); - } - var number = (int)input; + var number = input; if (number == 0) { @@ -133,4 +132,4 @@ public override string ConvertToOrdinal(int number) return word + "de"; } } -} \ No newline at end of file +} From 8600c33afb976117905eeeece4be41b1b8e95545 Mon Sep 17 00:00:00 2001 From: Phil Carbone Date: Fri, 15 May 2020 16:21:48 -0400 Subject: [PATCH 021/126] Making ByteSize IFormattable --- src/Humanizer/Bytes/ByteSize.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 2c9fe5e36..1a2597926 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -30,7 +30,7 @@ namespace Humanizer.Bytes /// Represents a byte size value. /// #pragma warning disable 1591 - public struct ByteSize : IComparable, IEquatable, IComparable + public struct ByteSize : IComparable, IEquatable, IComparable, IFormattable { public static readonly ByteSize MinValue = FromBits(long.MinValue); public static readonly ByteSize MaxValue = FromBits(long.MaxValue); From 977ad551ba4b318f1ef707f9d22b899df97027e5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 25 May 2020 06:10:57 +0000 Subject: [PATCH 022/126] Bump DiffPlex from 1.6.1 to 1.6.2 Bumps [DiffPlex](https://github.com/mmanela/diffplex) from 1.6.1 to 1.6.2. - [Release notes](https://github.com/mmanela/diffplex/releases) - [Commits](https://github.com/mmanela/diffplex/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index f6df35056..af3620230 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -17,7 +17,7 @@ - + From c32707b3777974258c92921d52016cceaf36fec1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2020 06:24:11 +0000 Subject: [PATCH 023/126] Bump DiffPlex from 1.6.2 to 1.6.3 Bumps [DiffPlex](https://github.com/mmanela/diffplex) from 1.6.2 to 1.6.3. - [Release notes](https://github.com/mmanela/diffplex/releases) - [Commits](https://github.com/mmanela/diffplex/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index af3620230..271513d5b 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -17,7 +17,7 @@ - + From 1d7526de9b789750bf1b652e694abad629a3c677 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2020 06:31:00 +0000 Subject: [PATCH 024/126] Bump coverlet.collector from 1.3.0-preview.6.ga0e22ec622 to 1.3.0 Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 1.3.0-preview.6.ga0e22ec622 to 1.3.0. - [Release notes](https://github.com/coverlet-coverage/coverlet/releases) - [Commits](https://github.com/coverlet-coverage/coverlet/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 271513d5b..d6a3d8266 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -8,7 +8,7 @@ $(DefineConstants);NETFX_CORE - + From 274889c99d7829aadd1b9db42524eb9825e0489b Mon Sep 17 00:00:00 2001 From: Kamran Asgarov Date: Tue, 2 Jun 2020 04:30:11 -0700 Subject: [PATCH 025/126] Add Azerbaijani language support --- NuSpecs/Humanizer.Core.az.nuspec | 25 ++ src/Humanizer.All.sln | 8 +- .../Humanizer.Tests.Shared.projitems | 3 + .../Localisation/az/DateHumanizeTests.cs | 113 ++++++++ .../Localisation/az/NumberToWordsTests.cs | 73 ++++++ .../Localisation/az/TimeSpanHumanizeTests.cs | 102 ++++++++ src/Humanizer.sln | 1 + .../Configuration/FormatterRegistry.cs | 1 + .../NumberToWordsConverterRegistry.cs | 1 + .../Configuration/OrdinalizerRegistry.cs | 1 + .../AzerbaijaniNumberToWordsConverter.cs | 113 ++++++++ .../Ordinalizers/AzerbaijaniOrdinalizer.cs | 10 + src/Humanizer/Properties/Resources.az.resx | 246 ++++++++++++++++++ 13 files changed, 694 insertions(+), 3 deletions(-) create mode 100644 NuSpecs/Humanizer.Core.az.nuspec create mode 100644 src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs create mode 100644 src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs create mode 100644 src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs create mode 100644 src/Humanizer/Properties/Resources.az.resx diff --git a/NuSpecs/Humanizer.Core.az.nuspec b/NuSpecs/Humanizer.Core.az.nuspec new file mode 100644 index 000000000..dd5367439 --- /dev/null +++ b/NuSpecs/Humanizer.Core.az.nuspec @@ -0,0 +1,25 @@ + + + + Humanizer.Core.az + $version$ + Humanizer Locale (az) + Mehdi Khalili, Claire Novotny + https://github.com/Humanizr/Humanizer + logo.png + false + Humanizer Locale Azerbaijani (az) + Copyright (c) .NET Foundation and Contributors + MIT + + az + + + + + + + + + + \ No newline at end of file diff --git a/src/Humanizer.All.sln b/src/Humanizer.All.sln index 0111bf446..c887ea6f0 100644 --- a/src/Humanizer.All.sln +++ b/src/Humanizer.All.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Humanizer.Tests", "Humanizer.Tests\Humanizer.Tests.csproj", "{F886A8DA-3EFC-4A89-91DD-06FAF13DA172}" EndProject @@ -87,7 +87,6 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{261367ca-00c7-4832-bd26-200cff8f575f}*SharedItemsImports = 4 Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{f0dc7771-ce59-4f3c-b160-56f661f5d220}*SharedItemsImports = 4 - Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{f886a8da-3efc-4a89-91dd-06faf13da172}*SharedItemsImports = 4 Humanizer.Tests.Shared\Humanizer.Tests.Shared.projitems*{fdec244b-f07e-4a5e-bb80-fbc6ac77a9aa}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -189,4 +188,7 @@ Global {F0DC7771-CE59-4F3C-B160-56F661F5D220} = {04B74BEC-A645-4D1A-BE21-F4EB4413A903} {AA449265-E001-486D-A0F4-04ACF0C83DC1} = {4779A7C9-9ED8-4146-A158-FBE0B1BE09D9} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E4063A70-EE14-49AE-A6C4-3108B1CA70EF} + EndGlobalSection EndGlobal diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index c39bb923a..2ca708a9f 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -40,6 +40,9 @@ + + + diff --git a/src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs new file mode 100644 index 000000000..6ad1ba80d --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/az/DateHumanizeTests.cs @@ -0,0 +1,113 @@ +using Humanizer.Localisation; +using Xunit; + +namespace Humanizer.Tests.Localisation.az +{ + [UseCulture("az")] + public class DateHumanizeTests + { + + [Theory] + [InlineData(1, "bir saniyə əvvəl")] + [InlineData(10, "10 saniyə əvvəl")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(1, "bir saniyə sonra")] + [InlineData(10, "10 saniyə sonra")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(1, "bir dəqiqə əvvəl")] + [InlineData(10, "10 dəqiqə əvvəl")] + [InlineData(60, "bir saat əvvəl")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "bir dəqiqə sonra")] + [InlineData(10, "10 dəqiqə sonra")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(1, "bir saat əvvəl")] + [InlineData(10, "10 saat əvvəl")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "bir saat sonra")] + [InlineData(10, "10 saat sonra")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(1, "dünən")] + [InlineData(10, "10 gün əvvəl")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "sabah")] + [InlineData(10, "10 gün sonra")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(1, "bir ay əvvəl")] + [InlineData(10, "10 ay əvvəl")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "bir ay sonra")] + [InlineData(10, "10 ay sonra")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(1, "bir il əvvəl")] + [InlineData(2, "2 il əvvəl")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "bir il sonra")] + [InlineData(2, "2 il sonra")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + + [Fact] + public void Now() + { + DateHumanize.Verify("indi", 0, TimeUnit.Year, Tense.Future); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs new file mode 100644 index 000000000..587cbd8ca --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs @@ -0,0 +1,73 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.az +{ + [UseCulture("az")] + public class NumberToWordsTests + { + [Theory] + [InlineData("sıfır", 0)] + [InlineData("bir", 1)] + [InlineData("iki", 2)] + [InlineData("on", 10)] + [InlineData("yüz on iki", 112)] + [InlineData("min dörd yüz qırx", 1440)] + [InlineData("yirmi iki", 22)] + [InlineData("on bir", 11)] + [InlineData("üç min beş yüz bir", 3501)] + [InlineData("bir milyon bir", 1000001)] + [InlineData("mənfi bir milyon üç yüz qırx altı min yeddi yüz on bir", -1346711)] + public void ToWords(string expected, int number) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(0, "sıfırıncı")] + [InlineData(1, "birinci")] + [InlineData(2, "ikinci")] + [InlineData(3, "üçüncü")] + [InlineData(4, "dördüncü")] + [InlineData(5, "beşinci")] + [InlineData(6, "altıncı")] + [InlineData(7, "yeddinci")] + [InlineData(8, "səkkizinci")] + [InlineData(9, "doqquzuncu")] + [InlineData(10, "onuncu")] + [InlineData(11, "on birinci")] + [InlineData(12, "on ikinci")] + [InlineData(13, "on üçüncü")] + [InlineData(14, "on dördüncü")] + [InlineData(15, "on beşinci")] + [InlineData(16, "on altıncı")] + [InlineData(17, "on yeddinci")] + [InlineData(18, "on səkkizinci")] + [InlineData(19, "on doqquzuncu")] + [InlineData(20, "yirminci")] + [InlineData(21, "yirmi birinci")] + [InlineData(30, "otuzuncu")] + [InlineData(40, "qırxıncı")] + [InlineData(50, "əllinci")] + [InlineData(60, "altmışıncı")] + [InlineData(70, "yetmişinci")] + [InlineData(80, "səksəninci")] + [InlineData(90, "doxsanıncı")] + [InlineData(100, "yüzüncü")] + [InlineData(120, "yüz yirminci")] + [InlineData(121, "yüz yirmi birinci")] + [InlineData(200, "iki yüzüncü")] + [InlineData(221, "iki yüz yirmi birinci")] + [InlineData(300, "üç yüzüncü")] + [InlineData(321, "üç yüz yirmi birinci")] + [InlineData(1000, "mininci")] + [InlineData(1001, "min birinci")] + [InlineData(10000, "on mininci")] + [InlineData(100000, "yüz mininci")] + [InlineData(1000000, "bir milyonuncu")] + [InlineData(1022135, "bir milyon yirmi iki min yüz otuz beşinci")] + public void ToOrdinalWords(int number, string words) + { + Assert.Equal(words, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..87a691aca --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/az/TimeSpanHumanizeTests.cs @@ -0,0 +1,102 @@ +using System; +using Xunit; + +namespace Humanizer.Tests.Localisation.az +{ + [UseCulture("az")] + public class TimeSpanHumanizeTests + { + + [Theory] + [Trait("Translation", "Google")] + [InlineData(366, "1 il")] + [InlineData(731, "2 il")] + [InlineData(1096, "3 il")] + [InlineData(4018, "11 il")] + public void Years(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [Trait("Translation", "Google")] + [InlineData(31, "1 ay")] + [InlineData(61, "2 ay")] + [InlineData(92, "3 ay")] + [InlineData(335, "11 ay")] + public void Months(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [InlineData(14, "2 həftə")] + [InlineData(7, "1 həftə")] + public void Weeks(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(6, "6 gün")] + [InlineData(2, "2 gün")] + public void Days(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 saat")] + [InlineData(1, "1 saat")] + public void Hours(int hours, string expected) + { + var actual = TimeSpan.FromHours(hours).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 dəqiqə")] + [InlineData(1, "1 dəqiqə")] + public void Minutes(int minutes, string expected) + { + var actual = TimeSpan.FromMinutes(minutes).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 saniyə")] + [InlineData(1, "1 saniyə")] + public void Seconds(int seconds, string expected) + { + var actual = TimeSpan.FromSeconds(seconds).Humanize(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(2, "2 millisaniyə")] + [InlineData(1, "1 millisaniyə")] + public void Milliseconds(int ms, string expected) + { + var actual = TimeSpan.FromMilliseconds(ms).Humanize(); + Assert.Equal(expected, actual); + } + + [Fact] + public void NoTime() + { + var noTime = TimeSpan.Zero; + var actual = noTime.Humanize(); + Assert.Equal("0 millisaniyə", actual); + } + + [Fact] + public void NoTimeToWords() + { + var noTime = TimeSpan.Zero; + var actual = noTime.Humanize(toWords: true); + Assert.Equal("zaman fərqi yoxdur", actual); + } + } +} diff --git a/src/Humanizer.sln b/src/Humanizer.sln index c633f5497..dccb47691 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ProjectSection(SolutionItems) = preProject ..\NuSpecs\Humanizer.Core.af.nuspec = ..\NuSpecs\Humanizer.Core.af.nuspec ..\NuSpecs\Humanizer.Core.ar.nuspec = ..\NuSpecs\Humanizer.Core.ar.nuspec + ..\NuSpecs\Humanizer.Core.az.nuspec = ..\NuSpecs\Humanizer.Core.az.nuspec ..\NuSpecs\Humanizer.Core.bg.nuspec = ..\NuSpecs\Humanizer.Core.bg.nuspec ..\NuSpecs\Humanizer.Core.bn-BD.nuspec = ..\NuSpecs\Humanizer.Core.bn-BD.nuspec ..\NuSpecs\Humanizer.Core.cs.nuspec = ..\NuSpecs\Humanizer.Core.cs.nuspec diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index 2b048cfdb..510e25b73 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -27,6 +27,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterDefaultFormatter("vi"); RegisterDefaultFormatter("en-US"); RegisterDefaultFormatter("af"); + RegisterDefaultFormatter("az"); RegisterDefaultFormatter("da"); RegisterDefaultFormatter("de"); RegisterDefaultFormatter("el"); diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index 5e1603d76..c874f11bd 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -44,6 +44,7 @@ public NumberToWordsConverterRegistry() Register("zh-CN", new ChineseNumberToWordsConverter()); Register("bg", new BulgarianNumberToWordsConverter()); Register("hy", new ArmenianNumberToWordsConverter()); + Register("az", new AzerbaijaniNumberToWordsConverter()); } } } diff --git a/src/Humanizer/Configuration/OrdinalizerRegistry.cs b/src/Humanizer/Configuration/OrdinalizerRegistry.cs index ba3224c68..175ec19c7 100644 --- a/src/Humanizer/Configuration/OrdinalizerRegistry.cs +++ b/src/Humanizer/Configuration/OrdinalizerRegistry.cs @@ -18,6 +18,7 @@ public OrdinalizerRegistry() : base(new DefaultOrdinalizer()) Register("tr", new TurkishOrdinalizer()); Register("uk", new UkrainianOrdinalizer()); Register("hy", new ArmenianOrdinalizer()); + Register("az", new AzerbaijaniOrdinalizer()); } } } diff --git a/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs new file mode 100644 index 000000000..c6fe167e6 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class AzerbaijaniNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private static readonly string[] UnitsMap = { "sıfır", "bir", "iki", "üç", "dörd", "beş", "altı", "yeddi", "səkkiz", "doqquz" }; + private static readonly string[] TensMap = { "sıfır", "on", "yirmi", "otuz", "qırx", "əlli", "altmış", "yetmiş", "səksən", "doxsan" }; + + private static readonly Dictionary OrdinalSuffix = new Dictionary + { + {'ı', "ıncı"}, + {'i', "inci"}, + {'u', "uncu"}, + {'ü', "üncü"}, + {'o', "uncu"}, + {'ö', "üncü"}, + {'e', "inci"}, + {'a', "ıncı"}, + {'ə', "inci"}, + }; + + public override string Convert(long input) + { + if (input > Int32.MaxValue || input < Int32.MinValue) + { + throw new NotImplementedException(); + } + var number = (int)input; + if (number == 0) + { + return UnitsMap[0]; + } + + if (number < 0) + { + return string.Format("mənfi {0}", Convert(-number)); + } + + var parts = new List(); + + if ((number / 1000000000) > 0) + { + parts.Add(string.Format("{0} milyard", Convert(number / 1000000000))); + number %= 1000000000; + } + + if ((number / 1000000) > 0) + { + parts.Add(string.Format("{0} milyon", Convert(number / 1000000))); + number %= 1000000; + } + + var thousand = (number / 1000); + if (thousand > 0) + { + parts.Add(string.Format("{0} min", thousand > 1 ? Convert(thousand) : "").Trim()); + number %= 1000; + } + + var hundred = (number / 100); + if (hundred > 0) + { + parts.Add(string.Format("{0} yüz", hundred > 1 ? Convert(hundred) : "").Trim()); + number %= 100; + } + + if ((number / 10) > 0) + { + parts.Add(TensMap[number / 10]); + number %= 10; + } + + if (number > 0) + { + parts.Add(UnitsMap[number]); + } + + var toWords = string.Join(" ", parts.ToArray()); + + return toWords; + } + + public override string ConvertToOrdinal(int number) + { + var word = Convert(number); + var wordSuffix = string.Empty; + var suffixFoundOnLastVowel = false; + + for (var i = word.Length - 1; i >= 0; i--) + { + if (OrdinalSuffix.TryGetValue(word[i], out wordSuffix)) + { + suffixFoundOnLastVowel = i == word.Length - 1; + break; + } + } + + if (word[word.Length - 1] == 't') + { + word = word.Substring(0, word.Length - 1) + 'd'; + } + + if (suffixFoundOnLastVowel) + { + word = word.Substring(0, word.Length - 1); + } + + return string.Format("{0}{1}", word, wordSuffix); + } + } +} diff --git a/src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs b/src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs new file mode 100644 index 000000000..2e7fe6ce2 --- /dev/null +++ b/src/Humanizer/Localisation/Ordinalizers/AzerbaijaniOrdinalizer.cs @@ -0,0 +1,10 @@ +namespace Humanizer.Localisation.Ordinalizers +{ + internal class AzerbaijaniOrdinalizer : DefaultOrdinalizer + { + public override string Convert(int number, string numberString) + { + return numberString + "."; + } + } +} diff --git a/src/Humanizer/Properties/Resources.az.resx b/src/Humanizer/Properties/Resources.az.resx new file mode 100644 index 000000000..670fb9788 --- /dev/null +++ b/src/Humanizer/Properties/Resources.az.resx @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bir saniyə əvvəl + + + {0} saniyə əvvəl + + + bir dəqiqə əvvəl + + + {0} dəqiqə əvvəl + + + bir saat əvvəl + + + {0} saat əvvəl + + + dünən + + + {0} gün əvvəl + + + bir ay əvvəl + + + {0} ay əvvəl + + + bir il əvvəl + + + {0} il əvvəl + + + {0} gün + + + {0} saat + + + {0} millisaniyə + + + {0} dəqiqə + + + {0} saniyə + + + 1 gün + + + 1 saat + + + 1 millisaniyə + + + 1 dəqiqə + + + 1 saniyə + + + zaman fərqi yoxdur + + + {0} həftə + + + 1 həftə + + + {0} gün sonra + + + {0} saat sonra + + + {0} dəqiqə sonra + + + {0} ay sonra + + + {0} saniyə sonra + + + {0} il sonra + + + indi + + + sabah + + + bir saat sonra + + + bir dəqiqə sonra + + + bir ay sonra + + + bir saniyə sonra + + + bir il sonra + + + {0} ay + + + {0} il + + + 1 ay + + + 1 il + + \ No newline at end of file From 3aacc2108d122a420698d6d80e9a85ea528c6706 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Tue, 2 Jun 2020 08:25:35 -0400 Subject: [PATCH 026/126] Remove workarounds --- NuGet.config | 8 -------- src/Directory.Build.targets | 10 +--------- src/Directory.build.props | 1 - 3 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 NuGet.config diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index baca44cbf..000000000 --- a/NuGet.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 6760c3c20..12c61a902 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,13 +1,5 @@ - - - $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) - - - - - + latest true true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb From b9ab6e02e6795dc1fe561144efef365cb343d5be Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Tue, 2 Jun 2020 08:34:14 -0400 Subject: [PATCH 027/126] Add missing doc tag values to fix warnings --- src/Humanizer/HeadingExtensions.cs | 1 + src/Humanizer/InflectorExtensions.cs | 2 -- src/Humanizer/TimeSpanHumanizeExtensions.cs | 2 ++ src/Humanizer/ToQuantityExtensions.cs | 11 ++++++++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Humanizer/HeadingExtensions.cs b/src/Humanizer/HeadingExtensions.cs index 2bb524874..a897427e7 100644 --- a/src/Humanizer/HeadingExtensions.cs +++ b/src/Humanizer/HeadingExtensions.cs @@ -36,6 +36,7 @@ public static class HeadingExtensions /// This representation has a maximum deviation of 11.25 degrees. /// /// A textual representation of the heading + /// The heading value /// The culture to return the textual representation in /// Whether to return a short result or not. public static string ToHeading(this double heading, HeadingStyle style = HeadingStyle.Abbreviated, CultureInfo culture = null) diff --git a/src/Humanizer/InflectorExtensions.cs b/src/Humanizer/InflectorExtensions.cs index 4e0e554ca..42bb8cb5d 100644 --- a/src/Humanizer/InflectorExtensions.cs +++ b/src/Humanizer/InflectorExtensions.cs @@ -68,7 +68,6 @@ public static string Titleize(this string input) /// By default, pascalize converts strings to UpperCamelCase also removing underscores /// /// - /// "This has a space" -> "ThisHasASpace" if true /// public static string Pascalize(this string input) { @@ -79,7 +78,6 @@ public static string Pascalize(this string input) /// Same as Pascalize except that the first character is lower case /// /// - /// "This has a space" -> "thisHasASpace" if true /// public static string Camelize(this string input) { diff --git a/src/Humanizer/TimeSpanHumanizeExtensions.cs b/src/Humanizer/TimeSpanHumanizeExtensions.cs index f02e2b4f5..7ab751898 100644 --- a/src/Humanizer/TimeSpanHumanizeExtensions.cs +++ b/src/Humanizer/TimeSpanHumanizeExtensions.cs @@ -26,6 +26,7 @@ public static class TimeSpanHumanizeExtensions /// The maximum unit of time to output. The default value is . The time units and will give approximations for time spans bigger 30 days by calculating with 365.2425 days a year and 30.4369 days a month. /// The minimum unit of time to output. /// The separator to use when combining humanized time parts. If null, the default collection formatter for the current culture is used. + /// Uses words instead of numbers if true. E.g. one day. /// public static string Humanize(this TimeSpan timeSpan, int precision = 1, CultureInfo culture = null, TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false) { @@ -42,6 +43,7 @@ public static string Humanize(this TimeSpan timeSpan, int precision = 1, Culture /// The maximum unit of time to output. The default value is . The time units and will give approximations for time spans bigger than 30 days by calculating with 365.2425 days a year and 30.4369 days a month. /// The minimum unit of time to output. /// The separator to use when combining humanized time parts. If null, the default collection formatter for the current culture is used. + /// Uses words instead of numbers if true. E.g. one day. /// public static string Humanize(this TimeSpan timeSpan, int precision, bool countEmptyUnits, CultureInfo culture = null, TimeUnit maxUnit = TimeUnit.Week, TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false) { diff --git a/src/Humanizer/ToQuantityExtensions.cs b/src/Humanizer/ToQuantityExtensions.cs index ac40dbfba..58c90d0a2 100644 --- a/src/Humanizer/ToQuantityExtensions.cs +++ b/src/Humanizer/ToQuantityExtensions.cs @@ -145,7 +145,16 @@ public static string ToQuantity(this string input, double quantity, string forma return string.Format(formatProvider, "{0} {1}", quantity.ToString(format, formatProvider), transformedInput); } - + + /// + /// Prefixes the provided word with the number and accordingly pluralizes or singularizes the word + /// + /// The word to be prefixed + /// The quantity of the word + /// + /// "request".ToQuantity(0.2) => "0.2 requests" + /// + /// public static string ToQuantity(this string input, double quantity) { return ToQuantity(input, quantity, null, null); From 7f07276dab51366f54def454d01f0e0de8c918fa Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Tue, 2 Jun 2020 09:02:38 -0400 Subject: [PATCH 028/126] Add .az to main package --- NuSpecs/Humanizer.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index 1c1824d20..a77b4c94f 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -15,6 +15,7 @@ + From 19ba7c89f4c5d88a5b7abc23e762af118410b7f9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2020 06:04:02 +0000 Subject: [PATCH 029/126] Bump xunit.runner.visualstudio from 2.4.1 to 2.4.2 Bumps [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 2.4.1 to 2.4.2. - [Release notes](https://github.com/xunit/visualstudio.xunit/releases) - [Commits](https://github.com/xunit/visualstudio.xunit/commits/v2.4.2) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj | 2 +- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj index c501b9474..03f514df4 100644 --- a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj +++ b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj @@ -129,7 +129,7 @@ Humanizer - + diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index d6a3d8266..86120e85f 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -10,7 +10,7 @@ - + From 60877d7c397416bc57c06d679db84d428528d729 Mon Sep 17 00:00:00 2001 From: Xilent Date: Thu, 25 Jun 2020 00:01:05 -0400 Subject: [PATCH 030/126] fix #950 --- src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs | 12 +++++++++++- .../Localisation/Formatters/DefaultFormatter.cs | 2 +- src/Humanizer/TimeSpanHumanizeExtensions.cs | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs index 0062b699e..cacdb66e7 100644 --- a/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs @@ -364,7 +364,7 @@ public void TimeSpanWithPrecisionAndCountingEmptyUnits(int milliseconds, int pre [InlineData(1299630020, 3, "2 weeks, 1 day, and 1 hour")] [InlineData(1299630020, 4, "2 weeks, 1 day, 1 hour, and 30 seconds")] [InlineData(1299630020, 5, "2 weeks, 1 day, 1 hour, 30 seconds, and 20 milliseconds")] - public void TimeSpanWithPrecisionAndAlternativeCollectionFormatter(int milliseconds, int precision, + public void TimeSpanWithPrecisionAndAlternativeCollectionFormatter(int milliseconds, int precision, string expected, bool toWords = false) { var actual = TimeSpan.FromMilliseconds(milliseconds).Humanize(precision, collectionSeparator: null, toWords: toWords); @@ -433,5 +433,15 @@ public void CanSpecifyCultureExplicitly(int ms, int precision, string culture, s var actual = TimeSpan.FromMilliseconds(ms).Humanize(precision: precision, culture: new CultureInfo(culture), collectionSeparator: collectionSeparator); Assert.Equal(expected, actual); } + [Theory] + [InlineData(31 * 4, 1, "en-US", "four months")] + [InlineData(236,2,"ar", "سبعة أشهر, اثنان و عشرون يوم")] + [InlineData(321, 2,"es", "diez meses, dieciséis días")] + public void CanSpecifyCultureExplicitlyToWords(int days, int precision,string culture, string expected) + { + var timeSpan = new TimeSpan(days, 0, 0, 0); + var actual = timeSpan.Humanize(precision: precision, culture: new CultureInfo(culture), maxUnit: TimeUnit.Year, toWords: true); + Assert.Equal(expected: expected, actual); + } } } diff --git a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs index 341437042..2efe8d73b 100644 --- a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs @@ -119,7 +119,7 @@ protected virtual string Format(string resourceKey, int number, bool toWords = f } return toWords - ? resourceString.FormatWith(number.ToWords()) + ? resourceString.FormatWith(number.ToWords(_culture)) : resourceString.FormatWith(number); } diff --git a/src/Humanizer/TimeSpanHumanizeExtensions.cs b/src/Humanizer/TimeSpanHumanizeExtensions.cs index 7ab751898..73cfeafb7 100644 --- a/src/Humanizer/TimeSpanHumanizeExtensions.cs +++ b/src/Humanizer/TimeSpanHumanizeExtensions.cs @@ -62,7 +62,7 @@ private static IEnumerable CreateTheTimePartsWithUpperAndLowerLimits(Tim foreach (var timeUnitType in timeUnitsEnumTypes) { - var timepart = GetTimeUnitPart(timeUnitType, timespan, culture, maxUnit, minUnit, cultureFormatter, toWords); + var timepart = GetTimeUnitPart(timeUnitType,culture, timespan, maxUnit, minUnit, cultureFormatter, toWords); if (timepart != null || firstValueFound) { @@ -85,7 +85,7 @@ private static IEnumerable GetEnumTypesForTimeUnit() return enumTypeEnumerator.Reverse(); } - private static string GetTimeUnitPart(TimeUnit timeUnitToGet, TimeSpan timespan, CultureInfo culture, TimeUnit maximumTimeUnit, TimeUnit minimumTimeUnit, IFormatter cultureFormatter, bool toWords = false) + private static string GetTimeUnitPart(TimeUnit timeUnitToGet,CultureInfo culture, TimeSpan timespan, TimeUnit maximumTimeUnit, TimeUnit minimumTimeUnit, IFormatter cultureFormatter, bool toWords = false) { if (timeUnitToGet <= maximumTimeUnit && timeUnitToGet >= minimumTimeUnit) { From 0e5e1a33a65a7f6ba35da12fb4c8e9a36f84b875 Mon Sep 17 00:00:00 2001 From: Xilent Date: Thu, 25 Jun 2020 00:23:13 -0400 Subject: [PATCH 031/126] removed unused parameter --- src/Humanizer/TimeSpanHumanizeExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Humanizer/TimeSpanHumanizeExtensions.cs b/src/Humanizer/TimeSpanHumanizeExtensions.cs index 73cfeafb7..0ac06b718 100644 --- a/src/Humanizer/TimeSpanHumanizeExtensions.cs +++ b/src/Humanizer/TimeSpanHumanizeExtensions.cs @@ -62,7 +62,7 @@ private static IEnumerable CreateTheTimePartsWithUpperAndLowerLimits(Tim foreach (var timeUnitType in timeUnitsEnumTypes) { - var timepart = GetTimeUnitPart(timeUnitType,culture, timespan, maxUnit, minUnit, cultureFormatter, toWords); + var timepart = GetTimeUnitPart(timeUnitType,timespan, maxUnit, minUnit, cultureFormatter, toWords); if (timepart != null || firstValueFound) { @@ -85,7 +85,7 @@ private static IEnumerable GetEnumTypesForTimeUnit() return enumTypeEnumerator.Reverse(); } - private static string GetTimeUnitPart(TimeUnit timeUnitToGet,CultureInfo culture, TimeSpan timespan, TimeUnit maximumTimeUnit, TimeUnit minimumTimeUnit, IFormatter cultureFormatter, bool toWords = false) + private static string GetTimeUnitPart(TimeUnit timeUnitToGet, TimeSpan timespan, TimeUnit maximumTimeUnit, TimeUnit minimumTimeUnit, IFormatter cultureFormatter, bool toWords = false) { if (timeUnitToGet <= maximumTimeUnit && timeUnitToGet >= minimumTimeUnit) { From 9c186937452c0dd1fa65f500c498a5b158654b38 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Jul 2020 17:44:53 +0900 Subject: [PATCH 032/126] Fix ByteSize not handling default formatting correctly --- src/Humanizer/Bytes/ByteSize.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 1a2597926..0edb4a5dc 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -220,9 +220,7 @@ public override string ToString() public string ToString(IFormatProvider provider) { if (provider == null) - { - throw new ArgumentNullException(nameof(provider)); - } + provider = CultureInfo.CurrentCulture; return string.Format("{0} {1}", LargestWholeNumberValue.ToString(provider), LargestWholeNumberSymbol); } @@ -235,9 +233,13 @@ public string ToString(string format) public string ToString(string format, IFormatProvider provider) { if (format == null) - throw new ArgumentNullException(nameof(format)); + format = "G"; + if (provider == null) - throw new ArgumentNullException(nameof(provider)); + provider = CultureInfo.CurrentCulture; + + if (format == "G") + format = "0.##"; if (!format.Contains("#") && !format.Contains("0")) { From c51490777b1c426c8d8be3ecac6c40a828caf1dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Jul 2020 17:57:44 +0900 Subject: [PATCH 033/126] Actually commit test --- src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs b/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs index b22faa657..2426c3954 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs @@ -99,5 +99,11 @@ public void ReturnsLargestMetricPrefixLargerThanZeroForNegativeValues() { Assert.Equal("-512 KB", ByteSize.FromMegabytes(-.5).ToString("#.#")); } + + [Fact] + public void ReturnsBytesViaGeneralFormat() + { + Assert.Equal("10 B", $"{10.Bytes()}"); + } } } From f4d71321e884c17de892a59f78476c0af6f7cb9a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Jul 2020 18:01:58 +0900 Subject: [PATCH 034/126] Use FromBytes() for conformity --- src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs b/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs index 2426c3954..4bc8b9a73 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs @@ -21,6 +21,7 @@ //THE SOFTWARE. using Humanizer.Bytes; + using Xunit; namespace Humanizer.Tests.Bytes @@ -103,7 +104,7 @@ public void ReturnsLargestMetricPrefixLargerThanZeroForNegativeValues() [Fact] public void ReturnsBytesViaGeneralFormat() { - Assert.Equal("10 B", $"{10.Bytes()}"); + Assert.Equal("10 B", $"{ByteSize.FromBytes(10)}"); } } } From 3067de8e565a80b66bad05dda49fdc8a354078d5 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 14 Jul 2020 10:34:27 +0900 Subject: [PATCH 035/126] Remove whitespace --- src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs b/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs index 4bc8b9a73..7463f91b9 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ToStringTests.cs @@ -21,7 +21,6 @@ //THE SOFTWARE. using Humanizer.Bytes; - using Xunit; namespace Humanizer.Tests.Bytes From 6a0cd3f1ea2f0ebe43b5c6b57ca753e266eaccdd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2020 05:50:53 +0000 Subject: [PATCH 036/126] Bump Nerdbank.GitVersioning from 3.1.91 to 3.2.31 Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.1.91 to 3.2.31. - [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases) - [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/compare/v3.1.91...v3.2.31) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index c2752444d..61f778234 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -6,7 +6,7 @@ - + From e2da435d967aba1e644abfdffc6a727dc417c94b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2020 05:51:58 +0000 Subject: [PATCH 037/126] Bump xunit.runner.visualstudio from 2.4.2 to 2.4.3 Bumps [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 2.4.2 to 2.4.3. - [Release notes](https://github.com/xunit/visualstudio.xunit/releases) - [Commits](https://github.com/xunit/visualstudio.xunit/compare/v2.4.2...v2.4.3) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj | 2 +- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj index 03f514df4..4602da095 100644 --- a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj +++ b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj @@ -129,7 +129,7 @@ Humanizer - + diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 86120e85f..59ec8e670 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -10,7 +10,7 @@ - + From aea32909d5281fb1774c7d549e185d0398145631 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2020 05:55:06 +0000 Subject: [PATCH 038/126] Bump Microsoft.NET.Test.Sdk from 16.6.1 to 16.7.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.6.1 to 16.7.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.6.1...v16.7.0) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 59ec8e670..ba32c6dde 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 70672110a268dc3733aa9831fde7e132f4aec050 Mon Sep 17 00:00:00 2001 From: Vladimir Sharanin Date: Thu, 20 Aug 2020 11:10:04 +0300 Subject: [PATCH 039/126] fixes #963 Fixed Swedish number translations after 100 --- .../NumberToWords/SwedishNumberToWordsConverter.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs index 1219bc42a..072b96a8d 100644 --- a/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs @@ -166,7 +166,8 @@ public override string ConvertToOrdinal(int number) { word += tens + "de"; } - else { + else + { word += tens + "nde"; } @@ -192,8 +193,10 @@ public override string ConvertToOrdinal(int number) word += Convert(divided, m.Gender) + m.Prefix + m.Name; } + number %= m.Value; + // suffix -de/-te - if (divided > 0) + if (divided > 0 && number == 0) { switch (number) { @@ -206,10 +209,9 @@ public override string ConvertToOrdinal(int number) } } - number %= m.Value; if (number > 0) { - word += m.Postfix; + word += ConvertToOrdinal(number); } } From 86ffb29b548acebe8bb328c6dd465d54a4db902d Mon Sep 17 00:00:00 2001 From: Vladimir Sharanin Date: Thu, 20 Aug 2020 12:00:15 +0300 Subject: [PATCH 040/126] fixes #963 Added tests and set change number after suffix --- .../Localisation/sv/NumberToWordsTests.cs | 6 ++++++ .../NumberToWords/SwedishNumberToWordsConverter.cs | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/sv/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/sv/NumberToWordsTests.cs index bb29edd98..39b3d3399 100644 --- a/src/Humanizer.Tests.Shared/Localisation/sv/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/sv/NumberToWordsTests.cs @@ -27,6 +27,9 @@ public class NumberToWordsTests [InlineData(90, "nittio")] [InlineData(100, "hundra")] [InlineData(200, "tvåhundra")] + [InlineData(201, "tvåhundraett")] + [InlineData(211, "tvåhundraelva")] + [InlineData(221, "tvåhundratjugoett")] [InlineData(1000, "ett tusen")] [InlineData(10000, "tio tusen")] [InlineData(100000, "hundra tusen")] @@ -67,6 +70,9 @@ public void ToWords(long number, string expected) [InlineData(90, "nittionde")] [InlineData(100, "hundrade")] [InlineData(200, "tvåhundrade")] + [InlineData(201, "tvåhundraförsta")] + [InlineData(211, "tvåhundraelfte")] + [InlineData(221, "tvåhundratjugoförsta")] [InlineData(1000, "ett tusende")] [InlineData(10000, "tio tusende")] [InlineData(100000, "hundra tusende")] diff --git a/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs index 072b96a8d..b5c150810 100644 --- a/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs @@ -193,10 +193,8 @@ public override string ConvertToOrdinal(int number) word += Convert(divided, m.Gender) + m.Prefix + m.Name; } - number %= m.Value; - // suffix -de/-te - if (divided > 0 && number == 0) + if (divided > 0 && (number % m.Value) == 0) { switch (number) { @@ -209,6 +207,7 @@ public override string ConvertToOrdinal(int number) } } + number %= m.Value; if (number > 0) { word += ConvertToOrdinal(number); From f32e9a0d25cc2bdfa149357e528f693d19d7c4c9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 05:31:17 +0000 Subject: [PATCH 041/126] Bump Microsoft.NET.Test.Sdk from 16.7.0 to 16.7.1 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.7.0 to 16.7.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.7.0...v16.7.1) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index ba32c6dde..bdfd5f692 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 2c45ee4f67274662ffc390c09e829c82718b578d Mon Sep 17 00:00:00 2001 From: Kamran Asgarov Date: Fri, 21 Aug 2020 00:08:45 -0800 Subject: [PATCH 042/126] fix mistake in number 20 to word (az) --- .../Localisation/az/NumberToWordsTests.cs | 16 ++++++++-------- .../AzerbaijaniNumberToWordsConverter.cs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs index 587cbd8ca..7e4105e86 100644 --- a/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/az/NumberToWordsTests.cs @@ -12,7 +12,7 @@ public class NumberToWordsTests [InlineData("on", 10)] [InlineData("yüz on iki", 112)] [InlineData("min dörd yüz qırx", 1440)] - [InlineData("yirmi iki", 22)] + [InlineData("iyirmi iki", 22)] [InlineData("on bir", 11)] [InlineData("üç min beş yüz bir", 3501)] [InlineData("bir milyon bir", 1000001)] @@ -43,8 +43,8 @@ public void ToWords(string expected, int number) [InlineData(17, "on yeddinci")] [InlineData(18, "on səkkizinci")] [InlineData(19, "on doqquzuncu")] - [InlineData(20, "yirminci")] - [InlineData(21, "yirmi birinci")] + [InlineData(20, "iyirminci")] + [InlineData(21, "iyirmi birinci")] [InlineData(30, "otuzuncu")] [InlineData(40, "qırxıncı")] [InlineData(50, "əllinci")] @@ -53,18 +53,18 @@ public void ToWords(string expected, int number) [InlineData(80, "səksəninci")] [InlineData(90, "doxsanıncı")] [InlineData(100, "yüzüncü")] - [InlineData(120, "yüz yirminci")] - [InlineData(121, "yüz yirmi birinci")] + [InlineData(120, "yüz iyirminci")] + [InlineData(121, "yüz iyirmi birinci")] [InlineData(200, "iki yüzüncü")] - [InlineData(221, "iki yüz yirmi birinci")] + [InlineData(221, "iki yüz iyirmi birinci")] [InlineData(300, "üç yüzüncü")] - [InlineData(321, "üç yüz yirmi birinci")] + [InlineData(321, "üç yüz iyirmi birinci")] [InlineData(1000, "mininci")] [InlineData(1001, "min birinci")] [InlineData(10000, "on mininci")] [InlineData(100000, "yüz mininci")] [InlineData(1000000, "bir milyonuncu")] - [InlineData(1022135, "bir milyon yirmi iki min yüz otuz beşinci")] + [InlineData(1022135, "bir milyon iyirmi iki min yüz otuz beşinci")] public void ToOrdinalWords(int number, string words) { Assert.Equal(words, number.ToOrdinalWords()); diff --git a/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs index c6fe167e6..0afb93a07 100644 --- a/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/AzerbaijaniNumberToWordsConverter.cs @@ -6,7 +6,7 @@ namespace Humanizer.Localisation.NumberToWords internal class AzerbaijaniNumberToWordsConverter : GenderlessNumberToWordsConverter { private static readonly string[] UnitsMap = { "sıfır", "bir", "iki", "üç", "dörd", "beş", "altı", "yeddi", "səkkiz", "doqquz" }; - private static readonly string[] TensMap = { "sıfır", "on", "yirmi", "otuz", "qırx", "əlli", "altmış", "yetmiş", "səksən", "doxsan" }; + private static readonly string[] TensMap = { "sıfır", "on", "iyirmi", "otuz", "qırx", "əlli", "altmış", "yetmiş", "səksən", "doxsan" }; private static readonly Dictionary OrdinalSuffix = new Dictionary { From 97292830a5e68aecccfa133bd9ecca88b116fce6 Mon Sep 17 00:00:00 2001 From: iskcal Date: Mon, 24 Aug 2020 01:12:00 +0800 Subject: [PATCH 043/126] Compare two DateTime object with the same DateTimeKind --- src/Humanizer.Tests.Shared/DateHumanize.cs | 6 +++++- src/Humanizer/DateHumanizeExtensions.cs | 19 ++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Humanizer.Tests.Shared/DateHumanize.cs b/src/Humanizer.Tests.Shared/DateHumanize.cs index edfd67c1a..ce6b6c796 100644 --- a/src/Humanizer.Tests.Shared/DateHumanize.cs +++ b/src/Humanizer.Tests.Shared/DateHumanize.cs @@ -32,6 +32,10 @@ private static void VerifyWithDate(string expectedString, TimeSpan deltaFromBase { Assert.Equal(expectedString, baseDateUtc.Add(deltaFromBase).Humanize(utcDate: true, dateToCompareAgainst: baseDateUtc, culture: culture)); Assert.Equal(expectedString, baseDate.Add(deltaFromBase).Humanize(false, baseDate, culture: culture)); + + // Compared with default utcDate + Assert.Equal(expectedString, baseDateUtc.Add(deltaFromBase).Humanize(utcDate: null, dateToCompareAgainst: baseDateUtc, culture: culture)); + Assert.Equal(expectedString, baseDate.Add(deltaFromBase).Humanize(null, baseDate, culture: culture)); } public static void Verify(string expectedString, int unit, TimeUnit timeUnit, Tense tense, double? precision = null, CultureInfo culture = null, DateTime? baseDate = null, DateTime? baseDateUtc = null) @@ -93,4 +97,4 @@ public static void Verify(string expectedString, int unit, TimeUnit timeUnit, Te } } } -} \ No newline at end of file +} diff --git a/src/Humanizer/DateHumanizeExtensions.cs b/src/Humanizer/DateHumanizeExtensions.cs index 7047b2f69..86c6493ac 100644 --- a/src/Humanizer/DateHumanizeExtensions.cs +++ b/src/Humanizer/DateHumanizeExtensions.cs @@ -13,18 +13,15 @@ public static class DateHumanizeExtensions /// Turns the current or provided date into a human readable sentence /// /// The date to be humanized - /// Boolean value indicating whether the date is in UTC or local + /// Nullable boolean value indicating whether the date is in UTC or local. If null, current date is used with the same DateTimeKind of input /// Date to compare the input against. If null, current date is used as base /// Culture to use. If null, current thread's UI culture is used. /// distance of time in words - public static string Humanize(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = null, CultureInfo culture = null) + public static string Humanize(this DateTime input, bool? utcDate = null, DateTime? dateToCompareAgainst = null, CultureInfo culture = null) { - var comparisonBase = dateToCompareAgainst ?? DateTime.UtcNow; - - if (!utcDate) - { - comparisonBase = comparisonBase.ToLocalTime(); - } + var comparisonBase = dateToCompareAgainst.HasValue ? dateToCompareAgainst.Value : DateTime.UtcNow; + utcDate ??= input.Kind != DateTimeKind.Local; + comparisonBase = utcDate.Value ? comparisonBase.ToUniversalTime() : comparisonBase.ToLocalTime(); return Configurator.DateTimeHumanizeStrategy.Humanize(input, comparisonBase, culture); } @@ -33,11 +30,11 @@ public static string Humanize(this DateTime input, bool utcDate = true, DateTime /// Turns the current or provided date into a human readable sentence, overload for the nullable DateTime, returning 'never' in case null /// /// The date to be humanized - /// Boolean value indicating whether the date is in UTC or local + /// Nullable boolean value indicating whether the date is in UTC or local. If null, current date is used with the same DateTimeKind of input /// Date to compare the input against. If null, current date is used as base /// Culture to use. If null, current thread's UI culture is used. /// distance of time in words - public static string Humanize(this DateTime? input, bool utcDate = true, DateTime? dateToCompareAgainst = null, CultureInfo culture = null) + public static string Humanize(this DateTime? input, bool? utcDate = null, DateTime? dateToCompareAgainst = null, CultureInfo culture = null) { if (input.HasValue) { @@ -82,4 +79,4 @@ public static string Humanize(this DateTimeOffset? input, DateTimeOffset? dateTo } } } -} \ No newline at end of file +} From 036811dd89a149884ba630560a3eded34ca8f6a3 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Sat, 29 Aug 2020 20:39:45 -0400 Subject: [PATCH 044/126] Fix TimeSpan.Humanize for precision > 1 and number of days > _daysInAMonth --- .../TimeSpanHumanizeTests.cs | 10 ++++++++++ src/Humanizer/TimeSpanHumanizeExtensions.cs | 14 +++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs index 0062b699e..f765b3aad 100644 --- a/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/TimeSpanHumanizeTests.cs @@ -278,6 +278,16 @@ public void TimeSpanWithPrecision(long milliseconds, int precision, string expec Assert.Equal(expected, actual); } + [Theory] + [InlineData(3 * 7 + 4, 2, "3 weeks, 4 days")] + [InlineData(6 * 7 + 3, 2, "6 weeks, 3 days")] + [InlineData(72 * 7 + 6, 2, "72 weeks, 6 days")] + public void DaysWithPrecision(int days, int precision, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(precision: precision); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(50)] [InlineData(52)] diff --git a/src/Humanizer/TimeSpanHumanizeExtensions.cs b/src/Humanizer/TimeSpanHumanizeExtensions.cs index 7ab751898..e250633ac 100644 --- a/src/Humanizer/TimeSpanHumanizeExtensions.cs +++ b/src/Humanizer/TimeSpanHumanizeExtensions.cs @@ -89,15 +89,15 @@ private static string GetTimeUnitPart(TimeUnit timeUnitToGet, TimeSpan timespan, { if (timeUnitToGet <= maximumTimeUnit && timeUnitToGet >= minimumTimeUnit) { - var isTimeUnitToGetTheMaximumTimeUnit = (timeUnitToGet == maximumTimeUnit); - var numberOfTimeUnits = GetTimeUnitNumericalValue(timeUnitToGet, timespan, isTimeUnitToGetTheMaximumTimeUnit); + var numberOfTimeUnits = GetTimeUnitNumericalValue(timeUnitToGet, timespan, maximumTimeUnit); return BuildFormatTimePart(cultureFormatter, timeUnitToGet, numberOfTimeUnits, toWords); } return null; } - private static int GetTimeUnitNumericalValue(TimeUnit timeUnitToGet, TimeSpan timespan, bool isTimeUnitToGetTheMaximumTimeUnit) + private static int GetTimeUnitNumericalValue(TimeUnit timeUnitToGet, TimeSpan timespan, TimeUnit maximumTimeUnit) { + var isTimeUnitToGetTheMaximumTimeUnit = (timeUnitToGet == maximumTimeUnit); switch (timeUnitToGet) { case TimeUnit.Millisecond: @@ -109,7 +109,7 @@ private static int GetTimeUnitNumericalValue(TimeUnit timeUnitToGet, TimeSpan ti case TimeUnit.Hour: return GetNormalCaseTimeAsInteger(timespan.Hours, timespan.TotalHours, isTimeUnitToGetTheMaximumTimeUnit); case TimeUnit.Day: - return GetSpecialCaseDaysAsInteger(timespan, isTimeUnitToGetTheMaximumTimeUnit); + return GetSpecialCaseDaysAsInteger(timespan, maximumTimeUnit); case TimeUnit.Week: return GetSpecialCaseWeeksAsInteger(timespan, isTimeUnitToGetTheMaximumTimeUnit); case TimeUnit.Month: @@ -148,13 +148,13 @@ private static int GetSpecialCaseWeeksAsInteger(TimeSpan timespan, bool isTimeUn return 0; } - private static int GetSpecialCaseDaysAsInteger(TimeSpan timespan, bool isTimeUnitToGetTheMaximumTimeUnit) + private static int GetSpecialCaseDaysAsInteger(TimeSpan timespan, TimeUnit maximumTimeUnit) { - if (isTimeUnitToGetTheMaximumTimeUnit) + if (maximumTimeUnit == TimeUnit.Day) { return timespan.Days; } - if (timespan.Days < _daysInAMonth) + if (timespan.Days < _daysInAMonth || maximumTimeUnit == TimeUnit.Week) { var remainingDays = timespan.Days % _daysInAWeek; return remainingDays; From 012f8fd6547892185268866e70953eacdddc2014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Souza?= Date: Sun, 6 Sep 2020 16:36:48 +0200 Subject: [PATCH 045/126] Fixing typo in the readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 2480d5585..50915b3aa 100644 --- a/readme.md +++ b/readme.md @@ -542,7 +542,7 @@ Normally you would call `Singularize` on a plural word but if you're unsure abou The overload of `Singularize` with `plurality` argument is obsolete and was removed in version 2.0. ## Adding Words -Sometimes, you may need to add a rule from the singularization/pluralization vocabulary (the examples below are already in the `DefaultVocabluary` used by `Inflector`): +Sometimes, you may need to add a rule from the singularization/pluralization vocabulary (the examples below are already in the `DefaultVocabulary` used by `Inflector`): ```C# // Adds a word to the vocabulary which cannot easily be pluralized/singularized by RegEx. From d39968afe4d78f03f69ae01469b7a138c738cc0d Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Sun, 6 Sep 2020 10:27:31 -0400 Subject: [PATCH 046/126] Fix TimeSpan-related humanization for French culture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace "année" with "an" - Make sure 0 is followed by a singular time unit (not plural, as in English) - Replace "pas de temps" by "temps nul", which arguably sounds more natural --- .../fr-BE/TimeSpanHumanizeTests.cs | 26 +++++++++++----- .../Localisation/fr/TimeSpanHumanizeTests.cs | 30 ++++++++++++------- .../Formatters/FrenchFormatter.cs | 5 ++++ src/Humanizer/Properties/Resources.fr-BE.resx | 4 +-- src/Humanizer/Properties/Resources.fr.resx | 28 +++++++++++++++-- 5 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/fr-BE/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr-BE/TimeSpanHumanizeTests.cs index 63c94c222..777cb115d 100644 --- a/src/Humanizer.Tests.Shared/Localisation/fr-BE/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/fr-BE/TimeSpanHumanizeTests.cs @@ -1,4 +1,6 @@ using System; +using Humanizer.Localisation; + using Xunit; namespace Humanizer.Tests.Localisation.frBE @@ -9,13 +11,13 @@ public class TimeSpanHumanizeTests [Theory] [Trait("Translation", "Google")] - [InlineData(366, "1 année")] + [InlineData(366, "1 an")] [InlineData(731, "2 ans")] [InlineData(1096, "3 ans")] [InlineData(4018, "11 ans")] public void Years(int days, string expected) { - Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year)); } [Theory] @@ -26,7 +28,7 @@ public void Years(int days, string expected) [InlineData(335, "11 mois")] public void Months(int days, string expected) { - Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year)); } [Theory] @@ -83,12 +85,20 @@ public void Milliseconds(int ms, string expected) Assert.Equal(expected, actual); } - [Fact] - public void NoTime() + [Theory] + [InlineData(TimeUnit.Year, "0 an")] + [InlineData(TimeUnit.Month, "0 mois")] + [InlineData(TimeUnit.Week, "0 semaine")] + [InlineData(TimeUnit.Day, "0 jour")] + [InlineData(TimeUnit.Hour, "0 heure")] + [InlineData(TimeUnit.Minute, "0 minute")] + [InlineData(TimeUnit.Second, "0 seconde")] + [InlineData(TimeUnit.Millisecond, "0 milliseconde")] + public void NoTime(TimeUnit minUnit, string expected) { var noTime = TimeSpan.Zero; - var actual = noTime.Humanize(); - Assert.Equal("0 millisecondes", actual); + var actual = noTime.Humanize(minUnit: minUnit); + Assert.Equal(expected, actual); } [Fact] @@ -96,7 +106,7 @@ public void NoTimeToWords() { var noTime = TimeSpan.Zero; var actual = noTime.Humanize(toWords: true); - Assert.Equal("pas de temps", actual); + Assert.Equal("temps nul", actual); } } } diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs index 56cf5e80f..8b4b5d43c 100644 --- a/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs @@ -1,4 +1,6 @@ using System; +using Humanizer.Localisation; + using Xunit; namespace Humanizer.Tests.Localisation.fr @@ -7,15 +9,15 @@ namespace Humanizer.Tests.Localisation.fr public class TimeSpanHumanizeTests { - [InlineData(366, "1 année")] + [Theory] + [Trait("Translation", "Google")] + [InlineData(366, "1 an")] [InlineData(731, "2 ans")] [InlineData(1096, "3 ans")] [InlineData(4018, "11 ans")] - [Theory] - [Trait("Translation", "Google")] public void Years(int days, string expected) { - Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year)); } [Theory] @@ -26,7 +28,7 @@ public void Years(int days, string expected) [InlineData(335, "11 mois")] public void Months(int days, string expected) { - Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: Humanizer.Localisation.TimeUnit.Year)); + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year)); } [Theory] @@ -83,12 +85,20 @@ public void Milliseconds(int ms, string expected) Assert.Equal(expected, actual); } - [Fact] - public void NoTime() + [Theory] + [InlineData(TimeUnit.Year, "0 an")] + [InlineData(TimeUnit.Month, "0 mois")] + [InlineData(TimeUnit.Week, "0 semaine")] + [InlineData(TimeUnit.Day, "0 jour")] + [InlineData(TimeUnit.Hour, "0 heure")] + [InlineData(TimeUnit.Minute, "0 minute")] + [InlineData(TimeUnit.Second, "0 seconde")] + [InlineData(TimeUnit.Millisecond, "0 milliseconde")] + public void NoTime(TimeUnit minUnit, string expected) { var noTime = TimeSpan.Zero; - var actual = noTime.Humanize(); - Assert.Equal("0 millisecondes", actual); + var actual = noTime.Humanize(minUnit: minUnit); + Assert.Equal(expected, actual); } [Fact] @@ -96,7 +106,7 @@ public void NoTimeToWords() { var noTime = TimeSpan.Zero; var actual = noTime.Humanize(toWords: true); - Assert.Equal("pas de temps", actual); + Assert.Equal("temps nul", actual); } } } diff --git a/src/Humanizer/Localisation/Formatters/FrenchFormatter.cs b/src/Humanizer/Localisation/Formatters/FrenchFormatter.cs index 706770e6a..cfa6bb202 100644 --- a/src/Humanizer/Localisation/Formatters/FrenchFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/FrenchFormatter.cs @@ -16,6 +16,11 @@ protected override string GetResourceKey(string resourceKey, int number) return resourceKey + DualPostfix; } + if (number == 0 && resourceKey.StartsWith("TimeSpanHumanize_Multiple")) + { + return resourceKey + "_Singular"; + } + return resourceKey; } } diff --git a/src/Humanizer/Properties/Resources.fr-BE.resx b/src/Humanizer/Properties/Resources.fr-BE.resx index 759adcf6c..d23e46501 100644 --- a/src/Humanizer/Properties/Resources.fr-BE.resx +++ b/src/Humanizer/Properties/Resources.fr-BE.resx @@ -229,7 +229,7 @@ 1 semaine - pas de temps + temps nul {0} mois @@ -241,7 +241,7 @@ 1 mois - 1 année + 1 an avant-hier diff --git a/src/Humanizer/Properties/Resources.fr.resx b/src/Humanizer/Properties/Resources.fr.resx index a54634bb8..7cf0af1bf 100644 --- a/src/Humanizer/Properties/Resources.fr.resx +++ b/src/Humanizer/Properties/Resources.fr.resx @@ -229,7 +229,7 @@ 1 semaine - pas de temps + temps nul jamais @@ -244,7 +244,7 @@ 1 mois - 1 année + 1 an avant-hier @@ -252,4 +252,28 @@ après-demain + + {0} an + + + {0} mois + + + {0} semaine + + + {0} jour + + + {0} heure + + + {0} minute + + + {0} seconde + + + {0} milliseconde + \ No newline at end of file From d6ae2f588935d6b49a22b58d282af1c42403fdeb Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Thu, 17 Sep 2020 11:57:19 +0800 Subject: [PATCH 047/126] Create TamilNumberToWordsConverter.cs --- .../TamilNumberToWordsConverter.cs | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs new file mode 100644 index 000000000..db1d76bc2 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class TamilNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private static readonly string[] UnitsMap = { "சுழியம்", "ஒன்று", "இரண்டு", "மூன்று", "நான்கு", "ஐந்து", "ஆறு", "ஏழு", "எட்டு", "ஒன்பது", "பத்து", "பதினொன்று", "பனிரெண்டு", "பதிமூன்று", "பதிநான்கு", "பதினைந்து", "பதினாறு", "பதினேழு", "பதினெட்டு", "பத்தொன்பது" }; + private static readonly string[] TensMap = { "zero", "பத்து", "இருபது", "முப்பது", "நாற்பது", "ஐம்பது", "அறுபது", "எழுபது", "என்பது", "தொன்னூறு" }; + + private static readonly Dictionary OrdinalExceptions = new Dictionary + { + {1, "first"}, + {2, "second"}, + {3, "third"}, + {4, "fourth"}, + {5, "fifth"}, + {8, "eighth"}, + {9, "ninth"}, + {12, "twelfth"}, + }; + + public override string Convert(long number) + { + return Convert(number, false); + } + + public override string ConvertToOrdinal(int number) + { + return Convert(number, true); + } + + private string Convert(long number, bool isOrdinal) + { + if (number == 0) + { + return GetUnitValue(0, isOrdinal); + } + + if (number < 0) + { + return string.Format("minus {0}", Convert(-number)); + } + + var parts = new List(); + + if ((number / 1000000000000000000) > 0) + { + parts.Add(string.Format("{0} quintillion", Convert(number / 1000000000000000000))); + number %= 1000000000000000000; + } + + if ((number / 1000000000000000) > 0) + { + parts.Add(string.Format("{0} quadrillion", Convert(number / 1000000000000000))); + number %= 1000000000000000; + } + + if ((number / 1000000000000) > 0) + { + parts.Add(string.Format("{0} trillion", Convert(number / 1000000000000))); + number %= 1000000000000; + } + + if ((number / 1000000000) > 0) + { + parts.Add(string.Format("{0} பில்லியன்", Convert(number / 1000000000))); + number %= 1000000000; + } + + if ((number / 1000000) > 0) + { + parts.Add(string.Format("{0} மில்லியன்", Convert(number / 1000000))); + number %= 1000000; + } + + if ((number / 1000) > 0) + { + parts.Add(string.Format("{0} ஆயிரம்", Convert(number / 1000))); + number %= 1000; + } + + if ((number / 100) > 0) + { + parts.Add(string.Format("{0} நூறு", Convert(number / 100))); + number %= 100; + } + + if (number > 0) + { + if (parts.Count != 0) + { + parts.Add("மற்றும்"); + } + + if (number < 20) + { + parts.Add(GetUnitValue(number, isOrdinal)); + } + else + { + var lastPart = TensMap[number / 10]; + if ((number % 10) > 0) + { + lastPart += string.Format("-{0}", GetUnitValue(number % 10, isOrdinal)); + } + else if (isOrdinal) + { + lastPart = lastPart.TrimEnd('y') + "ieth"; + } + + parts.Add(lastPart); + } + } + else if (isOrdinal) + { + parts[parts.Count - 1] += "th"; + } + + var toWords = string.Join(" ", parts.ToArray()); + + if (isOrdinal) + { + toWords = RemoveOnePrefix(toWords); + } + + return toWords; + } + + private static string GetUnitValue(long number, bool isOrdinal) + { + if (isOrdinal) + { + if (ExceptionNumbersToWords(number, out var exceptionString)) + { + return exceptionString; + } + else + { + return UnitsMap[number] + "th"; + } + } + else + { + return UnitsMap[number]; + } + } + + private static string RemoveOnePrefix(string toWords) + { + // one hundred => hundredth + if (toWords.IndexOf("one", StringComparison.Ordinal) == 0) + { + toWords = toWords.Remove(0, 4); + } + + return toWords; + } + + private static bool ExceptionNumbersToWords(long number, out string words) + { + return OrdinalExceptions.TryGetValue(number, out words); + } + } +} From 174d71d0ad2bfd50f05ad2d77889893f3f9940ff Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Thu, 17 Sep 2020 12:38:39 +0800 Subject: [PATCH 048/126] Update Humanizer.nuspec ta --- NuSpecs/Humanizer.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index a77b4c94f..dd995adba 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -49,6 +49,7 @@ + From 3fd6d9bfee4f6326e487d0b694c2f4c9d2e6e7d5 Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Thu, 17 Sep 2020 12:42:23 +0800 Subject: [PATCH 049/126] Create Humanizer.Core.ta.nuspecs ta --- NuSpecs/Humanizer.Core.ta.nuspecs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 NuSpecs/Humanizer.Core.ta.nuspecs diff --git a/NuSpecs/Humanizer.Core.ta.nuspecs b/NuSpecs/Humanizer.Core.ta.nuspecs new file mode 100644 index 000000000..93d5e10d6 --- /dev/null +++ b/NuSpecs/Humanizer.Core.ta.nuspecs @@ -0,0 +1,25 @@ + + + + Humanizer.Core.ta + $version$ + Humanizer Locale (ta) + Mehdi Khalili, Claire Novotny, Mohamed Mahir + https://github.com/Humanizr/Humanizer + logo.png + false + Humanizer Locale Tamil (ta) + Copyright (c) .NET Foundation and Contributors + MIT + + ar + + + + + + + + + + From db30092f43d0c0c70f240adc07d422262b6bb253 Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Thu, 17 Sep 2020 12:45:13 +0800 Subject: [PATCH 050/126] Rename Humanizer.Core.ta.nuspecs to Humanizer.Core.ta.nuspec --- NuSpecs/{Humanizer.Core.ta.nuspecs => Humanizer.Core.ta.nuspec} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename NuSpecs/{Humanizer.Core.ta.nuspecs => Humanizer.Core.ta.nuspec} (100%) diff --git a/NuSpecs/Humanizer.Core.ta.nuspecs b/NuSpecs/Humanizer.Core.ta.nuspec similarity index 100% rename from NuSpecs/Humanizer.Core.ta.nuspecs rename to NuSpecs/Humanizer.Core.ta.nuspec From 74c50054cd8d15b1b457db05fd60c8d95fece452 Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sat, 19 Sep 2020 18:36:23 +0800 Subject: [PATCH 051/126] Tamil numbers to words --- .../Localisation/ta/NumberToWordsTests.cs | 217 +++++++++++++ .../NumberToWordsTests.cs | 3 + .../Configuration/FormatterRegistry.cs | 1 + .../NumberToWordsConverterRegistry.cs | 1 + .../TamilNumberToWordsConverter.cs | 285 ++++++++++++++---- 5 files changed, 448 insertions(+), 59 deletions(-) create mode 100644 src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs diff --git a/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs new file mode 100644 index 000000000..33a26eda6 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs @@ -0,0 +1,217 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.ta +{ + [UseCulture("ta")] + public class NumberToWordsTests + { + //http://tnschools.gov.in/media/textbooks/3rd_Maths_Science_and_Social_science_TM_Combine_pd_Full_Book.pdf page 12 + [Theory] + //[InlineData(0, "சுழியம்")] + //[InlineData(1, "ஒன்று")] + //[InlineData(2, "இரண்டு")] + //[InlineData(3, "மூன்று")] + //[InlineData(4, "நான்கு")] + //[InlineData(5, "ஐந்து")] + //[InlineData(6, "ஆறு")] + //[InlineData(7, "ஏழு")] + //[InlineData(8, "எட்டு")] + //[InlineData(9, "ஒன்பது")] + //[InlineData(10, "பத்து")] + + //[InlineData(11, "பதினொன்று")] + //[InlineData(12, "பனிரெண்டு")] + //[InlineData(13, "பதிமூன்று")] + //[InlineData(14, "பதினான்கு")] + //[InlineData(15, "பதினைந்து")] + //[InlineData(16, "பதினாறு")] + //[InlineData(17, "பதினேழு")] + //[InlineData(18, "பதினெட்டு")] + //[InlineData(19, "பத்தொன்பது")] + //[InlineData(20, "இருபது")] + //[InlineData(21, "இருபத்து ஒன்று")] + + //[InlineData(30, "முப்பது")] + //[InlineData(31, "முப்பத்து ஒன்று")] + + //[InlineData(40, "நாற்பது")] + + //[InlineData(41, "நாற்பத்தி ஒன்று")] + + + //[InlineData(50, "ஐம்பது")] + //[InlineData(60, "அறுபது")] + //[InlineData(64, "அறுபத்து நான்கு")] + + //[InlineData(70, "எழுபது")] + //[InlineData(80, "எண்பது")] + //[InlineData(89, "எண்பத்தி ஒன்பது")] + //[InlineData(90, "தொண்ணூறு")] + //[InlineData(95, "தொண்ணூற்றி ஐந்து")] + + //[InlineData(100, "நூறு")] + //[InlineData(101, "நூற்று ஒன்று")] + //[InlineData(121, "நூற்று இருபத்து ஒன்று")] + //[InlineData(191, "நூற்று தொண்ணூற்றி ஒன்று")] + + //[InlineData(200, "இருநூறு")] + //[InlineData(201, "இருநூற்று ஒன்று")] + //[InlineData(411, "நானூற்று பதினொன்று")] + + //[InlineData(535, "ஐந்நூற்று முப்பத்து ஐந்து")] + [InlineData(985, "தொள்ளாயிரத்து எண்பத்தி ஐந்து")] + + //[InlineData(1000, "ஆயிரம்")] + //[InlineData(1535, "ஆயிரத்து ஐந்நூற்று முப்பத்து ஐந்து")] + + //[InlineData(2000, "இரண்டாயிரம்")] + //[InlineData(3000, "மூன்றாயிரம்")] + //[InlineData(4000, "நான்காயிரம்")] + //[InlineData(5000, "ஐந்தாயிரம்")] + //[InlineData(6000, "ஆறாயிரம்")] + //[InlineData(7000, "ஏழாயிரம்")] + //[InlineData(8000, "எட்டாயிரம்")] + //[InlineData(8888, "எட்டாயிரத்து எண்ணூற்று எண்பத்தி எட்டு")] + //[InlineData(9000, "ஒன்பதாயிரம்")] + [InlineData(9999, "ஒன்பதாயிரத்து தொள்ளாயிரத்து தொண்ணூற்றி ஒன்பது")] + + //[InlineData(10000, "பத்தாயிரம்")] + //[InlineData(20000, "இருபதாயிரம்")] + //[InlineData(20005, "இருபதாயிரத்து ஐந்து")] + //[InlineData(20205, "இருபதாயிரத்து இருநூற்று ஐந்து")] + //[InlineData(25435, "இருபத்து ஐந்தாயிரத்து நானூற்று முப்பத்து ஐந்து")] + //[InlineData(90995, "தொண்ணூறாயிரத்து தொள்ளாயிரத்து தொண்ணூற்றி ஐந்து")] + + //[InlineData(100000, "ஒரு இலட்சம்")] + //[InlineData(1000000, "பத்து இலட்சம்")] + + //[InlineData(10000000, "ஒரு கோடி")] + //[InlineData(100000000, "பத்து கோடி")] + //[InlineData(1000000000, "நூறு கோடி")] + //[InlineData(2000000000, "இருநூறு கோடி")] + //[InlineData(122, "நூற்று இருபத்து இரண்டு")] + //[InlineData(3501, "மூன்றாயிரத்து ஐநூற்று ஒன்று")] + //[InlineData(111, "நூற்று பதினொன்று")] + //[InlineData(1112, "ஆயிரத்து நூற்று பனிரெண்டு")] + //[InlineData(11213, "பதினொன்றாயிரத்து இருநூற்று பதிமூன்று")] + //[InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehn")] + //[InlineData(2132415, "zwei Millionen einhundertzweiunddreißigtausendvierhundertfünfzehn")] + //[InlineData(12345516, "zwölf Millionen dreihundertfünfundvierzigtausendfünfhundertsechzehn")] + //[InlineData(751633617, "siebenhunderteinundfünfzig Millionen sechshundertdreiunddreißigtausendsechshundertsiebzehn")] + //[InlineData(1111111118, "eine Milliarde einhundertelf Millionen einhundertelftausendeinhundertachtzehn")] + //[InlineData(35484694489515, "fünfunddreißig Billionen vierhundertvierundachtzig Milliarden sechshundertvierundneunzig Millionen vierhundertneunundachtzigtausendfünfhundertfünfzehn")] + //[InlineData(8183162164626926, "acht Billiarden einhundertdreiundachtzig Billionen einhundertzweiundsechzig Milliarden einhundertvierundsechzig Millionen sechshundertsechsundzwanzigtausendneunhundertsechsundzwanzig")] + //[InlineData(4564121926659524672, "vier Trillionen fünfhundertvierundsechzig Billiarden einhunderteinundzwanzig Billionen neunhundertsechsundzwanzig Milliarden sechshundertneunundfünfzig Millionen fünfhundertvierundzwanzigtausendsechshundertzweiundsiebzig")] + //[InlineData(-751633619, "minus siebenhunderteinundfünfzig Millionen sechshundertdreiunddreißigtausendsechshundertneunzehn")] + public void ToWords(long number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(1, "ஒன்று")] + [InlineData(3501, "மூன்றாயிரத்து ஐநூற்று ஒன்று")] + public void ToWordsFeminine(long number, string expected) + { + Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine)); + } + + [Theory] + [InlineData(0, "nullter")] + [InlineData(1, "erster")] + [InlineData(2, "zweiter")] + [InlineData(3, "dritter")] + [InlineData(4, "vierter")] + [InlineData(5, "fünfter")] + [InlineData(6, "sechster")] + [InlineData(7, "siebter")] + [InlineData(8, "achter")] + [InlineData(9, "neunter")] + [InlineData(10, "zehnter")] + [InlineData(20, "zwanzigster")] + [InlineData(30, "dreißigster")] + [InlineData(40, "vierzigster")] + [InlineData(50, "fünfzigster")] + [InlineData(60, "sechzigster")] + [InlineData(70, "siebzigster")] + [InlineData(80, "achtzigster")] + [InlineData(90, "neunzigster")] + [InlineData(100, "einhundertster")] + [InlineData(200, "zweihundertster")] + [InlineData(1000, "eintausendster")] + [InlineData(10000, "zehntausendster")] + [InlineData(100000, "einhunderttausendster")] + [InlineData(1000000, "einmillionster")] + [InlineData(10000000, "zehnmillionster")] + [InlineData(100000000, "einhundertmillionster")] + [InlineData(1000000000, "einmilliardster")] + [InlineData(2000000000, "zweimilliardster")] + [InlineData(122, "einhundertzweiundzwanzigster")] + [InlineData(3501, "dreitausendfünfhunderterster")] + [InlineData(111, "einhundertelfter")] + [InlineData(1112, "eintausendeinhundertzwölfter")] + [InlineData(11213, "elftausendzweihundertdreizehnter")] + [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehnter")] + [InlineData(2132415, "zweimillioneneinhundertzweiunddreißigtausendvierhundertfünfzehnter")] + [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnter")] + [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertsiebzehnter")] + [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnter")] + [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertneunzehnter")] + public void ToOrdinalWords(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords()); + } + + [Theory] + [InlineData(0, "nullte")] + [InlineData(1, "erste")] + [InlineData(2, "zweite")] + [InlineData(3, "dritte")] + [InlineData(4, "vierte")] + [InlineData(5, "fünfte")] + [InlineData(6, "sechste")] + [InlineData(7, "siebte")] + [InlineData(8, "achte")] + [InlineData(9, "neunte")] + [InlineData(10, "zehnte")] + [InlineData(111, "einhundertelfte")] + [InlineData(1112, "eintausendeinhundertzwölfte")] + [InlineData(11213, "elftausendzweihundertdreizehnte")] + [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehnte")] + [InlineData(2132415, "zweimillioneneinhundertzweiunddreißigtausendvierhundertfünfzehnte")] + [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnte")] + [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertsiebzehnte")] + [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnte")] + [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertneunzehnte")] + public void ToOrdinalWordsFeminine(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Feminine)); + } + + [Theory] + [InlineData(0, "nulltes")] + [InlineData(1, "erstes")] + [InlineData(2, "zweites")] + [InlineData(3, "drittes")] + [InlineData(4, "viertes")] + [InlineData(5, "fünftes")] + [InlineData(6, "sechstes")] + [InlineData(7, "siebtes")] + [InlineData(8, "achtes")] + [InlineData(9, "neuntes")] + [InlineData(10, "zehntes")] + [InlineData(111, "einhundertelftes")] + [InlineData(1112, "eintausendeinhundertzwölftes")] + [InlineData(11213, "elftausendzweihundertdreizehntes")] + [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehntes")] + [InlineData(2132415, "zweimillioneneinhundertzweiunddreißigtausendvierhundertfünfzehntes")] + [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehntes")] + [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertsiebzehntes")] + [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehntes")] + [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertneunzehntes")] + public void ToOrdinalWordsNeuter(int number, string expected) + { + Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Neuter)); + } + } +} diff --git a/src/Humanizer.Tests.Shared/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/NumberToWordsTests.cs index 3d8b9ca3e..bd120be3e 100644 --- a/src/Humanizer.Tests.Shared/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/NumberToWordsTests.cs @@ -119,6 +119,9 @@ public void ToOrdinalWords(int number, string words) [InlineData(22, "ar", "اثنان و عشرون")] [InlineData(40, "ru", "сорок")] [InlineData(1021, "hr", "tisuću dvadeset jedan")] + [InlineData(11,"ta","பதினொன்று")] + [InlineData(12,"ta","பனிரெண்டு")] + [InlineData(555, "ta", "ஐநூற்று ஐம்பத்தி ஐந்து")] public void ToWords_CanSpecifyCultureExplicitly(int number, string culture, string expected) { Assert.Equal(expected, number.ToWords(new CultureInfo(culture))); diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index 510e25b73..f5660903b 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -44,6 +44,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterDefaultFormatter("nl"); RegisterDefaultFormatter("bn-BD"); RegisterDefaultFormatter("it"); + RegisterDefaultFormatter("ta"); RegisterDefaultFormatter("uz-Latn-UZ"); RegisterDefaultFormatter("uz-Cyrl-UZ"); RegisterDefaultFormatter("zh-CN"); diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index c874f11bd..e1f7114f7 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -38,6 +38,7 @@ public NumberToWordsConverterRegistry() Register("sv", new SwedishNumberToWordsConverter()); Register("sr", (culture) => new SerbianCyrlNumberToWordsConverter(culture)); Register("sr-Latn", (culture) => new SerbianNumberToWordsConverter(culture)); + Register("ta", new TamilNumberToWordsConverter()); Register("hr", (culture) => new CroatianNumberToWordsConverter(culture)); Register("nb", new NorwegianBokmalNumberToWordsConverter()); Register("vi", new VietnameseNumberToWordsConverter()); diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs index db1d76bc2..4d86512f4 100644 --- a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -1,23 +1,28 @@ -using System; +using System; using System.Collections.Generic; +//Author:Mohamed Mahir - mahir78@gmail.com namespace Humanizer.Localisation.NumberToWords { internal class TamilNumberToWordsConverter : GenderlessNumberToWordsConverter { - private static readonly string[] UnitsMap = { "சுழியம்", "ஒன்று", "இரண்டு", "மூன்று", "நான்கு", "ஐந்து", "ஆறு", "ஏழு", "எட்டு", "ஒன்பது", "பத்து", "பதினொன்று", "பனிரெண்டு", "பதிமூன்று", "பதிநான்கு", "பதினைந்து", "பதினாறு", "பதினேழு", "பதினெட்டு", "பத்தொன்பது" }; - private static readonly string[] TensMap = { "zero", "பத்து", "இருபது", "முப்பது", "நாற்பது", "ஐம்பது", "அறுபது", "எழுபது", "என்பது", "தொன்னூறு" }; + private static readonly string[] UnitsMap = { "சுழியம்", "ஒன்று", "இரண்டு", "மூன்று", "நான்கு", "ஐந்து", "ஆறு", "ஏழு", "எட்டு", "ஒன்பது", "பத்து", "பதினொன்று", "பனிரெண்டு", "பதிமூன்று", "பதினான்கு", "பதினைந்து", "பதினாறு", "பதினேழு", "பதினெட்டு", "பத்தொன்பது" }; + private static readonly string[] TensMap = { "சுழியம்", "பத்து", "இருப", "முப்ப", "நாற்ப", "ஐம்ப", "அறுப", "எழுப", "எண்ப", "தொண்ணூ" }; + private static readonly string[] HundredsMap = { "நூ", "இருநூ", "முன்னூ", "நானூ", "ஐந்நூ", "அறுநூ", "எழுநூ", "எண்ணூ", "தொள்ளாயிர", }; + private static readonly string[] ThousandsMap = { "ஆ", "இரண்டா", "மூன்றா", "நான்கா", "ஐந்தா", "ஆறா", "ஏழா", "எட்டா", "ஒன்பதா", "பத்தா", "பதினொன்றா", "பனிரெண்டா", "பதிமூன்றா", "பதினான்கா", "பதினைந்தா", "பதினாறா", "பதினேழா", "பதினெட்டா", "பத்தொன்பதா" }; + + private static readonly string[] LakhsMap = { "இலட்ச" }; private static readonly Dictionary OrdinalExceptions = new Dictionary { - {1, "first"}, - {2, "second"}, - {3, "third"}, - {4, "fourth"}, - {5, "fifth"}, - {8, "eighth"}, - {9, "ninth"}, - {12, "twelfth"}, + {1, "முதலாவது"}, + {2, "இரண்டாவது"}, + {3, "மூன்றாவது"}, + {4, "நான்காவது"}, + {5, "ஐந்தாவது"}, + {8, "எட்டாவது"}, + {9, "ஒன்பதாவது"}, + {12, "பனிரெண்டாவது"}, }; public override string Convert(long number) @@ -56,61 +61,36 @@ private string Convert(long number, bool isOrdinal) number %= 1000000000000000; } - if ((number / 1000000000000) > 0) - { - parts.Add(string.Format("{0} trillion", Convert(number / 1000000000000))); - number %= 1000000000000; - } + //if ((number / 1000000000000) > 0) + //{ + // parts.Add(string.Format("{0} trillion", Convert(number / 1000000000000))); + // number %= 1000000000000; + //} - if ((number / 1000000000) > 0) - { - parts.Add(string.Format("{0} பில்லியன்", Convert(number / 1000000000))); - number %= 1000000000; - } + //if ((number / 1000000000) > 0) + //{ + // parts.Add(string.Format("{0} பில்லியன்", Convert(number / 1000000000))); + // number %= 1000000000; + //} - if ((number / 1000000) > 0) - { - parts.Add(string.Format("{0} மில்லியன்", Convert(number / 1000000))); - number %= 1000000; - } + //if ((number / 1000000) > 0) + //{ + // parts.Add(string.Format("{0} மில்லியன்", Convert(number / 1000000))); + // number %= 1000000; + //} - if ((number / 1000) > 0) - { - parts.Add(string.Format("{0} ஆயிரம்", Convert(number / 1000))); - number %= 1000; - } + if ((number / 10000000) > 0) parts.Add(GetCroresValue(ref number)); - if ((number / 100) > 0) - { - parts.Add(string.Format("{0} நூறு", Convert(number / 100))); - number %= 100; - } + if ((number / 100000) > 0) parts.Add(GetLakhsValue(ref number, isOrdinal)); - if (number > 0) - { - if (parts.Count != 0) - { - parts.Add("மற்றும்"); - } + if ((number / 1000) > 0) parts.Add(GetThousandsValue(ref number)); - if (number < 20) - { - parts.Add(GetUnitValue(number, isOrdinal)); - } - else - { - var lastPart = TensMap[number / 10]; - if ((number % 10) > 0) - { - lastPart += string.Format("-{0}", GetUnitValue(number % 10, isOrdinal)); - } - else if (isOrdinal) - { - lastPart = lastPart.TrimEnd('y') + "ieth"; - } + if ((number / 100) > 0) parts.Add(GetHundredsValue(ref number)); - parts.Add(lastPart); - } + if (number > 0) + { + + parts.Add(GetTensValue(number, isOrdinal)); } else if (isOrdinal) { @@ -146,6 +126,193 @@ private static string GetUnitValue(long number, bool isOrdinal) } } + private static string GetTensValue(long number, bool isOrdinal, bool isThousand = false) + { + var local_word = ""; + if (number < 20) + { + local_word = GetUnitValue(number, isOrdinal); + } + else if ((number >= 20) && (number <= 99)) + { + var lastPart = TensMap[number / 10]; + var quot = number / 10; + if ((number % 10) > 0) + { + if (quot == 9) + { + lastPart += "ற்றி "; + } + else if (quot == 7 || quot == 8 || quot == 4) + { + lastPart += "த்தி "; + } + else + lastPart += "த்து "; + if (!isThousand) lastPart += string.Format("{0}", GetUnitValue(number % 10, isOrdinal)); + } + else if (number % 10 == 0) + { + if (isThousand) + { + if (quot == 9) + lastPart += "றா"; + else + lastPart += "தா"; + } + else + { + if (quot == 9) + lastPart += "று"; + else + lastPart += "து"; + } + } + else if (isOrdinal) + { + lastPart = lastPart.TrimEnd('y') + "ieth"; + } + + local_word = lastPart; + } + return local_word; + } + private static string GetLakhsValue(ref long number, bool isOrdinal) + { + var num_above_10 = number / 100000; + var local_word = ""; + if (num_above_10 >= 20) + { + local_word = GetTensValue(num_above_10, false, false); + local_word += " " + LakhsMap[0]; + } + else if (num_above_10 == 1) + { + local_word = "ஒரு " + LakhsMap[0]; + } + else local_word += GetTensValue((number / 100000), isOrdinal) + " " + LakhsMap[0]; + + if (number % 1000000 == 0 || number % 100000 == 0) + { + local_word += "ம்"; + } + else + local_word += "த்து"; + + number %= 100000; + return local_word; + } + private static string GetCroresValue(ref long number) + { + var local_word = ""; + var num_above_10 = number / 10000000; + var str_crore = "கோடி"; + + if (num_above_10 > 99999 && num_above_10 <= 9999999) + { + local_word = GetLakhsValue(ref num_above_10,false); + local_word += " "; + } + + if (num_above_10 > 999 && num_above_10 <= 99999) + { + local_word += GetThousandsValue(ref num_above_10); + local_word += " "; + } + if (num_above_10 > 99 && num_above_10<=999) + { + local_word += GetHundredsValue(ref num_above_10); + local_word += " "; + } + + if (num_above_10 >= 20) + { + local_word += GetTensValue(num_above_10, false, false); + local_word += " " ; + } + else if (num_above_10 == 1) + { + local_word = "ஒரு "; + } + else local_word += GetTensValue((number / 10000000), false)+ " " ; + + local_word += str_crore; + if (number % 10000000 == 0 || number % 100000000 == 0) + { + local_word += ""; + } + else + local_word += "யே"; + + + number %= 10000000; + return local_word; + + } + private static string GetThousandsValue(ref long number) + { + + var num_above_10 = number / 1000; + var local_word = ""; + if (num_above_10 >= 20) + { + local_word = GetTensValue(num_above_10, false, true); + + if (num_above_10 % 10 == 1) + local_word += "ஓரா"; + else if (num_above_10 % 10 > 1) + local_word += ThousandsMap[(num_above_10 % 10) - 1]; + + } + else + local_word += ThousandsMap[(number / 1000) - 1]; + + number %= 1000; + if (number > 10) + { + + } + if (number > 0) + { + local_word = local_word + "யிரத்து"; + } + else + { + local_word = local_word + "யிரம்"; + } + + + return local_word; + } + private static string GetHundredsValue(ref long number) + { + string local_word = ""; + + { + local_word = HundredsMap[(number / 100) - 1]; + if (number / 100 == 9) + { + if (number % 100 == 0) + local_word += "ம்"; + else + { + local_word += "த்து"; + } + } + else if (number % 100 >= 1) + { + local_word += "ற்று"; + } + else + local_word += "று"; + + number %= 100; + //parts.Add(local_word); + + return local_word; + } + } + private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth From a76bd974bca93ea3ca35bcdd5b555ae0dab8488e Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sun, 20 Sep 2020 00:38:48 +0800 Subject: [PATCH 052/126] fix in crores --- .../Localisation/ta/NumberToWordsTests.cs | 284 ++++++------------ .../TamilNumberToWordsConverter.cs | 10 +- 2 files changed, 99 insertions(+), 195 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs index 33a26eda6..5317c1556 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs @@ -7,102 +7,102 @@ public class NumberToWordsTests { //http://tnschools.gov.in/media/textbooks/3rd_Maths_Science_and_Social_science_TM_Combine_pd_Full_Book.pdf page 12 [Theory] - //[InlineData(0, "சுழியம்")] - //[InlineData(1, "ஒன்று")] - //[InlineData(2, "இரண்டு")] - //[InlineData(3, "மூன்று")] - //[InlineData(4, "நான்கு")] - //[InlineData(5, "ஐந்து")] - //[InlineData(6, "ஆறு")] - //[InlineData(7, "ஏழு")] - //[InlineData(8, "எட்டு")] - //[InlineData(9, "ஒன்பது")] - //[InlineData(10, "பத்து")] - - //[InlineData(11, "பதினொன்று")] - //[InlineData(12, "பனிரெண்டு")] - //[InlineData(13, "பதிமூன்று")] - //[InlineData(14, "பதினான்கு")] - //[InlineData(15, "பதினைந்து")] - //[InlineData(16, "பதினாறு")] - //[InlineData(17, "பதினேழு")] - //[InlineData(18, "பதினெட்டு")] - //[InlineData(19, "பத்தொன்பது")] - //[InlineData(20, "இருபது")] - //[InlineData(21, "இருபத்து ஒன்று")] - - //[InlineData(30, "முப்பது")] - //[InlineData(31, "முப்பத்து ஒன்று")] - - //[InlineData(40, "நாற்பது")] - - //[InlineData(41, "நாற்பத்தி ஒன்று")] - - - //[InlineData(50, "ஐம்பது")] - //[InlineData(60, "அறுபது")] - //[InlineData(64, "அறுபத்து நான்கு")] - - //[InlineData(70, "எழுபது")] - //[InlineData(80, "எண்பது")] - //[InlineData(89, "எண்பத்தி ஒன்பது")] - //[InlineData(90, "தொண்ணூறு")] - //[InlineData(95, "தொண்ணூற்றி ஐந்து")] - - //[InlineData(100, "நூறு")] - //[InlineData(101, "நூற்று ஒன்று")] - //[InlineData(121, "நூற்று இருபத்து ஒன்று")] - //[InlineData(191, "நூற்று தொண்ணூற்றி ஒன்று")] - - //[InlineData(200, "இருநூறு")] - //[InlineData(201, "இருநூற்று ஒன்று")] - //[InlineData(411, "நானூற்று பதினொன்று")] - - //[InlineData(535, "ஐந்நூற்று முப்பத்து ஐந்து")] + [InlineData(0, "சுழியம்")] + [InlineData(1, "ஒன்று")] + [InlineData(2, "இரண்டு")] + [InlineData(3, "மூன்று")] + [InlineData(4, "நான்கு")] + [InlineData(5, "ஐந்து")] + [InlineData(6, "ஆறு")] + [InlineData(7, "ஏழு")] + [InlineData(8, "எட்டு")] + [InlineData(9, "ஒன்பது")] + [InlineData(10, "பத்து")] + + [InlineData(11, "பதினொன்று")] + [InlineData(12, "பனிரெண்டு")] + [InlineData(13, "பதிமூன்று")] + [InlineData(14, "பதினான்கு")] + [InlineData(15, "பதினைந்து")] + [InlineData(16, "பதினாறு")] + [InlineData(17, "பதினேழு")] + [InlineData(18, "பதினெட்டு")] + [InlineData(19, "பத்தொன்பது")] + [InlineData(20, "இருபது")] + [InlineData(21, "இருபத்து ஒன்று")] + + [InlineData(30, "முப்பது")] + [InlineData(31, "முப்பத்து ஒன்று")] + + [InlineData(40, "நாற்பது")] + + [InlineData(41, "நாற்பத்தி ஒன்று")] + + + [InlineData(50, "ஐம்பது")] + [InlineData(60, "அறுபது")] + [InlineData(64, "அறுபத்து நான்கு")] + + [InlineData(70, "எழுபது")] + [InlineData(80, "எண்பது")] + [InlineData(89, "எண்பத்தி ஒன்பது")] + [InlineData(90, "தொண்ணூறு")] + [InlineData(95, "தொண்ணூற்றி ஐந்து")] + + [InlineData(100, "நூறு")] + [InlineData(101, "நூற்று ஒன்று")] + [InlineData(121, "நூற்று இருபத்து ஒன்று")] + [InlineData(191, "நூற்று தொண்ணூற்றி ஒன்று")] + + [InlineData(200, "இருநூறு")] + [InlineData(201, "இருநூற்று ஒன்று")] + [InlineData(411, "நானூற்று பதினொன்று")] + + [InlineData(535, "ஐந்நூற்று முப்பத்து ஐந்து")] [InlineData(985, "தொள்ளாயிரத்து எண்பத்தி ஐந்து")] - //[InlineData(1000, "ஆயிரம்")] - //[InlineData(1535, "ஆயிரத்து ஐந்நூற்று முப்பத்து ஐந்து")] - - //[InlineData(2000, "இரண்டாயிரம்")] - //[InlineData(3000, "மூன்றாயிரம்")] - //[InlineData(4000, "நான்காயிரம்")] - //[InlineData(5000, "ஐந்தாயிரம்")] - //[InlineData(6000, "ஆறாயிரம்")] - //[InlineData(7000, "ஏழாயிரம்")] - //[InlineData(8000, "எட்டாயிரம்")] - //[InlineData(8888, "எட்டாயிரத்து எண்ணூற்று எண்பத்தி எட்டு")] - //[InlineData(9000, "ஒன்பதாயிரம்")] + [InlineData(1000, "ஆயிரம்")] + [InlineData(1535, "ஆயிரத்து ஐந்நூற்று முப்பத்து ஐந்து")] + + [InlineData(2000, "இரண்டாயிரம்")] + [InlineData(3000, "மூன்றாயிரம்")] + [InlineData(4000, "நான்காயிரம்")] + [InlineData(5000, "ஐந்தாயிரம்")] + [InlineData(6000, "ஆறாயிரம்")] + [InlineData(7000, "ஏழாயிரம்")] + [InlineData(8000, "எட்டாயிரம்")] + [InlineData(8888, "எட்டாயிரத்து எண்ணூற்று எண்பத்தி எட்டு")] + [InlineData(9000, "ஒன்பதாயிரம்")] [InlineData(9999, "ஒன்பதாயிரத்து தொள்ளாயிரத்து தொண்ணூற்றி ஒன்பது")] - //[InlineData(10000, "பத்தாயிரம்")] - //[InlineData(20000, "இருபதாயிரம்")] - //[InlineData(20005, "இருபதாயிரத்து ஐந்து")] - //[InlineData(20205, "இருபதாயிரத்து இருநூற்று ஐந்து")] - //[InlineData(25435, "இருபத்து ஐந்தாயிரத்து நானூற்று முப்பத்து ஐந்து")] - //[InlineData(90995, "தொண்ணூறாயிரத்து தொள்ளாயிரத்து தொண்ணூற்றி ஐந்து")] - - //[InlineData(100000, "ஒரு இலட்சம்")] - //[InlineData(1000000, "பத்து இலட்சம்")] - - //[InlineData(10000000, "ஒரு கோடி")] - //[InlineData(100000000, "பத்து கோடி")] - //[InlineData(1000000000, "நூறு கோடி")] - //[InlineData(2000000000, "இருநூறு கோடி")] - //[InlineData(122, "நூற்று இருபத்து இரண்டு")] - //[InlineData(3501, "மூன்றாயிரத்து ஐநூற்று ஒன்று")] - //[InlineData(111, "நூற்று பதினொன்று")] - //[InlineData(1112, "ஆயிரத்து நூற்று பனிரெண்டு")] - //[InlineData(11213, "பதினொன்றாயிரத்து இருநூற்று பதிமூன்று")] - //[InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehn")] - //[InlineData(2132415, "zwei Millionen einhundertzweiunddreißigtausendvierhundertfünfzehn")] - //[InlineData(12345516, "zwölf Millionen dreihundertfünfundvierzigtausendfünfhundertsechzehn")] - //[InlineData(751633617, "siebenhunderteinundfünfzig Millionen sechshundertdreiunddreißigtausendsechshundertsiebzehn")] - //[InlineData(1111111118, "eine Milliarde einhundertelf Millionen einhundertelftausendeinhundertachtzehn")] - //[InlineData(35484694489515, "fünfunddreißig Billionen vierhundertvierundachtzig Milliarden sechshundertvierundneunzig Millionen vierhundertneunundachtzigtausendfünfhundertfünfzehn")] - //[InlineData(8183162164626926, "acht Billiarden einhundertdreiundachtzig Billionen einhundertzweiundsechzig Milliarden einhundertvierundsechzig Millionen sechshundertsechsundzwanzigtausendneunhundertsechsundzwanzig")] - //[InlineData(4564121926659524672, "vier Trillionen fünfhundertvierundsechzig Billiarden einhunderteinundzwanzig Billionen neunhundertsechsundzwanzig Milliarden sechshundertneunundfünfzig Millionen fünfhundertvierundzwanzigtausendsechshundertzweiundsiebzig")] - //[InlineData(-751633619, "minus siebenhunderteinundfünfzig Millionen sechshundertdreiunddreißigtausendsechshundertneunzehn")] + [InlineData(10000, "பத்தாயிரம்")] + [InlineData(20000, "இருபதாயிரம்")] + [InlineData(20005, "இருபதாயிரத்து ஐந்து")] + [InlineData(20205, "இருபதாயிரத்து இருநூற்று ஐந்து")] + [InlineData(25435, "இருபத்து ஐந்தாயிரத்து நானூற்று முப்பத்து ஐந்து")] + [InlineData(90995, "தொண்ணூறாயிரத்து தொள்ளாயிரத்து தொண்ணூற்றி ஐந்து")] + + [InlineData(100000, "ஒரு இலட்சம்")] + [InlineData(1000000, "பத்து இலட்சம்")] + + [InlineData(10000000, "ஒரு கோடி")] + [InlineData(100000000, "பத்து கோடி")] + [InlineData(1000000000, "நூறு கோடி")] + [InlineData(2000000000, "இருநூறு கோடி")] + [InlineData(122, "நூற்று இருபத்து இரண்டு")] + [InlineData(3501, "மூன்றாயிரத்து ஐந்நூற்று ஒன்று")] + [InlineData(111, "நூற்று பதினொன்று")] + [InlineData(1112, "ஆயிரத்து நூற்று பனிரெண்டு")] + [InlineData(11213, "பதினொன்றாயிரத்து இருநூற்று பதிமூன்று")] + [InlineData(121314, "ஒரு இலட்சத்து இருபத்து ஓராயிரத்து முன்னூற்று பதினான்கு")] + [InlineData(2132415, "இருபத்து ஒன்று இலட்சத்து முப்பத்து இரண்டாயிரத்து நானூற்று பதினைந்து")] + [InlineData(12345516, "ஒரு கோடியே இருபத்து மூன்று இலட்சத்து நாற்பத்தி ஐந்தாயிரத்து ஐந்நூற்று பதினாறு")] + [InlineData(751633617, "எழுபத்தி ஐந்து கோடியே பதினாறு இலட்சத்து முப்பத்து மூன்றாயிரத்து அறுநூற்று பதினேழு")] + [InlineData(1111111118, "நூற்று பதினொன்று கோடியே பதினொன்று இலட்சத்து பதினொன்றாயிரத்து நூற்று பதினெட்டு")] + [InlineData(35484694489515, "முப்பத்து ஐந்து இலட்சத்து நாற்பத்தி எட்டாயிரத்து நானூற்று அறுபத்து ஒன்பது கோடியே நாற்பத்தி நான்கு இலட்சத்து எண்பத்தி ஒன்பதாயிரத்து ஐந்நூற்று பதினைந்து")] + [InlineData(8183162164626926, "எட்டு quadrillion கோடியே நாற்பத்தி ஆறு இலட்சத்து இருபத்து ஆறாயிரத்து தொள்ளாயிரத்து இருபத்து ஆறு")] + [InlineData(4564121926659524672, "நான்கு quintillion ஐந்நூற்று அறுபத்து நான்கு quadrillion கோடியே தொண்ணூற்றி ஐந்து இலட்சத்து இருபத்து நான்காயிரத்து அறுநூற்று எழுபத்தி இரண்டு")] + [InlineData(-751633619, "கழித்தல் எழுபத்தி ஐந்து கோடியே பதினாறு இலட்சத்து முப்பத்து மூன்றாயிரத்து அறுநூற்று பத்தொன்பது")] public void ToWords(long number, string expected) { Assert.Equal(expected, number.ToWords()); @@ -110,108 +110,12 @@ public void ToWords(long number, string expected) [Theory] [InlineData(1, "ஒன்று")] - [InlineData(3501, "மூன்றாயிரத்து ஐநூற்று ஒன்று")] + [InlineData(3501, "மூன்றாயிரத்து ஐந்நூற்று ஒன்று")] public void ToWordsFeminine(long number, string expected) { Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine)); } + - [Theory] - [InlineData(0, "nullter")] - [InlineData(1, "erster")] - [InlineData(2, "zweiter")] - [InlineData(3, "dritter")] - [InlineData(4, "vierter")] - [InlineData(5, "fünfter")] - [InlineData(6, "sechster")] - [InlineData(7, "siebter")] - [InlineData(8, "achter")] - [InlineData(9, "neunter")] - [InlineData(10, "zehnter")] - [InlineData(20, "zwanzigster")] - [InlineData(30, "dreißigster")] - [InlineData(40, "vierzigster")] - [InlineData(50, "fünfzigster")] - [InlineData(60, "sechzigster")] - [InlineData(70, "siebzigster")] - [InlineData(80, "achtzigster")] - [InlineData(90, "neunzigster")] - [InlineData(100, "einhundertster")] - [InlineData(200, "zweihundertster")] - [InlineData(1000, "eintausendster")] - [InlineData(10000, "zehntausendster")] - [InlineData(100000, "einhunderttausendster")] - [InlineData(1000000, "einmillionster")] - [InlineData(10000000, "zehnmillionster")] - [InlineData(100000000, "einhundertmillionster")] - [InlineData(1000000000, "einmilliardster")] - [InlineData(2000000000, "zweimilliardster")] - [InlineData(122, "einhundertzweiundzwanzigster")] - [InlineData(3501, "dreitausendfünfhunderterster")] - [InlineData(111, "einhundertelfter")] - [InlineData(1112, "eintausendeinhundertzwölfter")] - [InlineData(11213, "elftausendzweihundertdreizehnter")] - [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehnter")] - [InlineData(2132415, "zweimillioneneinhundertzweiunddreißigtausendvierhundertfünfzehnter")] - [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnter")] - [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertsiebzehnter")] - [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnter")] - [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertneunzehnter")] - public void ToOrdinalWords(int number, string expected) - { - Assert.Equal(expected, number.ToOrdinalWords()); - } - - [Theory] - [InlineData(0, "nullte")] - [InlineData(1, "erste")] - [InlineData(2, "zweite")] - [InlineData(3, "dritte")] - [InlineData(4, "vierte")] - [InlineData(5, "fünfte")] - [InlineData(6, "sechste")] - [InlineData(7, "siebte")] - [InlineData(8, "achte")] - [InlineData(9, "neunte")] - [InlineData(10, "zehnte")] - [InlineData(111, "einhundertelfte")] - [InlineData(1112, "eintausendeinhundertzwölfte")] - [InlineData(11213, "elftausendzweihundertdreizehnte")] - [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehnte")] - [InlineData(2132415, "zweimillioneneinhundertzweiunddreißigtausendvierhundertfünfzehnte")] - [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnte")] - [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertsiebzehnte")] - [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnte")] - [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertneunzehnte")] - public void ToOrdinalWordsFeminine(int number, string expected) - { - Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Feminine)); - } - - [Theory] - [InlineData(0, "nulltes")] - [InlineData(1, "erstes")] - [InlineData(2, "zweites")] - [InlineData(3, "drittes")] - [InlineData(4, "viertes")] - [InlineData(5, "fünftes")] - [InlineData(6, "sechstes")] - [InlineData(7, "siebtes")] - [InlineData(8, "achtes")] - [InlineData(9, "neuntes")] - [InlineData(10, "zehntes")] - [InlineData(111, "einhundertelftes")] - [InlineData(1112, "eintausendeinhundertzwölftes")] - [InlineData(11213, "elftausendzweihundertdreizehntes")] - [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehntes")] - [InlineData(2132415, "zweimillioneneinhundertzweiunddreißigtausendvierhundertfünfzehntes")] - [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehntes")] - [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertsiebzehntes")] - [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehntes")] - [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreißigtausendsechshundertneunzehntes")] - public void ToOrdinalWordsNeuter(int number, string expected) - { - Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Neuter)); - } } } diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs index 4d86512f4..ba7071a75 100644 --- a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -44,7 +44,7 @@ private string Convert(long number, bool isOrdinal) if (number < 0) { - return string.Format("minus {0}", Convert(-number)); + return string.Format("கழித்தல் {0}", Convert(-number)); } var parts = new List(); @@ -94,7 +94,7 @@ private string Convert(long number, bool isOrdinal) } else if (isOrdinal) { - parts[parts.Count - 1] += "th"; + parts[parts.Count - 1] += "வது"; } var toWords = string.Join(" ", parts.ToArray()); @@ -117,7 +117,7 @@ private static string GetUnitValue(long number, bool isOrdinal) } else { - return UnitsMap[number] + "th"; + return UnitsMap[number] + "வது"; } } else @@ -234,9 +234,9 @@ private static string GetCroresValue(ref long number) { local_word = "ஒரு "; } - else local_word += GetTensValue((number / 10000000), false)+ " " ; + else if(num_above_10>0) local_word += GetTensValue(num_above_10, false)+ " " ; - local_word += str_crore; + local_word =local_word.TrimEnd()+" "+ str_crore; if (number % 10000000 == 0 || number % 100000000 == 0) { local_word += ""; From 75dd9ae3b38dd82df40f3bb815da84bcd1662ebf Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sun, 20 Sep 2020 01:00:52 +0800 Subject: [PATCH 053/126] code cleanup --- .../TamilNumberToWordsConverter.cs | 68 +++---------------- 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs index ba7071a75..05dd51e40 100644 --- a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -38,14 +38,10 @@ public override string ConvertToOrdinal(int number) private string Convert(long number, bool isOrdinal) { if (number == 0) - { return GetUnitValue(0, isOrdinal); - } if (number < 0) - { return string.Format("கழித்தல் {0}", Convert(-number)); - } var parts = new List(); @@ -88,21 +84,14 @@ private string Convert(long number, bool isOrdinal) if ((number / 100) > 0) parts.Add(GetHundredsValue(ref number)); if (number > 0) - { - parts.Add(GetTensValue(number, isOrdinal)); - } else if (isOrdinal) - { parts[parts.Count - 1] += "வது"; - } var toWords = string.Join(" ", parts.ToArray()); if (isOrdinal) - { toWords = RemoveOnePrefix(toWords); - } return toWords; } @@ -112,43 +101,32 @@ private static string GetUnitValue(long number, bool isOrdinal) if (isOrdinal) { if (ExceptionNumbersToWords(number, out var exceptionString)) - { return exceptionString; - } else - { return UnitsMap[number] + "வது"; - } } else - { return UnitsMap[number]; - } } private static string GetTensValue(long number, bool isOrdinal, bool isThousand = false) { var local_word = ""; - if (number < 20) - { + if (number < 20) local_word = GetUnitValue(number, isOrdinal); - } else if ((number >= 20) && (number <= 99)) { var lastPart = TensMap[number / 10]; var quot = number / 10; if ((number % 10) > 0) { - if (quot == 9) - { + if (quot == 9) lastPart += "ற்றி "; - } else if (quot == 7 || quot == 8 || quot == 4) - { lastPart += "த்தி "; - } else lastPart += "த்து "; + if (!isThousand) lastPart += string.Format("{0}", GetUnitValue(number % 10, isOrdinal)); } else if (number % 10 == 0) @@ -169,9 +147,7 @@ private static string GetTensValue(long number, bool isOrdinal, bool isThousand } } else if (isOrdinal) - { lastPart = lastPart.TrimEnd('y') + "ieth"; - } local_word = lastPart; } @@ -187,18 +163,14 @@ private static string GetLakhsValue(ref long number, bool isOrdinal) local_word += " " + LakhsMap[0]; } else if (num_above_10 == 1) - { local_word = "ஒரு " + LakhsMap[0]; - } else local_word += GetTensValue((number / 100000), isOrdinal) + " " + LakhsMap[0]; if (number % 1000000 == 0 || number % 100000 == 0) - { local_word += "ம்"; - } else local_word += "த்து"; - + number %= 100000; return local_word; } @@ -210,7 +182,7 @@ private static string GetCroresValue(ref long number) if (num_above_10 > 99999 && num_above_10 <= 9999999) { - local_word = GetLakhsValue(ref num_above_10,false); + local_word = GetLakhsValue(ref num_above_10, false); local_word += " "; } @@ -219,7 +191,7 @@ private static string GetCroresValue(ref long number) local_word += GetThousandsValue(ref num_above_10); local_word += " "; } - if (num_above_10 > 99 && num_above_10<=999) + if (num_above_10 > 99 && num_above_10 <= 999) { local_word += GetHundredsValue(ref num_above_10); local_word += " "; @@ -228,23 +200,18 @@ private static string GetCroresValue(ref long number) if (num_above_10 >= 20) { local_word += GetTensValue(num_above_10, false, false); - local_word += " " ; + local_word += " "; } else if (num_above_10 == 1) - { local_word = "ஒரு "; - } - else if(num_above_10>0) local_word += GetTensValue(num_above_10, false)+ " " ; + else if (num_above_10 > 0) local_word += GetTensValue(num_above_10, false) + " "; - local_word =local_word.TrimEnd()+" "+ str_crore; + local_word = local_word.TrimEnd() + " " + str_crore; if (number % 10000000 == 0 || number % 100000000 == 0) - { local_word += ""; - } else local_word += "யே"; - number %= 10000000; return local_word; @@ -268,19 +235,11 @@ private static string GetThousandsValue(ref long number) local_word += ThousandsMap[(number / 1000) - 1]; number %= 1000; - if (number > 10) - { - - } + if (number > 0) - { local_word = local_word + "யிரத்து"; - } else - { local_word = local_word + "யிரம்"; - } - return local_word; } @@ -295,19 +254,14 @@ private static string GetHundredsValue(ref long number) if (number % 100 == 0) local_word += "ம்"; else - { local_word += "த்து"; - } } else if (number % 100 >= 1) - { local_word += "ற்று"; - } else local_word += "று"; number %= 100; - //parts.Add(local_word); return local_word; } @@ -317,9 +271,7 @@ private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth if (toWords.IndexOf("one", StringComparison.Ordinal) == 0) - { toWords = toWords.Remove(0, 4); - } return toWords; } From c9bd1366ab90ce6dcaccd68d08b084dcc82742bb Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sun, 20 Sep 2020 01:11:56 +0800 Subject: [PATCH 054/126] Removed two test cases --- .../Localisation/ta/NumberToWordsTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs index 5317c1556..2fb217a6e 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ta/NumberToWordsTests.cs @@ -100,8 +100,8 @@ public class NumberToWordsTests [InlineData(751633617, "எழுபத்தி ஐந்து கோடியே பதினாறு இலட்சத்து முப்பத்து மூன்றாயிரத்து அறுநூற்று பதினேழு")] [InlineData(1111111118, "நூற்று பதினொன்று கோடியே பதினொன்று இலட்சத்து பதினொன்றாயிரத்து நூற்று பதினெட்டு")] [InlineData(35484694489515, "முப்பத்து ஐந்து இலட்சத்து நாற்பத்தி எட்டாயிரத்து நானூற்று அறுபத்து ஒன்பது கோடியே நாற்பத்தி நான்கு இலட்சத்து எண்பத்தி ஒன்பதாயிரத்து ஐந்நூற்று பதினைந்து")] - [InlineData(8183162164626926, "எட்டு quadrillion கோடியே நாற்பத்தி ஆறு இலட்சத்து இருபத்து ஆறாயிரத்து தொள்ளாயிரத்து இருபத்து ஆறு")] - [InlineData(4564121926659524672, "நான்கு quintillion ஐந்நூற்று அறுபத்து நான்கு quadrillion கோடியே தொண்ணூற்றி ஐந்து இலட்சத்து இருபத்து நான்காயிரத்து அறுநூற்று எழுபத்தி இரண்டு")] + //[InlineData(8183162164626926, "எட்டு quadrillion கோடியே நாற்பத்தி ஆறு இலட்சத்து இருபத்து ஆறாயிரத்து தொள்ளாயிரத்து இருபத்து ஆறு")] + //[InlineData(4564121926659524672, "நான்கு quintillion ஐந்நூற்று அறுபத்து நான்கு quadrillion கோடியே தொண்ணூற்றி ஐந்து இலட்சத்து இருபத்து நான்காயிரத்து அறுநூற்று எழுபத்தி இரண்டு")] [InlineData(-751633619, "கழித்தல் எழுபத்தி ஐந்து கோடியே பதினாறு இலட்சத்து முப்பத்து மூன்றாயிரத்து அறுநூற்று பத்தொன்பது")] public void ToWords(long number, string expected) { From 421833700100d353be0ef5908ecbad78684c7021 Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sun, 20 Sep 2020 03:31:29 +0800 Subject: [PATCH 055/126] New test --- src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems | 1 + src/Humanizer.Tests.Shared/NumberToWordsTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index 2ca708a9f..d795936c3 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -151,6 +151,7 @@ + diff --git a/src/Humanizer.Tests.Shared/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/NumberToWordsTests.cs index bd120be3e..9e6ceaeae 100644 --- a/src/Humanizer.Tests.Shared/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/NumberToWordsTests.cs @@ -119,9 +119,9 @@ public void ToOrdinalWords(int number, string words) [InlineData(22, "ar", "اثنان و عشرون")] [InlineData(40, "ru", "сорок")] [InlineData(1021, "hr", "tisuću dvadeset jedan")] - [InlineData(11,"ta","பதினொன்று")] - [InlineData(12,"ta","பனிரெண்டு")] - [InlineData(555, "ta", "ஐநூற்று ஐம்பத்தி ஐந்து")] + [InlineData(11, "ta", "பதினொன்று")] + [InlineData(12, "ta", "பனிரெண்டு")] + [InlineData(555, "ta", "ஐந்நூற்று ஐம்பத்து ஐந்து")] public void ToWords_CanSpecifyCultureExplicitly(int number, string culture, string expected) { Assert.Equal(expected, number.ToWords(new CultureInfo(culture))); From 1aaccaf185f21be40ea7de57426b7f9421c90e81 Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sun, 20 Sep 2020 03:57:54 +0800 Subject: [PATCH 056/126] fixing language --- NuSpecs/Humanizer.Core.ta.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuSpecs/Humanizer.Core.ta.nuspec b/NuSpecs/Humanizer.Core.ta.nuspec index 93d5e10d6..9c5a45f4d 100644 --- a/NuSpecs/Humanizer.Core.ta.nuspec +++ b/NuSpecs/Humanizer.Core.ta.nuspec @@ -12,7 +12,7 @@ Copyright (c) .NET Foundation and Contributors MIT - ar + ta From 927266948c441b050ee6dbafcc9aeb90d6f4c5a5 Mon Sep 17 00:00:00 2001 From: Mohamed Mahir Date: Sun, 20 Sep 2020 10:52:24 +0800 Subject: [PATCH 057/126] adding solution file --- src/Humanizer.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Humanizer.sln b/src/Humanizer.sln index dccb47691..82780231d 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -71,6 +71,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ..\NuSpecs\Humanizer.Core.sr-Latn.nuspec = ..\NuSpecs\Humanizer.Core.sr-Latn.nuspec ..\NuSpecs\Humanizer.Core.sr.nuspec = ..\NuSpecs\Humanizer.Core.sr.nuspec ..\NuSpecs\Humanizer.Core.sv.nuspec = ..\NuSpecs\Humanizer.Core.sv.nuspec + ..\NuSpecs\Humanizer.Core.ta.nuspec = ..\NuSpecs\Humanizer.Core.ta.nuspec ..\NuSpecs\Humanizer.Core.tr.nuspec = ..\NuSpecs\Humanizer.Core.tr.nuspec ..\NuSpecs\Humanizer.Core.uk.nuspec = ..\NuSpecs\Humanizer.Core.uk.nuspec ..\NuSpecs\Humanizer.Core.uz-Cyrl-UZ.nuspec = ..\NuSpecs\Humanizer.Core.uz-Cyrl-UZ.nuspec From 824c0416565d8c61f9ee0948962f0be305456b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=B8=E3=83=BC=E3=81=95=E3=82=93?= Date: Thu, 24 Sep 2020 12:50:01 +0900 Subject: [PATCH 058/126] Implement ja-JP for Number to Word --- .../Localisation/ja/NumberToWordsTests.cs | 85 +++++++++++++++++++ .../NumberToWordsConverterRegistry.cs | 1 + .../JapaneseNumberToWordsConverter.cs | 68 +++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 src/Humanizer.Tests/Localisation/ja/NumberToWordsTests.cs create mode 100644 src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs diff --git a/src/Humanizer.Tests/Localisation/ja/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/ja/NumberToWordsTests.cs new file mode 100644 index 000000000..da1780b4e --- /dev/null +++ b/src/Humanizer.Tests/Localisation/ja/NumberToWordsTests.cs @@ -0,0 +1,85 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.ja +{ + [UseCulture("ja")] + public class NumberToWordsTests + { + [Theory] + [InlineData(0, "〇")] + [InlineData(1, "一")] + [InlineData(10, "十")] + [InlineData(11, "十一")] + [InlineData(122, "百二十二")] + [InlineData(3501, "三千五百一")] + [InlineData(100, "百")] + [InlineData(1000, "千")] + [InlineData(10000, "一万")] + [InlineData(100000, "十万")] + [InlineData(1000000, "百万")] + [InlineData(10000000, "千万")] + [InlineData(100000000, "一億")] + [InlineData(1000000000, "十億")] + [InlineData(111, "百十一")] + [InlineData(1111, "千百十一")] + [InlineData(11111, "一万千百十一")] + [InlineData(111111, "十一万千百十一")] + [InlineData(1111111, "百十一万千百十一")] + [InlineData(11111111, "千百十一万千百十一")] + [InlineData(111111111, "一億千百十一万千百十一")] + [InlineData(1111111111, "十一億千百十一万千百十一")] + [InlineData(123, "百二十三")] + [InlineData(1234, "千二百三十四")] + [InlineData(12345, "一万二千三百四十五")] + [InlineData(123456, "十二万三千四百五十六")] + [InlineData(1234567, "百二十三万四千五百六十七")] + [InlineData(12345678, "千二百三十四万五千六百七十八")] + [InlineData(123456789, "一億二千三百四十五万六千七百八十九")] + [InlineData(1234567890, "十二億三千四百五十六万七千八百九十")] + [InlineData(-123, "マイナス 百二十三")] + public void ToWordsInt(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(1L, "一")] + [InlineData(11L, "十一")] + [InlineData(111L, "百十一")] + [InlineData(1111L, "千百十一")] + [InlineData(11111L, "一万千百十一")] + [InlineData(111111L, "十一万千百十一")] + [InlineData(1111111L, "百十一万千百十一")] + [InlineData(11111111L, "千百十一万千百十一")] + [InlineData(111111111L, "一億千百十一万千百十一")] + [InlineData(1111111111L, "十一億千百十一万千百十一")] + [InlineData(11111111111L, "百十一億千百十一万千百十一")] + [InlineData(111111111111L, "千百十一億千百十一万千百十一")] + [InlineData(1111111111111L, "一兆千百十一億千百十一万千百十一")] + [InlineData(11111111111111L, "十一兆千百十一億千百十一万千百十一")] + [InlineData(111111111111111L, "百十一兆千百十一億千百十一万千百十一")] + [InlineData(1111111111111111L, "千百十一兆千百十一億千百十一万千百十一")] + [InlineData(11111111111111111L, "一京千百十一兆千百十一億千百十一万千百十一")] + [InlineData(111111111111111111L, "十一京千百十一兆千百十一億千百十一万千百十一")] + [InlineData(1111111111111111111L, "百十一京千百十一兆千百十一億千百十一万千百十一")] + public void ToWordsLong(long number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(0, "〇番目")] + [InlineData(1, "一番目")] + [InlineData(2, "二番目")] + [InlineData(3, "三番目")] + [InlineData(10, "十番目")] + [InlineData(11, "十一番目")] + [InlineData(100, "百番目")] + [InlineData(112, "百十二番目")] + [InlineData(1000000, "百万番目")] + public void ToOrdinalWords(int number, string words) + { + Assert.Equal(words, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index c874f11bd..96e9d4c79 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -45,6 +45,7 @@ public NumberToWordsConverterRegistry() Register("bg", new BulgarianNumberToWordsConverter()); Register("hy", new ArmenianNumberToWordsConverter()); Register("az", new AzerbaijaniNumberToWordsConverter()); + Register("ja", new JapaneseNumberToWordsConverter()); } } } diff --git a/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs new file mode 100644 index 000000000..70ccd1939 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class JapaneseNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private static readonly string[] UnitsMap1 = { "", "", "二", "三", "四", "五", "六", "七", "八", "九" }; + private static readonly string[] UnitsMap2 = { "", "十", "百", "千" }; + private static readonly string[] UnitsMap3 = { "", "万", "億", "兆", "京", "垓", "𥝱", "穣", "溝", "澗", "正", "載", "極", + "恒河沙", "阿僧祇", "那由他", "不可思議", "無量大数"}; + + public override string Convert(long number) + { + return Convert(number, false); + } + + public override string ConvertToOrdinal(int number) + { + return Convert(number, true); + } + + private string Convert(long number, bool isOrdinal) + { + if (number == 0) + { + return isOrdinal ? "〇番目" : "〇"; + } + + if (number < 0) + { + return string.Format("マイナス {0}", Convert(-number, false)); + } + + var parts = new List(); + var groupLevel = 0; + while (number > 0) + { + var groupNumber = number % 10000; + number /= 10000; + + var n0 = groupNumber % 10; + var n1 = (groupNumber % 100 - groupNumber % 10) / 10; + var n2 = (groupNumber % 1000 - groupNumber % 100) / 100; + var n3 = (groupNumber - groupNumber % 1000) / 1000; + + parts.Add( + UnitsMap1[n3] + (n3 == 0 ? "" : UnitsMap2[3]) + + UnitsMap1[n2] + (n2 == 0 ? "" : UnitsMap2[2]) + + UnitsMap1[n1] + (n1 == 0 ? "" : UnitsMap2[1]) + + (n0 == 1 ? "一" : UnitsMap1[n0]) + + (groupNumber == 0 ? "" : UnitsMap3[groupLevel]) + ); + + groupLevel++; + } + + parts.Reverse(); + var toWords = string.Join("", parts.ToArray()); + + if (isOrdinal) + { + toWords = string.Format("{0}番目", toWords); + } + + return toWords; + } + } +} From 2f00c865c494e61c691f020ee25309911c80e4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=B8=E3=83=BC=E3=81=95=E3=82=93?= Date: Sat, 26 Sep 2020 02:35:34 +0900 Subject: [PATCH 059/126] Move Test.cs from Tests to Tests.Shared --- src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems | 1 + src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj | 2 +- .../Localisation/ja/NumberToWordsTests.cs | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename src/{Humanizer.Tests => Humanizer.Tests.Shared}/Localisation/ja/NumberToWordsTests.cs (100%) diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index 2ca708a9f..b228f2d97 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -190,5 +190,6 @@ + \ No newline at end of file diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj index ef1fb36a8..40658b887 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj @@ -9,7 +9,7 @@ - + diff --git a/src/Humanizer.Tests/Localisation/ja/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ja/NumberToWordsTests.cs similarity index 100% rename from src/Humanizer.Tests/Localisation/ja/NumberToWordsTests.cs rename to src/Humanizer.Tests.Shared/Localisation/ja/NumberToWordsTests.cs From 6e958b784c7ac299cf47eb7722e5eb5cf30c516d Mon Sep 17 00:00:00 2001 From: Alex Boutin Date: Fri, 25 Sep 2020 19:12:34 -0400 Subject: [PATCH 060/126] Added check for Blank string Added exception for word "Ex" to "Exes" --- src/Humanizer/Inflections/Vocabularies.cs | 3 +++ src/Humanizer/Inflections/Vocabulary.cs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/Humanizer/Inflections/Vocabularies.cs b/src/Humanizer/Inflections/Vocabularies.cs index ef20a9916..f94cccf3b 100644 --- a/src/Humanizer/Inflections/Vocabularies.cs +++ b/src/Humanizer/Inflections/Vocabularies.cs @@ -92,6 +92,9 @@ private static Vocabulary BuildDefault() //Fix #789 _default.AddIrregular("cache", "caches"); + //Fix 975 + _default.AddIrregular("ex", "exes"); + _default.AddIrregular("is", "are", matchEnding: false); _default.AddIrregular("that", "those", matchEnding: false); _default.AddIrregular("this", "these", matchEnding: false); diff --git a/src/Humanizer/Inflections/Vocabulary.cs b/src/Humanizer/Inflections/Vocabulary.cs index 9bbaf34d6..784af0a4f 100644 --- a/src/Humanizer/Inflections/Vocabulary.cs +++ b/src/Humanizer/Inflections/Vocabulary.cs @@ -126,6 +126,11 @@ private string ApplyRules(IList rules, string word, bool skipFirstRule) return null; } + if (word.Length < 1) + { + return null; + } + if (IsUncountable(word)) { return word; From ad2d0b8cea1b53fbc793638dc726c2afc31a77b7 Mon Sep 17 00:00:00 2001 From: Alex Boutin Date: Fri, 25 Sep 2020 20:21:55 -0400 Subject: [PATCH 061/126] forgot to add MathEnding, my bad --- src/Humanizer/Inflections/Vocabularies.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/Inflections/Vocabularies.cs b/src/Humanizer/Inflections/Vocabularies.cs index f94cccf3b..d61c35c84 100644 --- a/src/Humanizer/Inflections/Vocabularies.cs +++ b/src/Humanizer/Inflections/Vocabularies.cs @@ -93,7 +93,7 @@ private static Vocabulary BuildDefault() _default.AddIrregular("cache", "caches"); //Fix 975 - _default.AddIrregular("ex", "exes"); + _default.AddIrregular("ex", "exes", matchEnding: false); _default.AddIrregular("is", "are", matchEnding: false); _default.AddIrregular("that", "those", matchEnding: false); From fca597aa808257de04fc21a738b9666a1a93b26c Mon Sep 17 00:00:00 2001 From: Alex Boutin Date: Fri, 25 Sep 2020 20:35:08 -0400 Subject: [PATCH 062/126] Added test for blank lines, blank return blank to avoid NullPointer --- src/Humanizer.Tests.Shared/InflectorTests.cs | 4 ++++ src/Humanizer/Inflections/Vocabulary.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Humanizer.Tests.Shared/InflectorTests.cs b/src/Humanizer.Tests.Shared/InflectorTests.cs index 1bfa253f1..4f5aa2b35 100644 --- a/src/Humanizer.Tests.Shared/InflectorTests.cs +++ b/src/Humanizer.Tests.Shared/InflectorTests.cs @@ -364,6 +364,10 @@ public IEnumerator GetEnumerator() //Issue #789 yield return new object[] { "cache", "caches" }; + + //Issue #975, added by Alex Boutin + yield return new object[] { "ex", "exes" }; + yield return new object[] { "", "" }; } IEnumerator IEnumerable.GetEnumerator() diff --git a/src/Humanizer/Inflections/Vocabulary.cs b/src/Humanizer/Inflections/Vocabulary.cs index 784af0a4f..084204cc4 100644 --- a/src/Humanizer/Inflections/Vocabulary.cs +++ b/src/Humanizer/Inflections/Vocabulary.cs @@ -128,7 +128,7 @@ private string ApplyRules(IList rules, string word, bool skipFirstRule) if (word.Length < 1) { - return null; + return word; } if (IsUncountable(word)) From 6ba5fa69793a695cdd7f222c5a2842f9e54408f7 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Sat, 26 Sep 2020 10:38:40 -0400 Subject: [PATCH 063/126] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5495c0dff..f0894a677 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ Here is a checklist you should tick through before submitting a pull request: - [ ] Implementation is clean - [ ] Code adheres to the existing coding standards; e.g. no curlies for one-line blocks, no redundant empty lines between methods or code blocks, spaces rather than tabs, etc. - - [ ] No ReSharper warnings + - [ ] No Code Analysis warnings - [ ] There is proper unit test coverage - [ ] If the code is copied from StackOverflow (or a blog or OSS) full disclosure is included. That includes required license files and/or file headers explaining where the code came from with proper attribution - [ ] There are very few or no comments (because comments shouldn't be needed if you write clean code) From 72474b9c522734f30059acd7d684a9dd73d33f2f Mon Sep 17 00:00:00 2001 From: Alex Boutin Date: Mon, 28 Sep 2020 08:10:37 -0400 Subject: [PATCH 064/126] Add overload to manage "and" before last number --- .../NumberToWordsTests.cs | 7 +++++++ .../ArabicNumberToWordsConverter.cs | 2 +- ...BrazilianPortugueseNumberToWordsConverter.cs | 2 +- .../BulgarianNumberToWordsConverter.cs | 4 ++-- .../CzechNumberToWordsConverter.cs | 2 +- .../EnglishNumberToWordsConverter.cs | 9 +++++++-- .../FrenchNumberToWordsConverterBase.cs | 2 +- .../GenderedNumberToWordsConverter.cs | 8 +++++++- .../GenderlessNumberToWordsConverter.cs | 8 +++++++- .../GermanNumberToWordsConverterBase.cs | 2 +- .../HebrewNumberToWordsConverter.cs | 2 +- .../NumberToWords/INumberToWordsConverter.cs | 9 ++++++++- .../ItalianNumberToWordsConverter.cs | 2 +- .../MalteseNumberToWordsConvertor.cs | 2 +- .../NorwegianBokmalNumberToWordsConverter.cs | 2 +- .../PortugueseNumberToWordsConverter.cs | 2 +- .../RomanianNumberToWordsConverter.cs | 2 +- .../RussianNumberToWordsConverter.cs | 2 +- .../SpanishNumberToWordsConverter.cs | 2 +- .../SwedishNumberToWordsConverter.cs | 4 ++-- .../UkrainianNumberToWordsConverter.cs | 2 +- src/Humanizer/NumberToWordsExtension.cs | 17 +++++++++++++++-- 22 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/Humanizer.Tests.Shared/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/NumberToWordsTests.cs index 3d8b9ca3e..908698428 100644 --- a/src/Humanizer.Tests.Shared/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/NumberToWordsTests.cs @@ -40,6 +40,13 @@ public void ToWordsInt(int number, string expected) Assert.Equal(expected, number.ToWords()); } + [InlineData(3501L, "three thousand five hundred one", false)] + [Theory] + public void ToWordsWithoutAnd(int number, string expected, bool addAnd) + { + Assert.Equal(expected, number.ToWords(addAnd)); + } + [InlineData(1L, "one")] [InlineData(11L, "eleven")] [InlineData(111L, "one hundred and eleven")] diff --git a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs index ae7f5f3a3..51576cf80 100644 --- a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs @@ -16,7 +16,7 @@ internal class ArabicNumberToWordsConverter : GenderedNumberToWordsConverter private static readonly string[] FeminineOnesGroup = { "", "واحدة", "اثنتان", "ثلاث", "أربع", "خمس", "ست", "سبع", "ثمان", "تسع", "عشر", "إحدى عشرة", "اثنتا عشرة", "ثلاث عشرة", "أربع عشرة", "خمس عشرة", "ست عشرة", "سبع عشرة", "ثمان عشرة", "تسع عشرة" }; - public override string Convert(long number, GrammaticalGender gender) + public override string Convert(long number, GrammaticalGender gender, bool addAnd = true) { if (number == 0) { diff --git a/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs index dca55b26f..7f4f101c3 100644 --- a/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/BrazilianPortugueseNumberToWordsConverter.cs @@ -13,7 +13,7 @@ internal class BrazilianPortugueseNumberToWordsConverter : GenderedNumberToWords private static readonly string[] PortugueseOrdinalTensMap = { "zero", "décimo", "vigésimo", "trigésimo", "quadragésimo", "quinquagésimo", "sexagésimo", "septuagésimo", "octogésimo", "nonagésimo" }; private static readonly string[] PortugueseOrdinalHundredsMap = { "zero", "centésimo", "ducentésimo", "trecentésimo", "quadringentésimo", "quingentésimo", "sexcentésimo", "septingentésimo", "octingentésimo", "noningentésimo" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > 999999999999 || input < -999999999999) { diff --git a/src/Humanizer/Localisation/NumberToWords/BulgarianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/BulgarianNumberToWordsConverter.cs index 57d78410e..e6a908f5f 100644 --- a/src/Humanizer/Localisation/NumberToWords/BulgarianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/BulgarianNumberToWordsConverter.cs @@ -39,12 +39,12 @@ internal class BulgarianNumberToWordsConverter : GenderedNumberToWordsConverter "осемнадесет", "деветнадесет" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { return Convert(input, gender, false); } - private string Convert(long input, GrammaticalGender gender, bool isOrdinal) + private string Convert(long input, GrammaticalGender gender, bool isOrdinal, bool addAnd = true) { if (input > int.MaxValue || input < int.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/CzechNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/CzechNumberToWordsConverter.cs index 454c4464d..9c190fef9 100644 --- a/src/Humanizer/Localisation/NumberToWords/CzechNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/CzechNumberToWordsConverter.cs @@ -26,7 +26,7 @@ public CzechNumberToWordsConverter(CultureInfo culture) _culture = culture; } - public override string Convert(long number, GrammaticalGender gender) + public override string Convert(long number, GrammaticalGender gender, bool addAnd = true) { if (number == 0) { diff --git a/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs index 0c81b7ae8..e2b50167c 100644 --- a/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs @@ -25,12 +25,17 @@ public override string Convert(long number) return Convert(number, false); } + public override string Convert(long number, bool addAnd = true) + { + return Convert(number, false, addAnd); + } + public override string ConvertToOrdinal(int number) { return Convert(number, true); } - private string Convert(long number, bool isOrdinal) + private string Convert(long number, bool isOrdinal, bool addAnd = true) { if (number == 0) { @@ -88,7 +93,7 @@ private string Convert(long number, bool isOrdinal) if (number > 0) { - if (parts.Count != 0) + if (parts.Count != 0 && addAnd) { parts.Add("and"); } diff --git a/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs b/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs index 65c0c790a..8ac9832f3 100644 --- a/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs +++ b/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs @@ -8,7 +8,7 @@ internal abstract class FrenchNumberToWordsConverterBase : GenderedNumberToWords 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", "septante", "octante", "nonante" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > Int32.MaxValue || input < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs index 86f1a24cb..093b00144 100644 --- a/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs @@ -19,13 +19,19 @@ public string Convert(long number) return Convert(number, _defaultGender); } + public string Convert(long number, bool addAnd) + { + return Convert(number, _defaultGender); + } + /// /// Converts the number to string using the provided grammatical gender /// /// /// /// - public abstract string Convert(long number, GrammaticalGender gender); + public abstract string Convert(long number, GrammaticalGender gender, bool addAnd = true); + /// /// Converts the number to ordinal string using the locale's default grammatical gender diff --git a/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs index e7c4fe529..87e651cff 100644 --- a/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs @@ -9,17 +9,23 @@ internal abstract class GenderlessNumberToWordsConverter : INumberToWordsConvert /// public abstract string Convert(long number); + public virtual string Convert(long number, bool addAnd) + { + return Convert(number); + } + /// /// Converts the number to string ignoring the provided grammatical gender /// /// /// /// - public virtual string Convert(long number, GrammaticalGender gender) + public virtual string Convert(long number, GrammaticalGender gender, bool addAnd = true) { return Convert(number); } + /// /// Converts the number to ordinal string /// diff --git a/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs b/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs index aef3c062b..c21ba1d8d 100644 --- a/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs +++ b/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs @@ -17,7 +17,7 @@ internal abstract class GermanNumberToWordsConverterBase : GenderedNumberToWords private readonly string[] BillionOrdinalSingular = { "einmilliard", "einemilliarde" }; private readonly string[] BillionOrdinalPlural = { "{0}milliard", "{0}milliarden" }; - public override string Convert(long number, GrammaticalGender gender) + public override string Convert(long number, GrammaticalGender gender, bool addAnd = true) { if (number == 0) { diff --git a/src/Humanizer/Localisation/NumberToWords/HebrewNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/HebrewNumberToWordsConverter.cs index 92f7857cb..7ecda3774 100644 --- a/src/Humanizer/Localisation/NumberToWords/HebrewNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/HebrewNumberToWordsConverter.cs @@ -38,7 +38,7 @@ public HebrewNumberToWordsConverter(CultureInfo culture) _culture = culture; } - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > Int32.MaxValue || input < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs index 151770be1..19033d15f 100644 --- a/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs @@ -12,13 +12,20 @@ public interface INumberToWordsConverter /// string Convert(long number); + /// + /// Converts the number to string using the locale's default grammatical gender with or without adding 'And' + /// + /// + /// + string Convert(long number, bool addAnd); + /// /// Converts the number to string using the provided grammatical gender /// /// /// /// - string Convert(long number, GrammaticalGender gender); + string Convert(long number, GrammaticalGender gender, bool addAnd = true); /// /// Converts the number to ordinal string using the locale's default grammatical gender diff --git a/src/Humanizer/Localisation/NumberToWords/ItalianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ItalianNumberToWordsConverter.cs index e3ac780bc..29c82d1a9 100644 --- a/src/Humanizer/Localisation/NumberToWords/ItalianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/ItalianNumberToWordsConverter.cs @@ -5,7 +5,7 @@ namespace Humanizer.Localisation.NumberToWords { internal class ItalianNumberToWordsConverter : GenderedNumberToWordsConverter { - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > Int32.MaxValue || input < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs b/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs index 9237e9ccf..b872432b5 100644 --- a/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs +++ b/src/Humanizer/Localisation/NumberToWords/MalteseNumberToWordsConvertor.cs @@ -34,7 +34,7 @@ internal class MalteseNumberToWordsConvertor : GenderedNumberToWordsConverter "tmintax-il", "dsatax-il" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { bool negativeNumber = false; diff --git a/src/Humanizer/Localisation/NumberToWords/NorwegianBokmalNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/NorwegianBokmalNumberToWordsConverter.cs index baa730059..c94b85e2a 100644 --- a/src/Humanizer/Localisation/NumberToWords/NorwegianBokmalNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/NorwegianBokmalNumberToWordsConverter.cs @@ -21,7 +21,7 @@ internal class NorwegianBokmalNumberToWordsConverter : GenderedNumberToWordsConv {12, "tolvte"} }; - public override string Convert(long number, GrammaticalGender gender) + public override string Convert(long number, GrammaticalGender gender, bool addAnd = true) { if (number > Int32.MaxValue || number < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/PortugueseNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/PortugueseNumberToWordsConverter.cs index 553c1158b..9e7daa298 100644 --- a/src/Humanizer/Localisation/NumberToWords/PortugueseNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/PortugueseNumberToWordsConverter.cs @@ -13,7 +13,7 @@ internal class PortugueseNumberToWordsConverter : GenderedNumberToWordsConverter private static readonly string[] PortugueseOrdinalTensMap = { "zero", "décimo", "vigésimo", "trigésimo", "quadragésimo", "quinquagésimo", "sexagésimo", "septuagésimo", "octogésimo", "nonagésimo" }; private static readonly string[] PortugueseOrdinalHundredsMap = { "zero", "centésimo", "ducentésimo", "trecentésimo", "quadringentésimo", "quingentésimo", "sexcentésimo", "septingentésimo", "octingentésimo", "noningentésimo" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > 999999999999 || input < -999999999999) { diff --git a/src/Humanizer/Localisation/NumberToWords/RomanianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/RomanianNumberToWordsConverter.cs index f6071cf96..76524171c 100644 --- a/src/Humanizer/Localisation/NumberToWords/RomanianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/RomanianNumberToWordsConverter.cs @@ -5,7 +5,7 @@ namespace Humanizer.Localisation.NumberToWords { internal class RomanianNumberToWordsConverter : GenderedNumberToWordsConverter { - public override string Convert(long number, GrammaticalGender gender) + public override string Convert(long number, GrammaticalGender gender, bool addAnd = true) { if (number > Int32.MaxValue || number < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs index 1375cd5b8..50b870a72 100644 --- a/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/RussianNumberToWordsConverter.cs @@ -14,7 +14,7 @@ internal class RussianNumberToWordsConverter : GenderedNumberToWordsConverter private static readonly string[] TensOrdinal = { string.Empty, "десят", "двадцат", "тридцат", "сороков", "пятидесят", "шестидесят", "семидесят", "восьмидесят", "девяност" }; private static readonly string[] UnitsOrdinal = { string.Empty, "перв", "втор", "трет", "четверт", "пят", "шест", "седьм", "восьм", "девят", "десят", "одиннадцат", "двенадцат", "тринадцат", "четырнадцат", "пятнадцат", "шестнадцат", "семнадцат", "восемнадцат", "девятнадцат" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input == 0) { diff --git a/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs index 441b89ce5..f89f25959 100644 --- a/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/SpanishNumberToWordsConverter.cs @@ -29,7 +29,7 @@ internal class SpanishNumberToWordsConverter : GenderedNumberToWordsConverter {9, "noveno"}, }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > Int32.MaxValue || input < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs index b5c150810..325c3420a 100644 --- a/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/SwedishNumberToWordsConverter.cs @@ -26,8 +26,8 @@ private class Fact new Fact {Value = 1000, Name = "tusen", Prefix = " ", Postfix = " ", DisplayOneUnit = true}, new Fact {Value = 100, Name = "hundra", Prefix = "", Postfix = "", DisplayOneUnit = false} }; - - public override string Convert(long input, GrammaticalGender gender) + + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input > Int32.MaxValue || input < Int32.MinValue) { diff --git a/src/Humanizer/Localisation/NumberToWords/UkrainianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/UkrainianNumberToWordsConverter.cs index 87d05d6db..0ca5e6b7d 100644 --- a/src/Humanizer/Localisation/NumberToWords/UkrainianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/UkrainianNumberToWordsConverter.cs @@ -14,7 +14,7 @@ internal class UkrainianNumberToWordsConverter : GenderedNumberToWordsConverter private static readonly string[] TensOrdinal = { string.Empty, "десят", "двадцят", "тридцят", "сороков", "п'ятдесят", "шістдесят", "сімдесят", "вісімдесят", "дев'яност" }; private static readonly string[] UnitsOrdinal = { "нульов", "перш", "друг", "трет", "четверт", "п'ят", "шост", "сьом", "восьм", "дев'ят", "десят", "одинадцят", "дванадцят", "тринадцят", "чотирнадцят", "п'ятнадцят", "шістнадцят", "сімнадцят", "вісімнадцят", "дев'ятнадцят" }; - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input == 0) { diff --git a/src/Humanizer/NumberToWordsExtension.cs b/src/Humanizer/NumberToWordsExtension.cs index cfcb189fb..b93f2d0cc 100644 --- a/src/Humanizer/NumberToWordsExtension.cs +++ b/src/Humanizer/NumberToWordsExtension.cs @@ -19,6 +19,19 @@ public static string ToWords(this int number, CultureInfo culture = null) return ((long)number).ToWords(culture); } + + /// + /// 3501.ToWords(false) -> "three thousand five hundred one" + /// + /// Number to be turned to words + /// To add 'and' before the last number. + /// Culture to use. If null, current thread's UI culture is used. + /// + public static string ToWords(this int number,bool addAnd, CultureInfo culture = null) + { + return ((long)number).ToWords(culture, addAnd); + } + /// /// For locales that support gender-specific forms /// @@ -50,9 +63,9 @@ public static string ToWords(this int number, GrammaticalGender gender, CultureI /// Number to be turned to words /// Culture to use. If null, current thread's UI culture is used. /// - public static string ToWords(this long number, CultureInfo culture = null) + public static string ToWords(this long number, CultureInfo culture = null, bool addAnd = true) { - return Configurator.GetNumberToWordsConverter(culture).Convert(number); + return Configurator.GetNumberToWordsConverter(culture).Convert(number, addAnd); } /// From c207cb0eb0d5aaff6c5be05682e0d09d686a0272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=B8=E3=83=BC=E3=81=95=E3=82=93?= Date: Tue, 29 Sep 2020 18:03:19 +0900 Subject: [PATCH 065/126] Remove redundant code --- src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems | 2 +- src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index b228f2d97..7f8f94c13 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -104,6 +104,7 @@ + @@ -190,6 +191,5 @@ - \ No newline at end of file diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj index 40658b887..585175c8d 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.shproj @@ -7,10 +7,6 @@ - - - - \ No newline at end of file From eb8189a69879a2b3e6051358c58aca2e23ac893a Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Wed, 30 Sep 2020 18:29:49 -0400 Subject: [PATCH 066/126] Apply suggestions from code review Minor clean up prior to merge --- NuSpecs/Humanizer.Core.ta.nuspec | 2 +- .../Localisation/NumberToWords/TamilNumberToWordsConverter.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/NuSpecs/Humanizer.Core.ta.nuspec b/NuSpecs/Humanizer.Core.ta.nuspec index 9c5a45f4d..f071d14bf 100644 --- a/NuSpecs/Humanizer.Core.ta.nuspec +++ b/NuSpecs/Humanizer.Core.ta.nuspec @@ -4,7 +4,7 @@ Humanizer.Core.ta $version$ Humanizer Locale (ta) - Mehdi Khalili, Claire Novotny, Mohamed Mahir + Mehdi Khalili, Claire Novotny https://github.com/Humanizr/Humanizer logo.png false diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs index 05dd51e40..0c883461e 100644 --- a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -//Author:Mohamed Mahir - mahir78@gmail.com namespace Humanizer.Localisation.NumberToWords { From 489d020e3c54eb789de87b3d281b0d30868a167f Mon Sep 17 00:00:00 2001 From: lukasz Date: Thu, 1 Oct 2020 10:26:56 +0200 Subject: [PATCH 067/126] Make PolishNumberToWordsConverter gendered, add support for longs --- .../Localisation/pl/NumberToWordsTests.cs | 53 ++++- .../PolishNumberToWordsConverter.cs | 199 ++++++++++-------- 2 files changed, 165 insertions(+), 87 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/pl/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/pl/NumberToWordsTests.cs index 2050bc731..244e72e1c 100644 --- a/src/Humanizer.Tests.Shared/Localisation/pl/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/pl/NumberToWordsTests.cs @@ -42,9 +42,12 @@ public class NumberToWordsTests [InlineData(2000, "dwa tysiące")] [InlineData(5000, "pięć tysięcy")] [InlineData(10000, "dziesięć tysięcy")] + [InlineData(12000, "dwanaście tysięcy")] [InlineData(20000, "dwadzieścia tysięcy")] [InlineData(22000, "dwadzieścia dwa tysiące")] [InlineData(25000, "dwadzieścia pięć tysięcy")] + [InlineData(31000, "trzydzieści jeden tysięcy")] + [InlineData(34000, "trzydzieści cztery tysiące")] [InlineData(100000, "sto tysięcy")] [InlineData(500000, "pięćset tysięcy")] [InlineData(1000000, "milion")] @@ -55,9 +58,57 @@ public class NumberToWordsTests [InlineData(1501001892, "miliard pięćset jeden milionów tysiąc osiemset dziewięćdziesiąt dwa")] [InlineData(2147483647, "dwa miliardy sto czterdzieści siedem milionów czterysta osiemdziesiąt trzy tysiące sześćset czterdzieści siedem")] [InlineData(-1501001892, "minus miliard pięćset jeden milionów tysiąc osiemset dziewięćdziesiąt dwa")] - public void ToWordsPolish(int number, string expected) + [InlineData(long.MaxValue, + "dziewięć trylionów " + + "dwieście dwadzieścia trzy biliardy " + + "trzysta siedemdziesiąt dwa biliony " + + "trzydzieści sześć miliardów " + + "osiemset pięćdziesiąt cztery miliony " + + "siedemset siedemdziesiąt pięć tysięcy " + + "osiemset siedem")] + [InlineData(long.MinValue, + "minus dziewięć trylionów " + + "dwieście dwadzieścia trzy biliardy " + + "trzysta siedemdziesiąt dwa biliony " + + "trzydzieści sześć miliardów " + + "osiemset pięćdziesiąt cztery miliony " + + "siedemset siedemdziesiąt pięć tysięcy " + + "osiemset osiem")] + public void ToWordsPolish(long number, string expected) { Assert.Equal(expected, number.ToWords()); } + + [Theory] + [InlineData(-1, "minus jeden", GrammaticalGender.Masculine)] + [InlineData(-1, "minus jedna", GrammaticalGender.Feminine)] + [InlineData(-1, "minus jedno", GrammaticalGender.Neuter)] + [InlineData(-2, "minus dwa", GrammaticalGender.Masculine)] + [InlineData(-2, "minus dwie", GrammaticalGender.Feminine)] + [InlineData(-2, "minus dwa", GrammaticalGender.Neuter)] + [InlineData(1, "jeden", GrammaticalGender.Masculine)] + [InlineData(1, "jedna", GrammaticalGender.Feminine)] + [InlineData(1, "jedno", GrammaticalGender.Neuter)] + [InlineData(2, "dwa", GrammaticalGender.Masculine)] + [InlineData(2, "dwie", GrammaticalGender.Feminine)] + [InlineData(2, "dwa", GrammaticalGender.Neuter)] + [InlineData(121, "sto dwadzieścia jeden", GrammaticalGender.Masculine)] + [InlineData(121, "sto dwadzieścia jeden", GrammaticalGender.Feminine)] + [InlineData(121, "sto dwadzieścia jeden", GrammaticalGender.Neuter)] + [InlineData(122, "sto dwadzieścia dwa", GrammaticalGender.Masculine)] + [InlineData(122, "sto dwadzieścia dwie", GrammaticalGender.Feminine)] + [InlineData(122, "sto dwadzieścia dwa", GrammaticalGender.Neuter)] + [InlineData(-2542, "minus dwa tysiące pięćset czterdzieści dwa", GrammaticalGender.Masculine)] + [InlineData(-2542, "minus dwa tysiące pięćset czterdzieści dwie", GrammaticalGender.Feminine)] + [InlineData(-2542, "minus dwa tysiące pięćset czterdzieści dwa", GrammaticalGender.Neuter)] + [InlineData(1000001, "milion jeden", GrammaticalGender.Feminine)] + [InlineData(-1000001, "minus milion jeden", GrammaticalGender.Feminine)] + [InlineData(1000002, "milion dwa", GrammaticalGender.Masculine)] + [InlineData(1000002, "milion dwie", GrammaticalGender.Feminine)] + [InlineData(1000002, "milion dwa", GrammaticalGender.Neuter)] + public void ToWordsPolishWithGender(int number, string expected, GrammaticalGender gender) + { + Assert.Equal(expected, number.ToWords(gender)); + } } } diff --git a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs index fcb1ad077..8cf64dc9d 100644 --- a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs @@ -4,11 +4,37 @@ namespace Humanizer.Localisation.NumberToWords { - internal class PolishNumberToWordsConverter : GenderlessNumberToWordsConverter + internal class PolishNumberToWordsConverter : GenderedNumberToWordsConverter { - private static readonly string[] HundredsMap = { "zero", "sto", "dwieście", "trzysta", "czterysta", "pięćset", "sześćset", "siedemset", "osiemset", "dziewięćset" }; - private static readonly string[] TensMap = { "zero", "dziesięć", "dwadzieścia", "trzydzieści", "czterdzieści", "pięćdziesiąt", "sześćdziesiąt", "siedemdziesiąt", "osiemdziesiąt", "dziewięćdziesiąt" }; - private static readonly string[] UnitsMap = { "zero", "jeden", "dwa", "trzy", "cztery", "pięć", "sześć", "siedem", "osiem", "dziewięć", "dziesięć", "jedenaście", "dwanaście", "trzynaście", "czternaście", "piętnaście", "szesnaście", "siedemnaście", "osiemnaście", "dziewiętnaście" }; + private static readonly string[] HundredsMap = + { + "zero", "sto", "dwieście", "trzysta", "czterysta", "pięćset", "sześćset", "siedemset", "osiemset", "dziewięćset" + }; + + private static readonly string[] TensMap = + { + "zero", "dziesięć", "dwadzieścia", "trzydzieści", "czterdzieści", "pięćdziesiąt", "sześćdziesiąt", + "siedemdziesiąt", "osiemdziesiąt", "dziewięćdziesiąt" + }; + + private static readonly string[] UnitsMap = + { + "zero", "jeden", "dwa", "trzy", "cztery", "pięć", "sześć", "siedem", "osiem", "dziewięć", "dziesięć", + "jedenaście", "dwanaście", "trzynaście", "czternaście", "piętnaście", "szesnaście", "siedemnaście", + "osiemnaście", "dziewiętnaście" + }; + + private static readonly string[][] PowersOfThousandMap = + { + new []{"tysiąc", "tysiące", "tysięcy"}, + new []{"milion", "miliony", "milionów"}, + new []{"miliard", "miliardy", "miliardów"}, + new []{"bilion", "biliony", "bilionów"}, + new []{"biliard", "biliardy", "biliardów"}, + new []{"trylion", "tryliony", "trylionów"} + }; + + private const long MaxPossibleDivisor = 1_000_000_000_000_000_000; private readonly CultureInfo _culture; @@ -16,125 +42,126 @@ public PolishNumberToWordsConverter(CultureInfo culture) { _culture = culture; } - - private static void CollectPartsUnderThousand(ICollection parts, int number) + + public override string Convert(long input, GrammaticalGender gender) { - var hundreds = number / 100; - if (hundreds > 0) + if (input == 0) { - parts.Add(HundredsMap[hundreds]); - number = number % 100; + return "zero"; } - var tens = number / 10; - if (tens > 1) - { - parts.Add(TensMap[tens]); - number = number % 10; - } + var parts = new List(); + CollectParts(parts, input, gender); - if (number > 0) - { - parts.Add(UnitsMap[number]); - } + return string.Join(" ", parts); } - - private static int GetMappingIndex(int number) + + public override string ConvertToOrdinal(int number, GrammaticalGender gender) { - if (number == 1) - { - return 0; - } + return number.ToString(_culture); + } - if (number > 1 && number < 5) + private static void CollectParts(ICollection parts, long input, GrammaticalGender gender) + { + var inputSign = 1; + if (input < 0) { - return 1; //denominator + parts.Add("minus"); + inputSign = -1; } - var tens = number / 10; - if (tens > 1) + var number = input; + var divisor = MaxPossibleDivisor; + var power = PowersOfThousandMap.Length - 1; + while (divisor > 0) { - var unity = number % 10; - if (unity > 1 && unity < 5) + var multiplier = (int) Math.Abs(number / divisor); + if (divisor > 1) { - return 1; //denominator + if (multiplier > 1) + { + CollectPartsUnderThousand(parts, multiplier, GrammaticalGender.Masculine); + } + + if (multiplier > 0) + { + parts.Add(GetPowerOfThousandNameForm(multiplier, power)); + } + } + else if (multiplier > 0) + { + if (multiplier == 1 && Math.Abs(input) != 1) + { + gender = GrammaticalGender.Masculine; + } + CollectPartsUnderThousand(parts, multiplier, gender); } - } - return 2; //genitive + number -= multiplier * divisor * inputSign; + divisor /= 1000; + power--; + } } - public override string Convert(long input) + private static void CollectPartsUnderThousand(ICollection parts, int number, GrammaticalGender gender) { - if (input > Int32.MaxValue || input < Int32.MinValue) + var hundredsDigit = number / 100; + var tensDigit = (number % 100) / 10; + var unitsDigit = number % 10; + + if (hundredsDigit >= 1) { - throw new NotImplementedException(); + parts.Add(HundredsMap[hundredsDigit]); } - var number = (int)input; - if (number == 0) + + if (tensDigit >= 2) { - return "zero"; + parts.Add(TensMap[tensDigit]); } - var parts = new List(); - - if (number < 0) + if (tensDigit != 1 && unitsDigit == 2) { - parts.Add("minus"); - number = -number; + var genderedForm = gender == GrammaticalGender.Feminine ? "dwie" : "dwa"; + parts.Add(genderedForm); } - - var milliard = number / 1000000000; - if (milliard > 0) + else if (number == 1) { - if (milliard > 1) + var genderedForm = gender switch { - CollectPartsUnderThousand(parts, milliard); - } - - var map = new[] { "miliard", "miliardy", "miliardów" }; //one, denominator, genitive - parts.Add(map[GetMappingIndex(milliard)]); - number %= 1000000000; + GrammaticalGender.Masculine => "jeden", + GrammaticalGender.Feminine => "jedna", + GrammaticalGender.Neuter => "jedno", + _ => throw new ArgumentOutOfRangeException(nameof(gender)) + }; + parts.Add(genderedForm); } - - var million = number / 1000000; - if (million > 0) + else { - if (million > 1) + var unit = unitsDigit + 10 * (tensDigit == 1 ? 1 : 0); + if (unit > 0) { - CollectPartsUnderThousand(parts, million); + parts.Add(UnitsMap[unit]); } - - var map = new[] { "milion", "miliony", "milionów" }; //one, denominator, genitive - parts.Add(map[GetMappingIndex(million)]); - number %= 1000000; } + } - var thouthand = number / 1000; - if (thouthand > 0) + private static string GetPowerOfThousandNameForm(int multiplier, int power) + { + const int singularIndex = 0; + const int pluralIndex = 1; + const int genitiveIndex = 2; + if (multiplier == 1) { - if (thouthand > 1) - { - CollectPartsUnderThousand(parts, thouthand); - } - - var thousand = new[] { "tysiąc", "tysiące", "tysięcy" }; //one, denominator, genitive - parts.Add(thousand[GetMappingIndex(thouthand)]); - number %= 1000; + return PowersOfThousandMap[power][singularIndex]; } - - var units = number / 1; - if (units > 0) + + var multiplierUnitsDigit = multiplier % 10; + var multiplierTensDigit = (multiplier % 100) / 10; + if (multiplierTensDigit == 1 || multiplierUnitsDigit <= 1 || multiplierUnitsDigit >= 5) { - CollectPartsUnderThousand(parts, units); + return PowersOfThousandMap[power][genitiveIndex]; } - - return string.Join(" ", parts); - } - - public override string ConvertToOrdinal(int number) - { - return number.ToString(_culture); + return PowersOfThousandMap[power][pluralIndex]; } } } From f9a3677fe3fd0e14e1305cb7082189f4f0c301b3 Mon Sep 17 00:00:00 2001 From: Alex Boutin Date: Thu, 1 Oct 2020 08:15:33 -0400 Subject: [PATCH 068/126] Changed the readme to add new overload documentation. --- readme.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/readme.md b/readme.md index 50915b3aa..d94b52fc6 100644 --- a/readme.md +++ b/readme.md @@ -783,6 +783,14 @@ Also, culture to use can be specified explicitly. If it is not, current thread's 1.ToWords(GrammaticalGender.Masculine, new CultureInfo("ru")) => "один" ``` +Another overload of the method allow you to pass a bool to remove the "And" that can be added before the last number: + +```C# +3501.ToWords(false) => "three thousand five hundred one" +102.ToWords(false) => "one hundred two" +``` +This method can be useful for writing checks for example. + ### Number to ordinal words This is kind of mixing `ToWords` with `Ordinalize`. You can call `ToOrdinalWords` on a number to get an ordinal representation of the number in words! For example: From 725b0ca18945c6a7cb5ddf465143dea933472ab5 Mon Sep 17 00:00:00 2001 From: Tomasz Cielecki Date: Mon, 12 Oct 2020 21:21:32 +0200 Subject: [PATCH 069/126] Fix Convert signature in PolishNumberToWordsConverter --- .../Localisation/NumberToWords/PolishNumberToWordsConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs index 8cf64dc9d..256a73f93 100644 --- a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs @@ -43,7 +43,7 @@ public PolishNumberToWordsConverter(CultureInfo culture) _culture = culture; } - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input == 0) { From fae6d8e128638af38930df1161e9d315aa859293 Mon Sep 17 00:00:00 2001 From: kp73 Date: Fri, 23 Oct 2020 19:13:15 +0100 Subject: [PATCH 070/126] fix build --- .../Localisation/NumberToWords/PolishNumberToWordsConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs index 8cf64dc9d..256a73f93 100644 --- a/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/PolishNumberToWordsConverter.cs @@ -43,7 +43,7 @@ public PolishNumberToWordsConverter(CultureInfo culture) _culture = culture; } - public override string Convert(long input, GrammaticalGender gender) + public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) { if (input == 0) { From 12dd0554291a5b978240c9bb69b3ed50149b9ac7 Mon Sep 17 00:00:00 2001 From: kp73 Date: Fri, 23 Oct 2020 19:15:34 +0100 Subject: [PATCH 071/126] option to use English scale word in MetricNumeralExtensions.ToMetric output --- .../MetricNumeralTests.cs | 94 ++++++++++- src/Humanizer/MetricNumeralExtensions.cs | 153 +++++++++++++++--- src/Humanizer/MetricNumeralFormats.cs | 31 ++++ 3 files changed, 250 insertions(+), 28 deletions(-) create mode 100644 src/Humanizer/MetricNumeralFormats.cs diff --git a/src/Humanizer.Tests.Shared/MetricNumeralTests.cs b/src/Humanizer.Tests.Shared/MetricNumeralTests.cs index 0f1892018..5ad53ed81 100644 --- a/src/Humanizer.Tests.Shared/MetricNumeralTests.cs +++ b/src/Humanizer.Tests.Shared/MetricNumeralTests.cs @@ -103,12 +103,100 @@ public void TestAllSymbolsAsInt(int exponent) [InlineData("-3.9m", -3.91e-3, false, true, 1)] [InlineData("10 ", 10, true, false, 0)] [InlineData("1.2", 1.23, false, false, 1)] - public void ToMetric(string expected, double input, bool hasSpace, bool useSymbol, int? decimals) - { + public void ToMetricObsolete(string expected, double input, bool hasSpace, bool useSymbol, int? decimals) + { +#pragma warning disable CS0618 // Type or member is obsolete Assert.Equal(expected, input.ToMetric(hasSpace, useSymbol, decimals)); +#pragma warning restore CS0618 // Type or member is obsolete } - + [Theory] + [InlineData("1.3M", 1300000, null, null)] + [InlineData("1.3million", 1300000, MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1.3 million", 1300000, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1.3 million", 1300000, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("0", 0d, null, null)] + [InlineData("123", 123d, null, null)] + [InlineData("-123", -123d, null, null)] + [InlineData("1.23k", 1230d, null, null)] + [InlineData("1 k", 1000d, MetricNumeralFormats.WithSpace, null)] + [InlineData("1milli", 1E-3, MetricNumeralFormats.UseName, null)] + [InlineData("1.23milli", 1.234E-3, MetricNumeralFormats.UseName, 2)] + [InlineData("12.34k", 12345, null, 2)] + [InlineData("12k", 12345, null, 0)] + [InlineData("-3.9m", -3.91e-3, null, 1)] + [InlineData("10 ", 10, MetricNumeralFormats.WithSpace, 0)] + [InlineData("1.2", 1.23, null, 1)] + [InlineData("1thousand", 1000d, MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1.23 thousand", 1230d, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1Y", 1E24, null, null)] + [InlineData("1 yotta", 1E24, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 septillion", 1E24, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 quadrillion", 1E24, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1Z", 1E21, null, null)] + [InlineData("1 zetta", 1E21, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 sextillion", 1E21, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 trilliard", 1E21, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1E", 1E18, null, null)] + [InlineData("1 exa", 1E18, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 quintillion", 1E18, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 trillion", 1E18, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1P", 1E15, null, null)] + [InlineData("1 peta", 1E15, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 quadrillion", 1E15, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 billiard", 1E15, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1T", 1E12, null, null)] + [InlineData("1 tera", 1E12, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 trillion", 1E12, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 billion", 1E12, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1G", 1E9, null, null)] + [InlineData("1 giga", 1E9, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 billion", 1E9, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 milliard", 1E9, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1M", 1E6, null, null)] + [InlineData("1 mega", 1E6, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 million", 1E6, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 million", 1E6, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1k", 1E3, null, null)] + [InlineData("1 kilo", 1E3, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 thousand", 1E3, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 thousand", 1E3, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1y", 1E-24, null, null)] + [InlineData("1 yocto", 1E-24, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 septillionth", 1E-24, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 quadrillionth", 1E-24, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1z", 1E-21, null, null)] + [InlineData("1 zepto", 1E-21, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 sextillionth", 1E-21, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 trilliardth", 1E-21, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1a", 1E-18, null, null)] + [InlineData("1 atto", 1E-18, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 quintillionth", 1E-18, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 trillionth", 1E-18, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1f", 1E-15, null, null)] + [InlineData("1 femto", 1E-15, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 quadrillionth", 1E-15, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 billiardth", 1E-15, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1p", 1E-12, null, null)] + [InlineData("1 pico", 1E-12, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 trillionth", 1E-12, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 billionth", 1E-12, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1n", 1E-9, null, null)] + [InlineData("1 nano", 1E-9, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 billionth", 1E-9, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 milliardth", 1E-9, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1μ", 1E-6, null, null)] + [InlineData("1 micro", 1E-6, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 millionth", 1E-6, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 millionth", 1E-6, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + [InlineData("1m", 1E-3, null, null)] + [InlineData("1 milli", 1E-3, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseName, null)] + [InlineData("1 thousandth", 1E-3, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseShortScaleWord, null)] + [InlineData("1 thousandth", 1E-3, MetricNumeralFormats.WithSpace | MetricNumeralFormats.UseLongScaleWord, null)] + public void ToMetric(string expected, double input, MetricNumeralFormats? format, int? decimals) + { + Assert.Equal(expected, input.ToMetric(format, decimals)); + } [Theory] [InlineData(1E+27)] diff --git a/src/Humanizer/MetricNumeralExtensions.cs b/src/Humanizer/MetricNumeralExtensions.cs index 271f6b45d..76d1a9f9a 100644 --- a/src/Humanizer/MetricNumeralExtensions.cs +++ b/src/Humanizer/MetricNumeralExtensions.cs @@ -53,7 +53,7 @@ static MetricNumeralExtensions() }; /// - /// Names link a Metric symbol (as key) to its name (as value). + /// UnitPrefixes link a Metric symbol (as key) to its prefix (as value). /// /// /// We dont support : @@ -62,11 +62,26 @@ static MetricNumeralExtensions() /// {'d', "deci" }, /// {'c', "centi"}, /// - private static readonly Dictionary Names = new Dictionary - { - {'Y', "yotta" }, {'Z', "zetta" }, {'E', "exa" }, {'P', "peta" }, {'T', "tera" }, {'G', "giga" }, {'M', "mega" }, {'k', "kilo" }, - {'m', "milli" }, {'μ', "micro" }, {'n', "nano" }, {'p', "pico" }, {'f', "femto" }, {'a', "atto" }, {'z', "zepto" }, {'y', "yocto" } - }; + private static readonly Dictionary UnitPrefixes = new Dictionary + { + {'Y', new UnitPrefix("yotta", "septillion", "quadrillion")}, + {'Z', new UnitPrefix("zetta", "sextillion", "trilliard")}, + {'E', new UnitPrefix("exa", "quintillion", "trillion")}, + {'P', new UnitPrefix("peta", "quadrillion", "billiard")}, + {'T', new UnitPrefix("tera", "trillion", "billion")}, + {'G', new UnitPrefix("giga", "billion", "milliard")}, + {'M', new UnitPrefix("mega", "million")}, + {'k', new UnitPrefix("kilo", "thousand")}, + + {'m', new UnitPrefix("milli", "thousandth")}, + {'μ', new UnitPrefix("micro", "millionth")}, + {'n', new UnitPrefix("nano", "billionth", "milliardth")}, + {'p', new UnitPrefix("pico", "trillionth", "billionth")}, + {'f', new UnitPrefix("femto", "quadrillionth", "billiardth")}, + {'a', new UnitPrefix("atto", "quintillionth", "trillionth")}, + {'z', new UnitPrefix("zepto", "sextillionth", "trilliardth")}, + {'y', new UnitPrefix("yocto", "septillionth", "quadrillionth")} + }; /// /// Converts a Metric representation into a number. @@ -109,11 +124,36 @@ public static double FromMetric(this string input) /// /// /// A valid Metric representation - public static string ToMetric(this int input, bool hasSpace = false, bool useSymbol = true, int? decimals = null) + [Obsolete("Please use overload with MetricNumeralFormats")] + public static string ToMetric(this int input, bool hasSpace, bool useSymbol = true, int? decimals = null) { return ((double)input).ToMetric(hasSpace, useSymbol, decimals); } + + /// + /// Converts a number into a valid and Human-readable Metric representation. + /// + /// + /// Inspired by a snippet from Thom Smith. + /// See this link for more. + /// + /// Number to convert to a Metric representation. + /// A bitwise combination of enumeration values that format the metric representation. + /// If not null it is the numbers of decimals to round the number to + /// + /// + /// 1000.ToMetric() => "1k" + /// 123.ToMetric() => "123" + /// 1E-1.ToMetric() => "100m" + /// + /// + /// A valid Metric representation + public static string ToMetric(this int input, MetricNumeralFormats? formats = null, int? decimals = null) + { + return ((double)input).ToMetric(formats, decimals); + } + /// /// Converts a number into a valid and Human-readable Metric representation. /// @@ -133,7 +173,41 @@ public static string ToMetric(this int input, bool hasSpace = false, bool useSym /// /// /// A valid Metric representation - public static string ToMetric(this double input, bool hasSpace = false, bool useSymbol = true, int? decimals = null) + [Obsolete("Please use overload with MetricNumeralFormats")] + public static string ToMetric(this double input, bool hasSpace, bool useSymbol = true, int? decimals = null) + { + var formats = (MetricNumeralFormats?)null; + if (hasSpace) + { + formats = MetricNumeralFormats.WithSpace; + } + if (!useSymbol) + { + formats = formats.HasValue ? formats | MetricNumeralFormats.UseName + : MetricNumeralFormats.UseName; + } + return ToMetric(input, formats, decimals); + } + + /// + /// Converts a number into a valid and Human-readable Metric representation. + /// + /// + /// Inspired by a snippet from Thom Smith. + /// See this link for more. + /// + /// Number to convert to a Metric representation. + /// A bitwise combination of enumeration values that format the metric representation. + /// If not null it is the numbers of decimals to round the number to + /// + /// + /// 1000d.ToMetric() => "1k" + /// 123d.ToMetric() => "123" + /// 1E-1.ToMetric() => "100m" + /// + /// + /// A valid Metric representation + public static string ToMetric(this double input, MetricNumeralFormats? formats = null, int? decimals = null) { if (input.Equals(0)) { @@ -145,7 +219,7 @@ public static string ToMetric(this double input, bool hasSpace = false, bool use throw new ArgumentOutOfRangeException(nameof(input)); } - return BuildRepresentation(input, hasSpace, useSymbol, decimals); + return BuildRepresentation(input, formats, decimals); } /// @@ -206,25 +280,24 @@ private static double BuildMetricNumber(string input, char last) /// A metric representation with a symbol private static string ReplaceNameBySymbol(string input) { - return Names.Aggregate(input, (current, name) => - current.Replace(name.Value, name.Key.ToString())); + return UnitPrefixes.Aggregate(input, (current, unitPrefix) => + current.Replace(unitPrefix.Value.Name, unitPrefix.Key.ToString())); } /// /// Build a Metric representation of the number. /// /// Number to convert to a Metric representation. - /// True will split the number and the symbol with a whitespace. - /// True will use symbol instead of name + /// A bitwise combination of enumeration values that format the metric representation. /// If not null it is the numbers of decimals to round the number to /// A number in a Metric representation - private static string BuildRepresentation(double input, bool hasSpace, bool useSymbol, int? decimals) + private static string BuildRepresentation(double input, MetricNumeralFormats? formats, int? decimals) { var exponent = (int)Math.Floor(Math.Log10(Math.Abs(input)) / 3); - if (!exponent.Equals(0)) return BuildMetricRepresentation(input, exponent, hasSpace, useSymbol, decimals); + if (!exponent.Equals(0)) return BuildMetricRepresentation(input, exponent, formats, decimals); var representation = decimals.HasValue ? Math.Round(input, decimals.Value).ToString() : input.ToString(); - if (hasSpace) + if ((formats & MetricNumeralFormats.WithSpace) == MetricNumeralFormats.WithSpace) { representation += " "; } @@ -236,11 +309,10 @@ private static string BuildRepresentation(double input, bool hasSpace, bool useS /// /// Number to convert to a Metric representation. /// Exponent of the number in a scientific notation - /// True will split the number and the symbol with a whitespace. - /// True will use symbol instead of name + /// A bitwise combination of enumeration values that format the metric representation. /// If not null it is the numbers of decimals to round the number to /// A number in a Metric representation - private static string BuildMetricRepresentation(double input, int exponent, bool hasSpace, bool useSymbol, int? decimals) + private static string BuildMetricRepresentation(double input, int exponent, MetricNumeralFormats? formats, int? decimals) { var number = input * Math.Pow(1000, -exponent); if (decimals.HasValue) @@ -252,19 +324,34 @@ private static string BuildMetricRepresentation(double input, int exponent, bool ? Symbols[0][exponent - 1] : Symbols[1][-exponent - 1]; return number - + (hasSpace ? " " : string.Empty) - + GetUnit(symbol, useSymbol); + + (formats.HasValue && formats.Value.HasFlag(MetricNumeralFormats.WithSpace) ? " " : string.Empty) + + GetUnitText(symbol, formats); } /// /// Get the unit from a symbol of from the symbol's name. /// /// The symbol linked to the unit - /// True will use symbol instead of name - /// A symbol or a symbol's name - private static string GetUnit(char symbol, bool useSymbol) + /// A bitwise combination of enumeration values that format the metric representation. + /// A symbol, a symbol's name, a symbol's short scale word or a symbol's long scale word + private static string GetUnitText(char symbol, MetricNumeralFormats? formats) { - return useSymbol ? symbol.ToString() : Names[symbol]; + if (formats.HasValue + && formats.Value.HasFlag(MetricNumeralFormats.UseName)) + { + return UnitPrefixes[symbol].Name; + } + if (formats.HasValue + && formats.Value.HasFlag(MetricNumeralFormats.UseShortScaleWord)) + { + return UnitPrefixes[symbol].ShortScaleWord; + } + if (formats.HasValue + && formats.Value.HasFlag(MetricNumeralFormats.UseLongScaleWord)) + { + return UnitPrefixes[symbol].LongScaleWord; + } + return symbol.ToString(); } /// @@ -297,5 +384,21 @@ private static bool IsInvalidMetricNumeral(this string input) var isSymbol = Symbols[0].Contains(last) || Symbols[1].Contains(last); return !double.TryParse(isSymbol ? input.Remove(index) : input, out var number); } + + private struct UnitPrefix + { + private readonly string _longScaleWord; + + public string Name { get; } + public string ShortScaleWord { get; } + public string LongScaleWord => _longScaleWord ?? ShortScaleWord; + + public UnitPrefix(string name, string shortScaleWord, string longScaleWord = null) + { + Name = name; + ShortScaleWord = shortScaleWord; + _longScaleWord = longScaleWord; + } + } } } diff --git a/src/Humanizer/MetricNumeralFormats.cs b/src/Humanizer/MetricNumeralFormats.cs new file mode 100644 index 000000000..a15dcba88 --- /dev/null +++ b/src/Humanizer/MetricNumeralFormats.cs @@ -0,0 +1,31 @@ +using System; + +namespace Humanizer +{ + /// + /// Flags for formatting the metric representation of numerals. + /// + [Flags] + public enum MetricNumeralFormats + { + /// + /// Use the metric prefix long scale word. + /// + UseLongScaleWord = 1, + + /// + /// Use the metric prefix name instead of the symbol. + /// + UseName = 2, + + /// + /// Use the metric prefix short scale word. + /// + UseShortScaleWord = 4, + + /// + /// Include a space after the numeral. + /// + WithSpace = 8 + } +} From 287f0a824090baa21c4f178c4b09b6b03222f6b3 Mon Sep 17 00:00:00 2001 From: Simon Bauer Date: Sat, 24 Oct 2020 18:24:48 +0200 Subject: [PATCH 072/126] Replaced Humanizer.js with Humanizer.node The Humanizer.js repository has been archived and is linking to Humanizer.node. I guess it's best to replace the links in the readme here as well then! --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index d94b52fc6..ade8d2209 100644 --- a/readme.md +++ b/readme.md @@ -51,7 +51,7 @@ Humanizer meets all your .NET needs for manipulating and displaying strings, enu - [Humanizer ReSharper Annotations](#humanizer-resharper-annotations) - [PowerShell Humanizer](#powershell-humanizer) - [Humanizer JVM](#humanizerjvm) - - [Humanizer.JS](#humanizerjs) + - [Humanizer.node](#humanizernode) - [Main contributors](#main-contributors) - [License](#license) - [Icon](#icon) @@ -1243,8 +1243,8 @@ These annotations do not yet cover the entire library, but [pull requests are al [Humanizer.jvm](https://github.com/Humanizr/Humanizer.jvm) is an adaptation of the Humanizer framework for .Net which is made for the jvm and is written in Kotlin. Humanizer.jvm meets all your jvm needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities. -### Humanizer.JS -[Humanizer.JS](https://github.com/SamuelEnglard/Humanizer.Js) is a TypeScript port of the Humanizer framework. +### Humanizer.node +[Humanizer.node](https://github.com/fakoua/humanizer.node) is a TypeScript port of the Humanizer framework. ## Main contributors - Mehdi Khalili ([@MehdiKhalili](https://twitter.com/MehdiKhalili)) From b3961d4b6c889cd52c584cf41228528e3bf076e1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 3 Nov 2020 00:33:44 +0000 Subject: [PATCH 073/126] Bump Nerdbank.GitVersioning from 3.2.31 to 3.3.37 Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.2.31 to 3.3.37. - [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases) - [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/compare/v3.2.31...v3.3.37) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 61f778234..50f9d8679 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -6,7 +6,7 @@ - + From 5faeeb7658854bdf7d628c8f3d0da12a3bfee41c Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 3 Nov 2020 13:37:01 +1300 Subject: [PATCH 074/126] Fix singularization for "gloves" Fixes #998 --- src/Humanizer.Tests.Shared/InflectorTests.cs | 1 + src/Humanizer/Inflections/Vocabularies.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Humanizer.Tests.Shared/InflectorTests.cs b/src/Humanizer.Tests.Shared/InflectorTests.cs index 4f5aa2b35..c57b66cda 100644 --- a/src/Humanizer.Tests.Shared/InflectorTests.cs +++ b/src/Humanizer.Tests.Shared/InflectorTests.cs @@ -190,6 +190,7 @@ public IEnumerator GetEnumerator() yield return new object[] { "safe", "saves" }; yield return new object[] { "half", "halves" }; + yield return new object[] { "glove", "gloves" }; yield return new object[] { "move", "moves" }; yield return new object[] { "salesperson", "salespeople" }; diff --git a/src/Humanizer/Inflections/Vocabularies.cs b/src/Humanizer/Inflections/Vocabularies.cs index d61c35c84..1cb90c0f7 100644 --- a/src/Humanizer/Inflections/Vocabularies.cs +++ b/src/Humanizer/Inflections/Vocabularies.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; namespace Humanizer.Inflections @@ -79,6 +79,7 @@ private static Vocabulary BuildDefault() _default.AddIrregular("human", "humans"); _default.AddIrregular("child", "children"); _default.AddIrregular("sex", "sexes"); + _default.AddIrregular("glove", "gloves"); _default.AddIrregular("move", "moves"); _default.AddIrregular("goose", "geese"); _default.AddIrregular("wave", "waves"); From ef23c7fc341c8cf10f899ffb3d65d76ba37a8b40 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2020 05:36:14 +0000 Subject: [PATCH 075/126] Bump Microsoft.NET.Test.Sdk from 16.7.1 to 16.8.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.7.1 to 16.8.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.7.1...v16.8.0) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index bdfd5f692..33a9ae2ee 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 17efa6db2ce63f2bdcf4922ff538d1ef9423cd58 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Nov 2020 05:39:07 +0000 Subject: [PATCH 076/126] Bump Microsoft.NETCore.UniversalWindowsPlatform from 6.2.10 to 6.2.11 Bumps [Microsoft.NETCore.UniversalWindowsPlatform](https://github.com/Microsoft/dotnet) from 6.2.10 to 6.2.11. - [Release notes](https://github.com/Microsoft/dotnet/releases) - [Commits](https://github.com/Microsoft/dotnet/commits) Signed-off-by: dependabot-preview[bot] --- .../Humanizer.Tests.Uwp.Runner.csproj | 2 +- src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj b/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj index 6586ab4d8..af5e243ee 100644 --- a/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj +++ b/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj @@ -122,7 +122,7 @@ - + diff --git a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj index 4602da095..f87ae13ee 100644 --- a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj +++ b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj @@ -130,7 +130,7 @@ - + From 3d627776f7bbb403840c5b918e7496b6b5efd8ee Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 3 Dec 2020 05:23:04 +0000 Subject: [PATCH 077/126] Bump Microsoft.NET.Test.Sdk from 16.8.0 to 16.8.3 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.8.0 to 16.8.3. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.8.0...v16.8.3) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 33a9ae2ee..406c473a5 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 1d8eb974c731d6894cf81ae0de213737186f6c10 Mon Sep 17 00:00:00 2001 From: JNemra <59008745+JNemra@users.noreply.github.com> Date: Sat, 9 Jan 2021 01:04:51 +0200 Subject: [PATCH 078/126] Add files via upload --- .../NumberToWordsConverterRegistry.cs | 105 +++++++++--------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index b41bf1a63..40a578786 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -1,52 +1,53 @@ -using Humanizer.Localisation.NumberToWords; - -namespace Humanizer.Configuration -{ - internal class NumberToWordsConverterRegistry : LocaliserRegistry - { - public NumberToWordsConverterRegistry() - : base((culture) => new EnglishNumberToWordsConverter()) - { - Register("af", new AfrikaansNumberToWordsConverter()); - Register("en", new EnglishNumberToWordsConverter()); - Register("ar", new ArabicNumberToWordsConverter()); - Register("cs", (culture) => new CzechNumberToWordsConverter(culture)); - Register("fa", new FarsiNumberToWordsConverter()); - Register("es", new SpanishNumberToWordsConverter()); - Register("pl", (culture) => new PolishNumberToWordsConverter(culture)); - Register("pt", new PortugueseNumberToWordsConverter()); - Register("pt-BR", new BrazilianPortugueseNumberToWordsConverter()); - Register("ro", new RomanianNumberToWordsConverter()); - Register("ru", new RussianNumberToWordsConverter()); - Register("fi", new FinnishNumberToWordsConverter()); - Register("fr-BE", new FrenchBelgianNumberToWordsConverter()); - Register("fr-CH", new FrenchSwissNumberToWordsConverter()); - Register("fr", new FrenchNumberToWordsConverter()); - Register("nl", new DutchNumberToWordsConverter()); - Register("he", (culture) => new HebrewNumberToWordsConverter(culture)); - Register("sl", (culture) => new SlovenianNumberToWordsConverter(culture)); - Register("de", new GermanNumberToWordsConverter()); - Register("de-CH", new GermanSwissLiechtensteinNumberToWordsConverter()); - Register("de-LI", new GermanSwissLiechtensteinNumberToWordsConverter()); - Register("bn-BD", new BanglaNumberToWordsConverter()); - Register("tr", new TurkishNumberToWordConverter()); - Register("it", new ItalianNumberToWordsConverter()); - Register("mt", new MalteseNumberToWordsConvertor()); - Register("uk", new UkrainianNumberToWordsConverter()); - Register("uz-Latn-UZ", new UzbekLatnNumberToWordConverter()); - Register("uz-Cyrl-UZ", new UzbekCyrlNumberToWordConverter()); - Register("sv", new SwedishNumberToWordsConverter()); - Register("sr", (culture) => new SerbianCyrlNumberToWordsConverter(culture)); - Register("sr-Latn", (culture) => new SerbianNumberToWordsConverter(culture)); - Register("ta", new TamilNumberToWordsConverter()); - Register("hr", (culture) => new CroatianNumberToWordsConverter(culture)); - Register("nb", new NorwegianBokmalNumberToWordsConverter()); - Register("vi", new VietnameseNumberToWordsConverter()); - Register("zh-CN", new ChineseNumberToWordsConverter()); - Register("bg", new BulgarianNumberToWordsConverter()); - Register("hy", new ArmenianNumberToWordsConverter()); - Register("az", new AzerbaijaniNumberToWordsConverter()); - Register("ja", new JapaneseNumberToWordsConverter()); - } - } -} +using Humanizer.Localisation.NumberToWords; + +namespace Humanizer.Configuration +{ + internal class NumberToWordsConverterRegistry : LocaliserRegistry + { + public NumberToWordsConverterRegistry() + : base((culture) => new EnglishNumberToWordsConverter()) + { + Register("af", new AfrikaansNumberToWordsConverter()); + Register("en", new EnglishNumberToWordsConverter()); + Register("ar", new ArabicNumberToWordsConverter()); + Register("cs", (culture) => new CzechNumberToWordsConverter(culture)); + Register("fa", new FarsiNumberToWordsConverter()); + Register("es", new SpanishNumberToWordsConverter()); + Register("pl", (culture) => new PolishNumberToWordsConverter(culture)); + Register("pt", new PortugueseNumberToWordsConverter()); + Register("pt-BR", new BrazilianPortugueseNumberToWordsConverter()); + Register("ro", new RomanianNumberToWordsConverter()); + Register("ru", new RussianNumberToWordsConverter()); + Register("fi", new FinnishNumberToWordsConverter()); + Register("fr-BE", new FrenchBelgianNumberToWordsConverter()); + Register("fr-CH", new FrenchSwissNumberToWordsConverter()); + Register("fr", new FrenchNumberToWordsConverter()); + Register("nl", new DutchNumberToWordsConverter()); + Register("he", (culture) => new HebrewNumberToWordsConverter(culture)); + Register("sl", (culture) => new SlovenianNumberToWordsConverter(culture)); + Register("de", new GermanNumberToWordsConverter()); + Register("de-CH", new GermanSwissLiechtensteinNumberToWordsConverter()); + Register("de-LI", new GermanSwissLiechtensteinNumberToWordsConverter()); + Register("bn-BD", new BanglaNumberToWordsConverter()); + Register("tr", new TurkishNumberToWordConverter()); + Register("it", new ItalianNumberToWordsConverter()); + Register("mt", new MalteseNumberToWordsConvertor()); + Register("uk", new UkrainianNumberToWordsConverter()); + Register("uz-Latn-UZ", new UzbekLatnNumberToWordConverter()); + Register("uz-Cyrl-UZ", new UzbekCyrlNumberToWordConverter()); + Register("sv", new SwedishNumberToWordsConverter()); + Register("sr", (culture) => new SerbianCyrlNumberToWordsConverter(culture)); + Register("sr-Latn", (culture) => new SerbianNumberToWordsConverter(culture)); + Register("ta", new TamilNumberToWordsConverter()); + Register("hr", (culture) => new CroatianNumberToWordsConverter(culture)); + Register("nb", new NorwegianBokmalNumberToWordsConverter()); + Register("vi", new VietnameseNumberToWordsConverter()); + Register("zh-CN", new ChineseNumberToWordsConverter()); + Register("bg", new BulgarianNumberToWordsConverter()); + Register("hy", new ArmenianNumberToWordsConverter()); + Register("az", new AzerbaijaniNumberToWordsConverter()); + Register("ja", new JapaneseNumberToWordsConverter()); + Register("el", new GreekNumberToWordsConverter()); + } + } +} From 425c6e469cb10b1078c01e0222f75806a813bc33 Mon Sep 17 00:00:00 2001 From: JNemra <59008745+JNemra@users.noreply.github.com> Date: Sat, 9 Jan 2021 01:06:38 +0200 Subject: [PATCH 079/126] Add files via upload --- .../GreekNumberToWordsConverter.cs | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs diff --git a/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs new file mode 100644 index 000000000..8df3e3bbf --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class GreekNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private readonly string[] UnitMap = { "μηδέν", "ένα", "δύο", "τρία", "τέσσερα", "πέντε", "έξι", "επτά", "οκτώ", "εννέα", "δέκα", "έντεκα", "δώδεκα" }; + + private readonly string[] UnitsMap = { "μηδέν", "ένα", "δύο", "τρείς", "τέσσερις", "πέντε", "έξι", "επτά", "οκτώ", "εννέα", "δέκα", "έντεκα", "δώδεκα" }; + + private readonly string[] TensMap = { "", "δέκα", "είκοσι", "τριάντα", "σαράντα", "πενήντα", "εξήντα", "εβδομήντα", "ογδόντα", "ενενήντα" }; + + private readonly string[] TensNoDiacriticsMap = { "", "δεκα", "εικοσι", "τριαντα", "σαραντα", "πενηντα", "εξηντα", "εβδομηντα", "ογδοντα", "ενενηντα" }; + + private readonly string[] HundredMap = { "", "εκατό", "διακόσια", "τριακόσια", "τετρακόσια", "πεντακόσια", "εξακόσια", "επτακόσια", "οκτακόσια", "εννιακόσια" }; + + private readonly string[] HundredsMap = { "", "εκατόν", "διακόσιες", "τριακόσιες", "τετρακόσιες", "πεντακόσιες", "εξακόσιες", "επτακόσιες", "οκτακόσιες", "Εενιακόσιες" }; + + + public override string Convert(long number) + { + return Convert(number, false); + } + + public override string ConvertToOrdinal(int number) + { + return null; + } + + private string Convert(long number, bool returnPluralized) + { + if (number < 13) + { + return ConvertIntΒ13(number, returnPluralized); + } + else if (number < 100) + { + return ConvertIntBH(number, returnPluralized); + } + else if (number < 1000) + { + return ConvertIntBT(number, returnPluralized); + } + else if (number < 1000000) + { + return ConvertIntBM(number); + } + else if (number < 1000000000) + { + return ConvertIntBB(number); + } + else if (number < 1000000000000) + { + return ConvertIntBTR(number); + } + + return ""; + } + + private string ConvertIntΒ13(long number, bool returnPluralized) + { + return returnPluralized ? UnitsMap[number] : UnitMap[number]; + } + + private string ConvertIntBH(long number, bool returnPluralized) + { + var result = (number / 10 == 1) ? TensNoDiacriticsMap[number / 10] : TensMap[number / 10]; + + if (number % 10 != 0) + { + if (number / 10 != 1) + { + result += " "; + } + + result += Convert(number % 10, returnPluralized).ToLower(); + } + + return result; + } + + private string ConvertIntBT(long number, bool returnPluralized) + { + var result = ""; + + if (number / 100 == 1) + { + if (number % 100 == 0) + { + return HundredMap[number / 100]; + } + + result = HundredsMap[number / 100]; + } + else + { + result = returnPluralized ? HundredsMap[number / 100] : HundredMap[number / 100]; + } + + if (number % 100 != 0) + { + result += $" {Convert(number % 100, returnPluralized).ToLower()}"; + } + + return result; + } + + private string ConvertIntBM(long number) + { + if (number / 1000 == 1) + { + if (number % 1000 == 0) + { + return "χίλια"; + } + + return $"χίλια {Convert(number % 1000, false).ToLower()}"; + } + + var result = $"{Convert(number / 1000, true)} χιλιάδες"; + + if (number % 1000 != 0) + { + result += $" {Convert(number % 1000, false).ToLower()}"; + } + + return result; + } + + private string ConvertIntBB(long number) + { + if (number / 1000000 == 1) + { + if (number % 1000000 == 0) + { + return "ένα εκατομμύριο"; + } + + return $"ένα εκατομμύριο {Convert(number % 1000000, true).ToLower()}"; + } + + var result = $"{Convert(number / 1000000, false)} εκατομμύρια"; + + if (number % 1000000 != 0) + { + result += $" {Convert(number % 1000000, false).ToLower()}"; + } + + return result; + } + + private string ConvertIntBTR(long number) + { + if (number / 1000000000 == 1) + { + if (number % 1000000000 == 0) + { + return "ένα δισεκατομμύριο"; + } + + return $"ένα δισεκατομμύριο {Convert(number % 1000000000, true).ToLower()}"; + } + + var result = $"{Convert(number / 1000000000, false)} δισεκατομμύρια"; + + if (number % 1000000000 != 0) + { + result += $" {Convert(number % 1000000000, false).ToLower()}"; + } + + return result; + } + } +} From 1ea0b8f3b24cd29a2801d12c81b72c20e9a84112 Mon Sep 17 00:00:00 2001 From: JNemra <59008745+JNemra@users.noreply.github.com> Date: Sat, 9 Jan 2021 01:07:44 +0200 Subject: [PATCH 080/126] Add files via upload --- .../Localisation/el/NumberToWordsTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/Humanizer.Tests.Shared/Localisation/el/NumberToWordsTests.cs diff --git a/src/Humanizer.Tests.Shared/Localisation/el/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/el/NumberToWordsTests.cs new file mode 100644 index 000000000..c2d8c9649 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/el/NumberToWordsTests.cs @@ -0,0 +1,36 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.el +{ + [UseCulture("el")] + public class NumberToWordsTests + { + [InlineData(1, "ένα")] + [InlineData(10, "δέκα")] + [InlineData(11, "έντεκα")] + [InlineData(14, "δεκατέσσερα")] + [InlineData(20, "είκοσι")] + [InlineData(122, "εκατόν είκοσι δύο")] + [InlineData(3501, "τρείς χιλιάδες πεντακόσια ένα")] + [InlineData(100, "εκατό")] + [InlineData(1000, "χίλια")] + [InlineData(100000, "εκατό χιλιάδες")] + [InlineData(13448, "δεκατρείς χιλιάδες τετρακόσια σαράντα οκτώ")] + [InlineData(53, "πενήντα τρία")] + [InlineData(123647, "εκατόν είκοσι τρείς χιλιάδες εξακόσια σαράντα επτά")] + [InlineData(14000000, "δεκατέσσερα εκατομμύρια")] + [InlineData(578412, "πεντακόσιες εβδομήντα οκτώ χιλιάδες τετρακόσια δώδεκα")] + [InlineData(1000000000, "ένα δισεκατομμύριο")] + [InlineData(1000000001, "ένα δισεκατομμύριο ένα")] + [InlineData(1469, "χίλια τετρακόσια εξήντα εννέα")] + [InlineData(69, "εξήντα εννέα")] + [InlineData(619, "εξακόσια δεκαεννέα")] + [InlineData(1190, "χίλια εκατόν ενενήντα")] + + [Theory] + public void ToWordsInt(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + } +} From 14eb2b58c9b493dcbc486b1cec04509d393a2878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Tue, 12 Jan 2021 15:46:43 +0100 Subject: [PATCH 081/126] Add French support when using toWords: true in TimeSpan humanization Also removed [Trait("Translation", "Google")] from TimeSpanHumanizeTests after review. (I assumed that the trait means that the translation were automatically generated by Google vs manually written by a human) --- .../Localisation/fr/TimeSpanHumanizeTests.cs | 77 ++++++++++++++++++- src/Humanizer/Properties/Resources.fr.resx | 24 ++++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs index 8b4b5d43c..53f29ef2f 100644 --- a/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs @@ -8,9 +8,7 @@ namespace Humanizer.Tests.Localisation.fr [UseCulture("fr")] public class TimeSpanHumanizeTests { - [Theory] - [Trait("Translation", "Google")] [InlineData(366, "1 an")] [InlineData(731, "2 ans")] [InlineData(1096, "3 ans")] @@ -21,7 +19,16 @@ public void Years(int days, string expected) } [Theory] - [Trait("Translation", "Google")] + [InlineData(366, "un an")] + [InlineData(731, "deux ans")] + [InlineData(1096, "trois ans")] + [InlineData(4018, "onze ans")] + public void YearsToWord(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year, toWords: true)); + } + + [Theory] [InlineData(31, "1 mois")] [InlineData(61, "2 mois")] [InlineData(92, "3 mois")] @@ -31,6 +38,16 @@ public void Months(int days, string expected) Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year)); } + [Theory] + [InlineData(31, "un mois")] + [InlineData(61, "deux mois")] + [InlineData(92, "trois mois")] + [InlineData(335, "onze mois")] + public void MonthsToWords(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize(maxUnit: TimeUnit.Year, toWords: true)); + } + [Theory] [InlineData(14, "2 semaines")] [InlineData(7, "1 semaine")] @@ -40,6 +57,15 @@ public void Weeks(int days, string expected) Assert.Equal(expected, actual); } + [Theory] + [InlineData(14, "deux semaines")] + [InlineData(7, "une semaine")] + public void WeeksToWords(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(toWords: true); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(6, "6 jours")] [InlineData(1, "1 jour")] @@ -49,6 +75,15 @@ public void Days(int days, string expected) Assert.Equal(expected, actual); } + [Theory] + [InlineData(6, "six jours")] + [InlineData(1, "un jour")] + public void DaysToWords(int days, string expected) + { + var actual = TimeSpan.FromDays(days).Humanize(toWords: true); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(2, "2 heures")] [InlineData(1, "1 heure")] @@ -58,6 +93,15 @@ public void Hours(int hours, string expected) Assert.Equal(expected, actual); } + [Theory] + [InlineData(2, "deux heures")] + [InlineData(1, "une heure")] + public void HoursToWords(int hours, string expected) + { + var actual = TimeSpan.FromHours(hours).Humanize(toWords: true); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(2, "2 minutes")] [InlineData(1, "1 minute")] @@ -67,6 +111,15 @@ public void Minutes(int minutes, string expected) Assert.Equal(expected, actual); } + [Theory] + [InlineData(2, "deux minutes")] + [InlineData(1, "une minute")] + public void MinutesToWords(int minutes, string expected) + { + var actual = TimeSpan.FromMinutes(minutes).Humanize(toWords: true); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(2, "2 secondes")] [InlineData(1, "1 seconde")] @@ -76,6 +129,15 @@ public void Seconds(int seconds, string expected) Assert.Equal(expected, actual); } + [Theory] + [InlineData(2, "deux secondes")] + [InlineData(1, "une seconde")] + public void SecondsToWords(int seconds, string expected) + { + var actual = TimeSpan.FromSeconds(seconds).Humanize(toWords: true); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(2, "2 millisecondes")] [InlineData(1, "1 milliseconde")] @@ -85,6 +147,15 @@ public void Milliseconds(int ms, string expected) Assert.Equal(expected, actual); } + [Theory] + [InlineData(2, "deux millisecondes")] + [InlineData(1, "une milliseconde")] + public void MillisecondsToWords(int ms, string expected) + { + var actual = TimeSpan.FromMilliseconds(ms).Humanize(toWords: true); + Assert.Equal(expected, actual); + } + [Theory] [InlineData(TimeUnit.Year, "0 an")] [InlineData(TimeUnit.Month, "0 mois")] diff --git a/src/Humanizer/Properties/Resources.fr.resx b/src/Humanizer/Properties/Resources.fr.resx index 7cf0af1bf..f55793bbe 100644 --- a/src/Humanizer/Properties/Resources.fr.resx +++ b/src/Humanizer/Properties/Resources.fr.resx @@ -276,4 +276,28 @@ {0} milliseconde + + un jour + + + une heure + + + une milliseconde + + + une minute + + + un mois + + + une seconde + + + une semaine + + + un an + \ No newline at end of file From 32b1cfd14f184b1f29d9716452c9e89fa79223d2 Mon Sep 17 00:00:00 2001 From: Nattakit Sriburanapitak Date: Wed, 3 Feb 2021 22:31:04 +0700 Subject: [PATCH 082/126] Support Thai formatter and number to word --- .../Localisation/th-TH/DateHumanizeTests.cs | 21 +++++ .../Localisation/th-TH/NumberToWordsTests.cs | 36 +++++++++ .../Configuration/FormatterRegistry.cs | 1 + .../NumberToWordsConverterRegistry.cs | 1 + .../ThaiNumberToWordsConverter.cs | 81 +++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 src/Humanizer.Tests/Localisation/th-TH/DateHumanizeTests.cs create mode 100644 src/Humanizer.Tests/Localisation/th-TH/NumberToWordsTests.cs create mode 100644 src/Humanizer/Localisation/NumberToWords/ThaiNumberToWordsConverter.cs diff --git a/src/Humanizer.Tests/Localisation/th-TH/DateHumanizeTests.cs b/src/Humanizer.Tests/Localisation/th-TH/DateHumanizeTests.cs new file mode 100644 index 000000000..c6e53a5cb --- /dev/null +++ b/src/Humanizer.Tests/Localisation/th-TH/DateHumanizeTests.cs @@ -0,0 +1,21 @@ +using System; +using System.Globalization; +using Humanizer.Localisation; +using Xunit; + +namespace Humanizer.Tests.Localisation.thTH +{ + [UseCulture("th-TH")] + public class DateHumanizeTests + { + [Theory] + [InlineData(1, "หนึ่งวินาทีที่แล้ว")] + [InlineData(10, "10 วินาทีที่แล้ว")] + [InlineData(59, "59 วินาทีที่แล้ว")] + [InlineData(60, "หนึ่งนาทีที่แล้ว")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + } +} diff --git a/src/Humanizer.Tests/Localisation/th-TH/NumberToWordsTests.cs b/src/Humanizer.Tests/Localisation/th-TH/NumberToWordsTests.cs new file mode 100644 index 000000000..63a1a518d --- /dev/null +++ b/src/Humanizer.Tests/Localisation/th-TH/NumberToWordsTests.cs @@ -0,0 +1,36 @@ +using System.Globalization; +using Xunit; + +namespace Humanizer.Tests.Localisation.thTH +{ + [UseCulture("th-TH")] + public class NumberToWordsTests + { + [InlineData(1, "หนึ่ง")] + [InlineData(10, "สิบ")] + [InlineData(11, "สิบเอ็ด")] + [InlineData(20, "ยี่สิบ")] + [InlineData(-122, "ลบหนึ่งร้อยยี่สิบสอง")] + [InlineData(3501, "สามพันห้าร้อยหนึ่ง")] + [InlineData(100, "หนึ่งร้อย")] + [InlineData(1000, "หนึ่งพัน")] + [InlineData(10000, "หนึ่งหมื่น")] + [InlineData(-100000, "ลบหนึ่งแสน")] + [InlineData(1000000, "หนึ่งล้าน")] + [InlineData(10000000, "สิบล้าน")] + [InlineData(100000000, "หนึ่งร้อยล้าน")] + [InlineData(1000000000, "หนึ่งพันล้าน")] + [InlineData(111, "หนึ่งร้อยสิบเอ็ด")] + [InlineData(1111, "หนึ่งพันหนึ่งร้อยสิบเอ็ด")] + [InlineData(-111111, "ลบหนึ่งแสนหนึ่งหมื่นหนึ่งพันหนึ่งร้อยสิบเอ็ด")] + [InlineData(1111111, "หนึ่งล้านหนึ่งแสนหนึ่งหมื่นหนึ่งพันหนึ่งร้อยสิบเอ็ด")] + [InlineData(11111111, "สิบเอ็ดล้านหนึ่งแสนหนึ่งหมื่นหนึ่งพันหนึ่งร้อยสิบเอ็ด")] + [InlineData(111111111, "หนึ่งร้อยสิบเอ็ดล้านหนึ่งแสนหนึ่งหมื่นหนึ่งพันหนึ่งร้อยสิบเอ็ด")] + [InlineData(1111111111, "หนึ่งพันหนึ่งร้อยสิบเอ็ดล้านหนึ่งแสนหนึ่งหมื่นหนึ่งพันหนึ่งร้อยสิบเอ็ด")] + [Theory] + public void ToWords(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + } +} diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index f5660903b..eda23bf4c 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -50,6 +50,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterDefaultFormatter("zh-CN"); RegisterDefaultFormatter("zh-Hans"); RegisterDefaultFormatter("zh-Hant"); + RegisterDefaultFormatter("th-TH"); } private void RegisterDefaultFormatter(string localeCode) diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index b41bf1a63..4b74eb2e5 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -47,6 +47,7 @@ public NumberToWordsConverterRegistry() Register("hy", new ArmenianNumberToWordsConverter()); Register("az", new AzerbaijaniNumberToWordsConverter()); Register("ja", new JapaneseNumberToWordsConverter()); + Register("th-TH", new ThaiNumberToWordsConverter()); } } } diff --git a/src/Humanizer/Localisation/NumberToWords/ThaiNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ThaiNumberToWordsConverter.cs new file mode 100644 index 000000000..bf5eeec57 --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/ThaiNumberToWordsConverter.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class ThaiNumberToWordsConverter : GenderlessNumberToWordsConverter + { + public override string Convert(long numbermoney) + { + var Textreturn = ""; + if (numbermoney == 0) + { + return "ศูนย์"; + } + + if (numbermoney < 0) + { + Textreturn = "ลบ"; + numbermoney = -(numbermoney); + } + + if ((numbermoney / 1000000) > 0) + { + Textreturn += Convert(numbermoney / 1000000) + "ล้าน"; + numbermoney %= 1000000; + } + if ((numbermoney / 100000) > 0) + { + Textreturn += Convert(numbermoney / 100000) + "แสน"; + numbermoney %= 100000; + } + if ((numbermoney / 10000) > 0) + { + Textreturn += Convert(numbermoney / 10000) + "หมื่น"; + numbermoney %= 10000; + } + if ((numbermoney / 1000) > 0) + { + Textreturn += Convert(numbermoney / 1000) + "พัน"; + numbermoney %= 1000; + } + + if ((numbermoney / 100) > 0) + { + Textreturn += Convert(numbermoney / 100) + "ร้อย"; + numbermoney %= 100; + } + + if (numbermoney > 0) + { + if (Textreturn != "") + { + Textreturn += ""; + } + + var unitsMap = new[] { "ศูนย์", "หนึ่ง", "สอง", "สาม", "สี่", "ห้า", "หก", "เจ็ด", "เเปด", "เก้า", "สิบ", "สิบเอ็ด", "สิบสอง", "สิบสาม", "สิบสี่", "สิบห้า", "สิบหก", "สิบเจ็ด", "สิบเเปด", "สิบเก้า" }; + var tensMap = new[] { "ศูนย์", "สิบ", "ยี่สิบ", "สามสิบ", "สี่สิบ", "ห้าสิบ", "หกสิบ", "เจ็ดสิบ", "แปดสิบ", "เก้าสิบ" }; + + if (numbermoney < 20) + { + Textreturn += unitsMap[numbermoney]; + } + else + { + Textreturn += tensMap[numbermoney / 10]; + if ((numbermoney % 10) > 0) + { + Textreturn += "" + unitsMap[numbermoney % 10]; + } + } + } + + return Textreturn; + } + + public override string ConvertToOrdinal(int number) + { + return Convert(number); + } + } +} From 3014a153d460dfa7bc5e256fd9ac1fcea4b3f8ea Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 10 Feb 2021 05:25:56 +0000 Subject: [PATCH 083/126] Bump Microsoft.NETCore.UniversalWindowsPlatform from 6.2.11 to 6.2.12 Bumps [Microsoft.NETCore.UniversalWindowsPlatform](https://github.com/Microsoft/dotnet) from 6.2.11 to 6.2.12. - [Release notes](https://github.com/Microsoft/dotnet/releases) - [Commits](https://github.com/Microsoft/dotnet/commits) Signed-off-by: dependabot-preview[bot] --- .../Humanizer.Tests.Uwp.Runner.csproj | 2 +- src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj b/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj index af5e243ee..1813fe27d 100644 --- a/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj +++ b/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner.csproj @@ -122,7 +122,7 @@ - + diff --git a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj index f87ae13ee..ad6f8a4e8 100644 --- a/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj +++ b/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp.csproj @@ -130,7 +130,7 @@ - + From f2dc9a6525345348c621d7ddd0e581f39087576a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 05:24:43 +0000 Subject: [PATCH 084/126] Bump Microsoft.NET.Test.Sdk from 16.8.3 to 16.9.1 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.8.3 to 16.9.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.8.3...v16.9.1) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 406c473a5..738816e5d 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 4c1e91e2229562cb26ad80f2212d3afa15250b8e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 11 Mar 2021 05:24:22 +0000 Subject: [PATCH 085/126] Bump DiffPlex from 1.6.3 to 1.7.0 Bumps [DiffPlex](https://github.com/mmanela/diffplex) from 1.6.3 to 1.7.0. - [Release notes](https://github.com/mmanela/diffplex/releases) - [Commits](https://github.com/mmanela/diffplex/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 738816e5d..8101b13a1 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -17,7 +17,7 @@ - + From e3fd2184cbdfd828fc84384995a6b37ab661f596 Mon Sep 17 00:00:00 2001 From: Ronald Marcus Davenport Date: Sun, 14 Mar 2021 14:20:14 -0500 Subject: [PATCH 086/126] fixes #1030 and #1040 --- .gitignore | 1 + src/Humanizer/Inflections/Vocabularies.cs | 9 ++++----- src/Humanizer/Inflections/Vocabulary.cs | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 771f57f1f..bc00272fc 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,4 @@ src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.receive .DS_Store /samples/Humanizer.MvcSample/Humanizer.MvcSample.sln +/src/Humanizer/Properties/launchSettings.json diff --git a/src/Humanizer/Inflections/Vocabularies.cs b/src/Humanizer/Inflections/Vocabularies.cs index 1cb90c0f7..f546aa7de 100644 --- a/src/Humanizer/Inflections/Vocabularies.cs +++ b/src/Humanizer/Inflections/Vocabularies.cs @@ -38,7 +38,7 @@ private static Vocabulary BuildDefault() _default.AddPlural("(hive)$", "$1s"); _default.AddPlural("([^aeiouy]|qu)y$", "$1ies"); _default.AddPlural("(x|ch|ss|sh)$", "$1es"); - _default.AddPlural("(matr|vert|ind|d)ix|ex$", "$1ices"); + _default.AddPlural("(matr|vert|ind|d)(ix|ex)$", "$1ices"); _default.AddPlural("(^[m|l])ouse$", "$1ice"); _default.AddPlural("^(ox)$", "$1en"); _default.AddPlural("(quiz)$", "$1zes"); @@ -83,7 +83,6 @@ private static Vocabulary BuildDefault() _default.AddIrregular("move", "moves"); _default.AddIrregular("goose", "geese"); _default.AddIrregular("wave", "waves"); - _default.AddIrregular("die", "dice"); _default.AddIrregular("foot", "feet"); _default.AddIrregular("tooth", "teeth"); _default.AddIrregular("curriculum", "curricula"); @@ -95,14 +94,14 @@ private static Vocabulary BuildDefault() //Fix 975 _default.AddIrregular("ex", "exes", matchEnding: false); - _default.AddIrregular("is", "are", matchEnding: false); _default.AddIrregular("that", "those", matchEnding: false); _default.AddIrregular("this", "these", matchEnding: false); _default.AddIrregular("bus", "buses", matchEnding: false); - _default.AddIrregular("staff", "staff", matchEnding: false); - _default.AddIrregular("training", "training", matchEnding: false); + _default.AddIrregular("die", "dice", matchEnding: false); + _default.AddUncountable("staff"); + _default.AddUncountable("training"); _default.AddUncountable("equipment"); _default.AddUncountable("information"); _default.AddUncountable("corn"); diff --git a/src/Humanizer/Inflections/Vocabulary.cs b/src/Humanizer/Inflections/Vocabulary.cs index 084204cc4..e78ea5c71 100644 --- a/src/Humanizer/Inflections/Vocabulary.cs +++ b/src/Humanizer/Inflections/Vocabulary.cs @@ -145,7 +145,7 @@ private string ApplyRules(IList rules, string word, bool skipFirstRule) break; } } - return result; + return result != null ? MatchUpperCase(word, result) : result; } private bool IsUncountable(string word) @@ -153,6 +153,11 @@ private bool IsUncountable(string word) return _uncountables.Contains(word.ToLower()); } + private string MatchUpperCase(string word, string replacement) + { + return char.IsUpper(word[0]) && char.IsLower(replacement[0]) ? char.ToUpper(replacement[0]) + replacement.Substring(1) : replacement; + } + private class Rule { private readonly Regex _regex; From 2ba67c0e9fd15e1ade21819a02744960010e5131 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Tue, 23 Mar 2021 15:19:48 +0300 Subject: [PATCH 087/126] add basic support for central kurdish --- .../Humanizer.Tests.Shared.projitems | 1 + .../Localisation/ckb/DateHumanizeTests.cs | 130 ++++ .../Configuration/FormatterRegistry.cs | 1 + src/Humanizer/Properties/Resources.ku.resx | 582 ++++++++++++++++++ 4 files changed, 714 insertions(+) create mode 100644 src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs create mode 100644 src/Humanizer/Properties/Resources.ku.resx diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index 3631468ff..fbfe00087 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -48,6 +48,7 @@ + diff --git a/src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs new file mode 100644 index 000000000..3aa322d49 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs @@ -0,0 +1,130 @@ +using Humanizer.Localisation; +using Xunit; + +namespace Humanizer.Tests.Localisation.ckb +{ + [UseCulture("ku")] + public class DateHumanizeTests + { + [Theory] + [InlineData(-1, "دوێنێ")] + [InlineData(-2, "2 ڕۆژ لەمەوبەر")] + [InlineData(-3, "3 ڕۆژ لەمەوبەر")] + [InlineData(-11, "11 ڕۆژ لەمەوبەر")] + public void DaysAgo(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Past); + } + + [Theory] + [InlineData(1, "بەیانی")] + [InlineData(2, "دوای 2 ڕۆژی دیکە")] + [InlineData(10, "دوای 10 ڕۆژی دیکە")] + [InlineData(17, "دوای 17 ڕۆژی دیکە")] + public void DaysFromNow(int days, string expected) + { + DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); + } + + [Theory] + [InlineData(-2, "2 کاتژمێر لەمەوبەر")] + [InlineData(-1, "کاتژمێرێک لەمەوبەر")] + [InlineData(-3, "3 کاتژمێر لەمەوبەر")] + [InlineData(-11, "11 کاتژمێر لەمەوبەر")] + public void HoursAgo(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Past); + } + + [Theory] + [InlineData(1, "دوای کاتژمێرێکی دیکە")] + [InlineData(2, "دوای 2 کاتژمێری دیکە")] + [InlineData(10, "دوای 10 کاتژمێری دیکە")] + [InlineData(23, "دوای 23 کاتژمێری دیکە")] + public void HoursFromNow(int hours, string expected) + { + DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); + } + + [Theory] + [InlineData(-2, "2 خولەک لەمەوبەر")] + [InlineData(-1, "یەک خولەک لەمەوبەر")] + [InlineData(-3, "3 خولەک لەمەوبەر")] + [InlineData(-11, "11 خولەک لەمەوبەر")] + [InlineData(60, "یەک کاتژمێر لەمەوبەر")] + public void MinutesAgo(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); + } + + [Theory] + [InlineData(1, "دوای خولەکێکی دیکە")] + [InlineData(2, "دوای 2 خولەکی دیکە")] + [InlineData(10, "دوای 10 خولەکی دیکە")] + [InlineData(23, "دوای 23 خولەکی دیکە")] + public void MinutesFromNow(int minutes, string expected) + { + DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); + } + + [Theory] + [InlineData(-2, "2 مانگ لەمەوبەر")] + [InlineData(-1, "یەک مانگ لەمەوبەر")] + [InlineData(-3, "3 مانگ لەمەوبەر")] + [InlineData(-11, "11 مانگ لەمەوبەر")] + public void MonthsAgo(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Past); + } + + [Theory] + [InlineData(1, "دوای مانگێکی دیکە")] + [InlineData(2, "دوای 2 مانگی دیکە")] + [InlineData(10, "دوای 10 مانگی دیکە")] + public void MonthsFromNow(int months, string expected) + { + DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); + } + + [Theory] + [InlineData(-2, "2 چرکە لەمەوبەر")] + [InlineData(-1, "یەک چرکە لەمەوبەر")] + [InlineData(-3, "3 چرکە لەمەوبەر")] + [InlineData(-11, "11 چرکە لەمەوبەر")] + public void SecondsAgo(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Past); + } + + [Theory] + [InlineData(0, "ئێستا")] + [InlineData(1, "یەک چرکەی دیکە")] + [InlineData(2, "2 چرکەی دیکە")] + [InlineData(10, "10 چرکەی دیکە")] + [InlineData(24, "2٤ چرکەی دیکە")] + public void SecondsFromNow(int seconds, string expected) + { + DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); + } + + [Theory] + [InlineData(-2, "2 ساڵ لەمەوبەر")] + [InlineData(-1, "یەک ساڵ لەمەوبەر")] + [InlineData(-3, "3 ساڵ لەمەوبەر")] + [InlineData(-11, "11 ساڵ لەمەوبەر")] + public void YearsAgo(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Past); + } + + [Theory] + [InlineData(1, "یەک ساڵی دیکە")] + [InlineData(2, "2 ساڵی دیکە")] + [InlineData(7, "7 ساڵی دیکە")] + [InlineData(55, "55 ساڵی دیکە")] + public void YearsFromNow(int years, string expected) + { + DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); + } + } +} diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index f5660903b..e17f2f9b4 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -21,6 +21,7 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) RegisterCzechSlovakPolishFormatter("pl"); RegisterCzechSlovakPolishFormatter("sk"); RegisterDefaultFormatter("bg"); + RegisterDefaultFormatter("ku"); RegisterDefaultFormatter("pt"); RegisterDefaultFormatter("sv"); RegisterDefaultFormatter("tr"); diff --git a/src/Humanizer/Properties/Resources.ku.resx b/src/Humanizer/Properties/Resources.ku.resx new file mode 100644 index 000000000..2ad95e92a --- /dev/null +++ b/src/Humanizer/Properties/Resources.ku.resx @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + چرکەیەک لەمەوبەر + + + {0} چرکە لەمەوبەر + + + خولەکێک لەمەوبەر + + + {0} خولەک لەمەوبەر + + + کاتژمێرێک لەمەوبەر + + + {0} کاتژمێر لەمەوبەر + + + دوێنێ + + + {0} ڕۆژ لەمەوبەر + + + مانگێک لەمەوبەر + + + {0} مانگ لەمەوبەر + + + ساڵێک لەمەوبەر + + + {0} ساڵ لەمەوبەر + + + {0} ڕۆژ + + + {0} کاتژمێر + + + {0} میلیچرکە + + + {0} خولەک + + + {0} چرکە + + + 1 ڕۆژ + + + 1 کاتژمێر + + + 1 میلیچرکە + + + 1 خولەک + + + 1 چرکە + + + ئێستا + + + {0} هەفتە + + + 1 هەفتە + + + دوای {0} ڕۆژی دیکە + + + دوای {0} کاتژمێری دیکە + + + دوای {0} خولەکی دیکە + + + دوای {0} مانگی دیکە + + + دوای {0} چرکەی دیکە + + + دوای {0} ساڵی دیکە + + + ئێستا + + + بەیانی + + + دوای کاتژمێرێکی دیکە + + + دوای خولەکێکی دیکە + + + دوای مانگێکی دیکە + + + دوای چرکەیەکی دیکە + + + دوای ساڵێکی دیکە + + + دوای {0} کاتژمێری دیکە + + + دوای {0} خولەکی دیکە + + + {0} چرکە لەمەوبەر + + + دوای {0} چرکەی دیکە + + + دوای {0} ساڵی دیکە + + + {0} ڕۆژ + + + {0} ڕۆژ + + + {0} کاتژمێر + + + {0} میلیچرکە + + + {0} خولەک + + + {0} خولەک + + + {0} چرکە + + + {0} میلیچرکە + + + {0} ڕۆژ لەمەوبەر + + + {0} ڕۆژ لەمەوبەر + + + {0} ڕۆژ لەمەوبەر + + + {0} ڕۆژ لەمەوبەر + + + {0} ڕۆژ لەمەوبەر + + + دوای {0} ڕۆژی دیکە + + + دوای {0} ڕۆژی دیکە + + + دوای {0} ڕۆژی دیکە + + + دوای {0} ڕۆژی دیکە + + + {0} کاتژمێر لەمەوبەر + + + {0} کاتژمێر لەمەوبەر + + + {0} کاتژمێر لەمەوبەر + + + {0} کاتژمێر لەمەوبەر + + + {0} کاتژمێر لەمەوبەر + + + دوای {0} کاتژمێری دیکە + + + دوای {0} کاتژمێری دیکە + + + دوای {0} کاتژمێری دیکە + + + {0} خولەک لەمەوبەر + + + {0} خولەک لەمەوبەر + + + {0} خولەک لەمەوبەر + + + {0} خولەک لەمەوبەر + + + {0} خولەک لەمەوبەر + + + دوای {0} خولەکی دیکە + + + دوای {0} خولەکی دیکە + + + دوای {0} خولەکی دیکە + + + {0} مانگ لەمەوبەر + + + {0} مانگ لەمەوبەر + + + {0} مانگ لەمەوبەر + + + {0} مانگ لەمەوبەر + + + دوای {0} مانگی دیکە + + + دوای {0} مانگی دیکە + + + دوای {0} مانگی دیکە + + + دوای {0} مانگی دیکە + + + {0} چرکە لەمەوبەر + + + {0} چرکە لەمەوبەر + + + {0} چرکە لەمەوبەر + + + {0} چرکە لەمەوبەر + + + دوای {0} چرکەی دیکە + + + دوای {0} چرکەی دیکە + + + دوای {0} چرکەی دیکە + + + {0} ساڵ لەمەوبەر + + + {0} ساڵ لەمەوبەر + + + {0} ساڵ لەمەوبەر + + + {0} ساڵ لەمەوبەر + + + {0} ساڵ لەمەوبەر + + + دوای {0} ساڵی دیکە + + + دوای {0} ساڵی دیکە + + + دوای {0} ساڵی دیکە + + + {0} ڕۆژ + + + {0} ڕۆژ + + + {0} کاتژمێر + + + {0} کاتژمێر + + + {0} کاتژمێر + + + {0} میلیچرکە + + + {0} میلیچرکە + + + {0} خولەک + + + {0} خولەک + + + {0} چرکە + + + {0} چرکە + + + {0} چرکە + + + {0} هەفتە + + + {0} هەفتە + + + {0} هەفتە + + + {0} هەفتە + + + {0} مانگ + + + {0} ساڵ + + + 1 مانگ + + + 1 ساڵ + + + {0} کاتژمێر لەمەوبەر + + + دوای {0} کاتژمێری دیکە + + + {0} خولەک لەمەوبەر + + + دوای {0} خولەکی دیکە + + + {0} مانگ لەمەوبەر + + + دوای {0} مانگی دیکە + + + {0} چرکە لەمەوبەر + + + دوای {0} چرکەی دیکە + + + {0} ساڵ لەمەوبەر + + + دوای {0} ساڵی دیکە + + + {0} کاتژمێر + + + {0} میلیچرکە + + + {0} خولەک + + + {0} چرکە + + + {0} هەفتە + + + هەرگیز + + + {0} مانگ + + + {0} مانگ + + + {0} مانگ + + + {0} مانگ + + + {0} مانگ + + + {0} ساڵ + + + {0} ساڵ + + + {0} ساڵ + + + {0} ساڵ + + + {0} ساڵ + + + یەک ڕۆژ + + + یەک کاتژمێر + + + یەک میلیچرکە + + + یەک خولەک + + + یەک مانگ + + + یەک چرکە + + + یەک هەفتە + + + یەک ساڵ + + + {0} ڕۆژ + + \ No newline at end of file From 253e692f8fd1e2d1cde4d1b72b41b42735578382 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Tue, 23 Mar 2021 17:08:37 +0300 Subject: [PATCH 088/126] add number to words localizer for central kurdish --- .../Humanizer.Tests.Shared.projitems | 3 +- .../{ckb => ku}/DateHumanizeTests.cs | 28 +++---- .../Localisation/ku/NumberToWordsTests.cs | 78 ++++++++++++++++++ .../NumberToWordsConverterRegistry.cs | 1 + .../CentralKurdishNumberToWordsConverter.cs | 79 +++++++++++++++++++ 5 files changed, 174 insertions(+), 15 deletions(-) rename src/Humanizer.Tests.Shared/Localisation/{ckb => ku}/DateHumanizeTests.cs (84%) create mode 100644 src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs create mode 100644 src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index fbfe00087..dde7e0e36 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -48,7 +48,7 @@ - + @@ -107,6 +107,7 @@ + diff --git a/src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs similarity index 84% rename from src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs rename to src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs index 3aa322d49..a484345fb 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ckb/DateHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs @@ -1,7 +1,7 @@ using Humanizer.Localisation; using Xunit; -namespace Humanizer.Tests.Localisation.ckb +namespace Humanizer.Tests.Localisation.ku { [UseCulture("ku")] public class DateHumanizeTests @@ -48,10 +48,10 @@ public void HoursFromNow(int hours, string expected) [Theory] [InlineData(-2, "2 خولەک لەمەوبەر")] - [InlineData(-1, "یەک خولەک لەمەوبەر")] + [InlineData(-1, "خولەکێک لەمەوبەر")] [InlineData(-3, "3 خولەک لەمەوبەر")] [InlineData(-11, "11 خولەک لەمەوبەر")] - [InlineData(60, "یەک کاتژمێر لەمەوبەر")] + [InlineData(60, "کاتژمێرێک لەمەوبەر")] public void MinutesAgo(int minutes, string expected) { DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Past); @@ -69,7 +69,7 @@ public void MinutesFromNow(int minutes, string expected) [Theory] [InlineData(-2, "2 مانگ لەمەوبەر")] - [InlineData(-1, "یەک مانگ لەمەوبەر")] + [InlineData(-1, "مانگێک لەمەوبەر")] [InlineData(-3, "3 مانگ لەمەوبەر")] [InlineData(-11, "11 مانگ لەمەوبەر")] public void MonthsAgo(int months, string expected) @@ -88,7 +88,7 @@ public void MonthsFromNow(int months, string expected) [Theory] [InlineData(-2, "2 چرکە لەمەوبەر")] - [InlineData(-1, "یەک چرکە لەمەوبەر")] + [InlineData(-1, "چرکەیەک لەمەوبەر")] [InlineData(-3, "3 چرکە لەمەوبەر")] [InlineData(-11, "11 چرکە لەمەوبەر")] public void SecondsAgo(int seconds, string expected) @@ -98,10 +98,10 @@ public void SecondsAgo(int seconds, string expected) [Theory] [InlineData(0, "ئێستا")] - [InlineData(1, "یەک چرکەی دیکە")] - [InlineData(2, "2 چرکەی دیکە")] - [InlineData(10, "10 چرکەی دیکە")] - [InlineData(24, "2٤ چرکەی دیکە")] + [InlineData(1, "دوای چرکەیەکی دیکە")] + [InlineData(2, "دوای 2 چرکەی دیکە")] + [InlineData(10, "دوای 10 چرکەی دیکە")] + [InlineData(24, "دوای 24 چرکەی دیکە")] public void SecondsFromNow(int seconds, string expected) { DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); @@ -109,7 +109,7 @@ public void SecondsFromNow(int seconds, string expected) [Theory] [InlineData(-2, "2 ساڵ لەمەوبەر")] - [InlineData(-1, "یەک ساڵ لەمەوبەر")] + [InlineData(-1, "ساڵێک لەمەوبەر")] [InlineData(-3, "3 ساڵ لەمەوبەر")] [InlineData(-11, "11 ساڵ لەمەوبەر")] public void YearsAgo(int years, string expected) @@ -118,10 +118,10 @@ public void YearsAgo(int years, string expected) } [Theory] - [InlineData(1, "یەک ساڵی دیکە")] - [InlineData(2, "2 ساڵی دیکە")] - [InlineData(7, "7 ساڵی دیکە")] - [InlineData(55, "55 ساڵی دیکە")] + [InlineData(1, "دوای ساڵێکی دیکە")] + [InlineData(2, "دوای 2 ساڵی دیکە")] + [InlineData(7, "دوای 7 ساڵی دیکە")] + [InlineData(55, "دوای 55 ساڵی دیکە")] public void YearsFromNow(int years, string expected) { DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); diff --git a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs new file mode 100644 index 000000000..5a22fa425 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs @@ -0,0 +1,78 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.ku +{ + [UseCulture("ku")] + public class NumberToWordsTests + { + [Theory] + [InlineData(1, "یەک")] + [InlineData(10, "دە")] + [InlineData(11, "یازدە")] + [InlineData(122, "سەد و بیست و دوو")] + [InlineData(3501, "سێ هەزار و پێنج سەد و یەک")] + [InlineData(100, "سەد")] + [InlineData(1000, "یەک هەزار")] + [InlineData(100000, "سەد هەزار")] + [InlineData(1000000, "یەک میلیۆن")] + [InlineData(10000000, "دە میلیۆن")] + [InlineData(100000000, "سەد میلیۆن")] + [InlineData(1000000000, "یەک میلیارد")] + [InlineData(111, "سەد و یازدە")] + [InlineData(1111, "یەک هەزار و سەد و یازدە")] + [InlineData(111111, "سەد و یازدە هەزار و سەد و یازدە")] + [InlineData(1111111, "یەک میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] + [InlineData(11111111, "یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] + [InlineData(111111111, "سەد و یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] + [InlineData(1111111111, "یەک میلیارد و سەد و یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] + [InlineData(123, "سەد و بیست و سێ")] + [InlineData(1234, "یەک هەزار و دوو سەد و سی و چوار")] + [InlineData(12345, "دوازدە هەزار و سێ سەد و چل و پێنج")] + [InlineData(123456, "سەد و بیست و سێ هەزار و چوار سەد و پەنجا و شەش")] + [InlineData(1234567, "یەک میلیۆن و دوو سەد و سی و چوار هەزار و پێنج سەد و شەست و حەوت")] + [InlineData(12345678, "دوازدە میلیۆن و سێ سەد و چل و پێنج هەزار و شەش سەد و حەفتا و هەشت")] + [InlineData(123456789, "سەد و بیست و سێ میلیۆن و چوار سەد و پەنجا و شەش هەزار و حەوت سەد و هەشتا و نۆ")] + [InlineData(1234567890, "یەک میلیارد و دوو سەد و سی و چوار میلیۆن و پێنج سەد و شەست و حەوت هەزار و هەشت سەد و نەوەد")] + public void ToWordsKurdish(int number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + + [Theory] + [InlineData(0, "سفرەم")] + [InlineData(1, "یەکەم")] + [InlineData(2, "دووەم")] + [InlineData(3, "سێیەم")] + [InlineData(4, "چوارەم")] + [InlineData(5, "پێنجەم")] + [InlineData(6, "شەشەم")] + [InlineData(7, "حەوتەم")] + [InlineData(8, "هەشتەم")] + [InlineData(9, "نۆیەم")] + [InlineData(10, "دەیەم")] + [InlineData(11, "یازدەیەم")] + [InlineData(12, "دوازدەیەم")] + [InlineData(13, "سێزدەیەم")] + [InlineData(21, "بیست و یەکەم")] + [InlineData(22, "بیست و دووەم")] + [InlineData(23, "بیست و سێیەم")] + [InlineData(24, "بیست و چوارەم")] + [InlineData(25, "بیست و پێنجەم")] + [InlineData(30, "سییەم")] + [InlineData(40, "چلەم")] + [InlineData(50, "پەنجایەم")] + [InlineData(60, "شەستەم")] + [InlineData(70, "حەفتایەم")] + [InlineData(80, "هەشتایەم")] + [InlineData(90, "نەوەدەم")] + [InlineData(100, "سەدەم")] + [InlineData(200, "دوو سەدەم")] + [InlineData(1000, "یەک هەزارەم")] + [InlineData(1333, "یەک هەزار و سێ سەد و سی و سێیەم")] + [InlineData(1000000, "یەک میلیۆنەم")] + public void ToOrdinalWordsKurdish(int number, string words) + { + Assert.Equal(words, number.ToOrdinalWords()); + } + } +} diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs index b41bf1a63..a25bc3d29 100644 --- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs +++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs @@ -47,6 +47,7 @@ public NumberToWordsConverterRegistry() Register("hy", new ArmenianNumberToWordsConverter()); Register("az", new AzerbaijaniNumberToWordsConverter()); Register("ja", new JapaneseNumberToWordsConverter()); + Register("ku", new CentralKurdishNumberToWordsConverter()); } } } diff --git a/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs new file mode 100644 index 000000000..c1270acac --- /dev/null +++ b/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; + +namespace Humanizer.Localisation.NumberToWords +{ + internal class CentralKurdishNumberToWordsConverter : GenderlessNumberToWordsConverter + { + private static readonly string[] KurdishHundredsMap = { "سفر", "سەد", "دوو سەد", "سێ سەد", "چوار سەد", "پێنج سەد", "شەش سەد", "حەوت سەد", "هەشت سەد", "نۆ سەد" }; + private static readonly string[] KurdishTensMap = { "سفر", "دە", "بیست", "سی", "چل", "پەنجا", "شەست", "حەفتا", "هەشتا", "نەوەد" }; + private static readonly string[] KurdishUnitsMap = { "سفر", "یەک", "دوو", "سێ", "چوار", "پێنج", "شەش", "حەوت", "هەشت", "نۆ", "دە", "یازدە", "دوازدە", "سێزدە", "چواردە", "پازدە", "شازدە", "حەڤدە", "هەژدە", "نۆزدە" }; + + public override string Convert(long number) + { + var largestNumber = (Math.Pow(10, 15) * 1000) - 1; + if (number > largestNumber || number < -largestNumber) + { + throw new NotImplementedException(); + } + + if (number < 0) + { + return string.Format("نێگەتیڤ {0}", Convert(-number)); + } + + if (number == 0) + { + return "سفر"; + } + + var kurdishGroupsMap = new Dictionary> + { + {(long)Math.Pow(10, 15), n => string.Format("{0} کوادریلیۆن", Convert(n)) }, + {(long)Math.Pow(10, 12), n => string.Format("{0} تریلیۆن", Convert(n)) }, + {(long)Math.Pow(10, 9), n => string.Format("{0} میلیارد", Convert(n)) }, + {(long)Math.Pow(10, 6), n => string.Format("{0} میلیۆن", Convert(n)) }, + {(long)Math.Pow(10, 3), n => string.Format("{0} هەزار", Convert(n)) }, + {(long)Math.Pow(10, 2), n => KurdishHundredsMap[n]} + }; + + var parts = new List(); + foreach (var group in kurdishGroupsMap.Keys) + { + if (number / group > 0) + { + parts.Add(kurdishGroupsMap[group](number / group)); + number %= group; + } + } + + if (number >= 20) + { + parts.Add(KurdishTensMap[number / 10]); + number %= 10; + } + + if (number > 0) + { + parts.Add(KurdishUnitsMap[number]); + } + + return string.Join(" و ", parts); + } + + public override string ConvertToOrdinal(int number) + { + var word = Convert(number); + return string.Format("{0}{1}", word, IsVowel(word[word.Length - 1]) ? "یەم" : "ەم"); + } + + private bool IsVowel(char c) + { + return c == 'ا' || + c == 'ێ' || + c == 'ۆ' || + c == 'ە' || + c == 'ی'; + } + } +} From ac6800ac29a151ffb585f99728bd57ff0c89882f Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Tue, 23 Mar 2021 17:21:42 +0300 Subject: [PATCH 089/126] add central kurdish timespan unit tests --- .../Humanizer.Tests.Shared.projitems | 1 + .../Localisation/ku/TimeSpanHumanizeTests.cs | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/Humanizer.Tests.Shared/Localisation/ku/TimeSpanHumanizeTests.cs diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems index dde7e0e36..07e7b2d0f 100644 --- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems +++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems @@ -108,6 +108,7 @@ + diff --git a/src/Humanizer.Tests.Shared/Localisation/ku/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/TimeSpanHumanizeTests.cs new file mode 100644 index 000000000..cd8eb0188 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/ku/TimeSpanHumanizeTests.cs @@ -0,0 +1,92 @@ +using System; +using Xunit; + +namespace Humanizer.Tests.Localisation.ku +{ + [UseCulture("ku")] + public class TimeSpanHumanizeTests + { + + [Theory] + [Trait("Translation", "Native speaker")] + [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: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [Trait("Translation", "Native speaker")] + [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: Humanizer.Localisation.TimeUnit.Year)); + } + + [Theory] + [InlineData(7, "1 هەفتە")] + [InlineData(77, "11 هەفتە")] + public void Weeks(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize()); + } + + [Theory] + [InlineData(1, "1 ڕۆژ")] + [InlineData(3, "3 ڕۆژ")] + public void Days(int days, string expected) + { + Assert.Equal(expected, TimeSpan.FromDays(days).Humanize()); + } + + [Theory] + [InlineData(1, "1 کاتژمێر")] + [InlineData(11, "11 کاتژمێر")] + public void Hours(int hours, string expected) + { + Assert.Equal(expected, TimeSpan.FromHours(hours).Humanize()); + } + + [Theory] + [InlineData(1, "1 خولەک")] + [InlineData(11, "11 خولەک")] + public void Minutes(int minutes, string expected) + { + Assert.Equal(expected, TimeSpan.FromMinutes(minutes).Humanize()); + } + + [Theory] + [InlineData(1, "1 چرکە")] + [InlineData(11, "11 چرکە")] + public void Seconds(int seconds, string expected) + { + Assert.Equal(expected, TimeSpan.FromSeconds(seconds).Humanize()); + } + + [Theory] + [InlineData(1, "1 میلیچرکە")] + [InlineData(11, "11 میلیچرکە")] + public void Milliseconds(int milliseconds, string expected) + { + Assert.Equal(expected, TimeSpan.FromMilliseconds(milliseconds).Humanize()); + } + + [Fact] + public void NoTime() + { + Assert.Equal("0 میلیچرکە", TimeSpan.Zero.Humanize()); + } + + [Fact] + public void NoTimeToWords() + { + Assert.Equal("ئێستا", TimeSpan.Zero.Humanize(toWords: true)); + } + } +} From 4b381b02b22b1abe92326fb2e2fd11430a468b13 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Tue, 23 Mar 2021 17:24:18 +0300 Subject: [PATCH 090/126] add central kurdish nuspec --- NuSpecs/Humanizer.Core.ku.nuspec | 25 +++++++++++++++++++++++++ NuSpecs/Humanizer.nuspec | 1 + src/Humanizer.sln | 1 + 3 files changed, 27 insertions(+) create mode 100644 NuSpecs/Humanizer.Core.ku.nuspec diff --git a/NuSpecs/Humanizer.Core.ku.nuspec b/NuSpecs/Humanizer.Core.ku.nuspec new file mode 100644 index 000000000..c0d067fd8 --- /dev/null +++ b/NuSpecs/Humanizer.Core.ku.nuspec @@ -0,0 +1,25 @@ + + + + Humanizer.Core.ku + $version$ + Humanizer Locale (ku) + Mehdi Khalili, Claire Novotny + https://github.com/Humanizr/Humanizer + logo.png + false + Humanizer Locale Central Kurdish (ku) + Copyright (c) .NET Foundation and Contributors + MIT + + ku + + + + + + + + + + \ No newline at end of file diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index dd995adba..d55e45d90 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -58,6 +58,7 @@ + diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 82780231d..388020df7 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -57,6 +57,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ..\NuSpecs\Humanizer.Core.id.nuspec = ..\NuSpecs\Humanizer.Core.id.nuspec ..\NuSpecs\Humanizer.Core.it.nuspec = ..\NuSpecs\Humanizer.Core.it.nuspec ..\NuSpecs\Humanizer.Core.ja.nuspec = ..\NuSpecs\Humanizer.Core.ja.nuspec + ..\NuSpecs\Humanizer.Core.ku.nuspec = ..\NuSpecs\Humanizer.Core.ku.nuspec ..\NuSpecs\Humanizer.Core.lv.nuspec = ..\NuSpecs\Humanizer.Core.lv.nuspec ..\NuSpecs\Humanizer.Core.nb-NO.nuspec = ..\NuSpecs\Humanizer.Core.nb-NO.nuspec ..\NuSpecs\Humanizer.Core.nb.nuspec = ..\NuSpecs\Humanizer.Core.nb.nuspec From ed40fb977c77e147654467656778cc2e5719f572 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Tue, 23 Mar 2021 22:09:23 +0300 Subject: [PATCH 091/126] adjust some of the localization strings to make them more natural --- .../Localisation/ku/DateHumanizeTests.cs | 44 +++++----- .../Localisation/ku/NumberToWordsTests.cs | 18 +++-- .../CentralKurdishNumberToWordsConverter.cs | 2 +- src/Humanizer/Properties/Resources.ku.resx | 80 +++++++++---------- 4 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs index a484345fb..6310d66e8 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ku/DateHumanizeTests.cs @@ -18,9 +18,9 @@ public void DaysAgo(int days, string expected) [Theory] [InlineData(1, "بەیانی")] - [InlineData(2, "دوای 2 ڕۆژی دیکە")] - [InlineData(10, "دوای 10 ڕۆژی دیکە")] - [InlineData(17, "دوای 17 ڕۆژی دیکە")] + [InlineData(2, "2 ڕۆژی دیکە")] + [InlineData(10, "10 ڕۆژی دیکە")] + [InlineData(17, "17 ڕۆژی دیکە")] public void DaysFromNow(int days, string expected) { DateHumanize.Verify(expected, days, TimeUnit.Day, Tense.Future); @@ -37,10 +37,10 @@ public void HoursAgo(int hours, string expected) } [Theory] - [InlineData(1, "دوای کاتژمێرێکی دیکە")] - [InlineData(2, "دوای 2 کاتژمێری دیکە")] - [InlineData(10, "دوای 10 کاتژمێری دیکە")] - [InlineData(23, "دوای 23 کاتژمێری دیکە")] + [InlineData(1, "کاتژمێرێکی دیکە")] + [InlineData(2, "2 کاتژمێری دیکە")] + [InlineData(10, "10 کاتژمێری دیکە")] + [InlineData(23, "23 کاتژمێری دیکە")] public void HoursFromNow(int hours, string expected) { DateHumanize.Verify(expected, hours, TimeUnit.Hour, Tense.Future); @@ -58,10 +58,10 @@ public void MinutesAgo(int minutes, string expected) } [Theory] - [InlineData(1, "دوای خولەکێکی دیکە")] - [InlineData(2, "دوای 2 خولەکی دیکە")] - [InlineData(10, "دوای 10 خولەکی دیکە")] - [InlineData(23, "دوای 23 خولەکی دیکە")] + [InlineData(1, "خولەکێکی دیکە")] + [InlineData(2, "2 خولەکی دیکە")] + [InlineData(10, "10 خولەکی دیکە")] + [InlineData(23, "23 خولەکی دیکە")] public void MinutesFromNow(int minutes, string expected) { DateHumanize.Verify(expected, minutes, TimeUnit.Minute, Tense.Future); @@ -78,9 +78,9 @@ public void MonthsAgo(int months, string expected) } [Theory] - [InlineData(1, "دوای مانگێکی دیکە")] - [InlineData(2, "دوای 2 مانگی دیکە")] - [InlineData(10, "دوای 10 مانگی دیکە")] + [InlineData(1, "مانگێکی دیکە")] + [InlineData(2, "2 مانگی دیکە")] + [InlineData(10, "10 مانگی دیکە")] public void MonthsFromNow(int months, string expected) { DateHumanize.Verify(expected, months, TimeUnit.Month, Tense.Future); @@ -98,10 +98,10 @@ public void SecondsAgo(int seconds, string expected) [Theory] [InlineData(0, "ئێستا")] - [InlineData(1, "دوای چرکەیەکی دیکە")] - [InlineData(2, "دوای 2 چرکەی دیکە")] - [InlineData(10, "دوای 10 چرکەی دیکە")] - [InlineData(24, "دوای 24 چرکەی دیکە")] + [InlineData(1, "چرکەیەکی دیکە")] + [InlineData(2, "2 چرکەی دیکە")] + [InlineData(10, "10 چرکەی دیکە")] + [InlineData(24, "24 چرکەی دیکە")] public void SecondsFromNow(int seconds, string expected) { DateHumanize.Verify(expected, seconds, TimeUnit.Second, Tense.Future); @@ -118,10 +118,10 @@ public void YearsAgo(int years, string expected) } [Theory] - [InlineData(1, "دوای ساڵێکی دیکە")] - [InlineData(2, "دوای 2 ساڵی دیکە")] - [InlineData(7, "دوای 7 ساڵی دیکە")] - [InlineData(55, "دوای 55 ساڵی دیکە")] + [InlineData(1, "ساڵێکی دیکە")] + [InlineData(2, "2 ساڵی دیکە")] + [InlineData(7, "7 ساڵی دیکە")] + [InlineData(55, "55 ساڵی دیکە")] public void YearsFromNow(int years, string expected) { DateHumanize.Verify(expected, years, TimeUnit.Year, Tense.Future); diff --git a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs index 5a22fa425..5a7e8585d 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs @@ -6,27 +6,34 @@ namespace Humanizer.Tests.Localisation.ku public class NumberToWordsTests { [Theory] + [InlineData(-1, "نێگەتیڤ یەک")] + [InlineData(-10, "نێگەتیڤ دە")] + [InlineData(-100, "نێگەتیڤ سەد")] + [InlineData(-999, "نێگەتیڤ نۆ سەد و نەوەد و نۆ")] + [InlineData(-1000, "نێگەتیڤ هەزار")] + [InlineData(-1000000, "نێگەتیڤ یەک میلیۆن")] [InlineData(1, "یەک")] [InlineData(10, "دە")] [InlineData(11, "یازدە")] [InlineData(122, "سەد و بیست و دوو")] [InlineData(3501, "سێ هەزار و پێنج سەد و یەک")] [InlineData(100, "سەد")] - [InlineData(1000, "یەک هەزار")] + [InlineData(1000, "هەزار")] [InlineData(100000, "سەد هەزار")] [InlineData(1000000, "یەک میلیۆن")] [InlineData(10000000, "دە میلیۆن")] [InlineData(100000000, "سەد میلیۆن")] [InlineData(1000000000, "یەک میلیارد")] [InlineData(111, "سەد و یازدە")] - [InlineData(1111, "یەک هەزار و سەد و یازدە")] + [InlineData(1111, "هەزار و سەد و یازدە")] [InlineData(111111, "سەد و یازدە هەزار و سەد و یازدە")] + [InlineData(1001001, "یەک میلیۆن و هەزار و یەک")] [InlineData(1111111, "یەک میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] [InlineData(11111111, "یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] [InlineData(111111111, "سەد و یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] [InlineData(1111111111, "یەک میلیارد و سەد و یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] [InlineData(123, "سەد و بیست و سێ")] - [InlineData(1234, "یەک هەزار و دوو سەد و سی و چوار")] + [InlineData(1234, "هەزار و دوو سەد و سی و چوار")] [InlineData(12345, "دوازدە هەزار و سێ سەد و چل و پێنج")] [InlineData(123456, "سەد و بیست و سێ هەزار و چوار سەد و پەنجا و شەش")] [InlineData(1234567, "یەک میلیۆن و دوو سەد و سی و چوار هەزار و پێنج سەد و شەست و حەوت")] @@ -67,8 +74,9 @@ public void ToWordsKurdish(int number, string expected) [InlineData(90, "نەوەدەم")] [InlineData(100, "سەدەم")] [InlineData(200, "دوو سەدەم")] - [InlineData(1000, "یەک هەزارەم")] - [InlineData(1333, "یەک هەزار و سێ سەد و سی و سێیەم")] + [InlineData(1000, "هەزارەم")] + [InlineData(1001, "هەزار و یەکەم")] + [InlineData(1333, "هەزار و سێ سەد و سی و سێیەم")] [InlineData(1000000, "یەک میلیۆنەم")] public void ToOrdinalWordsKurdish(int number, string words) { diff --git a/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs index c1270acac..d379c208f 100644 --- a/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs @@ -58,7 +58,7 @@ public override string Convert(long number) parts.Add(KurdishUnitsMap[number]); } - return string.Join(" و ", parts); + return string.Join(" و ", parts).Replace("یەک هەزار", "هەزار"); } public override string ConvertToOrdinal(int number) diff --git a/src/Humanizer/Properties/Resources.ku.resx b/src/Humanizer/Properties/Resources.ku.resx index 2ad95e92a..0e9fde5cc 100644 --- a/src/Humanizer/Properties/Resources.ku.resx +++ b/src/Humanizer/Properties/Resources.ku.resx @@ -193,22 +193,22 @@ 1 هەفتە - دوای {0} ڕۆژی دیکە + {0} ڕۆژی دیکە - دوای {0} کاتژمێری دیکە + {0} کاتژمێری دیکە - دوای {0} خولەکی دیکە + {0} خولەکی دیکە - دوای {0} مانگی دیکە + {0} مانگی دیکە - دوای {0} چرکەی دیکە + {0} چرکەی دیکە - دوای {0} ساڵی دیکە + {0} ساڵی دیکە ئێستا @@ -217,34 +217,34 @@ بەیانی - دوای کاتژمێرێکی دیکە + کاتژمێرێکی دیکە - دوای خولەکێکی دیکە + خولەکێکی دیکە - دوای مانگێکی دیکە + مانگێکی دیکە - دوای چرکەیەکی دیکە + چرکەیەکی دیکە - دوای ساڵێکی دیکە + ساڵێکی دیکە - دوای {0} کاتژمێری دیکە + {0} کاتژمێری دیکە - دوای {0} خولەکی دیکە + {0} خولەکی دیکە {0} چرکە لەمەوبەر - دوای {0} چرکەی دیکە + {0} چرکەی دیکە - دوای {0} ساڵی دیکە + {0} ساڵی دیکە {0} ڕۆژ @@ -286,16 +286,16 @@ {0} ڕۆژ لەمەوبەر - دوای {0} ڕۆژی دیکە + {0} ڕۆژی دیکە - دوای {0} ڕۆژی دیکە + {0} ڕۆژی دیکە - دوای {0} ڕۆژی دیکە + {0} ڕۆژی دیکە - دوای {0} ڕۆژی دیکە + {0} ڕۆژی دیکە {0} کاتژمێر لەمەوبەر @@ -313,13 +313,13 @@ {0} کاتژمێر لەمەوبەر - دوای {0} کاتژمێری دیکە + {0} کاتژمێری دیکە - دوای {0} کاتژمێری دیکە + {0} کاتژمێری دیکە - دوای {0} کاتژمێری دیکە + {0} کاتژمێری دیکە {0} خولەک لەمەوبەر @@ -337,13 +337,13 @@ {0} خولەک لەمەوبەر - دوای {0} خولەکی دیکە + {0} خولەکی دیکە - دوای {0} خولەکی دیکە + {0} خولەکی دیکە - دوای {0} خولەکی دیکە + {0} خولەکی دیکە {0} مانگ لەمەوبەر @@ -358,16 +358,16 @@ {0} مانگ لەمەوبەر - دوای {0} مانگی دیکە + {0} مانگی دیکە - دوای {0} مانگی دیکە + {0} مانگی دیکە - دوای {0} مانگی دیکە + {0} مانگی دیکە - دوای {0} مانگی دیکە + {0} مانگی دیکە {0} چرکە لەمەوبەر @@ -382,13 +382,13 @@ {0} چرکە لەمەوبەر - دوای {0} چرکەی دیکە + {0} چرکەی دیکە - دوای {0} چرکەی دیکە + {0} چرکەی دیکە - دوای {0} چرکەی دیکە + {0} چرکەی دیکە {0} ساڵ لەمەوبەر @@ -406,13 +406,13 @@ {0} ساڵ لەمەوبەر - دوای {0} ساڵی دیکە + {0} ساڵی دیکە - دوای {0} ساڵی دیکە + {0} ساڵی دیکە - دوای {0} ساڵی دیکە + {0} ساڵی دیکە {0} ڕۆژ @@ -478,31 +478,31 @@ {0} کاتژمێر لەمەوبەر - دوای {0} کاتژمێری دیکە + {0} کاتژمێری دیکە {0} خولەک لەمەوبەر - دوای {0} خولەکی دیکە + {0} خولەکی دیکە {0} مانگ لەمەوبەر - دوای {0} مانگی دیکە + {0} مانگی دیکە {0} چرکە لەمەوبەر - دوای {0} چرکەی دیکە + {0} چرکەی دیکە {0} ساڵ لەمەوبەر - دوای {0} ساڵی دیکە + {0} ساڵی دیکە {0} کاتژمێر From 77e3e73b1df7b64ab02d926e774943e4e0bb57d7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 05:26:33 +0000 Subject: [PATCH 092/126] Bump Microsoft.NET.Test.Sdk from 16.9.1 to 16.9.4 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.9.1 to 16.9.4. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.9.1...v16.9.4) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 8101b13a1..053e3fddf 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From ca6c00d9146be11d891208b5dd383b2fcc43f002 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 7 Apr 2021 05:23:15 +0000 Subject: [PATCH 093/126] Bump Nerdbank.GitVersioning from 3.3.37 to 3.4.190 Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.3.37 to 3.4.190. - [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases) - [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/compare/v3.3.37...v3.4.190) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 50f9d8679..236e4c304 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -6,7 +6,7 @@ - + From 074e5554332f36e24f8f01bf16c48f03a7d05ca8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 7 Apr 2021 05:31:40 +0000 Subject: [PATCH 094/126] Bump coverlet.collector from 1.3.0 to 3.0.3 Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 1.3.0 to 3.0.3. - [Release notes](https://github.com/coverlet-coverage/coverlet/releases) - [Commits](https://github.com/coverlet-coverage/coverlet/commits) Signed-off-by: dependabot-preview[bot] --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 053e3fddf..5fef09d36 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -8,7 +8,7 @@ $(DefineConstants);NETFX_CORE - + From aa0e560398f10cc3373abd9e9d15a0aad0d8f023 Mon Sep 17 00:00:00 2001 From: Fabrizio Fortino Date: Thu, 22 Apr 2021 17:04:50 +0200 Subject: [PATCH 095/126] fix link for Humanizer.jvm --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index ade8d2209..2c1283eab 100644 --- a/readme.md +++ b/readme.md @@ -1240,7 +1240,7 @@ These annotations do not yet cover the entire library, but [pull requests are al [PowerShell Humanizer](https://github.com/dfinke/PowerShellHumanizer) is a PowerShell module that wraps Humanizer. ### Humanizer JVM -[Humanizer.jvm](https://github.com/Humanizr/Humanizer.jvm) is an adaptation of the Humanizer framework for .Net which is made for the jvm and is written in Kotlin. +[Humanizer.jvm](https://github.com/MehdiK/Humanizer.jvm) is an adaptation of the Humanizer framework for .Net which is made for the jvm and is written in Kotlin. Humanizer.jvm meets all your jvm needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities. ### Humanizer.node From 171e28e2905727b14455f3935b3219655aa14f3b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 23 Apr 2021 14:05:29 +0000 Subject: [PATCH 096/126] Bump Nerdbank.GitVersioning from 3.4.190 to 3.4.194 Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.4.190 to 3.4.194. - [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases) - [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/compare/v3.4.190...v3.4.194) Signed-off-by: dependabot-preview[bot] --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 236e4c304..d6a14c226 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -6,7 +6,7 @@ - + From 62d7f19e5d0464a00bccd891ed328298daea6cc3 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Fri, 23 Apr 2021 10:06:28 -0400 Subject: [PATCH 097/126] rename --- azure-pipelines.yml | 4 ++-- version.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index db4f4c96d..8a326d02b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,9 +1,9 @@ trigger: -- master +- main - rel/* pr: -- master +- main - rel/* variables: diff --git a/version.json b/version.json index 755a199ed..302c79d3c 100644 --- a/version.json +++ b/version.json @@ -1,7 +1,7 @@ { "version": "2.8", "publicReleaseRefSpec": [ - "^refs/heads/master$", // we release out of master + "^refs/heads/main$", // we release out of main "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N ], "nugetPackageVersion":{ From 3f909a8e8fa5214d2a34c1801af84a5c8d19ca75 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Fri, 23 Apr 2021 10:17:27 -0400 Subject: [PATCH 098/126] use latest sdk: --- azure-pipelines.yml | 15 +++++++-------- src/Directory.Build.targets | 11 ----------- src/Humanizer.Tests/Humanizer.Tests.csproj | 4 ++-- 3 files changed, 9 insertions(+), 21 deletions(-) delete mode 100644 src/Directory.Build.targets diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8a326d02b..7988e550f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,16 +19,15 @@ stages: steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.x' + displayName: 'Use .NET Core SDK 5.x' inputs: - version: 3.x + version: 5.x - task: UseDotNet@2 - displayName: 'Use .NET Core Runtime 2.1.x' + displayName: 'Use .NET Core Runtime 3.x' inputs: - version: 2.1.x - packageType: runtime - + version: 3.x + packageType: runtime - task: DotNetCoreCLI@2 inputs: @@ -76,14 +75,14 @@ stages: arguments: install --tool-path . dotnet-reportgenerator-globaltool displayName: Install ReportGenerator tool - - script: reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/Rx.NET/Source/coverlet/reports -reporttypes:"Cobertura" + - script: reportgenerator -reports:$(Agent.TempDirectory)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/coverlet/reports -reporttypes:"Cobertura" displayName: Create reports - task: PublishCodeCoverageResults@1 displayName: 'Publish code coverage' inputs: codeCoverageTool: Cobertura - summaryFileLocation: $(Build.SourcesDirectory)/Rx.NET/Source/coverlet/reports/Cobertura.xml + summaryFileLocation: $(Build.SourcesDirectory)/coverlet/reports/Cobertura.xml - publish: $(Build.ArtifactStagingDirectory)\Packages displayName: Publish build packages diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets deleted file mode 100644 index 12c61a902..000000000 --- a/src/Directory.Build.targets +++ /dev/null @@ -1,11 +0,0 @@ - - - - - <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/> - - - diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index 5fef09d36..c6cf93d93 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -1,10 +1,10 @@  - net48;netcoreapp2.1 + net48;net5.0;netcoreapp3.1 false - + $(DefineConstants);NETFX_CORE From e02418984282bab68529845183d4cb25acdab81b Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Fri, 23 Apr 2021 10:27:14 -0400 Subject: [PATCH 099/126] Update version.json --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 302c79d3c..552b36878 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "2.8", + "version": "2.9", "publicReleaseRefSpec": [ "^refs/heads/main$", // we release out of main "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N From 47a5f260a5206eb434b7a490b3dae0bd012ab264 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Sat, 24 Apr 2021 14:06:07 +0300 Subject: [PATCH 100/126] fix a bug in number to words for central kurdish --- .../Localisation/ku/NumberToWordsTests.cs | 3 ++- .../NumberToWords/CentralKurdishNumberToWordsConverter.cs | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs index 5a7e8585d..06ef8a231 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs @@ -21,13 +21,14 @@ public class NumberToWordsTests [InlineData(1000, "هەزار")] [InlineData(100000, "سەد هەزار")] [InlineData(1000000, "یەک میلیۆن")] + [InlineData(51000, "پەنجا و یەک هەزار")] [InlineData(10000000, "دە میلیۆن")] [InlineData(100000000, "سەد میلیۆن")] [InlineData(1000000000, "یەک میلیارد")] [InlineData(111, "سەد و یازدە")] [InlineData(1111, "هەزار و سەد و یازدە")] [InlineData(111111, "سەد و یازدە هەزار و سەد و یازدە")] - [InlineData(1001001, "یەک میلیۆن و هەزار و یەک")] + [InlineData(1001001, "یەک میلیۆن و یەک هەزار و یەک")] [InlineData(1111111, "یەک میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] [InlineData(11111111, "یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] [InlineData(111111111, "سەد و یازدە میلیۆن و سەد و یازدە هەزار و سەد و یازدە")] diff --git a/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs index d379c208f..7f42786e7 100644 --- a/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/CentralKurdishNumberToWordsConverter.cs @@ -58,7 +58,11 @@ public override string Convert(long number) parts.Add(KurdishUnitsMap[number]); } - return string.Join(" و ", parts).Replace("یەک هەزار", "هەزار"); + var sentence = string.Join(" و ", parts); + if (sentence.StartsWith("یەک هەزار")) + return sentence.Substring(" یەک".Length); + else + return sentence; } public override string ConvertToOrdinal(int number) From 2378d19f7c153884b309ec5625c313f1d9fd6784 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Sat, 24 Apr 2021 19:34:27 +0300 Subject: [PATCH 101/126] add more test cases --- .../Localisation/ku/NumberToWordsTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs index 06ef8a231..d45614977 100644 --- a/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/ku/NumberToWordsTests.cs @@ -20,8 +20,11 @@ public class NumberToWordsTests [InlineData(100, "سەد")] [InlineData(1000, "هەزار")] [InlineData(100000, "سەد هەزار")] + [InlineData(100001, "سەد هەزار و یەک")] + [InlineData(101000, "سەد و یەک هەزار")] [InlineData(1000000, "یەک میلیۆن")] [InlineData(51000, "پەنجا و یەک هەزار")] + [InlineData(151000, "سەد و پەنجا و یەک هەزار")] [InlineData(10000000, "دە میلیۆن")] [InlineData(100000000, "سەد میلیۆن")] [InlineData(1000000000, "یەک میلیارد")] From a42c930a4a9bd028a5f8130b6eac0e1d7f4d9234 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Mon, 26 Apr 2021 15:39:47 -0400 Subject: [PATCH 102/126] Fix rounding issue on .NET Core 3.1+ --- src/Humanizer/MetricNumeralExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/MetricNumeralExtensions.cs b/src/Humanizer/MetricNumeralExtensions.cs index 76d1a9f9a..126dc9c03 100644 --- a/src/Humanizer/MetricNumeralExtensions.cs +++ b/src/Humanizer/MetricNumeralExtensions.cs @@ -323,7 +323,7 @@ private static string BuildMetricRepresentation(double input, int exponent, Metr var symbol = Math.Sign(exponent) == 1 ? Symbols[0][exponent - 1] : Symbols[1][-exponent - 1]; - return number + return number.ToString("G15") + (formats.HasValue && formats.Value.HasFlag(MetricNumeralFormats.WithSpace) ? " " : string.Empty) + GetUnitText(symbol, formats); } From 7e93a78b70045958aae693b76660e486da6987ad Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Mon, 26 Apr 2021 15:52:32 -0400 Subject: [PATCH 103/126] Fix warnings --- .../ArmenianNumberToWordsConverter.cs | 8 +++--- .../GenderedNumberToWordsConverter.cs | 7 +++++ .../GenderlessNumberToWordsConverter.cs | 7 +++++ .../GreekNumberToWordsConverter.cs | 26 +++++++++---------- .../NumberToWords/INumberToWordsConverter.cs | 2 ++ .../JapaneseNumberToWordsConverter.cs | 8 +++--- .../TamilNumberToWordsConverter.cs | 6 ++--- src/Humanizer/NumberToWordsExtension.cs | 3 ++- 8 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs index 14ccb0006..a42bb341a 100644 --- a/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs @@ -19,7 +19,7 @@ internal class ArmenianNumberToWordsConverter : GenderlessNumberToWordsConverter public override string Convert(long number) { - return Convert(number, false); + return ConvertImpl(number, false); } public override string ConvertToOrdinal(int number) @@ -29,10 +29,10 @@ public override string ConvertToOrdinal(int number) return exceptionString; } - return Convert(number, true); + return ConvertImpl(number, true); } - private string Convert(long number, bool isOrdinal) + private string ConvertImpl(long number, bool isOrdinal) { if (number == 0) { @@ -52,7 +52,7 @@ private string Convert(long number, bool isOrdinal) if (number < 0) { - return string.Format("մինուս {0}", Convert(-number, isOrdinal)); + return string.Format("մինուս {0}", ConvertImpl(-number, isOrdinal)); } var parts = new List(); diff --git a/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs index 093b00144..4c63afb48 100644 --- a/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/GenderedNumberToWordsConverter.cs @@ -19,6 +19,12 @@ public string Convert(long number) return Convert(number, _defaultGender); } + /// + /// Converts the number to string using the locale's default gramatical gender and adds "and" + /// + /// + /// Whether "and" should be included. + /// public string Convert(long number, bool addAnd) { return Convert(number, _defaultGender); @@ -29,6 +35,7 @@ public string Convert(long number, bool addAnd) /// /// /// + /// /// public abstract string Convert(long number, GrammaticalGender gender, bool addAnd = true); diff --git a/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs index 87e651cff..15f6989cc 100644 --- a/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/GenderlessNumberToWordsConverter.cs @@ -9,6 +9,12 @@ internal abstract class GenderlessNumberToWordsConverter : INumberToWordsConvert /// public abstract string Convert(long number); + /// + /// Converts the number to string + /// + /// + /// Whether "and" should be included. + /// public virtual string Convert(long number, bool addAnd) { return Convert(number); @@ -19,6 +25,7 @@ public virtual string Convert(long number, bool addAnd) /// /// /// + /// /// public virtual string Convert(long number, GrammaticalGender gender, bool addAnd = true) { diff --git a/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs index 8df3e3bbf..7bbe432ff 100644 --- a/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/GreekNumberToWordsConverter.cs @@ -21,7 +21,7 @@ internal class GreekNumberToWordsConverter : GenderlessNumberToWordsConverter public override string Convert(long number) { - return Convert(number, false); + return ConvertImpl(number, false); } public override string ConvertToOrdinal(int number) @@ -29,7 +29,7 @@ public override string ConvertToOrdinal(int number) return null; } - private string Convert(long number, bool returnPluralized) + private string ConvertImpl(long number, bool returnPluralized) { if (number < 13) { @@ -75,7 +75,7 @@ private string ConvertIntBH(long number, bool returnPluralized) result += " "; } - result += Convert(number % 10, returnPluralized).ToLower(); + result += ConvertImpl(number % 10, returnPluralized).ToLower(); } return result; @@ -101,7 +101,7 @@ private string ConvertIntBT(long number, bool returnPluralized) if (number % 100 != 0) { - result += $" {Convert(number % 100, returnPluralized).ToLower()}"; + result += $" {ConvertImpl(number % 100, returnPluralized).ToLower()}"; } return result; @@ -116,14 +116,14 @@ private string ConvertIntBM(long number) return "χίλια"; } - return $"χίλια {Convert(number % 1000, false).ToLower()}"; + return $"χίλια {ConvertImpl(number % 1000, false).ToLower()}"; } - var result = $"{Convert(number / 1000, true)} χιλιάδες"; + var result = $"{ConvertImpl(number / 1000, true)} χιλιάδες"; if (number % 1000 != 0) { - result += $" {Convert(number % 1000, false).ToLower()}"; + result += $" {ConvertImpl(number % 1000, false).ToLower()}"; } return result; @@ -138,14 +138,14 @@ private string ConvertIntBB(long number) return "ένα εκατομμύριο"; } - return $"ένα εκατομμύριο {Convert(number % 1000000, true).ToLower()}"; + return $"ένα εκατομμύριο {ConvertImpl(number % 1000000, true).ToLower()}"; } - var result = $"{Convert(number / 1000000, false)} εκατομμύρια"; + var result = $"{ConvertImpl(number / 1000000, false)} εκατομμύρια"; if (number % 1000000 != 0) { - result += $" {Convert(number % 1000000, false).ToLower()}"; + result += $" {ConvertImpl(number % 1000000, false).ToLower()}"; } return result; @@ -160,14 +160,14 @@ private string ConvertIntBTR(long number) return "ένα δισεκατομμύριο"; } - return $"ένα δισεκατομμύριο {Convert(number % 1000000000, true).ToLower()}"; + return $"ένα δισεκατομμύριο {ConvertImpl(number % 1000000000, true).ToLower()}"; } - var result = $"{Convert(number / 1000000000, false)} δισεκατομμύρια"; + var result = $"{ConvertImpl(number / 1000000000, false)} δισεκατομμύρια"; if (number % 1000000000 != 0) { - result += $" {Convert(number % 1000000000, false).ToLower()}"; + result += $" {ConvertImpl(number % 1000000000, false).ToLower()}"; } return result; diff --git a/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs index 19033d15f..a8abb9469 100644 --- a/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/INumberToWordsConverter.cs @@ -16,6 +16,7 @@ public interface INumberToWordsConverter /// Converts the number to string using the locale's default grammatical gender with or without adding 'And' /// /// + /// Specify with our without adding "And" /// string Convert(long number, bool addAnd); @@ -24,6 +25,7 @@ public interface INumberToWordsConverter /// /// /// + /// /// string Convert(long number, GrammaticalGender gender, bool addAnd = true); diff --git a/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs index 70ccd1939..95dc8883d 100644 --- a/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/JapaneseNumberToWordsConverter.cs @@ -11,15 +11,15 @@ internal class JapaneseNumberToWordsConverter : GenderlessNumberToWordsConverter public override string Convert(long number) { - return Convert(number, false); + return ConvertImpl(number, false); } public override string ConvertToOrdinal(int number) { - return Convert(number, true); + return ConvertImpl(number, true); } - private string Convert(long number, bool isOrdinal) + private string ConvertImpl(long number, bool isOrdinal) { if (number == 0) { @@ -28,7 +28,7 @@ private string Convert(long number, bool isOrdinal) if (number < 0) { - return string.Format("マイナス {0}", Convert(-number, false)); + return string.Format("マイナス {0}", ConvertImpl(-number, false)); } var parts = new List(); diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs index 0c883461e..a79158c91 100644 --- a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -26,15 +26,15 @@ internal class TamilNumberToWordsConverter : GenderlessNumberToWordsConverter public override string Convert(long number) { - return Convert(number, false); + return ConvertImpl(number, false); } public override string ConvertToOrdinal(int number) { - return Convert(number, true); + return ConvertImpl(number, true); } - private string Convert(long number, bool isOrdinal) + private string ConvertImpl(long number, bool isOrdinal) { if (number == 0) return GetUnitValue(0, isOrdinal); diff --git a/src/Humanizer/NumberToWordsExtension.cs b/src/Humanizer/NumberToWordsExtension.cs index b93f2d0cc..a6c6daa76 100644 --- a/src/Humanizer/NumberToWordsExtension.cs +++ b/src/Humanizer/NumberToWordsExtension.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using Humanizer.Configuration; namespace Humanizer @@ -62,6 +62,7 @@ public static string ToWords(this int number, GrammaticalGender gender, CultureI /// /// Number to be turned to words /// Culture to use. If null, current thread's UI culture is used. + /// Whether "and" should be included or not. /// public static string ToWords(this long number, CultureInfo culture = null, bool addAnd = true) { From f62cef642a2b2ac74dee9dbd4f9decb95a3d1d44 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Mon, 26 Apr 2021 17:14:17 -0400 Subject: [PATCH 104/126] Fix nuspecs --- ...nizer.Core.ta.nuspec => Humanizer.Core.ta.nuspec.unused} | 0 NuSpecs/Humanizer.nuspec | 5 ++++- src/Humanizer.sln | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) rename NuSpecs/{Humanizer.Core.ta.nuspec => Humanizer.Core.ta.nuspec.unused} (100%) diff --git a/NuSpecs/Humanizer.Core.ta.nuspec b/NuSpecs/Humanizer.Core.ta.nuspec.unused similarity index 100% rename from NuSpecs/Humanizer.Core.ta.nuspec rename to NuSpecs/Humanizer.Core.ta.nuspec.unused diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index d55e45d90..3b4243414 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -25,6 +25,7 @@ + @@ -34,6 +35,8 @@ + + @@ -49,7 +52,7 @@ - + diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 388020df7..54f0dc6e2 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28711.165 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31223.326 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Humanizer.Tests", "Humanizer.Tests\Humanizer.Tests.csproj", "{F886A8DA-3EFC-4A89-91DD-06FAF13DA172}" EndProject @@ -72,7 +72,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ..\NuSpecs\Humanizer.Core.sr-Latn.nuspec = ..\NuSpecs\Humanizer.Core.sr-Latn.nuspec ..\NuSpecs\Humanizer.Core.sr.nuspec = ..\NuSpecs\Humanizer.Core.sr.nuspec ..\NuSpecs\Humanizer.Core.sv.nuspec = ..\NuSpecs\Humanizer.Core.sv.nuspec - ..\NuSpecs\Humanizer.Core.ta.nuspec = ..\NuSpecs\Humanizer.Core.ta.nuspec + ..\NuSpecs\Humanizer.Core.ta.nuspec.unused = ..\NuSpecs\Humanizer.Core.ta.nuspec.unused ..\NuSpecs\Humanizer.Core.tr.nuspec = ..\NuSpecs\Humanizer.Core.tr.nuspec ..\NuSpecs\Humanizer.Core.uk.nuspec = ..\NuSpecs\Humanizer.Core.uk.nuspec ..\NuSpecs\Humanizer.Core.uz-Cyrl-UZ.nuspec = ..\NuSpecs\Humanizer.Core.uz-Cyrl-UZ.nuspec From 27fe5b4b5fc4ce80e2e1b6a4a23f9aedbee02c61 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Mon, 26 Apr 2021 20:02:03 -0400 Subject: [PATCH 105/126] Add package to dependencies --- NuSpecs/Humanizer.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index 3b4243414..d1012e884 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -53,6 +53,7 @@ + From 6a7df0fafcb9e9e1e0c176fcbcfdd2264dea6b5c Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Tue, 27 Apr 2021 10:35:24 -0400 Subject: [PATCH 106/126] ensure repository info is in the nuspec --- NuSpecs/Humanizer.Core.fil-PH.nuspec | 1 + src/Humanizer.sln | 1 + 2 files changed, 2 insertions(+) diff --git a/NuSpecs/Humanizer.Core.fil-PH.nuspec b/NuSpecs/Humanizer.Core.fil-PH.nuspec index 64c3ce0ed..5484dff84 100644 --- a/NuSpecs/Humanizer.Core.fil-PH.nuspec +++ b/NuSpecs/Humanizer.Core.fil-PH.nuspec @@ -11,6 +11,7 @@ Humanizer Locale Filipino (Philippines) (fil-PH) Copyright (c) .NET Foundation and Contributors MIT + fil-PH diff --git a/src/Humanizer.sln b/src/Humanizer.sln index 54f0dc6e2..18271e3a4 100644 --- a/src/Humanizer.sln +++ b/src/Humanizer.sln @@ -48,6 +48,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44 ..\NuSpecs\Humanizer.Core.es.nuspec = ..\NuSpecs\Humanizer.Core.es.nuspec ..\NuSpecs\Humanizer.Core.fa.nuspec = ..\NuSpecs\Humanizer.Core.fa.nuspec ..\NuSpecs\Humanizer.Core.fi-FI.nuspec = ..\NuSpecs\Humanizer.Core.fi-FI.nuspec + ..\NuSpecs\Humanizer.Core.fil-PH.nuspec = ..\NuSpecs\Humanizer.Core.fil-PH.nuspec ..\NuSpecs\Humanizer.Core.fr-BE.nuspec = ..\NuSpecs\Humanizer.Core.fr-BE.nuspec ..\NuSpecs\Humanizer.Core.fr.nuspec = ..\NuSpecs\Humanizer.Core.fr.nuspec ..\NuSpecs\Humanizer.Core.he.nuspec = ..\NuSpecs\Humanizer.Core.he.nuspec From 858b7c538ccdf8db8d3a6126c203c6090bc86389 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Tue, 27 Apr 2021 11:06:03 -0400 Subject: [PATCH 107/126] Remove fil-PH from default due to NuGet bug. --- NuSpecs/Humanizer.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec index d1012e884..6514f603c 100644 --- a/NuSpecs/Humanizer.nuspec +++ b/NuSpecs/Humanizer.nuspec @@ -25,7 +25,7 @@ - + From d90b040475d69773422c69055a02adfd3909f54b Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Fri, 30 Apr 2021 09:40:54 -0500 Subject: [PATCH 108/126] Use provider CompareInfo if available --- src/Humanizer/Bytes/ByteSize.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 0edb4a5dc..42d465383 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -248,7 +248,9 @@ public string ToString(string format, IFormatProvider provider) format = format.Replace("#.##", "0.##"); - bool has(string s) => format.IndexOf(s, StringComparison.CurrentCultureIgnoreCase) != -1; + var culture = provider as CultureInfo ?? CultureInfo.CurrentCulture; + + bool has(string s) => culture.CompareInfo.IndexOf(format, s, CompareOptions.IgnoreCase) != -1; string output(double n) => n.ToString(format, provider); if (has(TerabyteSymbol)) From 9a6f1f7041ae99cfc20caaf33f6b4d40f6defac9 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Fri, 30 Apr 2021 09:47:00 -0500 Subject: [PATCH 109/126] Use StartsWith() instead of IndexOf() == 0 --- .../NumberToWords/AfrikaansNumberToWordsConverter.cs | 2 +- .../NumberToWords/ArmenianNumberToWordsConverter.cs | 2 +- .../Localisation/NumberToWords/EnglishNumberToWordsConverter.cs | 2 +- .../Localisation/NumberToWords/TamilNumberToWordsConverter.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Humanizer/Localisation/NumberToWords/AfrikaansNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/AfrikaansNumberToWordsConverter.cs index ba56b625b..a926b24ec 100644 --- a/src/Humanizer/Localisation/NumberToWords/AfrikaansNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/AfrikaansNumberToWordsConverter.cs @@ -149,7 +149,7 @@ private static string GetUnitValue(int number, bool isOrdinal) private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth - if (toWords.IndexOf("een", StringComparison.Ordinal) == 0) + if (toWords.StartsWith("een", StringComparison.Ordinal)) { if (toWords.IndexOf("een en", StringComparison.Ordinal) != 0) { diff --git a/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs index a42bb341a..9385e60d6 100644 --- a/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/ArmenianNumberToWordsConverter.cs @@ -167,7 +167,7 @@ private static string GetUnitValue(long number, bool isOrdinal) private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth - if (toWords.IndexOf("մեկ", StringComparison.Ordinal) == 0) + if (toWords.StartsWith("մեկ", StringComparison.Ordinal)) { toWords = toWords.Remove(0, 4); } diff --git a/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs index e2b50167c..47f01e99c 100644 --- a/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/EnglishNumberToWordsConverter.cs @@ -154,7 +154,7 @@ private static string GetUnitValue(long number, bool isOrdinal) private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth - if (toWords.IndexOf("one", StringComparison.Ordinal) == 0) + if (toWords.StartsWith("one", StringComparison.Ordinal)) { toWords = toWords.Remove(0, 4); } diff --git a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs index a79158c91..d8cc1d73d 100644 --- a/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TamilNumberToWordsConverter.cs @@ -269,7 +269,7 @@ private static string GetHundredsValue(ref long number) private static string RemoveOnePrefix(string toWords) { // one hundred => hundredth - if (toWords.IndexOf("one", StringComparison.Ordinal) == 0) + if (toWords.StartsWith("one", StringComparison.Ordinal)) toWords = toWords.Remove(0, 4); return toWords; From 17e98849b52fa4e3bc50d57b53f778e8a8a264f2 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Thu, 12 Nov 2020 17:02:23 -0500 Subject: [PATCH 110/126] Add support for culture-specific ByteSize unit expression --- .../Bytes/ToFullWordsTests.cs | 12 +- .../fr/Bytes/ByteSizeExtensionsTests.cs | 77 ++++++++++ .../Localisation/fr/Bytes/ToFullWordsTests.cs | 89 ++++++++++++ .../Localisation/fr/Bytes/ToStringTests.cs | 87 ++++++++++++ src/Humanizer/Bytes/ByteSize.cs | 133 ++++++++++-------- src/Humanizer/Localisation/DataUnit.cs | 15 ++ .../Formatters/DefaultFormatter.cs | 12 ++ .../Localisation/Formatters/IFormatter.cs | 9 ++ src/Humanizer/Properties/Resources.fr.resx | 36 +++++ src/Humanizer/Properties/Resources.resx | 36 +++++ 10 files changed, 444 insertions(+), 62 deletions(-) create mode 100644 src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ByteSizeExtensionsTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToFullWordsTests.cs create mode 100644 src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToStringTests.cs create mode 100644 src/Humanizer/Localisation/DataUnit.cs diff --git a/src/Humanizer.Tests.Shared/Bytes/ToFullWordsTests.cs b/src/Humanizer.Tests.Shared/Bytes/ToFullWordsTests.cs index 9730f2e48..db88528b2 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ToFullWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ToFullWordsTests.cs @@ -26,13 +26,13 @@ namespace Humanizer.Tests.Bytes { public class ToFullWordsTests - { + { [Fact] public void ReturnsSingularBit() { Assert.Equal("1 bit", ByteSize.FromBits(1).ToFullWords()); } - + [Fact] public void ReturnsPluralBits() { @@ -98,5 +98,13 @@ public void ReturnsPluralTerabytes() { Assert.Equal("10 terabytes", ByteSize.FromTerabytes(10).ToFullWords()); } + + [Theory] + [InlineData(229376, "B", "229376 bytes")] + [InlineData(229376, "# KB", "224 kilobytes")] + public void ToFullWordsFormatted(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, ByteSize.FromBytes(input).ToFullWords(format)); + } } } diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ByteSizeExtensionsTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ByteSizeExtensionsTests.cs new file mode 100644 index 000000000..5554d0de7 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ByteSizeExtensionsTests.cs @@ -0,0 +1,77 @@ +using Xunit; + +namespace Humanizer.Tests.Localisation.fr.Bytes +{ + [UseCulture("fr-FR")] + public class ByteSizeExtensionsTests + { + [Theory] + [InlineData(2, null, "2 To")] + [InlineData(2, "GB", "2048 Go")] + [InlineData(2.123, "#.#", "2,1 To")] + public void HumanizesTerabytes(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, input.Terabytes().Humanize(format)); + } + + [Theory] + [InlineData(0, null, "0 b")] + [InlineData(0, "GB", "0 Go")] + [InlineData(2, null, "2 Go")] + [InlineData(2, "MB", "2048 Mo")] + [InlineData(2.123, "#.##", "2,12 Go")] + public void HumanizesGigabytes(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, input.Gigabytes().Humanize(format)); + } + + [Theory] + [InlineData(0, null, "0 b")] + [InlineData(0, "MB", "0 Mo")] + [InlineData(2, null, "2 Mo")] + [InlineData(2, "KB", "2048 Ko")] + [InlineData(2.123, "#", "2 Mo")] + public void HumanizesMegabytes(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, input.Megabytes().Humanize(format)); + } + + [Theory] + [InlineData(0, null, "0 b")] + [InlineData(0, "KB", "0 Ko")] + [InlineData(2, null, "2 Ko")] + [InlineData(2, "B", "2048 o")] + [InlineData(2.123, "#.####", "2,123 Ko")] + public void HumanizesKilobytes(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, input.Kilobytes().Humanize(format)); + } + + [Theory] + [InlineData(0, null, "0 b")] + [InlineData(0, "#.##", "0 b")] + [InlineData(0, "#.## B", "0 o")] + [InlineData(0, "B", "0 o")] + [InlineData(2, null, "2 o")] + [InlineData(2000, "KB", "1,95 Ko")] + [InlineData(2123, "#.##", "2,07 Ko")] + [InlineData(10000000, "KB", "9765,63 Ko")] + [InlineData(10000000, "#,##0 KB", "9 766 Ko")] + [InlineData(10000000, "#,##0.# KB", "9 765,6 Ko")] + public void HumanizesBytes(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, input.Bytes().Humanize(format)); + } + + [Theory] + [InlineData(0, null, "0 b")] + [InlineData(0, "b", "0 b")] + [InlineData(2, null, "2 b")] + [InlineData(12, "B", "1,5 o")] + [InlineData(10000, "#.# KB", "1,2 Ko")] + public void HumanizesBits(long input, string format, string expectedValue) + { + Assert.Equal(expectedValue, input.Bits().Humanize(format)); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToFullWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToFullWordsTests.cs new file mode 100644 index 000000000..a1bea5985 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToFullWordsTests.cs @@ -0,0 +1,89 @@ +using Humanizer.Bytes; +using Xunit; + +namespace Humanizer.Tests.Localisation.fr.Bytes +{ + [UseCulture("fr-FR")] + public class ToFullWordsTests + { + [Fact] + public void ReturnsSingularBit() + { + Assert.Equal("1 bit", ByteSize.FromBits(1).ToFullWords()); + } + + [Fact] + public void ReturnsPluralBits() + { + Assert.Equal("2 bits", ByteSize.FromBits(2).ToFullWords()); + } + + [Fact] + public void ReturnsSingularByte() + { + Assert.Equal("1 octet", ByteSize.FromBytes(1).ToFullWords()); + } + + [Fact] + public void ReturnsPluralBytes() + { + Assert.Equal("10 octets", ByteSize.FromBytes(10).ToFullWords()); + } + + [Fact] + public void ReturnsSingularKiloByte() + { + Assert.Equal("1 kilooctet", ByteSize.FromKilobytes(1).ToFullWords()); + } + + [Fact] + public void ReturnsPluralKilobytes() + { + Assert.Equal("10 kilooctets", ByteSize.FromKilobytes(10).ToFullWords()); + } + + [Fact] + public void ReturnsSingularMegabyte() + { + Assert.Equal("1 mégaoctet", ByteSize.FromMegabytes(1).ToFullWords()); + } + + [Fact] + public void ReturnsPluralMegabytes() + { + Assert.Equal("10 mégaoctets", ByteSize.FromMegabytes(10).ToFullWords()); + } + + [Fact] + public void ReturnsSingularGigabyte() + { + Assert.Equal("1 gigaoctet", ByteSize.FromGigabytes(1).ToFullWords()); + } + + [Fact] + public void ReturnsPluralGigabytes() + { + Assert.Equal("10 gigaoctets", ByteSize.FromGigabytes(10).ToFullWords()); + } + + [Fact] + public void ReturnsSingularTerabyte() + { + Assert.Equal("1 téraoctet", ByteSize.FromTerabytes(1).ToFullWords()); + } + + [Fact] + public void ReturnsPluralTerabytes() + { + Assert.Equal("10 téraoctets", ByteSize.FromTerabytes(10).ToFullWords()); + } + + [Theory] + [InlineData(229376, "B", "229376 octets")] + [InlineData(229376, "# KB", "224 kilooctets")] + public void ToFullWordsFormatted(double input, string format, string expectedValue) + { + Assert.Equal(expectedValue, ByteSize.FromBytes(input).ToFullWords(format)); + } + } +} diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToStringTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToStringTests.cs new file mode 100644 index 000000000..54deee964 --- /dev/null +++ b/src/Humanizer.Tests.Shared/Localisation/fr/Bytes/ToStringTests.cs @@ -0,0 +1,87 @@ +using Humanizer.Bytes; +using Xunit; + +namespace Humanizer.Tests.Localisation.fr.Bytes +{ + [UseCulture("fr-FR")] + public class ToStringTests + { + [Fact] + public void ReturnsLargestMetricSuffix() + { + Assert.Equal("10,5 Ko", ByteSize.FromKilobytes(10.5).ToString()); + } + + [Fact] + public void ReturnsDefaultNumberFormat() + { + Assert.Equal("10,5 Ko", ByteSize.FromKilobytes(10.5).ToString("KB")); + } + + [Fact] + public void ReturnsProvidedNumberFormat() + { + Assert.Equal("10,1234 Ko", ByteSize.FromKilobytes(10.1234).ToString("#.#### KB")); + } + + [Fact] + public void ReturnsBits() + { + Assert.Equal("10 b", ByteSize.FromBits(10).ToString("##.#### b")); + } + + [Fact] + public void ReturnsBytes() + { + Assert.Equal("10 o", ByteSize.FromBytes(10).ToString("##.#### B")); + } + + [Fact] + public void ReturnsKilobytes() + { + Assert.Equal("10 Ko", ByteSize.FromKilobytes(10).ToString("##.#### KB")); + } + + [Fact] + public void ReturnsMegabytes() + { + Assert.Equal("10 Mo", ByteSize.FromMegabytes(10).ToString("##.#### MB")); + } + + [Fact] + public void ReturnsGigabytes() + { + Assert.Equal("10 Go", ByteSize.FromGigabytes(10).ToString("##.#### GB")); + } + + [Fact] + public void ReturnsTerabytes() + { + Assert.Equal("10 To", ByteSize.FromTerabytes(10).ToString("##.#### TB")); + } + + [Fact] + public void ReturnsSelectedFormat() + { + Assert.Equal("10,0 To", ByteSize.FromTerabytes(10).ToString("0.0 TB")); + } + + [Fact] + public void ReturnsLargestMetricPrefixLargerThanZero() + { + Assert.Equal("512 Ko", ByteSize.FromMegabytes(.5).ToString("#.#")); + } + + [Fact] + public void ReturnsLargestMetricPrefixLargerThanZeroForNegativeValues() + { + Assert.Equal("-512 Ko", ByteSize.FromMegabytes(-.5).ToString("#.#")); + } + + [Fact] + public void ReturnsBytesViaGeneralFormat() + { + Assert.Equal("10 o", $"{ByteSize.FromBytes(10)}"); + } + } +} diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 42d465383..de5d8865f 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -22,7 +22,8 @@ using System; using System.Globalization; -using System.Text.RegularExpressions; +using Humanizer.Configuration; +using Humanizer.Localisation; namespace Humanizer.Bytes { @@ -61,72 +62,74 @@ public struct ByteSize : IComparable, IEquatable, IComparabl public double Gigabytes { get; private set; } public double Terabytes { get; private set; } - public string LargestWholeNumberSymbol + public string LargestWholeNumberSymbol => GetLargestWholeNumberSymbol(); + + public string GetLargestWholeNumberSymbol(IFormatProvider provider = null) { - get - { - // Absolute value is used to deal with negative values - if (Math.Abs(Terabytes) >= 1) - { - return TerabyteSymbol; - } + var cultureFormatter = Configurator.GetFormatter(provider as CultureInfo); - if (Math.Abs(Gigabytes) >= 1) - { - return GigabyteSymbol; - } + // Absolute value is used to deal with negative values + if (Math.Abs(Terabytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Terabyte, Terabytes, toSymbol: true); + } - if (Math.Abs(Megabytes) >= 1) - { - return MegabyteSymbol; - } + if (Math.Abs(Gigabytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Gigabyte, Gigabytes, toSymbol: true); + } - if (Math.Abs(Kilobytes) >= 1) - { - return KilobyteSymbol; - } + if (Math.Abs(Megabytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Megabyte, Megabytes, toSymbol: true); + } - if (Math.Abs(Bytes) >= 1) - { - return ByteSymbol; - } + if (Math.Abs(Kilobytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Kilobyte, Kilobytes, toSymbol: true); + } - return BitSymbol; + if (Math.Abs(Bytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Byte, Bytes, toSymbol: true); } + + return cultureFormatter.DataUnitHumanize(DataUnit.Bit, Bits, toSymbol: true); } - public string LargestWholeNumberFullWord + public string LargestWholeNumberFullWord => GetLargestWholeNumberFullWord(); + + public string GetLargestWholeNumberFullWord(IFormatProvider provider = null) { - get - { - // Absolute value is used to deal with negative values - if (Math.Abs(Terabytes) >= 1) - { - return Terabyte; - } + var cultureFormatter = Configurator.GetFormatter(provider as CultureInfo); - if (Math.Abs(Gigabytes) >= 1) - { - return Gigabyte; - } + // Absolute value is used to deal with negative values + if (Math.Abs(Terabytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Terabyte, Terabytes, toSymbol: false); + } - if (Math.Abs(Megabytes) >= 1) - { - return Megabyte; - } + if (Math.Abs(Gigabytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Gigabyte, Gigabytes, toSymbol: false); + } - if (Math.Abs(Kilobytes) >= 1) - { - return Kilobyte; - } + if (Math.Abs(Megabytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Megabyte, Megabytes, toSymbol: false); + } - if (Math.Abs(Bytes) >= 1) - { - return Byte; - } + if (Math.Abs(Kilobytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Kilobyte, Kilobytes, toSymbol: false); + } - return Bit; + if (Math.Abs(Bytes) >= 1) + { + return cultureFormatter.DataUnitHumanize(DataUnit.Byte, Bytes, toSymbol: false); } + + return cultureFormatter.DataUnitHumanize(DataUnit.Bit, Bits, toSymbol: false); } public double LargestWholeNumberValue @@ -222,7 +225,7 @@ public string ToString(IFormatProvider provider) if (provider == null) provider = CultureInfo.CurrentCulture; - return string.Format("{0} {1}", LargestWholeNumberValue.ToString(provider), LargestWholeNumberSymbol); + return string.Format("{0} {1}", LargestWholeNumberValue.ToString(provider), GetLargestWholeNumberSymbol(provider)); } public string ToString(string format) @@ -231,6 +234,11 @@ public string ToString(string format) } public string ToString(string format, IFormatProvider provider) + { + return ToString(format, provider, toSymbol: true); + } + + private string ToString(string format, IFormatProvider provider, bool toSymbol) { if (format == null) format = "G"; @@ -253,34 +261,42 @@ public string ToString(string format, IFormatProvider provider) bool has(string s) => culture.CompareInfo.IndexOf(format, s, CompareOptions.IgnoreCase) != -1; string output(double n) => n.ToString(format, provider); + var cultureFormatter = Configurator.GetFormatter(provider as CultureInfo); + if (has(TerabyteSymbol)) { + format = format.Replace(TerabyteSymbol, cultureFormatter.DataUnitHumanize(DataUnit.Terabyte, Terabytes, toSymbol)); return output(Terabytes); } if (has(GigabyteSymbol)) { + format = format.Replace(GigabyteSymbol, cultureFormatter.DataUnitHumanize(DataUnit.Gigabyte, Gigabytes, toSymbol)); return output(Gigabytes); } if (has(MegabyteSymbol)) { + format = format.Replace(MegabyteSymbol, cultureFormatter.DataUnitHumanize(DataUnit.Megabyte, Megabytes, toSymbol)); return output(Megabytes); } if (has(KilobyteSymbol)) { + format = format.Replace(KilobyteSymbol, cultureFormatter.DataUnitHumanize(DataUnit.Kilobyte, Kilobytes, toSymbol)); return output(Kilobytes); } // Byte and Bit symbol look must be case-sensitive if (format.IndexOf(ByteSymbol, StringComparison.Ordinal) != -1) { + format = format.Replace(ByteSymbol, cultureFormatter.DataUnitHumanize(DataUnit.Byte, Bytes, toSymbol)); return output(Bytes); } if (format.IndexOf(BitSymbol, StringComparison.Ordinal) != -1) { + format = format.Replace(BitSymbol, cultureFormatter.DataUnitHumanize(DataUnit.Bit, Bits, toSymbol)); return output(Bits); } @@ -290,7 +306,7 @@ public string ToString(string format, IFormatProvider provider) ? "0" : formattedLargeWholeNumberValue; - return string.Format("{0} {1}", formattedLargeWholeNumberValue, LargestWholeNumberSymbol); + return string.Format("{0} {1}", formattedLargeWholeNumberValue, toSymbol ? GetLargestWholeNumberSymbol(provider) : GetLargestWholeNumberFullWord(provider)); } /// @@ -299,12 +315,9 @@ public string ToString(string format, IFormatProvider provider) /// tera) used is the largest metric prefix such that the corresponding /// value is greater than or equal to one. /// - public string ToFullWords() + public string ToFullWords(string format = null, IFormatProvider provider = null) { - var byteSizeAsFullWords = string.Format("{0} {1}", LargestWholeNumberValue, LargestWholeNumberFullWord); - - // If there is more than one unit, make the word plural - return LargestWholeNumberValue > 1 ? byteSizeAsFullWords + "s" : byteSizeAsFullWords; + return ToString(format, provider, toSymbol: false); } public override bool Equals(object value) @@ -471,8 +484,8 @@ public static bool TryParse(string s, out ByteSize result) // Acquiring culture specific decimal separator - var decSep = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); - + var decSep = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); + // Pick first non-digit number for (num = 0; num < s.Length; num++) { diff --git a/src/Humanizer/Localisation/DataUnit.cs b/src/Humanizer/Localisation/DataUnit.cs new file mode 100644 index 000000000..c615ceecf --- /dev/null +++ b/src/Humanizer/Localisation/DataUnit.cs @@ -0,0 +1,15 @@ +namespace Humanizer.Localisation +{ + /// + /// Units of data + /// + public enum DataUnit + { + Bit, + Byte, + Kilobyte, + Megabyte, + Gigabyte, + Terabyte, + } +} diff --git a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs index 2efe8d73b..d3b410990 100644 --- a/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/DefaultFormatter.cs @@ -71,6 +71,18 @@ public virtual string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords return GetResourceForTimeSpan(timeUnit, unit, toWords); } + /// + public virtual string DataUnitHumanize(DataUnit dataUnit, double count, bool toSymbol = true) + { + var resourceKey = toSymbol ? $"DataUnit_{dataUnit}Symbol" : $"DataUnit_{dataUnit}"; + var resourceValue = Format(resourceKey); + + if (!toSymbol && count > 1) + resourceValue += 's'; + + return resourceValue; + } + private string GetResourceForDate(TimeUnit unit, Tense timeUnitTense, int count) { var resourceKey = ResourceKeys.DateHumanize.GetResourceKey(unit, timeUnitTense: timeUnitTense, count: count); diff --git a/src/Humanizer/Localisation/Formatters/IFormatter.cs b/src/Humanizer/Localisation/Formatters/IFormatter.cs index 398fbd4fd..64e06c3dd 100644 --- a/src/Humanizer/Localisation/Formatters/IFormatter.cs +++ b/src/Humanizer/Localisation/Formatters/IFormatter.cs @@ -42,5 +42,14 @@ public interface IFormatter /// /// string TimeSpanHumanize(TimeUnit timeUnit, int unit, bool toWords = false); + + /// + /// Returns the string representation of the provided DataUnit, either as a symbol or full word + /// + /// Data unit + /// Number of said units, to adjust for singular/plural forms + /// Indicates whether the data unit should be expressed as symbol or full word + /// String representation of the provided DataUnit + string DataUnitHumanize(DataUnit dataUnit, double count, bool toSymbol = true); } } diff --git a/src/Humanizer/Properties/Resources.fr.resx b/src/Humanizer/Properties/Resources.fr.resx index f55793bbe..5af112816 100644 --- a/src/Humanizer/Properties/Resources.fr.resx +++ b/src/Humanizer/Properties/Resources.fr.resx @@ -300,4 +300,40 @@ un an + + bit + + + b + + + octet + + + o + + + kilooctet + + + Ko + + + mégaoctet + + + Mo + + + gigaoctet + + + Go + + + téraoctet + + + To + \ No newline at end of file diff --git a/src/Humanizer/Properties/Resources.resx b/src/Humanizer/Properties/Resources.resx index 678f0d5cf..8683674b6 100644 --- a/src/Humanizer/Properties/Resources.resx +++ b/src/Humanizer/Properties/Resources.resx @@ -675,4 +675,40 @@ NNW + + bit + + + b + + + byte + + + B + + + kilobyte + + + KB + + + megabyte + + + MB + + + gigabyte + + + GB + + + terabyte + + + TB + \ No newline at end of file From 75f96e330fc614c0a4095a2aa430775914da30df Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Sun, 2 May 2021 00:31:43 -0500 Subject: [PATCH 111/126] Add ByteSize Parse/TryParse with IFormatProvider --- .../Bytes/ParsingTests.cs | 31 ++++++++++++++ src/Humanizer/Bytes/ByteSize.cs | 42 +++++++++++++++---- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs index 844c53f3e..05a4fa4a1 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs @@ -21,6 +21,7 @@ //THE SOFTWARE. using System; +using System.Globalization; using Humanizer.Bytes; using Xunit; @@ -45,6 +46,36 @@ public void TryParse() Assert.Equal(ByteSize.FromKilobytes(1020), resultByteSize); } + [Theory] + [InlineData("2000.01KB", "")] // Invariant + [InlineData("2000.01KB", "en")] + [InlineData("2000,01KB", "de")] + public void TryParseWithCultureInfo(string value, string cultureName) + { + var culture = new CultureInfo(cultureName); + + Assert.True(ByteSize.TryParse(value, culture, out var resultByteSize)); + Assert.Equal(ByteSize.FromKilobytes(2000.01), resultByteSize); + + Assert.Equal(resultByteSize, ByteSize.Parse(value, culture)); + } + + [Fact] + public void TryParseWithNumberFormatInfo() + { + var numberFormat = new NumberFormatInfo + { + NumberDecimalSeparator = "_", + }; + + var value = "2000_01KB"; + + Assert.True(ByteSize.TryParse(value, numberFormat, out var resultByteSize)); + Assert.Equal(ByteSize.FromKilobytes(2000.01), resultByteSize); + + Assert.Equal(resultByteSize, ByteSize.Parse(value, numberFormat)); + } + [Fact] public void ParseDecimalMegabytes() { diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index de5d8865f..ca75fbe0c 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -22,9 +22,12 @@ using System; using System.Globalization; +using System.Linq; using Humanizer.Configuration; using Humanizer.Localisation; +using static System.Globalization.NumberStyles; + namespace Humanizer.Bytes { /// @@ -466,6 +469,11 @@ public ByteSize Subtract(ByteSize bs) } public static bool TryParse(string s, out ByteSize result) + { + return TryParse(s, null, out result); + } + + public static bool TryParse(string s, IFormatProvider formatProvider, out ByteSize result) { // Arg checking if (string.IsNullOrWhiteSpace(s)) @@ -473,6 +481,15 @@ public static bool TryParse(string s, out ByteSize result) throw new ArgumentNullException(nameof(s), "String is null or whitespace"); } + // Acquiring culture-specific parsing info + var numberFormat = GetNumberFormatInfo(formatProvider); + + const NumberStyles numberStyles = AllowDecimalPoint; + var numberSpecialChars = new[] + { + Convert.ToChar(numberFormat.NumberDecimalSeparator), + }; + // Setup the result result = new ByteSize(); @@ -482,14 +499,10 @@ public static bool TryParse(string s, out ByteSize result) int num; var found = false; - // Acquiring culture specific decimal separator - - var decSep = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); - // Pick first non-digit number for (num = 0; num < s.Length; num++) { - if (!(char.IsDigit(s[num]) || s[num] == decSep)) + if (!(char.IsDigit(s[num]) || numberSpecialChars.Contains(s[num]))) { found = true; break; @@ -508,7 +521,7 @@ public static bool TryParse(string s, out ByteSize result) var sizePart = s.Substring(lastNumber, s.Length - lastNumber).Trim(); // Get the numeric part - if (!double.TryParse(numberPart, out var number)) + if (!double.TryParse(numberPart, numberStyles, formatProvider, out var number)) { return false; } @@ -552,9 +565,24 @@ public static bool TryParse(string s, out ByteSize result) return true; } + private static NumberFormatInfo GetNumberFormatInfo(IFormatProvider formatProvider) + { + if (formatProvider is NumberFormatInfo numberFormat) + return numberFormat; + + var culture = formatProvider as CultureInfo ?? CultureInfo.CurrentCulture; + + return culture.NumberFormat; + } + public static ByteSize Parse(string s) { - if (TryParse(s, out var result)) + return Parse(s, null); + } + + public static ByteSize Parse(string s, IFormatProvider formatProvider) + { + if (TryParse(s, formatProvider, out var result)) { return result; } From 9159cbe469bf24a123494c750369f98ec07c3652 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Sun, 2 May 2021 01:21:12 -0500 Subject: [PATCH 112/126] Parse group separator --- src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs | 6 +++++- src/Humanizer/Bytes/ByteSize.cs | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs index 05a4fa4a1..b4478ba05 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs @@ -48,8 +48,11 @@ public void TryParse() [Theory] [InlineData("2000.01KB", "")] // Invariant + [InlineData("2,000.01KB", "")] [InlineData("2000.01KB", "en")] + [InlineData("2,000.01KB", "en")] [InlineData("2000,01KB", "de")] + [InlineData("2.000,01KB", "de")] public void TryParseWithCultureInfo(string value, string cultureName) { var culture = new CultureInfo(cultureName); @@ -66,9 +69,10 @@ public void TryParseWithNumberFormatInfo() var numberFormat = new NumberFormatInfo { NumberDecimalSeparator = "_", + NumberGroupSeparator = ";", }; - var value = "2000_01KB"; + var value = "2;000_01KB"; Assert.True(ByteSize.TryParse(value, numberFormat, out var resultByteSize)); Assert.Equal(ByteSize.FromKilobytes(2000.01), resultByteSize); diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index ca75fbe0c..dc54d5ace 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -484,10 +484,11 @@ public static bool TryParse(string s, IFormatProvider formatProvider, out ByteSi // Acquiring culture-specific parsing info var numberFormat = GetNumberFormatInfo(formatProvider); - const NumberStyles numberStyles = AllowDecimalPoint; + const NumberStyles numberStyles = AllowDecimalPoint | AllowThousands; var numberSpecialChars = new[] { Convert.ToChar(numberFormat.NumberDecimalSeparator), + Convert.ToChar(numberFormat.NumberGroupSeparator), }; // Setup the result From d4972e020bb759328a0969ee67da162303123bc3 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Sun, 2 May 2021 01:43:34 -0500 Subject: [PATCH 113/126] Fail parse for invalid suffix; unify tests --- .../Bytes/ParsingTests.cs | 23 +++++++++---------- src/Humanizer/Bytes/ByteSize.cs | 3 +++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs index b4478ba05..eec480745 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs @@ -37,6 +37,12 @@ public void Parse() Assert.Equal(ByteSize.FromKilobytes(1020), ByteSize.Parse("1020KB")); } + [Fact] + public void TryParseThrowsOnNull() + { + Assert.Throws(() => { ByteSize.TryParse(null, out var result); }); + } + [Fact] public void TryParse() { @@ -89,13 +95,18 @@ public void ParseDecimalMegabytes() [Theory] [InlineData("Unexpected Value")] [InlineData("1000")] + [InlineData(" 1000 ")] [InlineData("KB")] + [InlineData("1000.5b")] // Partial bits + [InlineData("1000KBB")] // Bad suffix public void TryParseReturnsFalseOnBadValue(string input) { var resultBool = ByteSize.TryParse(input, out var resultByteSize); Assert.False(resultBool); Assert.Equal(new ByteSize(), resultByteSize); + + Assert.Throws(() => { ByteSize.Parse(input); }); } [Fact] @@ -104,18 +115,6 @@ public void TryParseWorksWithLotsOfSpaces() Assert.Equal(ByteSize.FromKilobytes(100), ByteSize.Parse(" 100 KB ")); } - [Fact] - public void ParseThrowsOnPartialBits() - { - Assert.Throws(() => { ByteSize.Parse("10.5b"); }); - } - - [Fact] - public void ParseThrowsOnInvalid() - { - Assert.Throws(() => { ByteSize.Parse("Unexpected Value"); }); - } - [Fact] public void ParseThrowsOnNull() { diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index dc54d5ace..77d14d0da 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -561,6 +561,9 @@ public static bool TryParse(string s, IFormatProvider formatProvider, out ByteSi case TerabyteSymbol: result = FromTerabytes(number); break; + + default: + return false; } return true; From 94694afd58056ae924c1ae5a8064c292b4f39185 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Sun, 2 May 2021 09:45:30 -0500 Subject: [PATCH 114/126] Adjust ByteSize Parse/TryParse exceptions --- src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs | 8 ++++++-- src/Humanizer/Bytes/ByteSize.cs | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs index eec480745..9c10f7acb 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs @@ -38,9 +38,10 @@ public void Parse() } [Fact] - public void TryParseThrowsOnNull() + public void TryParseReturnsFalseOnNull() { - Assert.Throws(() => { ByteSize.TryParse(null, out var result); }); + Assert.False(ByteSize.TryParse(null, out var result)); + Assert.Equal(default, result); } [Fact] @@ -93,6 +94,9 @@ public void ParseDecimalMegabytes() } [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("\t")] [InlineData("Unexpected Value")] [InlineData("1000")] [InlineData(" 1000 ")] diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 77d14d0da..0cab8a7d7 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -478,7 +478,8 @@ public static bool TryParse(string s, IFormatProvider formatProvider, out ByteSi // Arg checking if (string.IsNullOrWhiteSpace(s)) { - throw new ArgumentNullException(nameof(s), "String is null or whitespace"); + result = default; + return false; } // Acquiring culture-specific parsing info @@ -586,6 +587,11 @@ public static ByteSize Parse(string s) public static ByteSize Parse(string s, IFormatProvider formatProvider) { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (TryParse(s, formatProvider, out var result)) { return result; From 8db043d96e17236b1bd0cdc96a7de3b557117bdf Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Mon, 3 May 2021 13:38:26 -0500 Subject: [PATCH 115/126] Parse positive/negative sign --- src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs | 8 ++++++-- src/Humanizer/Bytes/ByteSize.cs | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs index 9c10f7acb..d1b489c75 100644 --- a/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs +++ b/src/Humanizer.Tests.Shared/Bytes/ParsingTests.cs @@ -56,10 +56,13 @@ public void TryParse() [Theory] [InlineData("2000.01KB", "")] // Invariant [InlineData("2,000.01KB", "")] + [InlineData("+2000.01KB", "")] [InlineData("2000.01KB", "en")] [InlineData("2,000.01KB", "en")] + [InlineData("+2000.01KB", "en")] [InlineData("2000,01KB", "de")] [InlineData("2.000,01KB", "de")] + [InlineData("+2000,01KB", "de")] public void TryParseWithCultureInfo(string value, string cultureName) { var culture = new CultureInfo(cultureName); @@ -77,12 +80,13 @@ public void TryParseWithNumberFormatInfo() { NumberDecimalSeparator = "_", NumberGroupSeparator = ";", + NegativeSign = "−", // proper minus, not hyphen-minus }; - var value = "2;000_01KB"; + var value = "−2;000_01KB"; Assert.True(ByteSize.TryParse(value, numberFormat, out var resultByteSize)); - Assert.Equal(ByteSize.FromKilobytes(2000.01), resultByteSize); + Assert.Equal(ByteSize.FromKilobytes(-2000.01), resultByteSize); Assert.Equal(resultByteSize, ByteSize.Parse(value, numberFormat)); } diff --git a/src/Humanizer/Bytes/ByteSize.cs b/src/Humanizer/Bytes/ByteSize.cs index 0cab8a7d7..22517c0b9 100644 --- a/src/Humanizer/Bytes/ByteSize.cs +++ b/src/Humanizer/Bytes/ByteSize.cs @@ -485,11 +485,13 @@ public static bool TryParse(string s, IFormatProvider formatProvider, out ByteSi // Acquiring culture-specific parsing info var numberFormat = GetNumberFormatInfo(formatProvider); - const NumberStyles numberStyles = AllowDecimalPoint | AllowThousands; + const NumberStyles numberStyles = AllowDecimalPoint | AllowThousands | AllowLeadingSign; var numberSpecialChars = new[] { Convert.ToChar(numberFormat.NumberDecimalSeparator), Convert.ToChar(numberFormat.NumberGroupSeparator), + Convert.ToChar(numberFormat.PositiveSign), + Convert.ToChar(numberFormat.NegativeSign), }; // Setup the result From 92f76a4d36b44f22caf51e8066b787288595ad36 Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Wed, 5 May 2021 09:17:35 -0400 Subject: [PATCH 116/126] Add try/catch around formatter registration to handle OS's that don't support a particular culture. --- src/Humanizer/Configuration/FormatterRegistry.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Humanizer/Configuration/FormatterRegistry.cs b/src/Humanizer/Configuration/FormatterRegistry.cs index da6839ad4..9970c6a21 100644 --- a/src/Humanizer/Configuration/FormatterRegistry.cs +++ b/src/Humanizer/Configuration/FormatterRegistry.cs @@ -1,4 +1,6 @@ -using Humanizer.Localisation.Formatters; +using System.Globalization; + +using Humanizer.Localisation.Formatters; namespace Humanizer.Configuration { @@ -56,7 +58,14 @@ public FormatterRegistry() : base(new DefaultFormatter("en-US")) private void RegisterDefaultFormatter(string localeCode) { - Register(localeCode, new DefaultFormatter(localeCode)); + try + { + Register(localeCode, new DefaultFormatter(localeCode)); + } + catch (CultureNotFoundException) + { + // Some OS's may not support the particular culture. Not much we can do for those. + } } private void RegisterCzechSlovakPolishFormatter(string localeCode) From cc5d53d29e186d865f3d5cdfedadd36ed7332ab3 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Sun, 2 May 2021 21:55:09 -0500 Subject: [PATCH 117/126] Typo --- src/Humanizer/Transformer/IStringTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer/Transformer/IStringTransformer.cs b/src/Humanizer/Transformer/IStringTransformer.cs index 22ad18cda..ba35bc49f 100644 --- a/src/Humanizer/Transformer/IStringTransformer.cs +++ b/src/Humanizer/Transformer/IStringTransformer.cs @@ -1,7 +1,7 @@ namespace Humanizer { /// - /// Can tranform a string + /// Can transform a string /// public interface IStringTransformer { From f8e2df870540fff28efd3f578181b9823886ef38 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Sun, 2 May 2021 21:55:15 -0500 Subject: [PATCH 118/126] Transform with Culture via ICulturedStringTransformer --- .../Transformer/ICulturedStringTransformer.cs | 18 +++++++++++++++ src/Humanizer/Transformer/To.cs | 23 +++++++++++++++---- src/Humanizer/Transformer/ToLowerCase.cs | 11 +++++++-- src/Humanizer/Transformer/ToSentenceCase.cs | 15 +++++++++--- src/Humanizer/Transformer/ToTitleCase.cs | 18 +++++++++++---- src/Humanizer/Transformer/ToUpperCase.cs | 13 +++++++++-- 6 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 src/Humanizer/Transformer/ICulturedStringTransformer.cs diff --git a/src/Humanizer/Transformer/ICulturedStringTransformer.cs b/src/Humanizer/Transformer/ICulturedStringTransformer.cs new file mode 100644 index 000000000..40763188f --- /dev/null +++ b/src/Humanizer/Transformer/ICulturedStringTransformer.cs @@ -0,0 +1,18 @@ +using System.Globalization; + +namespace Humanizer +{ + /// + /// Can transform a string with the given culture + /// + public interface ICulturedStringTransformer : IStringTransformer + { + /// + /// Transform the input + /// + /// String to be transformed + /// The culture + /// + string Transform(string input, CultureInfo culture); + } +} diff --git a/src/Humanizer/Transformer/To.cs b/src/Humanizer/Transformer/To.cs index ad24f88f6..b5631ab54 100644 --- a/src/Humanizer/Transformer/To.cs +++ b/src/Humanizer/Transformer/To.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Globalization; +using System.Linq; namespace Humanizer { @@ -18,13 +19,25 @@ public static string Transform(this string input, params IStringTransformer[] tr return transformers.Aggregate(input, (current, stringTransformer) => stringTransformer.Transform(current)); } + /// + /// Transforms a string using the provided transformers. Transformations are applied in the provided order. + /// + /// + /// + /// + /// + public static string Transform(this string input, CultureInfo culture, params ICulturedStringTransformer[] transformers) + { + return transformers.Aggregate(input, (current, stringTransformer) => stringTransformer.Transform(current, culture)); + } + /// /// Changes string to title case /// /// /// "INvalid caSEs arE corrected" -> "Invalid Cases Are Corrected" /// - public static IStringTransformer TitleCase + public static ICulturedStringTransformer TitleCase { get { @@ -38,7 +51,7 @@ public static IStringTransformer TitleCase /// /// "Sentence casing" -> "sentence casing" /// - public static IStringTransformer LowerCase + public static ICulturedStringTransformer LowerCase { get { @@ -52,7 +65,7 @@ public static IStringTransformer LowerCase /// /// "lower case statement" -> "LOWER CASE STATEMENT" /// - public static IStringTransformer UpperCase + public static ICulturedStringTransformer UpperCase { get { @@ -66,7 +79,7 @@ public static IStringTransformer UpperCase /// /// "lower case statement" -> "Lower case statement" /// - public static IStringTransformer SentenceCase + public static ICulturedStringTransformer SentenceCase { get { diff --git a/src/Humanizer/Transformer/ToLowerCase.cs b/src/Humanizer/Transformer/ToLowerCase.cs index c6ba8043f..c44605651 100644 --- a/src/Humanizer/Transformer/ToLowerCase.cs +++ b/src/Humanizer/Transformer/ToLowerCase.cs @@ -2,11 +2,18 @@ namespace Humanizer { - internal class ToLowerCase : IStringTransformer + internal class ToLowerCase : ICulturedStringTransformer { public string Transform(string input) { - return CultureInfo.CurrentCulture.TextInfo.ToLower(input); + return Transform(input, null); + } + + public string Transform(string input, CultureInfo culture) + { + culture ??= CultureInfo.CurrentCulture; + + return culture.TextInfo.ToLower(input); } } } \ No newline at end of file diff --git a/src/Humanizer/Transformer/ToSentenceCase.cs b/src/Humanizer/Transformer/ToSentenceCase.cs index 1794a518d..927962a50 100644 --- a/src/Humanizer/Transformer/ToSentenceCase.cs +++ b/src/Humanizer/Transformer/ToSentenceCase.cs @@ -1,15 +1,24 @@ +using System.Globalization; + namespace Humanizer { - internal class ToSentenceCase : IStringTransformer + internal class ToSentenceCase : ICulturedStringTransformer { public string Transform(string input) { + return Transform(input, null); + } + + public string Transform(string input, CultureInfo culture) + { + culture ??= CultureInfo.CurrentCulture; + if (input.Length >= 1) { - return string.Concat(input.Substring(0, 1).ToUpper(), input.Substring(1)); + return culture.TextInfo.ToUpper(input[0]) + input.Substring(1); } - return input.ToUpper(); + return culture.TextInfo.ToUpper(input); } } } \ No newline at end of file diff --git a/src/Humanizer/Transformer/ToTitleCase.cs b/src/Humanizer/Transformer/ToTitleCase.cs index 92111d4dc..f0d34d438 100644 --- a/src/Humanizer/Transformer/ToTitleCase.cs +++ b/src/Humanizer/Transformer/ToTitleCase.cs @@ -1,19 +1,27 @@ -using System.Linq; +using System.Globalization; +using System.Linq; using System.Text.RegularExpressions; namespace Humanizer { - internal class ToTitleCase : IStringTransformer + internal class ToTitleCase : ICulturedStringTransformer { public string Transform(string input) { + return Transform(input, null); + } + + public string Transform(string input, CultureInfo culture) + { + culture ??= CultureInfo.CurrentCulture; + var result = input; var matches = Regex.Matches(input, @"(\w|[^\u0000-\u007F])+'?\w*"); foreach (Match word in matches) { if (!AllCapitals(word.Value)) { - result = ReplaceWithTitleCase(word, result); + result = ReplaceWithTitleCase(word, result, culture); } } @@ -25,10 +33,10 @@ private static bool AllCapitals(string input) return input.ToCharArray().All(char.IsUpper); } - private static string ReplaceWithTitleCase(Match word, string source) + private static string ReplaceWithTitleCase(Match word, string source, CultureInfo culture) { var wordToConvert = word.Value; - var replacement = char.ToUpper(wordToConvert[0]) + wordToConvert.Remove(0, 1).ToLower(); + var replacement = culture.TextInfo.ToUpper(wordToConvert[0]) + culture.TextInfo.ToLower(wordToConvert.Remove(0, 1)); return source.Substring(0, word.Index) + replacement + source.Substring(word.Index + word.Length); } } diff --git a/src/Humanizer/Transformer/ToUpperCase.cs b/src/Humanizer/Transformer/ToUpperCase.cs index d904bfa27..339bb53bb 100644 --- a/src/Humanizer/Transformer/ToUpperCase.cs +++ b/src/Humanizer/Transformer/ToUpperCase.cs @@ -1,10 +1,19 @@ +using System.Globalization; + namespace Humanizer { - internal class ToUpperCase : IStringTransformer + internal class ToUpperCase : ICulturedStringTransformer { public string Transform(string input) { - return input.ToUpper(); + return Transform(input, null); + } + + public string Transform(string input, CultureInfo culture) + { + culture ??= CultureInfo.CurrentCulture; + + return culture.TextInfo.ToUpper(input); } } } \ No newline at end of file From 7519adf01eaa7eeb6652a7f277c8f826225cad1b Mon Sep 17 00:00:00 2001 From: Claire Novotny Date: Wed, 5 May 2021 16:00:24 -0400 Subject: [PATCH 119/126] Update version.json --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 552b36878..9e56de81a 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "2.9", + "version": "2.10", "publicReleaseRefSpec": [ "^refs/heads/main$", // we release out of main "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N From b8e254d2afe4950642516fedc2d8be622616e036 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Fri, 14 May 2021 21:41:40 -0400 Subject: [PATCH 120/126] Add support for large numbers in French up to 10^18 level (#989) Note that French uses the "long scale" rather than the "short scale" for large numbers. Fixes #988 --- .../Localisation/fr/NumberToWordsTests.cs | 29 +++++++++++++++++-- .../Localisation/fr/TimeSpanHumanizeTests.cs | 1 - .../FrenchBelgianNumberToWordsConverter.cs | 6 ++-- .../FrenchNumberToWordsConverter.cs | 6 ++-- .../FrenchNumberToWordsConverterBase.cs | 28 ++++++++---------- 5 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/NumberToWordsTests.cs index 1bf110a0b..9207562d0 100644 --- a/src/Humanizer.Tests.Shared/Localisation/fr/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/fr/NumberToWordsTests.cs @@ -64,7 +64,7 @@ public class NumberToWordsTests [InlineData(80080080, "quatre-vingts millions quatre-vingt mille quatre-vingts")] [InlineData(200200200, "deux cents millions deux cent mille deux cents")] [InlineData(200200202, "deux cents millions deux cent mille deux cent deux")] - public void ToWords(int number, string expected) + public void ToWordsInt(int number, string expected) { Assert.Equal(expected, number.ToWords()); } @@ -98,11 +98,36 @@ public void ToWords(int number, string expected) [InlineData(10121, "dix mille cent vingt et un", GrammaticalGender.Masculine)] [InlineData(81000, "quatre-vingt-un mille", GrammaticalGender.Feminine)] [InlineData(81000, "quatre-vingt-un mille", GrammaticalGender.Masculine)] - public void ToWordsWithGender(int number, string expected, GrammaticalGender gender) + public void ToWordsIntWithGender(int number, string expected, GrammaticalGender gender) { Assert.Equal(expected, number.ToWords(gender)); } + [Theory] + [InlineData(1L, "un")] + [InlineData(11L, "onze")] + [InlineData(111L, "cent onze")] + [InlineData(1111L, "mille cent onze")] + [InlineData(11111L, "onze mille cent onze")] + [InlineData(111111L, "cent onze mille cent onze")] + [InlineData(1111111L, "un million cent onze mille cent onze")] + [InlineData(11111111L, "onze millions cent onze mille cent onze")] + [InlineData(111111111L, "cent onze millions cent onze mille cent onze")] + [InlineData(1111111111L, "un milliard cent onze millions cent onze mille cent onze")] + [InlineData(11111111111L, "onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(111111111111L, "cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(1111111111111L, "un billion cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(11111111111111L, "onze billions cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(111111111111111L, "cent onze billions cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(1111111111111111L, "un billiard cent onze billions cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(11111111111111111L, "onze billiards cent onze billions cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(111111111111111111L, "cent onze billiards cent onze billions cent onze milliards cent onze millions cent onze mille cent onze")] + [InlineData(1111111111111111111L, "un trillion cent onze billiards cent onze billions cent onze milliards cent onze millions cent onze mille cent onze")] + public void ToWordsLong(long number, string expected) + { + Assert.Equal(expected, number.ToWords()); + } + [Theory] [InlineData(0, "zérotième")] [InlineData(1, "premier")] diff --git a/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs b/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs index 53f29ef2f..7d3cfb3b8 100644 --- a/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/fr/TimeSpanHumanizeTests.cs @@ -1,6 +1,5 @@ using System; using Humanizer.Localisation; - using Xunit; namespace Humanizer.Tests.Localisation.fr diff --git a/src/Humanizer/Localisation/NumberToWords/FrenchBelgianNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/FrenchBelgianNumberToWordsConverter.cs index 25cf63d23..95279b2ba 100644 --- a/src/Humanizer/Localisation/NumberToWords/FrenchBelgianNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/FrenchBelgianNumberToWordsConverter.cs @@ -4,7 +4,7 @@ namespace Humanizer.Localisation.NumberToWords { internal class FrenchBelgianNumberToWordsConverter : FrenchNumberToWordsConverterBase { - protected override void CollectPartsUnderAHundred(ICollection parts, ref int number, GrammaticalGender gender, bool pluralize) + protected override void CollectPartsUnderAHundred(ICollection parts, ref long number, GrammaticalGender gender, bool pluralize) { if (number == 80) { @@ -20,7 +20,7 @@ protected override void CollectPartsUnderAHundred(ICollection parts, ref } } - protected override string GetTens(int tens) + protected override string GetTens(long tens) { if (tens == 8) { @@ -30,4 +30,4 @@ protected override string GetTens(int tens) return base.GetTens(tens); } } -} \ No newline at end of file +} diff --git a/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverter.cs index 45eac1f53..105053536 100644 --- a/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverter.cs @@ -4,7 +4,7 @@ namespace Humanizer.Localisation.NumberToWords { internal class FrenchNumberToWordsConverter : FrenchNumberToWordsConverterBase { - protected override void CollectPartsUnderAHundred(ICollection parts, ref int number, GrammaticalGender gender, bool pluralize) + protected override void CollectPartsUnderAHundred(ICollection parts, ref long number, GrammaticalGender gender, bool pluralize) { if (number == 71) { @@ -27,7 +27,7 @@ protected override void CollectPartsUnderAHundred(ICollection parts, ref } } - protected override string GetTens(int tens) + protected override string GetTens(long tens) { if (tens == 8) { @@ -37,4 +37,4 @@ protected override string GetTens(int tens) return base.GetTens(tens); } } -} \ No newline at end of file +} diff --git a/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs b/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs index 8ac9832f3..63b3ef31a 100644 --- a/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs +++ b/src/Humanizer/Localisation/NumberToWords/FrenchNumberToWordsConverterBase.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace Humanizer.Localisation.NumberToWords { @@ -8,14 +7,8 @@ internal abstract class FrenchNumberToWordsConverterBase : GenderedNumberToWords 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", "septante", "octante", "nonante" }; - public override string Convert(long input, GrammaticalGender gender, bool addAnd = true) + public override string Convert(long number, GrammaticalGender gender, bool addAnd = true) { - if (input > Int32.MaxValue || input < Int32.MinValue) - { - throw new NotImplementedException(); - } - var number = (int)input; - if (number == 0) { return UnitsMap[0]; @@ -29,6 +22,9 @@ public override string Convert(long input, GrammaticalGender gender, bool addAnd number = -number; } + CollectParts(parts, ref number, 1000000000000000000, "trillion"); + CollectParts(parts, ref number, 1000000000000000, "billiard"); + CollectParts(parts, ref number, 1000000000000, "billion"); CollectParts(parts, ref number, 1000000000, "milliard"); CollectParts(parts, ref number, 1000000, "million"); CollectThousands(parts, ref number, 1000, "mille"); @@ -75,7 +71,7 @@ public override string ConvertToOrdinal(int number, GrammaticalGender gender) return convertedNumber; } - protected static string GetUnits(int number, GrammaticalGender gender) + protected static string GetUnits(long number, GrammaticalGender gender) { if (number == 1 && gender == GrammaticalGender.Feminine) { @@ -85,7 +81,7 @@ protected static string GetUnits(int number, GrammaticalGender gender) return UnitsMap[number]; } - private static void CollectHundreds(ICollection parts, ref int number, int d, string form, bool pluralize) + private static void CollectHundreds(ICollection parts, ref long number, long d, string form, bool pluralize) { if (number < d) { @@ -113,7 +109,7 @@ private static void CollectHundreds(ICollection parts, ref int number, i number %= d; } - private void CollectParts(ICollection parts, ref int number, int d, string form) + private void CollectParts(ICollection parts, ref long number, long d, string form) { if (number < d) { @@ -136,7 +132,7 @@ private void CollectParts(ICollection parts, ref int number, int d, stri number %= d; } - private void CollectPartsUnderAThousand(ICollection parts, int number, GrammaticalGender gender, bool pluralize) + private void CollectPartsUnderAThousand(ICollection parts, long number, GrammaticalGender gender, bool pluralize) { CollectHundreds(parts, ref number, 100, "cent", pluralize); @@ -146,7 +142,7 @@ private void CollectPartsUnderAThousand(ICollection parts, int number, G } } - private void CollectThousands(ICollection parts, ref int number, int d, string form) + private void CollectThousands(ICollection parts, ref long number, int d, string form) { if (number < d) { @@ -164,7 +160,7 @@ private void CollectThousands(ICollection parts, ref int number, int d, number %= d; } - protected virtual void CollectPartsUnderAHundred(ICollection parts, ref int number, GrammaticalGender gender, bool pluralize) + protected virtual void CollectPartsUnderAHundred(ICollection parts, ref long number, GrammaticalGender gender, bool pluralize) { if (number < 20) { @@ -191,7 +187,7 @@ protected virtual void CollectPartsUnderAHundred(ICollection parts, ref } } - protected virtual string GetTens(int tens) + protected virtual string GetTens(long tens) { return TensMap[tens]; } From 30562ba699cdb282d859e71054115261b4d222b1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 15 May 2021 12:52:00 +0000 Subject: [PATCH 121/126] Upgrade to GitHub-native Dependabot (#1058) --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..aec207e43 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: nuget + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From 681194adce2a903c01fc76a85e593ced9c1a405b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 May 2021 03:17:42 +0000 Subject: [PATCH 122/126] Bump Nerdbank.GitVersioning from 3.4.194 to 3.4.203 (#1071) --- src/Directory.build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index d6a14c226..6eec80999 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -6,7 +6,7 @@ - + From 79f67a58605b3bc0c96900cb3fd844130e056817 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 29 May 2021 03:24:15 +0000 Subject: [PATCH 123/126] Bump Microsoft.NET.Test.Sdk from 16.9.4 to 16.10.0 (#1072) --- src/Humanizer.Tests/Humanizer.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index c6cf93d93..73497c26f 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -11,7 +11,7 @@ - + From 7b8628107e1721148b4031325f60bda6a8ac78f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20Top=C3=A7u?= Date: Mon, 31 May 2021 02:16:12 +0300 Subject: [PATCH 124/126] Add TurkishNumberToWordConverter support for values greater than 2147483647 (#1070) --- .../Localisation/tr/NumberToWordsTests.cs | 3 ++- .../TurkishNumberToWordConverter.cs | 27 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Humanizer.Tests.Shared/Localisation/tr/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/tr/NumberToWordsTests.cs index e30aa3f0c..891c6cc46 100644 --- a/src/Humanizer.Tests.Shared/Localisation/tr/NumberToWordsTests.cs +++ b/src/Humanizer.Tests.Shared/Localisation/tr/NumberToWordsTests.cs @@ -17,7 +17,8 @@ public class NumberToWordsTests [InlineData("üç bin beş yüz bir", 3501)] [InlineData("bir milyon bir", 1000001)] [InlineData("eksi bir milyon üç yüz kırk altı bin yedi yüz on bir", -1346711)] - public void ToWords(string expected, int number) + [InlineData("dokuz kentilyon iki yüz yirmi üç katrilyon üç yüz yetmiş iki trilyon otuz altı milyar sekiz yüz elli dört milyon yedi yüz yetmiş beş bin sekiz yüz yedi", 9223372036854775807)] + public void ToWords(string expected, long number) { Assert.Equal(expected, number.ToWords()); } diff --git a/src/Humanizer/Localisation/NumberToWords/TurkishNumberToWordConverter.cs b/src/Humanizer/Localisation/NumberToWords/TurkishNumberToWordConverter.cs index 908cb92bd..e36d3c942 100644 --- a/src/Humanizer/Localisation/NumberToWords/TurkishNumberToWordConverter.cs +++ b/src/Humanizer/Localisation/NumberToWords/TurkishNumberToWordConverter.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace Humanizer.Localisation.NumberToWords { @@ -22,11 +21,7 @@ internal class TurkishNumberToWordConverter : GenderlessNumberToWordsConverter public override string Convert(long input) { - if (input > Int32.MaxValue || input < Int32.MinValue) - { - throw new NotImplementedException(); - } - var number = (int)input; + var number = input; if (number == 0) { return UnitsMap[0]; @@ -39,6 +34,24 @@ public override string Convert(long input) var parts = new List(); + if ((number / 1000000000000000000) > 0) + { + parts.Add(string.Format("{0} kentilyon", Convert(number / 1000000000000000000))); + number %= 1000000000000000000; + } + + if ((number / 1000000000000000) > 0) + { + parts.Add(string.Format("{0} katrilyon", Convert(number / 1000000000000000))); + number %= 1000000000000000; + } + + if ((number / 1000000000000) > 0) + { + parts.Add(string.Format("{0} trilyon", Convert(number / 1000000000000))); + number %= 1000000000000; + } + if ((number / 1000000000) > 0) { parts.Add(string.Format("{0} milyar", Convert(number / 1000000000))); From a03a313c8490c8673f1a6a3fc30a870397d2ee7a Mon Sep 17 00:00:00 2001 From: Pandorax100 <44736094+Pandorax100@users.noreply.github.com> Date: Mon, 31 May 2021 00:24:37 +0100 Subject: [PATCH 125/126] Fix singularization of the word "ties" (#1066) --- src/Humanizer.Tests.Shared/InflectorTests.cs | 1 + src/Humanizer/Inflections/Vocabularies.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Humanizer.Tests.Shared/InflectorTests.cs b/src/Humanizer.Tests.Shared/InflectorTests.cs index c57b66cda..75c8ed54e 100644 --- a/src/Humanizer.Tests.Shared/InflectorTests.cs +++ b/src/Humanizer.Tests.Shared/InflectorTests.cs @@ -345,6 +345,7 @@ public IEnumerator GetEnumerator() yield return new object[] { "that", "those" }; yield return new object[] { "thief", "thieves" }; yield return new object[] { "this", "these" }; + yield return new object[] { "tie", "ties" }; yield return new object[] { "tooth", "teeth" }; yield return new object[] { "torpedo", "torpedoes" }; yield return new object[] { "trellis", "trellises" }; diff --git a/src/Humanizer/Inflections/Vocabularies.cs b/src/Humanizer/Inflections/Vocabularies.cs index f546aa7de..dcaf4756e 100644 --- a/src/Humanizer/Inflections/Vocabularies.cs +++ b/src/Humanizer/Inflections/Vocabularies.cs @@ -99,6 +99,7 @@ private static Vocabulary BuildDefault() _default.AddIrregular("this", "these", matchEnding: false); _default.AddIrregular("bus", "buses", matchEnding: false); _default.AddIrregular("die", "dice", matchEnding: false); + _default.AddIrregular("tie", "ties", matchEnding: false); _default.AddUncountable("staff"); _default.AddUncountable("training"); From 45785880346c73942a65dc6bb084bda5e8713a75 Mon Sep 17 00:00:00 2001 From: Tomasz Cielecki Date: Mon, 31 May 2021 01:41:30 +0200 Subject: [PATCH 126/126] Add Danish resources for cardinal directions (#997) --- src/Humanizer/Properties/Resources.da.resx | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/Humanizer/Properties/Resources.da.resx b/src/Humanizer/Properties/Resources.da.resx index 7466fefb9..60fd09fba 100644 --- a/src/Humanizer/Properties/Resources.da.resx +++ b/src/Humanizer/Properties/Resources.da.resx @@ -243,4 +243,94 @@ et år + + øst + + + østnordøst + + + ØNØ + + + østsydøst + + + ØSØ + + + Ø + + + nord + + + nordøst + + + + + + nordnordøst + + + NNØ + + + nordnordvest + + + NNV + + + nordvest + + + NV + + + syd + + + sydøst + + + + + + sydsydøst + + + SSØ + + + sydsydvest + + + SSV + + + sydvest + + + SV + + + vest + + + vestnordvest + + + VNV + + + vestsydvest + + + VSV + + + V + \ No newline at end of file