Nut 4.0.0
dotnet add package Nut --version 4.0.0
NuGet\Install-Package Nut -Version 4.0.0
<PackageReference Include="Nut" Version="4.0.0" />
<PackageVersion Include="Nut" Version="4.0.0" />
<PackageReference Include="Nut" />
paket add Nut --version 4.0.0
#r "nuget: Nut, 4.0.0"
#:package Nut@4.0.0
#addin nuget:?package=Nut&version=4.0.0
#tool nuget:?package=Nut&version=4.0.0
NUT — Number To Text
Writes numbers and money amounts as words, in 14 languages — for the amount in words field on invoices, cheques and contracts.
using Nut;
123456.ToText(); // one hundred twenty-three thousand four hundred fifty-six
123456.78m.ToText(Currency.USD, Language.English); // one hundred twenty-three thousand four hundred fifty-six dollars seventy-eight cents
123456.78m.ToText(Currency.TRY, Language.Turkish); // yüz yirmi üç bin dört yüz elli altı türk lirası yetmiş sekiz kuruş
Install
dotnet add package Nut
Targets .NET Standard 2.0, so it runs on .NET Framework 4.6.1+, .NET Core 2.0+ and .NET 5 and later. No dependencies.
Languages
Pass either the short code or the culture code. Matching ignores case.
| Language | Codes | Language | Codes | |
|---|---|---|---|---|
| English (US) | en, en-US |
Latvian | lv, lv-LV |
|
| English (UK) | en-GB |
Polish | pl, pl-PL |
|
| French | fr, fr-FR |
Portuguese | pt, pt-BR |
|
| German | de, de-DE |
Russian | ru, ru-RU |
|
| Spanish | es, es-ES |
Turkish | tr, tr-TR |
|
| Amharic | am, am-ET |
Ukrainian | uk, uk-UA |
|
| Belarusian | be, be-BY |
Uzbek | uz, uz-UZ |
|
| Bulgarian | bg, bg-BG |
en-GB differs from en-US: 101 reads as one hundred and one rather than one
hundred one.
Ukrainian and Belarusian used to be keyed by their ccTLDs, ua and by. Those still
resolve, but the codes above are the ones CultureInfo hands you.
Extensions.SupportedLanguages returns the full list at runtime.
Currencies
EUR USD GBP RUB TRY UAH BGN ETB PLN BYN ARS BRL UZS
Also accepted: TL for TRY, RUR for RUB.
Not every language covers every currency, and a combination a language does not cover
raises NotSupportedException rather than guessing at the wording:
| Language | Missing |
|---|---|
| Uzbek | everything but UZS, USD and RUB |
| Amharic | ARS, BRL, GBP, UZS |
| Belarusian | UZS, ETB |
| Polish, Bulgarian, Latvian | UZS |
These are gaps of evidence rather than of effort. Each one is a word nobody could source, and this text goes on documents people sign.
2575.50m.ToText(Currency.EUR, Language.German); // zweitausendfünfhundertfünfundsiebzig Euro fünfzig Cent
2575.50m.ToText(Currency.EUR, Language.French); // deux mille cinq cent soixante-quinze euros cinquante centimes
2575.50m.ToText(Currency.EUR, Language.Russian); // две тысячи пятьсот семьдесят пять евро пятьдесят евроцентов
2575.50m.ToText(Currency.EUR, Language.Latvian); // divi tūkstoši pieci simti septiņdesmit pieci eiro un piecdesmit centi
Options
var options = new Options { MainUnitFirstCharUpper = true, CurrencyFirstCharUpper = true };
2575.50m.ToText(Currency.USD, Language.English, options);
// Two thousand five hundred seventy-five Dollars fifty Cents
| Option | Effect |
|---|---|
MainUnitFirstCharUpper |
Capitalises the first word. On a negative amount the capital goes on the sign: Minus two thousand… |
SubUnitFirstCharUpper |
Capitalises the fractional part |
CurrencyFirstCharUpper |
Capitalises the currency names |
MainUnitNotConvertedToText |
Leaves the whole part as digits |
SubUnitZeroNotDisplayed |
Drops the fractional part when it is zero |
SubUnitFormat |
Words (default), Digits, or Fraction — see below |
SubUnitTruncated |
Cuts extra decimals instead of rounding them |
Writing the fraction the way cheques do
new Options { SubUnitFormat = SubUnitFormat.Fraction }
// two thousand five hundred seventy-five dollars and 50/100
Zero is written as 00/100 rather than dropped, so nothing can be added to the amount
afterwards.
Writing the sub-unit over a hundred is an anglophone cheque convention rather than a rule
of any language, so English is the only language it is available in. Asking for it in
any other raises NotSupportedException rather than borrowing the English and.
Behaviour worth knowing
Rounding. Amounts carrying more decimals than the currency has are rounded to the
sub-unit, half away from zero — the same result decimal.ToString("C") gives, so the
figures and the words on a document agree.
123.456m.ToText(Currency.USD, Language.English); // one hundred twenty-three dollars forty-six cents
Set SubUnitTruncated to cut the digits instead.
Negative amounts use the language's own word — minus, moins, menos, eksi, минус, mīnus, ሲቀነስ.
(-2575.50m).ToText(Currency.USD, Language.English); // minus two thousand five hundred seventy-five dollars fifty cents
Unsupported input throws. An unknown language, or a currency a language does not cover,
raises NotSupportedException naming what was asked for. It does not return an empty
string: a blank amount field on a document is not noticed until the document has gone out.
Ukrainian paper payment instructions require the amount in words to start with a
capital letter — NBU Board Resolution 29.07.2022 №163, Annex, field 4. Pass
MainUnitFirstCharUpper when printing one. The same resolution says the field is left
empty on electronic instructions.
var options = new Options { MainUnitFirstCharUpper = true };
105.50m.ToText(Currency.UAH, Language.Ukrainian, options);
// Сто п'ять гривень п'ятдесят копійок
Range. Magnitudes below one trillion. Beyond that raises ArgumentOutOfRangeException.
Rounding counts: an amount that crosses the limit only after being rounded to the sub-unit
raises it too.
Thread safety. Conversions are independent and safe to run in parallel.
Contributing
Pull requests are welcome. Two things make them much easier to accept:
- Add tests for what you change.
dotnet test src/Nut.slnruns the suite; CI runs it on every pull request and fails on warnings. - A new language or currency needs expected outputs written by someone who reads it.
Nobody here can check wording in a language they do not speak, and this text ends up on
financial documents. A table of expected results is what makes such a contribution
reviewable. Cover at least
0, 1, 2, 11, 21, 100, 1000, 2000, 41000, 1000000plus one decimal amount, and say where the forms come from.
src/Nut.Tests/behaviour-snapshot.tsv pins every language × currency × amount
combination. If a change is meant to alter output, regenerate it with
UPDATE_SNAPSHOT=1 dotnet test — the diff of that file becomes the record of what changed.
Changelog
See CHANGELOG.md.
Thanks
- Latif Turk — Ukrainian language and currency
- SecreT2k8 — Bulgarian language and currency
- ashGHub — .NET Standard migration, Ethiopian language and currency
- kashiash — Polish language and currency
- DeNcHiK3713 — Belarusian language and currency
- Marciel032 — Portuguese language and currency
- ArkadiuszMakosa — Polish unit tests
- Maryam1986 — German language and currency, negative numbers
- Furqat-Abduvosiqov — Uzbek language and currency
- IlyashenkoA — GBP currency, Latvian language
- Stepami — Russian gender fixes, RUR
- fulviocanducci — Portuguese separator
Licence
MIT — see LICENCE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.0 | 154 | 7/29/2026 |
| 3.5.0 | 89 | 7/28/2026 |
| 3.4.1 | 169,432 | 6/2/2023 |
| 3.3.0 | 97,475 | 5/24/2022 |
| 3.2.4 | 56,408 | 9/27/2021 |
| 3.2.3 | 1,098 | 9/19/2021 |
| 3.1.1 | 14,412 | 1/27/2021 |
| 3.1.0 | 1,084 | 1/27/2021 |
| 3.0.0 | 66,393 | 7/3/2020 |
| 2.4.2 | 60,299 | 5/27/2016 |
| 2.4.1 | 2,494 | 1/15/2016 |
| 2.4.0 | 1,990 | 1/15/2016 |
| 2.3.3 | 2,169 | 4/14/2015 |
| 2.3.2 | 2,027 | 4/7/2015 |
| 2.3.1 | 2,042 | 4/7/2015 |