ballasi / num2words Goto Github PK
View Code? Open in Web Editor NEWConvert numbers like 42 to forty-two
Home Page: https://crates.io/crates/num2words
License: Apache License 2.0
Convert numbers like 42 to forty-two
Home Page: https://crates.io/crates/num2words
License: Apache License 2.0
Hello. I might try add spanish support. I haven't properly fully read how other lang's implementations are, but is there anything I should know? Or should wait till I submit pull request first?
Also, since it is spanish impl I was thinking about using spanish names for statics, and possibly even variables as well. as for methods I guess can leave them in english.
Also I might not be able to implement the Currency and date and others (Only cardinal if I didn't misinterpret it) since they feel a bit out of my league.... at least not at first!
Something I would like to add for the English language before moving on to other languages is the ability to set preferences.
For instance, here is a sample code:
use num2words::*;
fn print_game_score(home_score: i64, away_score: i64) -> Result<(), Num2Err> {
println!(
"{} - {}",
Num2Words::new(home_score).to_words()?,
Num2Words::new(away_score).to_words()?
);
Ok(())
}
Calling print_game_score(3, 0).unwrap()
would print three - zero
, but in this case, we are more likely to be interested to see three - nil
, same for "oh" (e.g., in versioning).
I am interested in implementing a call similar to the one below:
Num2Words::new(home_score).prefer(["nil"]).to_words()
There's a lot of things that clippy isn't happy about. I was wondering if you plan to sort them out anytime. Also I got a file for rustfmt which uses nightly. In case you would like to use it. I personally like the option of grouping by modules
--> src\lang\fr.rs:18:16
|
18 | const UNITS: [&'static str; 9] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
= note: `#[warn(clippy::redundant_static_lifetimes)]` on by default
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:22:15
|
22 | const TENS: [&'static str; 9] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:34:16
|
34 | const TEENS: [&'static str; 10] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:39:16
|
39 | const MEGAS: [&'static str; 33] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: useless conversion to the same type: `std::string::String`
--> src\currency.rs:176:9
|
176 | / String::from(
177 | | match self {
178 | | Currency::AED | Currency::KWD => "fils",
179 | | Currency::ARS | Currency::BRL | Currency::CLP | Currency::COP | Currency::MXN => {
... |
192 | | .replace("{}", if plural_form { "s" } else { "" }),
193 | | )
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
= note: `#[warn(clippy::useless_conversion)]` on by default
help: consider removing `String::from()`
|
176 ~ match self {
177 + Currency::AED | Currency::KWD => "fils",
178 + Currency::ARS | Currency::BRL | Currency::CLP | Currency::COP | Currency::MXN => {
179 + "centavo{}"
180 + }
181 ~ Currency::CRC => "céntimo{}",
182 + Currency::IDR | Currency::MYR => "sen{}",
183 + Currency::KRW => "jeon{}",
184 + Currency::SAR => "halalat{}",
185 + Currency::THB => "satang{}",
186 + Currency::UAH => "kopiyok{}",
187 + Currency::UYU => "centesimo{}",
188 + Currency::VND => "xu{}",
189 + _ => cent,
190 + }
191 + .replace("{}", if plural_form { "s" } else { "" })
|
warning: module has the same name as its containing module
--> src\lang\mod.rs:2:1
|
2 | mod lang;
| ^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
= note: `#[warn(clippy::module_inception)]` on by default
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:104:18
|
104 | .find(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))
| __________________^
105 | | .is_some();
| |__________________________^ help: use `any()` instead: `any(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
= note: `#[warn(clippy::search_is_some)]` on by default
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:106:28
|
106 | let reformed = preferences
| ____________________________^
107 | | .iter()
108 | | .find(|v: &&String| {
109 | | ["reformed", "1990", "rectifié", "rectification"].contains(&v.as_str())
110 | | })
111 | | .is_some();
| |__________________________^
|
= help: this is more succinctly expressed by calling `any()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:118:18
|
118 | .find(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))
| __________________^
119 | | .is_some();
| |__________________________^ help: use `any()` instead: `any(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:120:28
|
120 | let reformed = preferences
| ____________________________^
121 | | .iter()
122 | | .find(|v: &&String| {
123 | | ["reformed", "1990", "rectifié", "rectification"].contains(&v.as_str())
124 | | })
125 | | .is_some();
| |__________________________^
|
= help: this is more succinctly expressed by calling `any()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:132:18
|
132 | .find(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))
| __________________^
133 | | .is_some();
| |__________________________^ help: use `any()` instead: `any(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:134:28
|
134 | let reformed = preferences
| ____________________________^
135 | | .iter()
136 | | .find(|v: &&String| {
137 | | ["reformed", "1990", "rectifié", "rectification"].contains(&v.as_str())
138 | | })
139 | | .is_some();
| |__________________________^
|
= help: this is more succinctly expressed by calling `any()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: stripping a suffix manually
--> src\lang\fr.rs:318:25
|
318 | &w[..w.len() - 1]
| ^^^^^^^^^^^^^^^^^
|
note: the suffix was tested here
--> src\lang\fr.rs:317:21
|
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:18:16
|
18 | const UNITS: [&'static str; 9] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
= note: `#[warn(clippy::redundant_static_lifetimes)]` on by default
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:22:15
|
22 | const TENS: [&'static str; 9] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:34:16
|
34 | const TEENS: [&'static str; 10] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: constants have by default a `'static` lifetime
--> src\lang\fr.rs:39:16
|
39 | const MEGAS: [&'static str; 33] = [
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: useless conversion to the same type: `std::string::String`
--> src\currency.rs:176:9
|
176 | / String::from(
177 | | match self {
178 | | Currency::AED | Currency::KWD => "fils",
179 | | Currency::ARS | Currency::BRL | Currency::CLP | Currency::COP | Currency::MXN => {
... |
192 | | .replace("{}", if plural_form { "s" } else { "" }),
193 | | )
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
= note: `#[warn(clippy::useless_conversion)]` on by default
help: consider removing `String::from()`
|
176 ~ match self {
177 + Currency::AED | Currency::KWD => "fils",
178 + Currency::ARS | Currency::BRL | Currency::CLP | Currency::COP | Currency::MXN => {
179 + "centavo{}"
180 + }
181 ~ Currency::CRC => "céntimo{}",
182 + Currency::IDR | Currency::MYR => "sen{}",
183 + Currency::KRW => "jeon{}",
184 + Currency::SAR => "halalat{}",
185 + Currency::THB => "satang{}",
186 + Currency::UAH => "kopiyok{}",
187 + Currency::UYU => "centesimo{}",
188 + Currency::VND => "xu{}",
189 + _ => cent,
190 + }
191 + .replace("{}", if plural_form { "s" } else { "" })
|
warning: module has the same name as its containing module
--> src\lang\mod.rs:2:1
|
2 | mod lang;
| ^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
= note: `#[warn(clippy::module_inception)]` on by default
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:104:18
|
104 | .find(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))
| __________________^
105 | | .is_some();
| |__________________________^ help: use `any()` instead: `any(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
= note: `#[warn(clippy::search_is_some)]` on by default
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:106:28
|
106 | let reformed = preferences
| ____________________________^
107 | | .iter()
108 | | .find(|v: &&String| {
109 | | ["reformed", "1990", "rectifié", "rectification"].contains(&v.as_str())
110 | | })
111 | | .is_some();
| |__________________________^
|
= help: this is more succinctly expressed by calling `any()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:118:18
|
118 | .find(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))
| __________________^
119 | | .is_some();
| |__________________________^ help: use `any()` instead: `any(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:120:28
|
120 | let reformed = preferences
| ____________________________^
121 | | .iter()
122 | | .find(|v: &&String| {
123 | | ["reformed", "1990", "rectifié", "rectification"].contains(&v.as_str())
124 | | })
125 | | .is_some();
| |__________________________^
|
= help: this is more succinctly expressed by calling `any()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:132:18
|
132 | .find(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))
| __________________^
133 | | .is_some();
| |__________________________^ help: use `any()` instead: `any(|v| ["feminine", "feminin", "féminin", "f"].contains(&v.as_str()))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: called `is_some()` after searching an `Iterator` with `find`
--> src\lang\lang.rs:134:28
|
134 | let reformed = preferences
| ____________________________^
135 | | .iter()
136 | | .find(|v: &&String| {
137 | | ["reformed", "1990", "rectifié", "rectification"].contains(&v.as_str())
138 | | })
139 | | .is_some();
| |__________________________^
|
= help: this is more succinctly expressed by calling `any()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
warning: stripping a suffix manually
--> src\lang\fr.rs:318:25
|
318 | &w[..w.len() - 1]
| ^^^^^^^^^^^^^^^^^
|
note: the suffix was tested here
--> src\lang\fr.rs:317:21
|
317 | if w.ends_with('e') {
| ^^^^^^^^^^^^^^^^^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
= note: `#[warn(clippy::manual_strip)]` on by default
help: try using the `strip_suffix` method
|
317 ~ if let Some(<stripped>) = w.strip_suffix('e') {
318 ~ <stripped>
|```
According to Wiki, there are some currencies that are split in other number of fractions, like BHD (1000) or BND (10). Even Yuan is split into 10 Jiao, not 100. Sometimes currencies that have the same name have the different number of fractions, like Iraqi dinar (IQD) has 1000 Fils, and Serbian dinar (RSD) has 100 Para. There should be an additional argument in to_currency
function.
Also, fractional units have their respective names.
This is a critical bug.
Historically, there were currencies with several fractional units, like the old British Pound sterling had 12 shillings, each shilling split into 20 pence before 1971. Thank God we should not support this.
The !num.is_zero()
doesn't work properly for float. The division isn't accurate so the exponent reduces on each division and eventually reaches 0. I think there is no need to use float. The input string can be split by .
and both be represented as u128 which gives precision of 38 digits which I think is equal to the Bigfloat.
For now, Into<Number>
only allows i64
and f64
.
This has been chosen as this is the largest common number type that features the greatest range of numbers.
However, people may work with other types and are fine using smaller number ranges.
This forces the user to cast it to the 64 bits version of the number.
This issue is a bit connected to #5 as if the int/float type is removed altogether with another it may need to add Into
case for each common number types.
use num2words::Num2Words;
fn main() {
let zero_one = Num2Words::parse("01").unwrap().to_words().unwrap();
println!("{zero_one}");
}
one
Note:
zero one
is a valid alternative, but010
yieldsten
, notzero ten
.
one duodecillion
This behavior holds for all inputs matching the pattern: 0[1-9]
(exactly two digits where the first digit is zero and the second digit is not zero).
As in the issue title the following code will panic:
if let Some(word) = Num2Words::parse(&word).and_then(|x| x.to_words().ok()) {
println!("Word: {}", word);
}
thread 'main' panicked at /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/num2words-1.0.0/src/lang/en.rs:78:53:
called `Option::unwrap()` on a `None` value
stack backtrace:
0: rust_begin_unwind
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:595:5
1: core::panicking::panic_fmt
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:67:14
2: core::panicking::panic
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:117:5
3: core::option::Option<T>::unwrap
at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/option.rs:935:21
4: num2words::lang::en::English::split_thousands
at /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/num2words-1.0.0/src/lang/en.rs:78:28
5: num2words::lang::en::English::int_to_cardinal
at /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/num2words-1.0.0/src/lang/en.rs:106:29
6: num2words::lang::en::English::float_to_cardinal
at /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/num2words-1.0.0/src/lang/en.rs:159:33
7: <num2words::lang::en::English as num2words::lang::lang::Language>::to_cardinal
at /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/num2words-1.0.0/src/lang/en.rs:188:13
8: num2words::num2words::Num2Words::to_words
at /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/num2words-1.0.0/src/num2words.rs:274:33
Currently, the stack is limited to i64
/f64
.
The program needs to have support for bignum (any finite numbers).
As I have studied German a long time ago, I could probably make this happen.
If anyone here knows German and would like to contribute, do shoot a message here! You are more than welcome! I probably won't start working on it as of today so contribution on that end would be greatly appreciated.
I was doing some benchmarks while writing stuffs up, but because I wasn't as expertised on this aspect, I deleted everything. However, it does work by accessing the library as a Lib User, so to test inner logic, you would have to either copy and paste it out of the private modules, or expose methods to test.
I have used Criterion, but it seems there's probably more appealing options like Bencher, which seems to support CI benching (Whatever that is) https://nnethercote.github.io/perf-book/benchmarking.html
I am planning to open PR for Nepali language. Is it ok?
Includes the following:
Nothing serious, really, but some places can really be better with fixing those lints, like
warning: `to_string` applied to a type that implements `Display` in `eprintln!` args
--> src/bin/bin.rs:120:51
|
120 | Err(err) => eprintln!("Error: {}", err.to_string()),
| ^^^^^^^^^^^^ help: remove this
if it still is there, will get to it after adding Ukrainian
I'm planning to make a PR for Ukrainian
For use in the future, languages should be able to batch some "default string" for each currencies. If we need to add a new currency, we may not know its translation and use in various languages, this helps having a first implementation that is likely to work well in the first place and might be patched in the future if needed to. In a way, the currencies() function may work in a "override currency name" way.
num2words 12 --to ordinal
produces
12nd
instead of expected
12th
It's not finished yet, and in future it will change. Making changes to all languages will be slow. So it's better to mark it as non_exhaustive and add default branches in all matches.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.