1 /// A few helper functions for manipulating English words. Extremely basic. 2 module arsd.english; 3 4 /++ 5 Given a non-one `count` argument, will attempt to return the plural version of `word`. Only handles basic cases. If count is one, simply returns the word. 6 7 I originally wrote this for cases like `You have {n} {messages|plural(n)}` in web templates. 8 +/ 9 string plural(int count, string word, string pluralWord = null) { 10 if(count == 1 || word.length == 0) 11 return word; // it isn't actually plural 12 13 if(pluralWord !is null) 14 return pluralWord; 15 16 switch(word[$ - 1]) { 17 case 's': 18 case 'a', 'e', 'i', 'o', 'u': 19 return word ~ "es"; 20 case 'f': 21 return word[0 .. $-1] ~ "ves"; 22 case 'y': 23 return word[0 .. $-1] ~ "ies"; 24 default: 25 return word ~ "s"; 26 } 27 } 28 29 /// Given an integer, tries to write out the long form number. For example, -5 becomes "negative five". 30 string numberToEnglish(long number) { 31 string word; 32 if(number == 0) 33 return "zero"; 34 35 if(number < 0) { 36 word = "negative"; 37 number = -number; 38 } 39 40 while(number) { 41 if(number < 100) { 42 if(number < singleWords.length) { 43 word ~= singleWords[cast(int) number]; 44 break; 45 } else { 46 auto tens = number / 10; 47 word ~= tensPlaceWords[cast(int) tens]; 48 number = number % 10; 49 if(number) 50 word ~= "-"; 51 } 52 } else if(number < 1_000) { 53 auto hundreds = number / 100; 54 word ~= onesPlaceWords[cast(int) hundreds] ~ " hundred"; 55 number = number % 100; 56 if(number) 57 word ~= " and "; 58 } else if(number < 1_000_000) { 59 auto thousands = number / 1_000; 60 word ~= numberToEnglish(thousands) ~ " thousand"; 61 number = number % 1_000; 62 if(number) 63 word ~= ", "; 64 } else if(number < 1_000_000_000) { 65 auto millions = number / 1_000_000; 66 word ~= numberToEnglish(millions) ~ " million"; 67 number = number % 1_000_000; 68 if(number) 69 word ~= ", "; 70 } else if(number < 1_000_000_000_000) { 71 auto n = number / 1_000_000_000; 72 word ~= numberToEnglish(n) ~ " billion"; 73 number = number % 1_000_000_000; 74 if(number) 75 word ~= ", "; 76 } else if(number < 1_000_000_000_000_000) { 77 auto n = number / 1_000_000_000_000; 78 word ~= numberToEnglish(n) ~ " trillion"; 79 number = number % 1_000_000_000_000; 80 if(number) 81 word ~= ", "; 82 } else { 83 import std.conv; 84 return to!string(number); 85 } 86 } 87 88 return word; 89 } 90 91 unittest { 92 assert(numberToEnglish(1) == "one"); 93 assert(numberToEnglish(5) == "five"); 94 assert(numberToEnglish(13) == "thirteen"); 95 assert(numberToEnglish(54) == "fifty-four"); 96 assert(numberToEnglish(178) == "one hundred and seventy-eight"); 97 assert(numberToEnglish(592) == "five hundred and ninety-two"); 98 assert(numberToEnglish(1234) == "one thousand, two hundred and thirty-four"); 99 assert(numberToEnglish(10234) == "ten thousand, two hundred and thirty-four"); 100 assert(numberToEnglish(105234) == "one hundred and five thousand, two hundred and thirty-four"); 101 } 102 103 enum onesPlaceWords = [ 104 "zero", 105 "one", 106 "two", 107 "three", 108 "four", 109 "five", 110 "six", 111 "seven", 112 "eight", 113 "nine", 114 ]; 115 116 enum singleWords = onesPlaceWords ~ [ 117 "ten", 118 "eleven", 119 "twelve", 120 "thirteen", 121 "fourteen", 122 "fifteen", 123 "sixteen", 124 "seventeen", 125 "eighteen", 126 "nineteen", 127 ]; 128 129 enum tensPlaceWords = [ 130 null, 131 "ten", 132 "twenty", 133 "thirty", 134 "forty", 135 "fifty", 136 "sixty", 137 "seventy", 138 "eighty", 139 "ninety", 140 ]; 141 142 /* 143 void main() { 144 import std.stdio; 145 foreach(i; 3433000 ..3433325) 146 writeln(numberToEnglish(i)); 147 } 148 */