spanish_numbers/
lib.rs

1//! # spanish-numbers
2//!
3//! A library for converting integers to their written spanish formats.  
4//! Supports both American "short" and European "long" number formats.
5
6#[derive(Copy, Clone)]
7pub enum ScaleType {
8    Short,
9    Long
10}
11
12struct NamedNumber {
13    number: u128,
14    singular_name: &'static str,
15    plural_name: &'static str
16}
17
18impl NamedNumber {
19
20    pub fn new(number: u128, singular_name: &'static str, plural_name: &'static str) -> NamedNumber {
21        NamedNumber { number, singular_name, plural_name }
22    }
23}
24
25pub struct NumberToSpanish {
26    scale: Vec<NamedNumber>
27}
28
29impl NumberToSpanish {
30
31    pub fn new(scale_type: ScaleType) -> NumberToSpanish {
32        let scale = Self::get_scale(scale_type);
33        NumberToSpanish { scale }
34    }
35
36    fn get_scale(scale_type: ScaleType) -> Vec<NamedNumber> {
37        match scale_type {
38            ScaleType::Short => vec![
39                NamedNumber::new(1000000000000000000000000000000000000, "undecillón", "undecillones"),
40                NamedNumber::new(1000000000000000000000000000000000, "decillón", "decillones"),
41                NamedNumber::new(1000000000000000000000000000000, "nonillón", "nonillones"),
42                NamedNumber::new(1000000000000000000000000000, "octillón", "octillones"),
43                NamedNumber::new(1000000000000000000000000, "septillón", "septillones"),
44                NamedNumber::new(1000000000000000000000, "sextillón", "sextillones"),
45                NamedNumber::new(1000000000000000000, "quintillón", "quintillones"),
46                NamedNumber::new(1000000000000000, "cuatrillón", "cuatrillones"),
47                NamedNumber::new(1000000000000, "trillón", "trillones"),
48                NamedNumber::new(1000000000, "billón", "billones"),
49                NamedNumber::new(1000000, "millón", "millones"),
50                NamedNumber::new(1, "", "")
51            ],
52            ScaleType::Long => vec![
53                NamedNumber::new(1000000000000000000000000000000000000, "sextillón", "sextillones"),
54                NamedNumber::new(1000000000000000000000000000000, "quintillón", "quintillones"),
55                NamedNumber::new(1000000000000000000000000, "cuatrillón", "cuatrillones"),
56                NamedNumber::new(1000000000000000000, "trillón", "trillones"),
57                NamedNumber::new(1000000000000, "billón", "billones"),
58                NamedNumber::new(1000000, "millón", "millones"),
59                NamedNumber::new(1, "", "")
60            ]
61        }
62    }
63
64    pub fn number_to_spanish(&self, number: u128, separator: &str) -> String {
65        match number {
66            0 => "cero".to_owned(),
67            _ => self.translate_positive_number(number).join(separator)
68        }
69    }
70
71    fn translate_positive_number(&self, number: u128) -> Vec<String> {
72        let mut translations = vec![];
73        let mut translation = String::from("");
74        
75        let (divisor, divisor_name) = self.get_greatest_divisor(number);
76        let fst_num = number / divisor;
77        let snd_num = number % divisor;
78        
79        if fst_num > 0 {
80            let is_not_end_of_sentence = snd_num > 0 || divisor > 1;
81            let fst_num_translation = Self::translate_1_until_million(fst_num, is_not_end_of_sentence);
82            translation.push_str(&fst_num_translation);
83        }
84        if translation.len() > 0 {
85            if divisor_name.len() > 0 { 
86                translation.push(' ');
87                translation.push_str(&divisor_name);
88            }
89            translations.push(translation);
90        }
91        if snd_num > 0 {
92            translations.extend(self.translate_positive_number(snd_num));
93        }
94        translations
95    }
96    
97    fn get_greatest_divisor(&self, number: u128) -> (u128, String) {
98        let divisor = self.scale.iter().find(|d| d.number <= number).unwrap();
99        let v = number / divisor.number;
100        let divisor_name = if v > 1 { divisor.plural_name } else { divisor.singular_name };
101        (divisor.number, divisor_name.to_owned())
102    }
103
104    fn translate_1_until_million(number: u128, is_not_end: bool) -> String {
105        match number {
106            1...999 => Self::translate_1_to_999(number, is_not_end),
107            1000...999999 => Self::translate_1000_until_million(number, is_not_end),
108            _ => panic!("number {}", number)
109        }
110    }
111
112    fn translate_1_to_999(number: u128, is_not_end: bool) -> String {
113        match number {
114            1...99 => Self::translate_1_to_99(number, is_not_end),
115            100 => "cien".to_owned(),
116            101...999 => Self::translate_101_to_999(number, is_not_end),
117            _ => panic!("number {}", number)
118        }
119    }
120
121    fn translate_1000_until_million(number: u128, is_not_end: bool) -> String {
122        let fst_num = number / 1000;
123        let snd_digit = number % 1000;
124        let mut translation = String::from("");
125        if fst_num > 1 {
126            translation.push_str(&Self::translate_1_to_999(fst_num, is_not_end || snd_digit > 0));
127            translation.push(' ');
128        }
129        translation.push_str("mil");
130        if snd_digit > 0 {
131            translation.push(' ');
132            translation.push_str(&Self::translate_1_to_999(snd_digit, is_not_end));
133        }
134        translation
135    }
136
137    fn translate_1_to_99(number: u128, is_not_end: bool) -> String {
138        match number {
139            1...30 => Self::translate_from_1_to_30(number, is_not_end),
140            31...99 => Self::translate_31_to_99(number, is_not_end),
141            _ => panic!("number {}", number)
142        }
143    }
144
145    fn translate_101_to_999(number: u128, is_not_end: bool) -> String {
146        let fst_digit = number / 100;
147        let snd_digit = number % 100;
148        let mut translation = Self::match_multiples_of_100_to_900(fst_digit);
149        if snd_digit > 0 {
150            translation.push(' ');
151            translation.push_str(&Self::translate_1_to_99(snd_digit, is_not_end));
152        }
153        translation
154    }
155
156    fn translate_from_1_to_30(number: u128, is_not_end: bool) -> String {
157        (match number {
158            n if n == 1 && is_not_end => "un",
159            1 => "uno",
160            2 => "dos",
161            3 => "tres",
162            4 => "cuatro",
163            5 => "cinco",
164            6 => "seis",
165            7 => "siete",
166            8 => "ocho",
167            9 => "nueve",
168            10 => "diez",
169            11 => "once",
170            12 => "doce",
171            13 => "trece",
172            14 => "catorce",
173            15 => "quince",
174            16 => "dieciséis",
175            17 => "diecisiete",
176            18 => "dieciocho",
177            19 => "diecinueve",
178            20 => "veinte",
179            21 => "veintiuno",
180            22 => "veintidós",
181            23 => "veintitrés",
182            24 => "venticuatro",
183            25 => "veinticinco",
184            26 => "veintiséis",
185            27 => "veintisiete",
186            28 => "veintiocho",
187            29 => "veintinueve",
188            30 => "treinta",
189            _ => panic!("number {}", number)
190        }).to_owned()
191    }
192
193    fn translate_31_to_99(number: u128, is_not_end: bool) -> String {
194        let fst_digit = number / 10;
195        let snd_digit = number % 10;
196        let mut translation = Self::match_multiples_of_10_to_90(fst_digit);
197        if snd_digit > 0 {
198            translation.push_str(" y ");
199            translation.push_str(&Self::translate_from_1_to_30(snd_digit, is_not_end));
200        }
201        translation
202    }
203
204    fn match_multiples_of_10_to_90(number: u128) -> String {
205        (match number {
206            1 => "diez",
207            2 => "veinte",
208            3 => "treinta",
209            4 => "cuarenta",
210            5 => "cincuenta",
211            6 => "sesenta",
212            7 => "setenta",
213            8 => "ochenta",
214            9 => "noventa",
215            _ => panic!("number {}", number)
216        }).to_owned()
217    }
218
219    fn match_multiples_of_100_to_900(number: u128) -> String {
220        (match number {
221            1 => "ciento",
222            2 => "doscientos",
223            3 => "trescientos",
224            4 => "cuatrocientos",
225            5 => "quinientos",
226            6 => "seiscientos",
227            7 => "setecientos",
228            8 => "ochocientos",
229            9 => "novecientos",
230            _ => panic!("number {}", number)
231        }).to_owned()
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn it_works() {
241        let nsl = NumberToSpanish::new(ScaleType::Long);
242        let nss = NumberToSpanish::new(ScaleType::Short);
243
244        assert_eq!(nsl.number_to_spanish(1, " "), "uno");
245        assert_eq!(nss.number_to_spanish(1, " "), "uno");
246        assert_eq!(nsl.number_to_spanish(1000, " "), "mil");
247        assert_eq!(nss.number_to_spanish(1000, " "), "mil");
248        assert_eq!(nsl.number_to_spanish(1000000, " "), "un millón");
249        assert_eq!(nss.number_to_spanish(1000000, " "), "un millón");
250        assert_eq!(nsl.number_to_spanish(1000000000, " "), "mil millones");
251        assert_eq!(nss.number_to_spanish(1000000000, " "), "un billón");
252        assert_eq!(nsl.number_to_spanish(1000000000000, " "), "un billón");
253        assert_eq!(nss.number_to_spanish(1000000000000, " "), "un trillón");
254        assert_eq!(nsl.number_to_spanish(1000000000000000, " "), "mil billones");
255        assert_eq!(nss.number_to_spanish(1000000000000000, " "), "un cuatrillón");
256        assert_eq!(nsl.number_to_spanish(1000000000000000000, " "), "un trillón");
257        assert_eq!(nss.number_to_spanish(1000000000000000000, " "), "un quintillón");
258        assert_eq!(nsl.number_to_spanish(1000000000000000000000, " "), "mil trillones");
259        assert_eq!(nss.number_to_spanish(1000000000000000000000, " "), "un sextillón");
260        assert_eq!(nsl.number_to_spanish(1000000000000000000000000, " "), "un cuatrillón");
261        assert_eq!(nss.number_to_spanish(1000000000000000000000000, " "), "un septillón");
262        assert_eq!(nsl.number_to_spanish(1000000000000000000000000000, " "), "mil cuatrillones");
263        assert_eq!(nss.number_to_spanish(1000000000000000000000000000, " "), "un octillón");
264        assert_eq!(nsl.number_to_spanish(1000000000000000000000000000000, " "), "un quintillón");
265        assert_eq!(nss.number_to_spanish(1000000000000000000000000000000, " "), "un nonillón");
266
267        assert_eq!(nsl.number_to_spanish(0, " "), "cero");
268        assert_eq!(nsl.number_to_spanish(7, " "), "siete");
269        assert_eq!(nsl.number_to_spanish(19, " "), "diecinueve");
270        assert_eq!(nsl.number_to_spanish(21, " "), "veintiuno");
271        assert_eq!(nsl.number_to_spanish(26, " "), "veintiséis");
272        assert_eq!(nsl.number_to_spanish(29, " "), "veintinueve");
273        assert_eq!(nsl.number_to_spanish(31, " "), "treinta y uno");
274        assert_eq!(nsl.number_to_spanish(40, " "), "cuarenta");
275        assert_eq!(nsl.number_to_spanish(56, " "), "cincuenta y seis");
276        assert_eq!(nsl.number_to_spanish(99, " "), "noventa y nueve");
277        assert_eq!(nsl.number_to_spanish(100, " "), "cien");
278        assert_eq!(nsl.number_to_spanish(101, " "), "ciento uno");
279        assert_eq!(nsl.number_to_spanish(120, " "), "ciento veinte");
280        assert_eq!(nsl.number_to_spanish(456, " "), "cuatrocientos cincuenta y seis");  
281        assert_eq!(nsl.number_to_spanish(999, " "), "novecientos noventa y nueve");
282        assert_eq!(nsl.number_to_spanish(1000, " "), "mil");
283        assert_eq!(nsl.number_to_spanish(1001, " "), "mil uno");
284        assert_eq!(nsl.number_to_spanish(1456, " "), "mil cuatrocientos cincuenta y seis");  
285        assert_eq!(nsl.number_to_spanish(2456, " "), "dos mil cuatrocientos cincuenta y seis");  
286        assert_eq!(nsl.number_to_spanish(100000, " "), "cien mil");  
287        assert_eq!(nsl.number_to_spanish(345456, " "), "trescientos cuarenta y cinco mil cuatrocientos cincuenta y seis");  
288        assert_eq!(nsl.number_to_spanish(999999, " "), "novecientos noventa y nueve mil novecientos noventa y nueve");  
289        assert_eq!(nsl.number_to_spanish(1000001, " "), "un millón uno");
290        assert_eq!(nsl.number_to_spanish(1200300400100, " "), "un billón doscientos mil trescientos millones cuatrocientos mil cien");
291        assert_eq!(nsl.number_to_spanish(12003004001000, " "), "doce billones tres mil cuatro millones mil");
292        assert_eq!(nsl.number_to_spanish(91928091829809, " "), "noventa y un billones novecientos veintiocho mil noventa y un millones ochocientos veintinueve mil ochocientos nueve");
293        assert_eq!(nsl.number_to_spanish(120030040010010, " "), "ciento veinte billones treinta mil cuarenta millones diez mil diez");
294        assert_eq!(nsl.number_to_spanish(120130340010410103, " "), "ciento veinte mil ciento treinta billones trescientos cuarenta mil diez millones cuatrocientos diez mil ciento tres");
295        assert_eq!(nsl.number_to_spanish(1201303400104101030, " "), "un trillón doscientos un mil trescientos tres billones cuatrocientos mil ciento cuatro millones ciento un mil treinta");
296        assert_eq!(nsl.number_to_spanish(1000000000000000000000000, " "), "un cuatrillón");
297        assert_eq!(nsl.number_to_spanish(1201303400104101035678987, " "), "un cuatrillón doscientos un mil trescientos tres trillones cuatrocientos mil ciento cuatro billones ciento un mil treinta y cinco millones seiscientos setenta y ocho mil novecientos ochenta y siete");
298        assert_eq!(nsl.number_to_spanish(1271313457184191135678987, " "), "un cuatrillón doscientos setenta y un mil trescientos trece trillones cuatrocientos cincuenta y siete mil ciento ochenta y cuatro billones ciento noventa y un mil ciento treinta y cinco millones seiscientos setenta y ocho mil novecientos ochenta y siete");
299        assert_eq!(nsl.number_to_spanish(193127131345718419113567898, " "), "ciento noventa y tres cuatrillones ciento veintisiete mil ciento treinta y un trillones trescientos cuarenta y cinco mil setecientos dieciocho billones cuatrocientos diecinueve mil ciento trece millones quinientos sesenta y siete mil ochocientos noventa y ocho");
300        assert_eq!(nsl.number_to_spanish(1931271313457184191135678980, " "), "mil novecientos treinta y un cuatrillones doscientos setenta y un mil trescientos trece trillones cuatrocientos cincuenta y siete mil ciento ochenta y cuatro billones ciento noventa y un mil ciento treinta y cinco millones seiscientos setenta y ocho mil novecientos ochenta");
301        assert_eq!(nsl.number_to_spanish(31415926535897932384626433832795, " "), "treinta y un quintillones cuatrocientos quince mil novecientos veintiséis cuatrillones quinientos treinta y cinco mil ochocientos noventa y siete trillones novecientos treinta y dos mil trescientos ochenta y cuatro billones seiscientos veintiséis mil cuatrocientos treinta y tres millones ochocientos treinta y dos mil setecientos noventa y cinco");
302
303        assert_eq!(nsl.number_to_spanish(31415926535897932384626433832795, ", "), "treinta y un quintillones, cuatrocientos quince mil novecientos veintiséis cuatrillones, quinientos treinta y cinco mil ochocientos noventa y siete trillones, novecientos treinta y dos mil trescientos ochenta y cuatro billones, seiscientos veintiséis mil cuatrocientos treinta y tres millones, ochocientos treinta y dos mil setecientos noventa y cinco");
304        assert_eq!(nss.number_to_spanish(31415926535897932384626433832795, ", "), "treinta y un nonillones, cuatrocientos quince octillones, novecientos veintiséis septillones, quinientos treinta y cinco sextillones, ochocientos noventa y siete quintillones, novecientos treinta y dos cuatrillones, trescientos ochenta y cuatro trillones, seiscientos veintiséis billones, cuatrocientos treinta y tres millones, ochocientos treinta y dos mil setecientos noventa y cinco");
305    }
306}