diff --git a/.editorconfig b/.editorconfig
index f7ec9b06d..7c33c3443 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -48,6 +48,7 @@ dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_require_accessibility_modifiers = always
+dotnet_style_prefer_conditional_expression_over_return = false
# CSharp code style settings:
[*.cs]
diff --git a/.vsts-shared.yml b/.vsts-shared.yml
index 05b5e9cbc..5f0a8e3ce 100644
--- a/.vsts-shared.yml
+++ b/.vsts-shared.yml
@@ -10,9 +10,9 @@ steps:
displayName: Setup Environment Variables
- task: NuGetToolInstaller@0
- displayName: Use NuGet 4.6.2
+ displayName: Use NuGet 4.7.0
inputs:
- versionSpec: 4.6.2
+ versionSpec: 4.7.0
- task: DotNetCoreCLI@2
inputs:
@@ -31,9 +31,10 @@ steps:
# version
nuget install NerdBank.GitVersioning -SolutionDir $(Build.SourcesDirectory) -Verbosity quiet -ExcludeVersion
$vers = & $(Build.SourcesDirectory)\packages\nerdbank.gitversioning\tools\Get-Version.ps1
- $nugetVer = $vers.NuGetPackageVersion
+ $nugetVer = $vers.NuGetPackageVersion
+ $commitId = $vers.GitCommitId
- ls .\NuSpecs\*.nuspec | %{ nuget pack $_.FullName -version "$nugetVer" -BasePath "src" -NoPackageAnalysis -OutputDirectory $(Build.ArtifactStagingDirectory)\Packages }
+ ls .\NuSpecs\*.nuspec | %{ nuget pack $_.FullName -version "$nugetVer" -BasePath "src" -NoPackageAnalysis -OutputDirectory $(Build.ArtifactStagingDirectory)\Packages -Properties "RepositoryType=git;RepositoryCommit=$commitId;RepositoryUrl=https://github.com/Humanizr/Humanizer"}
displayName: Create packages
- task: DotNetCoreCLI@2
diff --git a/NuSpecs/Humanizer.Core.af.nuspec b/NuSpecs/Humanizer.Core.af.nuspec
index 502f66d53..b2758e8aa 100644
--- a/NuSpecs/Humanizer.Core.af.nuspec
+++ b/NuSpecs/Humanizer.Core.af.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (af)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
af
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.ar.nuspec b/NuSpecs/Humanizer.Core.ar.nuspec
index 629de39bb..5f964cfba 100644
--- a/NuSpecs/Humanizer.Core.ar.nuspec
+++ b/NuSpecs/Humanizer.Core.ar.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (ar)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
ar
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.bg.nuspec b/NuSpecs/Humanizer.Core.bg.nuspec
index 08e860741..a3d6218c9 100644
--- a/NuSpecs/Humanizer.Core.bg.nuspec
+++ b/NuSpecs/Humanizer.Core.bg.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (bg)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
bg
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.bn-BD.nuspec b/NuSpecs/Humanizer.Core.bn-BD.nuspec
index 469b8313f..389598648 100644
--- a/NuSpecs/Humanizer.Core.bn-BD.nuspec
+++ b/NuSpecs/Humanizer.Core.bn-BD.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (bn-BD)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
bn-BD
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.cs.nuspec b/NuSpecs/Humanizer.Core.cs.nuspec
index 6c710ced1..2ec8e2890 100644
--- a/NuSpecs/Humanizer.Core.cs.nuspec
+++ b/NuSpecs/Humanizer.Core.cs.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (cs)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
cs
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.da.nuspec b/NuSpecs/Humanizer.Core.da.nuspec
index 138077db7..033c00502 100644
--- a/NuSpecs/Humanizer.Core.da.nuspec
+++ b/NuSpecs/Humanizer.Core.da.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (da)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
da
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.de-CH.nuspec b/NuSpecs/Humanizer.Core.de-CH.nuspec
new file mode 100644
index 000000000..4f7585d1a
--- /dev/null
+++ b/NuSpecs/Humanizer.Core.de-CH.nuspec
@@ -0,0 +1,25 @@
+
+
+
+ Humanizer.Core.de-CH
+ $version$
+ Humanizer Locale (de-CH)
+ Mehdi Khalili, Oren Novotny
+ Mehdi Khalili, onovotny
+ https://github.com/Humanizr/Humanizer
+ https://raw.github.com/Humanizr/Humanizer/master/logo.png
+ false
+ Humanizer Locale (de-CH)
+ Copyright 2012-2015 Mehdi Khalili
+ https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
+ de-CH
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.de-LI.nuspec b/NuSpecs/Humanizer.Core.de-LI.nuspec
new file mode 100644
index 000000000..e677d15d0
--- /dev/null
+++ b/NuSpecs/Humanizer.Core.de-LI.nuspec
@@ -0,0 +1,25 @@
+
+
+
+ Humanizer.Core.de-LI
+ $version$
+ Humanizer Locale (de-LI)
+ Mehdi Khalili, Oren Novotny
+ Mehdi Khalili, onovotny
+ https://github.com/Humanizr/Humanizer
+ https://raw.github.com/Humanizr/Humanizer/master/logo.png
+ false
+ Humanizer Locale (de-LI)
+ Copyright 2012-2015 Mehdi Khalili
+ https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
+ de-LI
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.de.nuspec b/NuSpecs/Humanizer.Core.de.nuspec
index 3bd1a1b0a..c1e39adb3 100644
--- a/NuSpecs/Humanizer.Core.de.nuspec
+++ b/NuSpecs/Humanizer.Core.de.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (de)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
de
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.el.nuspec b/NuSpecs/Humanizer.Core.el.nuspec
index fce38cd89..81f3cd8c0 100644
--- a/NuSpecs/Humanizer.Core.el.nuspec
+++ b/NuSpecs/Humanizer.Core.el.nuspec
@@ -12,12 +12,13 @@
Humanizer Locale (el)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
el
-
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.es.nuspec b/NuSpecs/Humanizer.Core.es.nuspec
index 725453cbb..1b2111be0 100644
--- a/NuSpecs/Humanizer.Core.es.nuspec
+++ b/NuSpecs/Humanizer.Core.es.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (es)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
es
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.fa.nuspec b/NuSpecs/Humanizer.Core.fa.nuspec
index a5742c1f2..2865d9a9d 100644
--- a/NuSpecs/Humanizer.Core.fa.nuspec
+++ b/NuSpecs/Humanizer.Core.fa.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (fa)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
fa
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.fi-FI.nuspec b/NuSpecs/Humanizer.Core.fi-FI.nuspec
index 6d87f321b..382e14f48 100644
--- a/NuSpecs/Humanizer.Core.fi-FI.nuspec
+++ b/NuSpecs/Humanizer.Core.fi-FI.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (fi-FI)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
fi-FI
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.fr-BE.nuspec b/NuSpecs/Humanizer.Core.fr-BE.nuspec
index 694d0772a..974bd1654 100644
--- a/NuSpecs/Humanizer.Core.fr-BE.nuspec
+++ b/NuSpecs/Humanizer.Core.fr-BE.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (fr-BE)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
fr-BE
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.fr.nuspec b/NuSpecs/Humanizer.Core.fr.nuspec
index cfa75aa9f..cf642cd49 100644
--- a/NuSpecs/Humanizer.Core.fr.nuspec
+++ b/NuSpecs/Humanizer.Core.fr.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (fr)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
fr
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.he.nuspec b/NuSpecs/Humanizer.Core.he.nuspec
index 92e99d01d..21ac87ba5 100644
--- a/NuSpecs/Humanizer.Core.he.nuspec
+++ b/NuSpecs/Humanizer.Core.he.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (he)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
he
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.hr.nuspec b/NuSpecs/Humanizer.Core.hr.nuspec
index c59d0d144..d9b820fe8 100644
--- a/NuSpecs/Humanizer.Core.hr.nuspec
+++ b/NuSpecs/Humanizer.Core.hr.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (hr)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
hr
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.hu.nuspec b/NuSpecs/Humanizer.Core.hu.nuspec
index a3a6b2eba..e1355a80f 100644
--- a/NuSpecs/Humanizer.Core.hu.nuspec
+++ b/NuSpecs/Humanizer.Core.hu.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (hu)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
hu
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.id.nuspec b/NuSpecs/Humanizer.Core.id.nuspec
index b79872b41..cf32a5a07 100644
--- a/NuSpecs/Humanizer.Core.id.nuspec
+++ b/NuSpecs/Humanizer.Core.id.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (id)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
id
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.it.nuspec b/NuSpecs/Humanizer.Core.it.nuspec
index 5e513fdf6..1ec5e0dcf 100644
--- a/NuSpecs/Humanizer.Core.it.nuspec
+++ b/NuSpecs/Humanizer.Core.it.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (it)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
it
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.ja.nuspec b/NuSpecs/Humanizer.Core.ja.nuspec
index c0244f78d..b6737c765 100644
--- a/NuSpecs/Humanizer.Core.ja.nuspec
+++ b/NuSpecs/Humanizer.Core.ja.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (ja)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
ja
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.lv.nuspec b/NuSpecs/Humanizer.Core.lv.nuspec
index 8ff5c2842..9761398a4 100644
--- a/NuSpecs/Humanizer.Core.lv.nuspec
+++ b/NuSpecs/Humanizer.Core.lv.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (lv)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
lv
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.nb-NO.nuspec b/NuSpecs/Humanizer.Core.nb-NO.nuspec
index 8b938716b..8e8f04f5d 100644
--- a/NuSpecs/Humanizer.Core.nb-NO.nuspec
+++ b/NuSpecs/Humanizer.Core.nb-NO.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (nb-NO)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
nb-NO
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.nb.nuspec b/NuSpecs/Humanizer.Core.nb.nuspec
index 585a81a78..78a26bddb 100644
--- a/NuSpecs/Humanizer.Core.nb.nuspec
+++ b/NuSpecs/Humanizer.Core.nb.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (nb)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
nb
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.nl.nuspec b/NuSpecs/Humanizer.Core.nl.nuspec
index 8d0c61d9a..c21c010d1 100644
--- a/NuSpecs/Humanizer.Core.nl.nuspec
+++ b/NuSpecs/Humanizer.Core.nl.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (nl)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
nl
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.nuspec b/NuSpecs/Humanizer.Core.nuspec
index 5056db553..ffa8832f2 100644
--- a/NuSpecs/Humanizer.Core.nuspec
+++ b/NuSpecs/Humanizer.Core.nuspec
@@ -13,14 +13,16 @@
Copyright 2012-2016
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
en
+
-
+
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.pl.nuspec b/NuSpecs/Humanizer.Core.pl.nuspec
index 5e9d84566..f4d8fb78e 100644
--- a/NuSpecs/Humanizer.Core.pl.nuspec
+++ b/NuSpecs/Humanizer.Core.pl.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (pl)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
pl
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.pt.nuspec b/NuSpecs/Humanizer.Core.pt.nuspec
index 26728c957..1484bfd5d 100644
--- a/NuSpecs/Humanizer.Core.pt.nuspec
+++ b/NuSpecs/Humanizer.Core.pt.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (pt)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
pt
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.ro.nuspec b/NuSpecs/Humanizer.Core.ro.nuspec
index 6026c8929..f2de305f1 100644
--- a/NuSpecs/Humanizer.Core.ro.nuspec
+++ b/NuSpecs/Humanizer.Core.ro.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (ro)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
ro
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.ru.nuspec b/NuSpecs/Humanizer.Core.ru.nuspec
index 336ac8e53..cf2364163 100644
--- a/NuSpecs/Humanizer.Core.ru.nuspec
+++ b/NuSpecs/Humanizer.Core.ru.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (ru)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
ru
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.sk.nuspec b/NuSpecs/Humanizer.Core.sk.nuspec
index f6f11c01e..3bc21fc95 100644
--- a/NuSpecs/Humanizer.Core.sk.nuspec
+++ b/NuSpecs/Humanizer.Core.sk.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (sk)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
sk
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.sl.nuspec b/NuSpecs/Humanizer.Core.sl.nuspec
index e56b77e14..94c80f2f6 100644
--- a/NuSpecs/Humanizer.Core.sl.nuspec
+++ b/NuSpecs/Humanizer.Core.sl.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (sl)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
sl
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.sr-Latn.nuspec b/NuSpecs/Humanizer.Core.sr-Latn.nuspec
index 7e26b6647..b3414ed43 100644
--- a/NuSpecs/Humanizer.Core.sr-Latn.nuspec
+++ b/NuSpecs/Humanizer.Core.sr-Latn.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (sr-Latn)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
sr-Latn
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.sr.nuspec b/NuSpecs/Humanizer.Core.sr.nuspec
index dd5e36b5a..0ea0d215a 100644
--- a/NuSpecs/Humanizer.Core.sr.nuspec
+++ b/NuSpecs/Humanizer.Core.sr.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (sr)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
sr
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.sv.nuspec b/NuSpecs/Humanizer.Core.sv.nuspec
index 595cf3555..7c8806d91 100644
--- a/NuSpecs/Humanizer.Core.sv.nuspec
+++ b/NuSpecs/Humanizer.Core.sv.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (sv)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
sv
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.tr.nuspec b/NuSpecs/Humanizer.Core.tr.nuspec
index 32dc47cdb..1de385ed6 100644
--- a/NuSpecs/Humanizer.Core.tr.nuspec
+++ b/NuSpecs/Humanizer.Core.tr.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (tr)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
tr
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.uk.nuspec b/NuSpecs/Humanizer.Core.uk.nuspec
index b3e6cdb23..e9a501f54 100644
--- a/NuSpecs/Humanizer.Core.uk.nuspec
+++ b/NuSpecs/Humanizer.Core.uk.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (uk)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
uk
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.uz-Cyrl-UZ.nuspec b/NuSpecs/Humanizer.Core.uz-Cyrl-UZ.nuspec
index e852d551f..eb18a2fe1 100644
--- a/NuSpecs/Humanizer.Core.uz-Cyrl-UZ.nuspec
+++ b/NuSpecs/Humanizer.Core.uz-Cyrl-UZ.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (uz-Cyrl-UZ)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
uz-Cyrl-UZ
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.uz-Latn-UZ.nuspec b/NuSpecs/Humanizer.Core.uz-Latn-UZ.nuspec
index 002f1aa59..6d1fb0838 100644
--- a/NuSpecs/Humanizer.Core.uz-Latn-UZ.nuspec
+++ b/NuSpecs/Humanizer.Core.uz-Latn-UZ.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (uz-Latn-UZ)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
uz-Latn-UZ
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.vi.nuspec b/NuSpecs/Humanizer.Core.vi.nuspec
index 4e3cd4eea..136d80dc6 100644
--- a/NuSpecs/Humanizer.Core.vi.nuspec
+++ b/NuSpecs/Humanizer.Core.vi.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (vi)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
vi
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.zh-CN.nuspec b/NuSpecs/Humanizer.Core.zh-CN.nuspec
index 7c7ecac3a..acd286f13 100644
--- a/NuSpecs/Humanizer.Core.zh-CN.nuspec
+++ b/NuSpecs/Humanizer.Core.zh-CN.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (zh-CN)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
zh-CN
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.zh-Hans.nuspec b/NuSpecs/Humanizer.Core.zh-Hans.nuspec
index 382a3af7d..bf6792dca 100644
--- a/NuSpecs/Humanizer.Core.zh-Hans.nuspec
+++ b/NuSpecs/Humanizer.Core.zh-Hans.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (zh-Hans)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
zh-Hans
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.Core.zh-Hant.nuspec b/NuSpecs/Humanizer.Core.zh-Hant.nuspec
index a0888b5c4..59c2dc97f 100644
--- a/NuSpecs/Humanizer.Core.zh-Hant.nuspec
+++ b/NuSpecs/Humanizer.Core.zh-Hant.nuspec
@@ -12,6 +12,7 @@
Humanizer Locale (zh-Hant)
Copyright 2012-2015 Mehdi Khalili
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
zh-Hant
@@ -19,5 +20,6 @@
+
\ No newline at end of file
diff --git a/NuSpecs/Humanizer.nuspec b/NuSpecs/Humanizer.nuspec
index 04a9be1b1..c6189470f 100644
--- a/NuSpecs/Humanizer.nuspec
+++ b/NuSpecs/Humanizer.nuspec
@@ -11,7 +11,8 @@
false
Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities
Copyright 2012-2017 Mehdi Khalili
- https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+ https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
+
@@ -20,6 +21,8 @@
+
+
diff --git a/readme.md b/readme.md
index 3f9dd58e8..ce0d8d40a 100644
--- a/readme.md
+++ b/readme.md
@@ -770,6 +770,7 @@ The possible values are `GrammaticalGender.Masculine`, `GrammaticalGender.Femini
1.ToWords(GrammaticalGender.Masculine) => "واحد"
1.ToWords(GrammaticalGender.Feminine) => "واحدة"
1.ToWords(GrammaticalGender.Neuter) => "واحد"
+(-1).ToWords() => "ناقص واحد"
```
Obviously this only applies to some cultures. For others passing gender in doesn't make any difference in the result.
diff --git a/scripts/Sign-Package.ps1 b/scripts/Sign-Package.ps1
index daedfbf48..7ac7d7e75 100644
--- a/scripts/Sign-Package.ps1
+++ b/scripts/Sign-Package.ps1
@@ -6,20 +6,18 @@ if([string]::IsNullOrEmpty($env:SignClientSecret)){
return;
}
-& nuget install SignClient -Version 0.9.1 -SolutionDir "$currentDirectory\..\" -Verbosity quiet -ExcludeVersion
+dotnet tool install --tool-path . SignClient
# Setup Variables we need to pass into the sign client tool
$appSettings = "$currentDirectory\SignClient.json"
-$appPath = "$currentDirectory\..\packages\SignClient\tools\netcoreapp2.0\SignClient.dll"
-
$nupgks = gci $Env:ArtifactDirectory\*.nupkg | Select -ExpandProperty FullName
foreach ($nupkg in $nupgks){
Write-Host "Submitting $nupkg for signing"
- dotnet $appPath 'sign' -c $appSettings -i $nupkg -r $env:SignClientUser -s $env:SignClientSecret -n 'Humanizer' -d 'Humanizer' -u 'https://github.com/Humanizr/Humanizer'
+ .\SignClient 'sign' -c $appSettings -i $nupkg -r $env:SignClientUser -s $env:SignClientSecret -n 'Humanizer' -d 'Humanizer' -u 'https://github.com/Humanizr/Humanizer'
Write-Host "Finished signing $nupkg"
}
diff --git a/src/Directory.build.props b/src/Directory.build.props
index 44762675b..84d7166db 100644
--- a/src/Directory.build.props
+++ b/src/Directory.build.props
@@ -1,11 +1,23 @@
latest
+ true
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
-
+
-
-
\ No newline at end of file
+
+
+
+
+ <_Parameter1>CommitHash
+ <_Parameter2>$(SourceRevisionId)
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems
index dead06d9a..d2be62186 100644
--- a/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems
+++ b/src/Humanizer.Tests.Shared/Humanizer.Tests.Shared.projitems
@@ -47,6 +47,8 @@
+
+
diff --git a/src/Humanizer.Tests.Shared/Localisation/ar/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/ar/NumberToWordsTests.cs
index 5379e5d9e..6914a793a 100644
--- a/src/Humanizer.Tests.Shared/Localisation/ar/NumberToWordsTests.cs
+++ b/src/Humanizer.Tests.Shared/Localisation/ar/NumberToWordsTests.cs
@@ -1,4 +1,5 @@
-using Xunit;
+using Humanizer.Localisation.NumberToWords;
+using Xunit;
namespace Humanizer.Tests.Localisation.ar
{
@@ -18,6 +19,47 @@ public void ToWordsArabic(string expected, int number)
Assert.Equal(expected, number.ToWords());
}
+ [Theory]
+ [InlineData("ناقص واحد", -1)]
+ [InlineData("ناقص اثنان", -2)]
+ [InlineData("ناقص اثنان و عشرون", -22)]
+ [InlineData("ناقص أحد عشر", -11)]
+ [InlineData("ناقص ثلاثة آلاف و خمس مئة و واحد", -3501)]
+ [InlineData("ناقص مليون و واحد", -1000001)]
+ public void ToWordsArabicNegative(string expected, int number)
+ {
+ 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, "كوينتليون و مئة و أحد عشر كوادريليوناً و مئة و أحد عشر تريليوناً و مئة و أحد عشر ملياراً و مئة و أحد عشر مليوناً و مئة و أحد عشر ألفاً و مئة و أحد عشر")]
+ [InlineData(10000000001L, "عشرة مليارات و واحد")]
+ [InlineData(8750000500001L, "ثمانية تريليونات و سبع مئة و خمسون ملياراً و خمس مئة ألفاً و واحد")]
+ [InlineData(-10000000001L, "ناقص عشرة مليارات و واحد")]
+ [InlineData(-8750000500001L, "ناقص ثمانية تريليونات و سبع مئة و خمسون ملياراً و خمس مئة ألفاً و واحد")]
+ public void ToWordsArabicLong(long number, string expected)
+ {
+ Assert.Equal(expected, number.ToWords());
+ }
+
[Theory]
[InlineData("صفر", 0)]
[InlineData("واحدة", 1)]
@@ -26,7 +68,15 @@ public void ToWordsArabic(string expected, int number)
[InlineData("إحدى عشرة", 11)]
[InlineData("ثلاثة آلاف و خمس مئة و واحدة", 3501)]
[InlineData("مليون و واحدة", 1000001)]
- public void ToWordsArabicFeminine(string expected, int number)
+ public void ToWordsArabicFeminine(string expected, long number)
+ {
+ Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine));
+ }
+
+ [Theory]
+ [InlineData("عشرة مليارات و واحدة", 10000000001)]
+ [InlineData("ثمانية تريليونات و سبع مئة و خمسون ملياراً و خمس مئة ألفاً و واحدة", 8750000500001)]
+ public void ToWordsArabicLongFeminine(string expected, long number)
{
Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine));
}
@@ -35,7 +85,7 @@ public void ToWordsArabicFeminine(string expected, int number)
[InlineData(122, "مئة و اثنتان و عشرون", GrammaticalGender.Feminine)]
[InlineData(3501, "ثلاثة آلاف و خمس مئة و واحدة", GrammaticalGender.Feminine)]
[InlineData(3501, "ثلاثة آلاف و خمس مئة و واحد", GrammaticalGender.Neuter)]
- public void ToWordsWithGender(int number, string expected, GrammaticalGender gender)
+ public void ToWordsWithGender(long number, string expected, GrammaticalGender gender)
{
Assert.Equal(expected, number.ToWords(gender));
}
diff --git a/src/Humanizer.Tests.Shared/Localisation/de-CH/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/de-CH/NumberToWordsTests.cs
new file mode 100644
index 000000000..f09bd5e9e
--- /dev/null
+++ b/src/Humanizer.Tests.Shared/Localisation/de-CH/NumberToWordsTests.cs
@@ -0,0 +1,163 @@
+using Xunit;
+
+namespace Humanizer.Tests.Localisation.deCH
+{
+ [UseCulture("de-CH")]
+ public class NumberToWordsTests
+ {
+ [Theory]
+ [InlineData(0, "null")]
+ [InlineData(1, "ein")]
+ [InlineData(2, "zwei")]
+ [InlineData(3, "drei")]
+ [InlineData(4, "vier")]
+ [InlineData(5, "fünf")]
+ [InlineData(6, "sechs")]
+ [InlineData(7, "sieben")]
+ [InlineData(8, "acht")]
+ [InlineData(9, "neun")]
+ [InlineData(10, "zehn")]
+ [InlineData(20, "zwanzig")]
+ [InlineData(30, "dreissig")]
+ [InlineData(40, "vierzig")]
+ [InlineData(50, "fünfzig")]
+ [InlineData(60, "sechzig")]
+ [InlineData(70, "siebzig")]
+ [InlineData(80, "achtzig")]
+ [InlineData(90, "neunzig")]
+ [InlineData(100, "einhundert")]
+ [InlineData(200, "zweihundert")]
+ [InlineData(1000, "eintausend")]
+ [InlineData(10000, "zehntausend")]
+ [InlineData(100000, "einhunderttausend")]
+ [InlineData(1000000, "eine Million")]
+ [InlineData(10000000, "zehn Millionen")]
+ [InlineData(100000000, "einhundert Millionen")]
+ [InlineData(1000000000, "eine Milliarde")]
+ [InlineData(2000000000, "zwei Milliarden")]
+ [InlineData(122, "einhundertzweiundzwanzig")]
+ [InlineData(3501, "dreitausendfünfhundertein")]
+ [InlineData(111, "einhundertelf")]
+ [InlineData(1112, "eintausendeinhundertzwölf")]
+ [InlineData(11213, "elftausendzweihundertdreizehn")]
+ [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehn")]
+ [InlineData(2132415, "zwei Millionen einhundertzweiunddreissigtausendvierhundertfünfzehn")]
+ [InlineData(12345516, "zwölf Millionen dreihundertfünfundvierzigtausendfünfhundertsechzehn")]
+ [InlineData(751633617, "siebenhunderteinundfünfzig Millionen sechshundertdreiunddreissigtausendsechshundertsiebzehn")]
+ [InlineData(1111111118, "eine Milliarde einhundertelf Millionen einhundertelftausendeinhundertachtzehn")]
+ [InlineData(35484694489515, "fünfunddreissig 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 sechshundertdreiunddreissigtausendsechshundertneunzehn")]
+ public void ToWords(long number, string expected)
+ {
+ Assert.Equal(expected, number.ToWords());
+ }
+
+ [Theory]
+ [InlineData(1, "eine")]
+ [InlineData(3501, "dreitausendfünfhunderteine")]
+ 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, "dreissigster")]
+ [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, "zweimillioneneinhundertzweiunddreissigtausendvierhundertfünfzehnter")]
+ [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnter")]
+ [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertsiebzehnter")]
+ [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnter")]
+ [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertneunzehnter")]
+ 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, "zweimillioneneinhundertzweiunddreissigtausendvierhundertfünfzehnte")]
+ [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnte")]
+ [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertsiebzehnte")]
+ [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnte")]
+ [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertneunzehnte")]
+ 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, "zweimillioneneinhundertzweiunddreissigtausendvierhundertfünfzehntes")]
+ [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehntes")]
+ [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertsiebzehntes")]
+ [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehntes")]
+ [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertneunzehntes")]
+ public void ToOrdinalWordsNeuter(int number, string expected)
+ {
+ Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Neuter));
+ }
+ }
+}
diff --git a/src/Humanizer.Tests.Shared/Localisation/de-LI/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/de-LI/NumberToWordsTests.cs
new file mode 100644
index 000000000..0c5baa803
--- /dev/null
+++ b/src/Humanizer.Tests.Shared/Localisation/de-LI/NumberToWordsTests.cs
@@ -0,0 +1,163 @@
+using Xunit;
+
+namespace Humanizer.Tests.Localisation.deLI
+{
+ [UseCulture("de-LI")]
+ public class NumberToWordsTests
+ {
+ [Theory]
+ [InlineData(0, "null")]
+ [InlineData(1, "ein")]
+ [InlineData(2, "zwei")]
+ [InlineData(3, "drei")]
+ [InlineData(4, "vier")]
+ [InlineData(5, "fünf")]
+ [InlineData(6, "sechs")]
+ [InlineData(7, "sieben")]
+ [InlineData(8, "acht")]
+ [InlineData(9, "neun")]
+ [InlineData(10, "zehn")]
+ [InlineData(20, "zwanzig")]
+ [InlineData(30, "dreissig")]
+ [InlineData(40, "vierzig")]
+ [InlineData(50, "fünfzig")]
+ [InlineData(60, "sechzig")]
+ [InlineData(70, "siebzig")]
+ [InlineData(80, "achtzig")]
+ [InlineData(90, "neunzig")]
+ [InlineData(100, "einhundert")]
+ [InlineData(200, "zweihundert")]
+ [InlineData(1000, "eintausend")]
+ [InlineData(10000, "zehntausend")]
+ [InlineData(100000, "einhunderttausend")]
+ [InlineData(1000000, "eine Million")]
+ [InlineData(10000000, "zehn Millionen")]
+ [InlineData(100000000, "einhundert Millionen")]
+ [InlineData(1000000000, "eine Milliarde")]
+ [InlineData(2000000000, "zwei Milliarden")]
+ [InlineData(122, "einhundertzweiundzwanzig")]
+ [InlineData(3501, "dreitausendfünfhundertein")]
+ [InlineData(111, "einhundertelf")]
+ [InlineData(1112, "eintausendeinhundertzwölf")]
+ [InlineData(11213, "elftausendzweihundertdreizehn")]
+ [InlineData(121314, "einhunderteinundzwanzigtausenddreihundertvierzehn")]
+ [InlineData(2132415, "zwei Millionen einhundertzweiunddreissigtausendvierhundertfünfzehn")]
+ [InlineData(12345516, "zwölf Millionen dreihundertfünfundvierzigtausendfünfhundertsechzehn")]
+ [InlineData(751633617, "siebenhunderteinundfünfzig Millionen sechshundertdreiunddreissigtausendsechshundertsiebzehn")]
+ [InlineData(1111111118, "eine Milliarde einhundertelf Millionen einhundertelftausendeinhundertachtzehn")]
+ [InlineData(35484694489515, "fünfunddreissig 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 sechshundertdreiunddreissigtausendsechshundertneunzehn")]
+ public void ToWords(long number, string expected)
+ {
+ Assert.Equal(expected, number.ToWords());
+ }
+
+ [Theory]
+ [InlineData(1, "eine")]
+ [InlineData(3501, "dreitausendfünfhunderteine")]
+ 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, "dreissigster")]
+ [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, "zweimillioneneinhundertzweiunddreissigtausendvierhundertfünfzehnter")]
+ [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnter")]
+ [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertsiebzehnter")]
+ [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnter")]
+ [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertneunzehnter")]
+ 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, "zweimillioneneinhundertzweiunddreissigtausendvierhundertfünfzehnte")]
+ [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehnte")]
+ [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertsiebzehnte")]
+ [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehnte")]
+ [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertneunzehnte")]
+ 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, "zweimillioneneinhundertzweiunddreissigtausendvierhundertfünfzehntes")]
+ [InlineData(12345516, "zwölfmillionendreihundertfünfundvierzigtausendfünfhundertsechzehntes")]
+ [InlineData(751633617, "siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertsiebzehntes")]
+ [InlineData(1111111118, "einemilliardeeinhundertelfmillioneneinhundertelftausendeinhundertachtzehntes")]
+ [InlineData(-751633619, "minus siebenhunderteinundfünfzigmillionensechshundertdreiunddreissigtausendsechshundertneunzehntes")]
+ public void ToOrdinalWordsNeuter(int number, string expected)
+ {
+ Assert.Equal(expected, number.ToOrdinalWords(GrammaticalGender.Neuter));
+ }
+ }
+}
diff --git a/src/Humanizer.Tests.Shared/Localisation/de/NumberToWordsTests.cs b/src/Humanizer.Tests.Shared/Localisation/de/NumberToWordsTests.cs
index 06dcb8f0b..e5e8d890a 100644
--- a/src/Humanizer.Tests.Shared/Localisation/de/NumberToWordsTests.cs
+++ b/src/Humanizer.Tests.Shared/Localisation/de/NumberToWordsTests.cs
@@ -7,7 +7,7 @@ public class NumberToWordsTests
{
[Theory]
[InlineData(0, "null")]
- [InlineData(1, "eins")]
+ [InlineData(1, "ein")]
[InlineData(2, "zwei")]
[InlineData(3, "drei")]
[InlineData(4, "vier")]
@@ -36,7 +36,7 @@ public class NumberToWordsTests
[InlineData(1000000000, "eine Milliarde")]
[InlineData(2000000000, "zwei Milliarden")]
[InlineData(122, "einhundertzweiundzwanzig")]
- [InlineData(3501, "dreitausendfünfhunderteins")]
+ [InlineData(3501, "dreitausendfünfhundertein")]
[InlineData(111, "einhundertelf")]
[InlineData(1112, "eintausendeinhundertzwölf")]
[InlineData(11213, "elftausendzweihundertdreizehn")]
@@ -45,12 +45,23 @@ public class NumberToWordsTests
[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(int number, string expected)
+ public void ToWords(long number, string expected)
{
Assert.Equal(expected, number.ToWords());
}
+ [Theory]
+ [InlineData(1, "eine")]
+ [InlineData(3501, "dreitausendfünfhunderteine")]
+ public void ToWordsFeminine(long number, string expected)
+ {
+ Assert.Equal(expected, number.ToWords(GrammaticalGender.Feminine));
+ }
+
[Theory]
[InlineData(0, "nullter")]
[InlineData(1, "erster")]
diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.cs b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.cs
index cd5bf39ca..d970355e9 100644
--- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.cs
+++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.cs
@@ -1,9 +1,9 @@
#if NET46
-using System.IO;
-using ApiApprover;
+using System;
+using System.Linq;
using ApprovalTests;
using ApprovalTests.Reporters;
-using Mono.Cecil;
+using PublicApiGenerator;
using Xunit;
namespace Humanizer.Tests.ApiApprover
@@ -17,21 +17,24 @@ public class PublicApiApprovalTest
[IgnoreLineEndings(true)]
public void approve_public_api()
{
- var assemblyPath = typeof(StringHumanizeExtensions).Assembly.Location;
- var assemblyResolver = new DefaultAssemblyResolver();
- assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
-
- var readSymbols = File.Exists(Path.ChangeExtension(assemblyPath, ".pdb"));
- var asm = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters(ReadingMode.Deferred)
- {
- ReadSymbols = readSymbols,
- AssemblyResolver = assemblyResolver
- });
-
- var publicApi = PublicApiGenerator.CreatePublicApiForAssembly(asm);
+ var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(StringHumanizeExtensions).Assembly));
Approvals.Verify(publicApi);
}
+
+ private static string Filter(string text)
+ {
+ return string.Join(Environment.NewLine, text.Split(new[]
+ {
+ Environment.NewLine
+ }, StringSplitOptions.RemoveEmptyEntries)
+ .Where(l => !l.StartsWith("[assembly: AssemblyVersion("))
+ .Where(l => !l.StartsWith("[assembly: AssemblyFileVersion("))
+ .Where(l => !l.StartsWith("[assembly: AssemblyInformationalVersion("))
+ .Where(l => !l.StartsWith("[assembly: System.Reflection.AssemblyMetadataAttribute(\"CommitHash\""))
+ .Where(l => !string.IsNullOrWhiteSpace(l))
+ );
+ }
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/Humanizer.Tests/App_Packages/ApiApprover.3.0.1/PublicApiApprover.cs b/src/Humanizer.Tests/App_Packages/ApiApprover.3.0.1/PublicApiApprover.cs
deleted file mode 100644
index e6fc8e78f..000000000
--- a/src/Humanizer.Tests/App_Packages/ApiApprover.3.0.1/PublicApiApprover.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-#if NET46
-using System.IO;
-using ApprovalTests;
-using ApprovalTests.Namers;
-using Mono.Cecil;
-
-namespace ApiApprover
-{
- public static class PublicApiApprover
- {
- public static void ApprovePublicApi(string assemblyPath)
- {
- var assemblyResolver = new DefaultAssemblyResolver();
- assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
-
- var readSymbols = File.Exists(Path.ChangeExtension(assemblyPath, ".pdb"));
- var asm = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters(ReadingMode.Deferred)
- {
- ReadSymbols = readSymbols,
- AssemblyResolver = assemblyResolver,
- });
-
- var publicApi = PublicApiGenerator.CreatePublicApiForAssembly(asm);
- var writer = new ApprovalTextWriter(publicApi, "cs");
- var approvalNamer = new AssemblyPathNamer(assemblyPath);
- ApprovalTests.Approvals.Verify(writer, approvalNamer, ApprovalTests.Approvals.GetReporter());
- }
-
- private class AssemblyPathNamer : UnitTestFrameworkNamer
- {
- private readonly string name;
-
- public AssemblyPathNamer(string assemblyPath)
- {
- name = Path.GetFileNameWithoutExtension(assemblyPath);
- }
-
- public override string Name
- {
- get { return name; }
- }
- }
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/Humanizer.Tests/App_Packages/ApiApprover.3.0.1/PublicApiGenerator.cs b/src/Humanizer.Tests/App_Packages/ApiApprover.3.0.1/PublicApiGenerator.cs
deleted file mode 100644
index 06438ec6e..000000000
--- a/src/Humanizer.Tests/App_Packages/ApiApprover.3.0.1/PublicApiGenerator.cs
+++ /dev/null
@@ -1,795 +0,0 @@
-#if NET46
-using System;
-using System.CodeDom;
-using System.CodeDom.Compiler;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Microsoft.CSharp;
-using Mono.Cecil;
-using Mono.Cecil.Rocks;
-using TypeAttributes = System.Reflection.TypeAttributes;
-
-// ReSharper disable CheckNamespace
-// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
-namespace ApiApprover
-{
- public static class CecilEx
- {
- public static IEnumerable GetMembers(this TypeDefinition type)
- {
- return type.Fields.Cast()
- .Concat(type.Methods)
- .Concat(type.Properties)
- .Concat(type.Events);
- }
- }
-
-
- public static class PublicApiGenerator
- {
- // TODO: Assembly references?
- // TODO: Better handle namespaces - using statements? - requires non-qualified type names
- public static string CreatePublicApiForAssembly(AssemblyDefinition assembly)
- {
- return CreatePublicApiForAssembly(assembly, t => true, true);
- }
-
- public static string CreatePublicApiForAssembly(AssemblyDefinition assembly, Func shouldIncludeType, bool shouldIncludeAssemblyAttributes)
- {
- var publicApiBuilder = new StringBuilder();
- var cgo = new CodeGeneratorOptions
- {
- BracingStyle = "C",
- BlankLinesBetweenMembers = false,
- VerbatimOrder = false
- };
-
- using (var provider = new CSharpCodeProvider())
- {
- var compileUnit = new CodeCompileUnit();
- if (shouldIncludeAssemblyAttributes && assembly.HasCustomAttributes)
- {
- PopulateCustomAttributes(assembly, compileUnit.AssemblyCustomAttributes);
- }
-
- var publicTypes = assembly.Modules.SelectMany(m => m.GetTypes())
- .Where(t => !t.IsNested && ShouldIncludeType(t) && shouldIncludeType(t))
- .OrderBy(t => t.FullName);
- foreach (var publicType in publicTypes)
- {
- var @namespace = compileUnit.Namespaces.Cast()
- .FirstOrDefault(n => n.Name == publicType.Namespace);
- if (@namespace == null)
- {
- @namespace = new CodeNamespace(publicType.Namespace);
- compileUnit.Namespaces.Add(@namespace);
- }
-
- var typeDeclaration = CreateTypeDeclaration(publicType);
- @namespace.Types.Add(typeDeclaration);
- }
-
- using (var writer = new StringWriter())
- {
- provider.GenerateCodeFromCompileUnit(compileUnit, writer, cgo);
- var typeDeclarationText = NormaliseGeneratedCode(writer);
- publicApiBuilder.AppendLine(typeDeclarationText);
- }
- }
- return NormaliseLineEndings(publicApiBuilder.ToString().Trim());
- }
-
- private static string NormaliseLineEndings(string value)
- {
- return Regex.Replace(value, @"\r\n|\n\r|\r|\n", Environment.NewLine);
- }
-
- private static bool IsDelegate(TypeDefinition publicType)
- {
- return publicType.BaseType != null && publicType.BaseType.FullName == "System.MulticastDelegate";
- }
-
- private static bool ShouldIncludeType(TypeDefinition t)
- {
- return (t.IsPublic || t.IsNestedPublic || t.IsNestedFamily) && !IsCompilerGenerated(t);
- }
-
- private static bool ShouldIncludeMember(IMemberDefinition m)
- {
- return !IsCompilerGenerated(m) && !IsDotNetTypeMember(m) && !(m is FieldDefinition);
- }
-
- private static bool IsCompilerGenerated(IMemberDefinition m)
- {
- return m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute");
- }
-
- private static bool IsDotNetTypeMember(IMemberDefinition m)
- {
- if (m.DeclaringType == null || m.DeclaringType.FullName == null)
- return false;
- return m.DeclaringType.FullName.StartsWith("System") || m.DeclaringType.FullName.StartsWith("Microsoft");
- }
-
- static void AddMemberToTypeDeclaration(CodeTypeDeclaration typeDeclaration, IMemberDefinition memberInfo)
- {
- if (memberInfo is MethodDefinition methodDefinition)
- {
- if (methodDefinition.IsConstructor)
- AddCtorToTypeDeclaration(typeDeclaration, methodDefinition);
- else
- AddMethodToTypeDeclaration(typeDeclaration, methodDefinition);
- }
- else if (memberInfo is PropertyDefinition)
- {
- AddPropertyToTypeDeclaration(typeDeclaration, (PropertyDefinition)memberInfo);
- }
- else if (memberInfo is EventDefinition)
- {
- typeDeclaration.Members.Add(GenerateEvent((EventDefinition)memberInfo));
- }
- else if (memberInfo is FieldDefinition)
- {
- AddFieldToTypeDeclaration(typeDeclaration, (FieldDefinition)memberInfo);
- }
- }
-
- static string NormaliseGeneratedCode(StringWriter writer)
- {
- var gennedClass = writer.ToString();
- const string autoGeneratedHeader = @"^//-+\s*$.*^//-+\s*$";
- const string emptyGetSet = @"\s+{\s+get\s+{\s+}\s+set\s+{\s+}\s+}";
- const string emptyGet = @"\s+{\s+get\s+{\s+}\s+}";
- const string emptySet = @"\s+{\s+set\s+{\s+}\s+}";
- const string getSet = @"\s+{\s+get;\s+set;\s+}";
- const string get = @"\s+{\s+get;\s+}";
- const string set = @"\s+{\s+set;\s+}";
- gennedClass = Regex.Replace(gennedClass, autoGeneratedHeader, string.Empty,
- RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Singleline);
- gennedClass = Regex.Replace(gennedClass, emptyGetSet, " { get; set; }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, getSet, " { get; set; }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, emptyGet, " { get; }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, emptySet, " { set; }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, get, " { get; }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, set, " { set; }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, @"\s+{\s+}", " { }", RegexOptions.IgnorePatternWhitespace);
- gennedClass = Regex.Replace(gennedClass, @"\)\s+;", ");", RegexOptions.IgnorePatternWhitespace);
- return gennedClass;
- }
-
- static CodeTypeDeclaration CreateTypeDeclaration(TypeDefinition publicType)
- {
- if (IsDelegate(publicType))
- return CreateDelegateDeclaration(publicType);
-
- bool @static = false;
- TypeAttributes attributes = 0;
- if (publicType.IsPublic || publicType.IsNestedPublic)
- attributes |= TypeAttributes.Public;
- if (publicType.IsNestedFamily)
- attributes |= TypeAttributes.NestedFamily;
- if (publicType.IsSealed && !publicType.IsAbstract)
- attributes |= TypeAttributes.Sealed;
- else if (!publicType.IsSealed && publicType.IsAbstract && !publicType.IsInterface)
- attributes |= TypeAttributes.Abstract;
- else if (publicType.IsSealed && publicType.IsAbstract)
- @static = true;
-
- // Static support is a hack. CodeDOM does support it, and this isn't
- // correct C#, but it's good enough for our API outline
- var name = publicType.Name;
-
- var index = name.IndexOf('`');
- if (index != -1)
- name = name.Substring(0, index);
- var declaration = new CodeTypeDeclaration(@static ? "static " + name : name)
- {
- CustomAttributes = CreateCustomAttributes(publicType),
- // TypeAttributes must be specified before the IsXXX as they manipulate TypeAttributes!
- TypeAttributes = attributes,
- IsClass = publicType.IsClass,
- IsEnum = publicType.IsEnum,
- IsInterface = publicType.IsInterface,
- IsStruct = publicType.IsValueType && !publicType.IsPrimitive && !publicType.IsEnum,
- };
-
- if (declaration.IsInterface && publicType.BaseType != null)
- throw new NotImplementedException("Base types for interfaces needs testing");
-
- PopulateGenericParameters(publicType, declaration.TypeParameters);
-
- if (publicType.BaseType != null && ShouldOutputBaseType(publicType))
- {
- if (publicType.BaseType.FullName == "System.Enum")
- {
- var underlyingType = publicType.GetEnumUnderlyingType();
- if (underlyingType.FullName != "System.Int32")
- declaration.BaseTypes.Add(CreateCodeTypeReference(underlyingType));
- }
- else
- declaration.BaseTypes.Add(CreateCodeTypeReference(publicType.BaseType));
- }
- foreach (var @interface in publicType.Interfaces.OrderBy(i => i.FullName))
- declaration.BaseTypes.Add(CreateCodeTypeReference(@interface));
-
- foreach (var memberInfo in publicType.GetMembers().Where(ShouldIncludeMember).OrderBy(m => m.Name))
- AddMemberToTypeDeclaration(declaration, memberInfo);
-
- // Fields should be in defined order for an enum
- var fields = !publicType.IsEnum
- ? publicType.Fields.OrderBy(f => f.Name)
- : (IEnumerable)publicType.Fields;
- foreach (var field in fields)
- AddMemberToTypeDeclaration(declaration, field);
-
- foreach (var nestedType in publicType.NestedTypes.Where(ShouldIncludeType).OrderBy(t => t.FullName))
- {
- var nestedTypeDeclaration = CreateTypeDeclaration(nestedType);
- declaration.Members.Add(nestedTypeDeclaration);
- }
-
- return declaration;
- }
-
- private static CodeTypeDeclaration CreateDelegateDeclaration(TypeDefinition publicType)
- {
- var invokeMethod = publicType.Methods.Single(m => m.Name == "Invoke");
- var name = publicType.Name;
- var index = name.IndexOf('`');
- if (index != -1)
- name = name.Substring(0, index);
- var declaration = new CodeTypeDelegate(name)
- {
- Attributes = MemberAttributes.Public,
- CustomAttributes = CreateCustomAttributes(publicType),
- ReturnType = CreateCodeTypeReference(invokeMethod.ReturnType),
- };
-
- // CodeDOM. No support. Return type attributes.
- PopulateCustomAttributes(invokeMethod.MethodReturnType, declaration.CustomAttributes, type => ModifyCodeTypeReference(type, "return:"));
- PopulateGenericParameters(publicType, declaration.TypeParameters);
- PopulateMethodParameters(invokeMethod, declaration.Parameters);
-
- // Of course, CodeDOM doesn't support generic type parameters for delegates. Of course.
- if (declaration.TypeParameters.Count > 0)
- {
- var parameterNames = from parameterType in declaration.TypeParameters.Cast()
- select parameterType.Name;
- declaration.Name = string.Format("{0}<{1}>", declaration.Name, string.Join(", ", parameterNames));
- }
-
- return declaration;
- }
-
- private static bool ShouldOutputBaseType(TypeDefinition publicType)
- {
- return publicType.BaseType.FullName != "System.Object" && publicType.BaseType.FullName != "System.ValueType";
- }
-
- private static void PopulateGenericParameters(IGenericParameterProvider publicType, CodeTypeParameterCollection parameters)
- {
- foreach (var parameter in publicType.GenericParameters)
- {
- if (parameter.HasCustomAttributes)
- throw new NotImplementedException("Attributes on type parameters is not supported. And weird");
-
- // A little hacky. Means we get "in" and "out" prefixed on any constraints, but it's either that
- // or add it as a custom attribute, which looks even weirder
- var name = parameter.Name;
- if (parameter.IsCovariant)
- name = "out " + name;
- if (parameter.IsContravariant)
- name = "in " + name;
-
- var typeParameter = new CodeTypeParameter(name)
- {
- HasConstructorConstraint =
- parameter.HasDefaultConstructorConstraint && !parameter.HasNotNullableValueTypeConstraint
- };
- if (parameter.HasNotNullableValueTypeConstraint)
- typeParameter.Constraints.Add(" struct"); // Extra space is a hack!
- if (parameter.HasReferenceTypeConstraint)
- typeParameter.Constraints.Add(" class");
- foreach (var constraint in parameter.Constraints.Where(t => t.FullName != "System.ValueType"))
- {
- typeParameter.Constraints.Add(CreateCodeTypeReference(constraint.GetElementType()));
- }
- parameters.Add(typeParameter);
- }
- }
-
- private static CodeAttributeDeclarationCollection CreateCustomAttributes(ICustomAttributeProvider type)
- {
- var attributes = new CodeAttributeDeclarationCollection();
- PopulateCustomAttributes(type, attributes);
- return attributes;
- }
-
- private static void PopulateCustomAttributes(ICustomAttributeProvider type,
- CodeAttributeDeclarationCollection attributes)
- {
- PopulateCustomAttributes(type, attributes, ctr => ctr);
- }
-
- private static void PopulateCustomAttributes(ICustomAttributeProvider type,
- CodeAttributeDeclarationCollection attributes, Func codeTypeModifier)
- {
- foreach (var customAttribute in type.CustomAttributes.Where(ShouldIncludeAttribute).OrderBy(a => a.AttributeType.FullName).ThenBy(a => ConvertAttrbuteToCode(codeTypeModifier, a)))
- {
- var attribute = GenerateCodeAttributeDeclaration(codeTypeModifier, customAttribute);
- attributes.Add(attribute);
- }
- }
-
- private static CodeAttributeDeclaration GenerateCodeAttributeDeclaration(Func codeTypeModifier, CustomAttribute customAttribute)
- {
- var attribute = new CodeAttributeDeclaration(codeTypeModifier(CreateCodeTypeReference(customAttribute.AttributeType)));
- foreach (var arg in customAttribute.ConstructorArguments)
- {
- attribute.Arguments.Add(new CodeAttributeArgument(CreateInitialiserExpression(arg)));
- }
- foreach (var field in customAttribute.Fields.OrderBy(f => f.Name))
- {
- attribute.Arguments.Add(new CodeAttributeArgument(field.Name, CreateInitialiserExpression(field.Argument)));
- }
- foreach (var property in customAttribute.Properties.OrderBy(p => p.Name))
- {
- attribute.Arguments.Add(new CodeAttributeArgument(property.Name, CreateInitialiserExpression(property.Argument)));
- }
- return attribute;
- }
-
- // Litee: This method is used for additional sorting of custom attributes when multiple values are allowed
- private static object ConvertAttrbuteToCode(Func codeTypeModifier, CustomAttribute customAttribute)
- {
- using (var provider = new CSharpCodeProvider())
- {
- var cgo = new CodeGeneratorOptions
- {
- BracingStyle = "C",
- BlankLinesBetweenMembers = false,
- VerbatimOrder = false
- };
- var attribute = GenerateCodeAttributeDeclaration(codeTypeModifier, customAttribute);
- var declaration = new CodeTypeDeclaration("DummyClass")
- {
- CustomAttributes = new CodeAttributeDeclarationCollection(new[] { attribute }),
- };
- using (var writer = new StringWriter())
- {
- provider.GenerateCodeFromType(declaration, writer, cgo);
- return writer.ToString();
- }
- }
- }
-
- private static readonly HashSet SkipAttributeNames = new HashSet
- {
- "System.CodeDom.Compiler.GeneratedCodeAttribute",
- "System.ComponentModel.EditorBrowsableAttribute",
- "System.Runtime.CompilerServices.AsyncStateMachineAttribute",
- "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
- "System.Runtime.CompilerServices.CompilationRelaxationsAttribute",
- "System.Runtime.CompilerServices.ExtensionAttribute",
- "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute",
- "System.Reflection.DefaultMemberAttribute",
- "System.Diagnostics.DebuggableAttribute",
- "System.Diagnostics.DebuggerNonUserCodeAttribute",
- "System.Diagnostics.DebuggerStepThroughAttribute",
- "System.Reflection.AssemblyCompanyAttribute",
- "System.Reflection.AssemblyConfigurationAttribute",
- "System.Reflection.AssemblyCopyrightAttribute",
- "System.Reflection.AssemblyDescriptionAttribute",
- "System.Reflection.AssemblyFileVersionAttribute",
- "System.Reflection.AssemblyInformationalVersionAttribute",
- "System.Reflection.AssemblyProductAttribute",
- "System.Reflection.AssemblyTitleAttribute",
- "System.Reflection.AssemblyTrademarkAttribute"
- };
-
- private static bool ShouldIncludeAttribute(CustomAttribute attribute)
- {
- return !SkipAttributeNames.Contains(attribute.AttributeType.FullName);
- }
-
- private static CodeExpression CreateInitialiserExpression(CustomAttributeArgument attributeArgument)
- {
- if (attributeArgument.Value is CustomAttributeArgument)
- {
- return CreateInitialiserExpression((CustomAttributeArgument) attributeArgument.Value);
- }
-
- if (attributeArgument.Value is CustomAttributeArgument[])
- {
- var initialisers = from argument in (CustomAttributeArgument[]) attributeArgument.Value
- select CreateInitialiserExpression(argument);
- return new CodeArrayCreateExpression(CreateCodeTypeReference(attributeArgument.Type), initialisers.ToArray());
- }
-
- var type = attributeArgument.Type.Resolve();
- var value = attributeArgument.Value;
- if (type.BaseType != null && type.BaseType.FullName == "System.Enum")
- {
- var originalValue = Convert.ToInt64(value);
- if (type.CustomAttributes.Any(a => a.AttributeType.FullName == "System.FlagsAttribute"))
- {
- //var allFlags = from f in type.Fields
- // where f.Constant != null
- // let v = Convert.ToInt64(f.Constant)
- // where v == 0 || (originalValue & v) != 0
- // select (CodeExpression)new CodeFieldReferenceExpression(typeExpression, f.Name);
- //return allFlags.Aggregate((current, next) => new CodeBinaryOperatorExpression(current, CodeBinaryOperatorType.BitwiseOr, next));
-
- // I'd rather use the above, as it's just using the CodeDOM, but it puts
- // brackets around each CodeBinaryOperatorExpression
- var flags = from f in type.Fields
- where f.Constant != null
- let v = Convert.ToInt64(f.Constant)
- where v == 0 || (originalValue & v) != 0
- select type.FullName + "." + f.Name;
- return new CodeSnippetExpression(flags.Aggregate((current, next) => current + " | " + next));
- }
-
- var allFlags = from f in type.Fields
- where f.Constant != null
- let v = Convert.ToInt64(f.Constant)
- where v == originalValue
- select new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(CreateCodeTypeReference(type)), f.Name);
- return allFlags.FirstOrDefault();
- }
-
- if (type.FullName == "System.Type" && value is TypeReference)
- {
- return new CodeTypeOfExpression(CreateCodeTypeReference((TypeReference)value));
- }
-
- if (value is string)
- {
- // CodeDOM outputs a verbatim string. Any string with \n is treated as such, so normalise
- // it to make it easier for comparisons
- value = Regex.Replace((string)value, @"\n", "\\n");
- value = Regex.Replace((string)value, @"\r\n|\r\\n", "\\r\\n");
- }
-
- return new CodePrimitiveExpression(value);
- }
-
- private static void AddCtorToTypeDeclaration(CodeTypeDeclaration typeDeclaration, MethodDefinition member)
- {
- if (member.IsAssembly || member.IsPrivate)
- return;
-
- var method = new CodeConstructor
- {
- CustomAttributes = CreateCustomAttributes(member),
- Name = member.Name,
- Attributes = GetMethodAttributes(member)
- };
- PopulateMethodParameters(member, method.Parameters);
-
- typeDeclaration.Members.Add(method);
- }
-
- private static void AddMethodToTypeDeclaration(CodeTypeDeclaration typeDeclaration, MethodDefinition member)
- {
- if (member.IsAssembly || member.IsPrivate || member.IsSpecialName)
- return;
-
- var returnType = CreateCodeTypeReference(member.ReturnType);
- if (IsAsyncMethod(member))
- returnType = MakeAsync(returnType);
-
- var method = new CodeMemberMethod
- {
- Name = member.Name,
- Attributes = GetMethodAttributes(member),
- CustomAttributes = CreateCustomAttributes(member),
- ReturnType = returnType,
- };
- PopulateCustomAttributes(member.MethodReturnType, method.ReturnTypeCustomAttributes);
- PopulateGenericParameters(member, method.TypeParameters);
- PopulateMethodParameters(member, method.Parameters, IsExtensionMethod(member));
-
- typeDeclaration.Members.Add(method);
- }
-
- private static bool IsAsyncMethod(ICustomAttributeProvider method)
- {
- return method.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.AsyncStateMachineAttribute");
- }
-
- private static bool IsExtensionMethod(ICustomAttributeProvider method)
- {
- return method.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
- }
-
- private static void PopulateMethodParameters(IMethodSignature member,
- CodeParameterDeclarationExpressionCollection parameters, bool isExtension = false)
- {
- foreach (var parameter in member.Parameters)
- {
- FieldDirection direction = 0;
- if (parameter.IsOut)
- direction |= FieldDirection.Out;
- else if (parameter.ParameterType.IsByReference)
- direction |= FieldDirection.Ref;
-
- var parameterType = parameter.ParameterType.IsByReference
- ? parameter.ParameterType.GetElementType()
- : parameter.ParameterType;
-
- var type = CreateCodeTypeReference(parameterType);
-
- if (isExtension)
- {
- type = ModifyCodeTypeReference(type, "this");
- isExtension = false;
- }
-
- var name = parameter.HasConstant
- ? string.Format("{0} = {1}", parameter.Name, FormatParameterConstant(parameter))
- : parameter.Name;
- var expression = new CodeParameterDeclarationExpression(type, name)
- {
- Direction = direction,
- CustomAttributes = CreateCustomAttributes(parameter)
- };
- parameters.Add(expression);
- }
- }
-
- private static object FormatParameterConstant(IConstantProvider parameter)
- {
- return parameter.Constant is string ? string.Format("\"{0}\"", parameter.Constant) : (parameter.Constant ?? "null");
- }
-
- static MemberAttributes GetMethodAttributes(MethodDefinition method)
- {
- MemberAttributes access = 0;
- if (method.IsFamily)
- access = MemberAttributes.Family;
- if (method.IsPublic)
- access = MemberAttributes.Public;
- if (method.IsAssembly)
- access = MemberAttributes.Assembly;
- if (method.IsFamilyAndAssembly)
- access = MemberAttributes.FamilyAndAssembly;
- if (method.IsFamilyOrAssembly)
- access = MemberAttributes.FamilyOrAssembly;
-
- MemberAttributes scope = 0;
- if (method.IsStatic)
- scope |= MemberAttributes.Static;
- if (method.IsFinal || !method.IsVirtual)
- scope |= MemberAttributes.Final;
- if (method.IsAbstract)
- scope |= MemberAttributes.Abstract;
- if (method.IsVirtual && !method.IsNewSlot)
- scope |= MemberAttributes.Override;
-
- MemberAttributes vtable = 0;
- if (IsHidingMethod(method))
- vtable = MemberAttributes.New;
-
- return access | scope | vtable;
- }
-
- private static bool IsHidingMethod(MethodDefinition method)
- {
- var typeDefinition = method.DeclaringType;
-
- // If we're an interface, just try and find any method with the same signature
- // in any of the interfaces that we implement
- if (typeDefinition.IsInterface)
- {
- var interfaceMethods = from @interfaceReference in typeDefinition.Interfaces
- let interfaceDefinition = @interfaceReference.Resolve()
- where interfaceDefinition != null
- select interfaceDefinition.Methods;
-
- return interfaceMethods.Any(ms => MetadataResolver.GetMethod(ms, method) != null);
- }
-
- // If we're not an interface, find a base method that isn't virtual
- return !method.IsVirtual && GetBaseTypes(typeDefinition).Any(d => MetadataResolver.GetMethod(d.Methods, method) != null);
- }
-
- private static IEnumerable GetBaseTypes(TypeDefinition type)
- {
- var baseType = type.BaseType;
- while (baseType != null)
- {
- var definition = baseType.Resolve();
- if (definition == null)
- yield break;
- yield return definition;
-
- baseType = baseType.DeclaringType;
- }
- }
-
- private static void AddPropertyToTypeDeclaration(CodeTypeDeclaration typeDeclaration, PropertyDefinition member)
- {
- var getterAttributes = member.GetMethod != null ? GetMethodAttributes(member.GetMethod) : 0;
- var setterAttributes = member.SetMethod != null ? GetMethodAttributes(member.SetMethod) : 0;
-
- if (!HasVisiblePropertyMethod(getterAttributes) && !HasVisiblePropertyMethod(setterAttributes))
- return;
-
- var propertyAttributes = GetPropertyAttributes(getterAttributes, setterAttributes);
-
- var propertyType = member.PropertyType.IsGenericParameter
- ? new CodeTypeReference(member.PropertyType.Name)
- : CreateCodeTypeReference(member.PropertyType);
-
- var property = new CodeMemberProperty
- {
- Name = member.Name,
- Type = propertyType,
- Attributes = propertyAttributes,
- CustomAttributes = CreateCustomAttributes(member),
- HasGet = member.GetMethod != null && HasVisiblePropertyMethod(getterAttributes),
- HasSet = member.SetMethod != null && HasVisiblePropertyMethod(setterAttributes)
- };
-
- // Here's a nice hack, because hey, guess what, the CodeDOM doesn't support
- // attributes on getters or setters
- if (member.GetMethod != null && member.GetMethod.HasCustomAttributes)
- {
- PopulateCustomAttributes(member.GetMethod, property.CustomAttributes, type => ModifyCodeTypeReference(type, "get:"));
- }
- if (member.SetMethod != null && member.SetMethod.HasCustomAttributes)
- {
- PopulateCustomAttributes(member.GetMethod, property.CustomAttributes, type => ModifyCodeTypeReference(type, "set:"));
- }
-
- foreach (var parameter in member.Parameters)
- {
- property.Parameters.Add(
- new CodeParameterDeclarationExpression(CreateCodeTypeReference(parameter.ParameterType),
- parameter.Name));
- }
-
- // TODO: CodeDOM has no support for different access modifiers for getters and setters
- // TODO: CodeDOM has no support for attributes on setters or getters - promote to property?
-
- typeDeclaration.Members.Add(property);
- }
-
- private static MemberAttributes GetPropertyAttributes(MemberAttributes getterAttributes, MemberAttributes setterAttributes)
- {
- MemberAttributes access = 0;
- var getterAccess = getterAttributes & MemberAttributes.AccessMask;
- var setterAccess = setterAttributes & MemberAttributes.AccessMask;
- if (getterAccess == MemberAttributes.Public || setterAccess == MemberAttributes.Public)
- access = MemberAttributes.Public;
- else if (getterAccess == MemberAttributes.Family || setterAccess == MemberAttributes.Family)
- access = MemberAttributes.Family;
- else if (getterAccess == MemberAttributes.FamilyAndAssembly || setterAccess == MemberAttributes.FamilyAndAssembly)
- access = MemberAttributes.FamilyAndAssembly;
- else if (getterAccess == MemberAttributes.FamilyOrAssembly || setterAccess == MemberAttributes.FamilyOrAssembly)
- access = MemberAttributes.FamilyOrAssembly;
- else if (getterAccess == MemberAttributes.Assembly || setterAccess == MemberAttributes.Assembly)
- access = MemberAttributes.Assembly;
- else if (getterAccess == MemberAttributes.Private || setterAccess == MemberAttributes.Private)
- access = MemberAttributes.Private;
-
- // Scope should be the same for getter and setter. If one isn't specified, it'll be 0
- var getterScope = getterAttributes & MemberAttributes.ScopeMask;
- var setterScope = setterAttributes & MemberAttributes.ScopeMask;
- var scope = (MemberAttributes) Math.Max((int) getterScope, (int) setterScope);
-
- // Vtable should be the same for getter and setter. If one isn't specified, it'll be 0
- var getterVtable = getterAttributes & MemberAttributes.VTableMask;
- var setterVtable = setterAttributes & MemberAttributes.VTableMask;
- var vtable = (MemberAttributes) Math.Max((int) getterVtable, (int) setterVtable);
-
- return access | scope | vtable;
- }
-
- private static bool HasVisiblePropertyMethod(MemberAttributes attributes)
- {
- var access = attributes & MemberAttributes.AccessMask;
- return access == MemberAttributes.Public || access == MemberAttributes.Family ||
- access == MemberAttributes.FamilyOrAssembly;
- }
-
- static CodeTypeMember GenerateEvent(EventDefinition eventDefinition)
- {
- var @event = new CodeMemberEvent
- {
- Name = eventDefinition.Name,
- Attributes = MemberAttributes.Public | MemberAttributes.Final,
- CustomAttributes = CreateCustomAttributes(eventDefinition),
- Type = CreateCodeTypeReference(eventDefinition.EventType)
- };
-
- return @event;
- }
-
- static void AddFieldToTypeDeclaration(CodeTypeDeclaration typeDeclaration, FieldDefinition memberInfo)
- {
- if (memberInfo.IsPrivate || memberInfo.IsAssembly || memberInfo.IsSpecialName)
- return;
-
- MemberAttributes attributes = 0;
- if (memberInfo.HasConstant)
- attributes |= MemberAttributes.Const;
- if (memberInfo.IsFamily)
- attributes |= MemberAttributes.Family;
- if (memberInfo.IsPublic)
- attributes |= MemberAttributes.Public;
- if (memberInfo.IsStatic && !memberInfo.HasConstant)
- attributes |= MemberAttributes.Static;
-
- // TODO: Values for readonly fields are set in the ctor
- var codeTypeReference = CreateCodeTypeReference(memberInfo.FieldType);
- if (memberInfo.IsInitOnly)
- codeTypeReference = MakeReadonly(codeTypeReference);
- var field = new CodeMemberField(codeTypeReference, memberInfo.Name)
- {
- Attributes = attributes,
- CustomAttributes = CreateCustomAttributes(memberInfo)
- };
-
- if (memberInfo.HasConstant)
- field.InitExpression = new CodePrimitiveExpression(memberInfo.Constant);
-
- typeDeclaration.Members.Add(field);
- }
-
- private static CodeTypeReference MakeReadonly(CodeTypeReference typeReference)
- {
- return ModifyCodeTypeReference(typeReference, "readonly");
- }
-
- private static CodeTypeReference MakeAsync(CodeTypeReference typeReference)
- {
- return ModifyCodeTypeReference(typeReference, "async");
- }
-
- private static CodeTypeReference ModifyCodeTypeReference(CodeTypeReference typeReference, string modifier)
- {
- using (var provider = new CSharpCodeProvider())
- return new CodeTypeReference(modifier + " " + provider.GetTypeOutput(typeReference));
- }
-
- private static CodeTypeReference CreateCodeTypeReference(TypeReference type)
- {
- var typeName = GetTypeName(type);
- return new CodeTypeReference(typeName, CreateGenericArguments(type));
- }
-
- private static string GetTypeName(TypeReference type)
- {
- if (type.IsGenericParameter)
- return type.Name;
-
- if (!type.IsNested)
- {
- return (!string.IsNullOrEmpty(type.Namespace) ? (type.Namespace + ".") : "") + type.Name;
- }
-
- return GetTypeName(type.DeclaringType) + "." + type.Name;
- }
-
- private static CodeTypeReference[] CreateGenericArguments(TypeReference type)
- {
- var genericInstance = type as IGenericInstance;
- if (genericInstance == null) return null;
-
- var genericArguments = new List();
- foreach (var argument in genericInstance.GenericArguments)
- {
- genericArguments.Add(CreateCodeTypeReference(argument));
- }
- return genericArguments.ToArray();
- }
- }
-}
-// ReSharper restore BitwiseOperatorOnEnumWihtoutFlags
-// ReSharper restore CheckNamespace
-#endif
diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj
index 2bab8a48e..5ac882661 100644
--- a/src/Humanizer.Tests/Humanizer.Tests.csproj
+++ b/src/Humanizer.Tests/Humanizer.Tests.csproj
@@ -1,22 +1,21 @@
- net46;netcoreapp2.0
+ net46;netcoreapp2.1
-
+
$(DefineConstants);NETFX_CORE
-
-
-
-
+
+
+
-
-
+
+
@@ -24,7 +23,9 @@
-
+
+ PreserveNewest
+
PreserveNewest
diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt b/src/Humanizer.Tests/PublicApiApprovalTest.approve_public_api.approved.txt
similarity index 99%
rename from src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt
rename to src/Humanizer.Tests/PublicApiApprovalTest.approve_public_api.approved.txt
index c62df0d51..433b7b3c8 100644
--- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt
+++ b/src/Humanizer.Tests/PublicApiApprovalTest.approve_public_api.approved.txt
@@ -1,9 +1,7 @@
[assembly: System.Resources.NeutralResourcesLanguageAttribute("en")]
[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v1.0", FrameworkDisplayName="")]
-
namespace Humanizer.Bytes
{
-
public class ByteRate
{
public ByteRate(Humanizer.Bytes.ByteSize size, System.TimeSpan interval) { }
@@ -63,7 +61,6 @@ namespace Humanizer.Bytes
}
namespace Humanizer
{
-
public class static ByteSizeExtensions
{
public static Humanizer.Bytes.ByteSize Bits(this byte input) { }
@@ -1020,7 +1017,6 @@ namespace Humanizer
}
namespace Humanizer.Configuration
{
-
public class static Configurator
{
public static Humanizer.Configuration.LocaliserRegistry CollectionFormatters { get; }
@@ -1045,7 +1041,6 @@ namespace Humanizer.Configuration
}
namespace Humanizer.DateTimeHumanizeStrategy
{
-
public class DefaultDateTimeHumanizeStrategy : Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy
{
public DefaultDateTimeHumanizeStrategy() { }
@@ -1077,7 +1072,6 @@ namespace Humanizer.DateTimeHumanizeStrategy
}
namespace Humanizer.Inflections
{
-
public class static Vocabularies
{
public static Humanizer.Inflections.Vocabulary Default { get; }
@@ -1094,7 +1088,6 @@ namespace Humanizer.Inflections
}
namespace Humanizer.Localisation.CollectionFormatters
{
-
public interface ICollectionFormatter
{
string Humanize(System.Collections.Generic.IEnumerable collection);
@@ -1107,7 +1100,6 @@ namespace Humanizer.Localisation.CollectionFormatters
}
namespace Humanizer.Localisation.DateToOrdinalWords
{
-
public interface IDateToOrdinalWordConverter
{
string Convert(System.DateTime date);
@@ -1116,7 +1108,6 @@ namespace Humanizer.Localisation.DateToOrdinalWords
}
namespace Humanizer.Localisation.Formatters
{
-
public class DefaultFormatter : Humanizer.Localisation.Formatters.IFormatter
{
public DefaultFormatter(string localeCode) { }
@@ -1141,7 +1132,6 @@ namespace Humanizer.Localisation.Formatters
}
namespace Humanizer.Localisation.NumberToWords
{
-
public interface INumberToWordsConverter
{
string Convert(long number);
@@ -1152,7 +1142,6 @@ namespace Humanizer.Localisation.NumberToWords
}
namespace Humanizer.Localisation.Ordinalizers
{
-
public interface IOrdinalizer
{
string Convert(int number, string numberString);
@@ -1161,7 +1150,6 @@ namespace Humanizer.Localisation.Ordinalizers
}
namespace Humanizer.Localisation
{
-
public class ResourceKeys
{
public ResourceKeys() { }
diff --git a/src/Humanizer.sln b/src/Humanizer.sln
index 7a62ee41a..2bfaf0a70 100644
--- a/src/Humanizer.sln
+++ b/src/Humanizer.sln
@@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\.editorconfig = ..\.editorconfig
..\.gitattributes = ..\.gitattributes
..\.gitignore = ..\.gitignore
- ..\appveyor.yml = ..\appveyor.yml
..\NuSpecs\Humanizer.Core.nuspec = ..\NuSpecs\Humanizer.Core.nuspec
..\NuSpecs\Humanizer.nuspec = ..\NuSpecs\Humanizer.nuspec
..\LICENSE = ..\LICENSE
@@ -23,7 +22,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{97AAE24D-0488-42AE-A585-86D882F23D5F}"
ProjectSection(SolutionItems) = preProject
..\build.cmd = ..\build.cmd
- ..\build.proj = ..\build.proj
Humanizer.ruleset = Humanizer.ruleset
EndProjectSection
EndProject
@@ -41,6 +39,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{AA44
..\NuSpecs\Humanizer.Core.bn-BD.nuspec = ..\NuSpecs\Humanizer.Core.bn-BD.nuspec
..\NuSpecs\Humanizer.Core.cs.nuspec = ..\NuSpecs\Humanizer.Core.cs.nuspec
..\NuSpecs\Humanizer.Core.da.nuspec = ..\NuSpecs\Humanizer.Core.da.nuspec
+ ..\NuSpecs\Humanizer.Core.de-CH.nuspec = ..\NuSpecs\Humanizer.Core.de-CH.nuspec
+ ..\NuSpecs\Humanizer.Core.de-LI.nuspec = ..\NuSpecs\Humanizer.Core.de-LI.nuspec
..\NuSpecs\Humanizer.Core.de.nuspec = ..\NuSpecs\Humanizer.Core.de.nuspec
..\NuSpecs\Humanizer.Core.el.nuspec = ..\NuSpecs\Humanizer.Core.el.nuspec
..\NuSpecs\Humanizer.Core.es.nuspec = ..\NuSpecs\Humanizer.Core.es.nuspec
diff --git a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
index 7e582082f..f5f1d76ad 100644
--- a/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
+++ b/src/Humanizer/Configuration/NumberToWordsConverterRegistry.cs
@@ -24,6 +24,8 @@ public NumberToWordsConverterRegistry()
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());
diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj
index a187b776f..2e9a2bfc3 100644
--- a/src/Humanizer/Humanizer.csproj
+++ b/src/Humanizer/Humanizer.csproj
@@ -1,12 +1,12 @@
- netstandard1.0
+ netstandard1.0;netstandard2.0
Mehdi Khalili, Oren Novotny
https://raw.githubusercontent.com/Humanizr/Humanizer/master/LICENSE
https://github.com/Humanizr/Humanizer
2.12
A micro-framework that turns your normal strings, type names, enum fields, date fields ETC into a human friendly format
- Copyright © 2012-2017 Mehdi Khalili
+ Copyright © 2012-2018 Mehdi Khalili
Humanizer ($(TargetFramework))
true
true
diff --git a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs
index ae6f47276..a3fd652df 100644
--- a/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs
+++ b/src/Humanizer/Localisation/NumberToWords/ArabicNumberToWordsConverter.cs
@@ -17,16 +17,12 @@ internal class ArabicNumberToWordsConverter : GenderedNumberToWordsConverter
private static readonly string[] FeminineOnesGroup = { "", "واحدة", "اثنتان", "ثلاث", "أربع", "خمس", "ست", "سبع", "ثمان", "تسع", "عشر", "إحدى عشرة", "اثنتا عشرة", "ثلاث عشرة", "أربع عشرة", "خمس عشرة", "ست عشرة", "سبع عشرة", "ثمان عشرة", "تسع عشرة" };
- public override string Convert(long input, GrammaticalGender gender)
+ public override string Convert(long number, GrammaticalGender gender)
{
- if (input > Int32.MaxValue || input < Int32.MinValue)
- {
- throw new NotImplementedException();
- }
- var number = (int)input;
-
if (number == 0)
return "صفر";
+ if (number < 0)
+ return string.Format("ناقص {0}", Convert(-number, gender));
var result = string.Empty;
var groupLevel = 0;
@@ -225,4 +221,4 @@ private static string ParseNumber(string word, int number, GrammaticalGender gen
return word;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverter.cs
index 9ca130a4e..7e2f5c29c 100644
--- a/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverter.cs
+++ b/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverter.cs
@@ -1,166 +1,6 @@
-using System;
-using System.Collections.Generic;
-
-namespace Humanizer.Localisation.NumberToWords
+namespace Humanizer.Localisation.NumberToWords
{
- internal class GermanNumberToWordsConverter : GenderedNumberToWordsConverter
+ internal class GermanNumberToWordsConverter : GermanNumberToWordsConverterBase
{
- private static readonly string[] UnitsMap = { "null", "ein", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn", "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn" };
- private static readonly string[] TensMap = { "null", "zehn", "zwanzig", "dreißig", "vierzig", "fünfzig", "sechzig", "siebzig", "achtzig", "neunzig" };
- private static readonly string[] UnitsOrdinal = { string.Empty, "ers", "zwei", "drit", "vier", "fünf", "sechs", "sieb", "ach", "neun", "zehn", "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn" };
- private static readonly string[] MillionOrdinalSingular = {"einmillion", "einemillion"};
- private static readonly string[] MillionOrdinalPlural = {"{0}million", "{0}millionen"};
- private static readonly string[] BillionOrdinalSingular = {"einmilliard", "einemilliarde"};
- private static readonly string[] BillionOrdinalPlural = {"{0}milliard", "{0}milliarden"};
-
- public override string Convert(long input, GrammaticalGender gender)
- {
- if (input > Int32.MaxValue || input < Int32.MinValue)
- {
- throw new NotImplementedException();
- }
- var number = (int)input;
-
- if (number == 0)
- return "null";
-
- if (number < 0)
- return string.Format("minus {0}", Convert(-number));
-
- var parts = new List();
-
- var billions = number/1000000000;
- if (billions > 0)
- {
- parts.Add(Part("{0} Milliarden", "eine Milliarde", billions));
- number %= 1000000000;
- if (number > 0)
- parts.Add(" ");
- }
-
- var millions = number/1000000;
- if (millions > 0)
- {
- parts.Add(Part("{0} Millionen", "eine Million", millions));
- number %= 1000000;
- if (number > 0)
- parts.Add(" ");
- }
-
- var thousands = number/1000;
- if (thousands > 0)
- {
- parts.Add(Part("{0}tausend", "eintausend", thousands));
- number %= 1000;
- }
-
- var hundreds = number/100;
- if (hundreds > 0)
- {
- parts.Add(Part("{0}hundert", "einhundert", hundreds));
- number %= 100;
- }
-
- if (number > 0)
- {
- if (number < 20)
- {
- if (number > 1)
- parts.Add(UnitsMap[number]);
- else
- parts.Add("eins");
- }
- else
- {
- var units = number%10;
- if (units > 0)
- parts.Add(string.Format("{0}und", UnitsMap[units]));
-
- parts.Add(TensMap[number/10]);
- }
- }
-
- return string.Join("", parts);
- }
-
- public override string ConvertToOrdinal(int number, GrammaticalGender gender)
- {
- if (number == 0)
- return "null" + GetEndingForGender(gender);
-
- var parts = new List();
- if (number < 0)
- {
- parts.Add("minus ");
- number = -number;
- }
-
- var billions = number/1000000000;
- if (billions > 0)
- {
- number %= 1000000000;
- var noRest = NoRestIndex(number);
- parts.Add(Part(BillionOrdinalPlural[noRest], BillionOrdinalSingular[noRest], billions));
- }
-
- var millions = number/1000000;
- if (millions > 0)
- {
- number %= 1000000;
- var noRest = NoRestIndex(number);
- parts.Add(Part(MillionOrdinalPlural[noRest], MillionOrdinalSingular[noRest], millions));
- }
-
- var thousands = number/1000;
- if (thousands > 0)
- {
- parts.Add(Part("{0}tausend", "eintausend", thousands));
- number %= 1000;
- }
-
- var hundreds = number/100;
- if (hundreds > 0)
- {
- parts.Add(Part("{0}hundert", "einhundert", hundreds));
- number %= 100;
- }
-
- if (number > 0)
- parts.Add(number < 20 ? UnitsOrdinal[number] : Convert(number));
-
- if (number == 0 || number >= 20)
- parts.Add("s");
-
- parts.Add(GetEndingForGender(gender));
-
- return string.Join("", parts);
- }
-
- private string Part(string pluralFormat, string singular, int number)
- {
- if (number == 1)
- return singular;
- return string.Format(pluralFormat, Convert(number));
- }
-
- private static int NoRestIndex(int number)
- {
- return number == 0 ? 0 : 1;
- }
-
- private static string GetEndingForGender(GrammaticalGender gender)
- {
- switch (gender)
- {
- case GrammaticalGender.Masculine:
- return "ter";
- case GrammaticalGender.Feminine:
- return "te";
- case GrammaticalGender.Neuter:
- return "tes";
- default:
- throw new ArgumentOutOfRangeException(nameof(gender));
- }
- }
}
-}
\ No newline at end of file
+}
diff --git a/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs b/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs
new file mode 100644
index 000000000..88cb69bd0
--- /dev/null
+++ b/src/Humanizer/Localisation/NumberToWords/GermanNumberToWordsConverterBase.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+
+namespace Humanizer.Localisation.NumberToWords
+{
+ abstract class GermanNumberToWordsConverterBase : GenderedNumberToWordsConverter
+ {
+ private readonly string[] UnitsMap = { "null", "ein", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn", "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn" };
+ private readonly string[] TensMap = { "null", "zehn", "zwanzig", "dreißig", "vierzig", "fünfzig", "sechzig", "siebzig", "achtzig", "neunzig" };
+ private readonly string[] UnitsOrdinal = { string.Empty, "ers", "zwei", "drit", "vier", "fünf", "sechs", "sieb", "ach", "neun", "zehn", "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn" };
+ private readonly string[] HundredOrdinalSingular = { "einhundert" };
+ private readonly string[] HundredOrdinalPlural = { "{0}hundert" };
+ private readonly string[] ThousandOrdinalSingular = { "eintausend" };
+ private readonly string[] ThousandOrdinalPlural = { "{0}tausend" };
+ private readonly string[] MillionOrdinalSingular = { "einmillion", "einemillion" };
+ private readonly string[] MillionOrdinalPlural = { "{0}million", "{0}millionen" };
+ private readonly string[] BillionOrdinalSingular = { "einmilliard", "einemilliarde" };
+ private readonly string[] BillionOrdinalPlural = { "{0}milliard", "{0}milliarden" };
+
+ public override string Convert(long number, GrammaticalGender gender)
+ {
+ if (number == 0)
+ return UnitsMap[number];
+
+ var parts = new List();
+ if (number < 0)
+ {
+ parts.Add("minus ");
+ number = -number;
+ }
+
+ CollectParts(parts, ref number, 1000000000000000000, true, "{0} Trillionen", "eine Trillion");
+ CollectParts(parts, ref number, 1000000000000000, true, "{0} Billiarden", "eine Billiarde");
+ CollectParts(parts, ref number, 1000000000000, true, "{0} Billionen", "eine Billion");
+ CollectParts(parts, ref number, 1000000000, true, "{0} Milliarden", "eine Milliarde");
+ CollectParts(parts, ref number, 1000000, true, "{0} Millionen", "eine Million");
+ CollectParts(parts, ref number, 1000, false, "{0}tausend", "eintausend");
+ CollectParts(parts, ref number, 100, false, "{0}hundert", "einhundert");
+
+ if (number > 0)
+ {
+ if (number < 20)
+ {
+ if (number == 1 && gender == GrammaticalGender.Feminine)
+ parts.Add("eine");
+ else
+ parts.Add(UnitsMap[number]);
+ }
+ else
+ {
+ var units = number % 10;
+ if (units > 0)
+ parts.Add(string.Format("{0}und", UnitsMap[units]));
+
+ parts.Add(GetTens(number / 10));
+ }
+ }
+
+ return string.Join("", parts);
+ }
+
+ public override string ConvertToOrdinal(int number, GrammaticalGender gender)
+ {
+ if (number == 0)
+ return UnitsMap[number] + GetEndingForGender(gender);
+
+ var parts = new List();
+ if (number < 0)
+ {
+ parts.Add("minus ");
+ number = -number;
+ }
+
+ CollectOrdinalParts(parts, ref number, 1000000000, true, BillionOrdinalPlural, BillionOrdinalSingular);
+ CollectOrdinalParts(parts, ref number, 1000000, true, MillionOrdinalPlural, MillionOrdinalSingular);
+ CollectOrdinalParts(parts, ref number, 1000, false, ThousandOrdinalPlural, ThousandOrdinalSingular);
+ CollectOrdinalParts(parts, ref number, 100, false, HundredOrdinalPlural, HundredOrdinalSingular);
+
+ if (number > 0)
+ parts.Add(number < 20 ? UnitsOrdinal[number] : Convert(number));
+
+ if (number == 0 || number >= 20)
+ parts.Add("s");
+
+ parts.Add(GetEndingForGender(gender));
+
+ return string.Join("", parts);
+ }
+
+ private void CollectParts(ICollection parts, ref long number, long divisor, bool addSpaceBeforeNextPart, string pluralFormat, string singular)
+ {
+ if (number / divisor > 0)
+ {
+ parts.Add(Part(pluralFormat, singular, number / divisor));
+ number %= divisor;
+ if (addSpaceBeforeNextPart && number > 0)
+ parts.Add(" ");
+ }
+ }
+
+ private void CollectOrdinalParts(ICollection parts, ref int number, int divisor, bool evaluateNoRest, string[] pluralFormats, string[] singulars)
+ {
+ if (number / divisor > 0)
+ {
+ var noRest = evaluateNoRest ? NoRestIndex(number % divisor) : 0;
+ parts.Add(Part(pluralFormats[noRest], singulars[noRest], number / divisor));
+ number %= divisor;
+ }
+ }
+
+ private string Part(string pluralFormat, string singular, long number)
+ {
+ if (number == 1)
+ return singular;
+ return string.Format(pluralFormat, Convert(number));
+ }
+
+ private int NoRestIndex(int number)
+ {
+ return number == 0 ? 0 : 1;
+ }
+
+ private string GetEndingForGender(GrammaticalGender gender)
+ {
+ switch (gender)
+ {
+ case GrammaticalGender.Masculine:
+ return "ter";
+ case GrammaticalGender.Feminine:
+ return "te";
+ case GrammaticalGender.Neuter:
+ return "tes";
+ default:
+ throw new ArgumentOutOfRangeException(nameof(gender));
+ }
+ }
+
+ protected virtual string GetTens(long tens)
+ {
+ return TensMap[tens];
+ }
+ }
+}
diff --git a/src/Humanizer/Localisation/NumberToWords/GermanSwissLiechtensteinNumberToWordsConverter.cs b/src/Humanizer/Localisation/NumberToWords/GermanSwissLiechtensteinNumberToWordsConverter.cs
new file mode 100644
index 000000000..20a649865
--- /dev/null
+++ b/src/Humanizer/Localisation/NumberToWords/GermanSwissLiechtensteinNumberToWordsConverter.cs
@@ -0,0 +1,13 @@
+namespace Humanizer.Localisation.NumberToWords
+{
+ internal class GermanSwissLiechtensteinNumberToWordsConverter : GermanNumberToWordsConverterBase
+ {
+ protected override string GetTens(long tens)
+ {
+ if (tens == 3)
+ return "dreissig";
+
+ return base.GetTens(tens);
+ }
+ }
+}
diff --git a/src/Humanizer/Properties/Resources.resx b/src/Humanizer/Properties/Resources.resx
index 1fec4c24f..ebde4142a 100644
--- a/src/Humanizer/Properties/Resources.resx
+++ b/src/Humanizer/Properties/Resources.resx
@@ -576,4 +576,7 @@
one year
+
+ {0} days
+
\ No newline at end of file
diff --git a/src/NuGet.config b/src/NuGet.config
index bb337ec79..1ab6bbd3b 100644
--- a/src/NuGet.config
+++ b/src/NuGet.config
@@ -1,8 +1,6 @@
-
-
-
+
\ No newline at end of file
diff --git a/version.json b/version.json
index 631cd1d41..bca583e1b 100644
--- a/version.json
+++ b/version.json
@@ -1,9 +1,8 @@
{
- "version": "2.2.1-preview.{height}",
+ "version": "2.3",
"publicReleaseRefSpec": [
"^refs/heads/master$", // we release out of master
- "^refs/heads/dev$", // we release out of develop
- "^refs/tags/v\\d+\\.\\d+" // we also release tags starting with vN.N
+ "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N
],
"nugetPackageVersion":{
"semVer": 2