mirror of https://github.com/adamdruppe/arsd.git
149 lines
3.3 KiB
D
149 lines
3.3 KiB
D
/// A few helper functions for manipulating English words. Extremely basic.
|
|
module arsd.english;
|
|
|
|
/++
|
|
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.
|
|
|
|
I originally wrote this for cases like `You have {n} {messages|plural(n)}` in web templates.
|
|
+/
|
|
string plural(int count, string word, string pluralWord = null) {
|
|
if(count == 1 || word.length == 0)
|
|
return word; // it isn't actually plural
|
|
|
|
if(pluralWord !is null)
|
|
return pluralWord;
|
|
|
|
switch(word[$ - 1]) {
|
|
case 's':
|
|
case 'a', 'e', 'i', 'o', 'u':
|
|
return word ~ "es";
|
|
case 'f':
|
|
return word[0 .. $-1] ~ "ves";
|
|
case 'y':
|
|
return word[0 .. $-1] ~ "ies";
|
|
default:
|
|
return word ~ "s";
|
|
}
|
|
}
|
|
|
|
/// Given an integer, tries to write out the long form number. For example, -5 becomes "negative five".
|
|
string numberToEnglish(long number) {
|
|
string word;
|
|
if(number == 0)
|
|
return "zero";
|
|
|
|
if(number < 0) {
|
|
word = "negative";
|
|
number = -number;
|
|
}
|
|
|
|
while(number) {
|
|
if(number < 100) {
|
|
if(number < singleWords.length) {
|
|
word ~= singleWords[cast(int) number];
|
|
break;
|
|
} else {
|
|
auto tens = number / 10;
|
|
word ~= tensPlaceWords[cast(int) tens];
|
|
number = number % 10;
|
|
if(number)
|
|
word ~= "-";
|
|
}
|
|
} else if(number < 1_000) {
|
|
auto hundreds = number / 100;
|
|
word ~= onesPlaceWords[cast(int) hundreds] ~ " hundred";
|
|
number = number % 100;
|
|
if(number)
|
|
word ~= " and ";
|
|
} else if(number < 1_000_000) {
|
|
auto thousands = number / 1_000;
|
|
word ~= numberToEnglish(thousands) ~ " thousand";
|
|
number = number % 1_000;
|
|
if(number)
|
|
word ~= ", ";
|
|
} else if(number < 1_000_000_000) {
|
|
auto millions = number / 1_000_000;
|
|
word ~= numberToEnglish(millions) ~ " million";
|
|
number = number % 1_000_000;
|
|
if(number)
|
|
word ~= ", ";
|
|
} else if(number < 1_000_000_000_000) {
|
|
auto n = number / 1_000_000_000;
|
|
word ~= numberToEnglish(n) ~ " billion";
|
|
number = number % 1_000_000_000;
|
|
if(number)
|
|
word ~= ", ";
|
|
} else if(number < 1_000_000_000_000_000) {
|
|
auto n = number / 1_000_000_000_000;
|
|
word ~= numberToEnglish(n) ~ " trillion";
|
|
number = number % 1_000_000_000_000;
|
|
if(number)
|
|
word ~= ", ";
|
|
} else {
|
|
import std.conv;
|
|
return to!string(number);
|
|
}
|
|
}
|
|
|
|
return word;
|
|
}
|
|
|
|
unittest {
|
|
assert(numberToEnglish(1) == "one");
|
|
assert(numberToEnglish(5) == "five");
|
|
assert(numberToEnglish(13) == "thirteen");
|
|
assert(numberToEnglish(54) == "fifty-four");
|
|
assert(numberToEnglish(178) == "one hundred and seventy-eight");
|
|
assert(numberToEnglish(592) == "five hundred and ninety-two");
|
|
assert(numberToEnglish(1234) == "one thousand, two hundred and thirty-four");
|
|
assert(numberToEnglish(10234) == "ten thousand, two hundred and thirty-four");
|
|
assert(numberToEnglish(105234) == "one hundred and five thousand, two hundred and thirty-four");
|
|
}
|
|
|
|
enum onesPlaceWords = [
|
|
"zero",
|
|
"one",
|
|
"two",
|
|
"three",
|
|
"four",
|
|
"five",
|
|
"six",
|
|
"seven",
|
|
"eight",
|
|
"nine",
|
|
];
|
|
|
|
enum singleWords = onesPlaceWords ~ [
|
|
"ten",
|
|
"eleven",
|
|
"twelve",
|
|
"thirteen",
|
|
"fourteen",
|
|
"fifteen",
|
|
"sixteen",
|
|
"seventeen",
|
|
"eighteen",
|
|
"nineteen",
|
|
];
|
|
|
|
enum tensPlaceWords = [
|
|
null,
|
|
"ten",
|
|
"twenty",
|
|
"thirty",
|
|
"forty",
|
|
"fifty",
|
|
"sixty",
|
|
"seventy",
|
|
"eighty",
|
|
"ninety",
|
|
];
|
|
|
|
/*
|
|
void main() {
|
|
import std.stdio;
|
|
foreach(i; 3433000 ..3433325)
|
|
writeln(numberToEnglish(i));
|
|
}
|
|
*/
|