arsd/english.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));
}
*/