/** * @file * @brief Translation filters. **/ #include "AppHdr.h" #include "lang-fake.h" #include "libutil.h" #include "options.h" #include "strings.h" #include "unicode.h" #define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define LOWER "abcdefghijklmnopqrstuvwxyz" #define LETTERS UPPER LOWER // Any resemblance to the speech of a particular character from Rich Burlew's // work is entirely accidental. Suggestion that this table has been produced // by transcribing the whole corpus of that character's speech and making it // produce the same output from regular English must be dismissed as a rumour. // For Perl version, "apt-get install filters". static const char* dwarven[][4] = { {"^yes$","aye"}, {"there","thar"}, {"eir$","ar"}, {"about","aboot"}, {"^he$","'e"}, {"them","'em"}, {"^him","'im"}, {"out of$","outta"}, {"of course","'course"}, {"^of$","o'"}, {"^and$","an'"}, {"to$","ta"}, {"tog","tag"}, {"that","tha"}, {"the","tha"}, {"wouldn't","wouldn'ta"}, {"cannot","cannae"}, {"can't","cannae"}, {"don't","dinnae"}, {"'re$","r"}, {"for$","fer"}, {"ver$","'er"}, {"ber$","b'r"}, {"every$","ev'ry"}, {"^is$","be"}, {"en$","'n"}, {"^if$","if'n"}, {"enl","'nl"}, {"eng","'ng"}, {"ing","in'"}, {"ment","mn't"}, {"^es","'s"}, {"^ex","'s"}, {"^not$","na"}, {"^no$","nay"}, {"n't have","n'tve"}, {"^are$","be"}, {"have","haf"}, {"abl","'bl"}, {"^you$","ye"}, {"^your","yer"}, {"^you'","ye'"}, {"noth","nuth"}, {"^this$","'tis"}, {"^here","'ere"}, {"doesn't","don't"}, {"at a$","atta"}, {"ith$","it'"}, {"ered$","'red"}, {"into$","inta"}, {"^before","'fore"}, {"wit' '","wit '"}, {"wit' t","wit t"}, {"wit' w","wit w"}, {"wit' y","wit y"}, {"get a","git a"}, {"ally$","'lly"}, {"^my","me"}, {"^i think$","methinks"}, {"nay w","na w"}, {"^one$","'un"}, {"^'un a","one a"}, {"at ta$","atta"}, {"ot ta$","otta"}, {"^isn't$","ain't"}, {"^so th","s'th"}, {"ned$","n'd"}, {"^because","'cause"}, {0} }; // Resemblances to the manner of speech of Jägermonsters from "Girl Genius" by // Phil and Kaja Foglio are... hrm dammit, using the proper name gives it out. // And s/dammit/gotterdammerung/. static const char* jager[][4] = { {"this","dis"}, {"that","dat"}, {"they","dey"}, {"their","der"}, {"there","dere"}, {"these","dese"}, {"those","doze"}, {"the$","de"}, {"then","den"}, {"them","dem"}, {"yes","yah"}, {"you","hyu"}, {"hyur","you"}, {"have","hef"}, {"wh","v"}, {"^w","v"}, {"w","v", "o"}, {"^ha","he"}, {"ha","he", "c"}, {"any","henny"}, {"smart","schmot"}, {"n't","n'"}, {"ain'","hain't"}, {"am not","hain't"}, {"isn'","hain't"}, {"is not","hain't"}, {"aren'","hain't"}, {"are not","haint't"}, {"good","goot"}, {"dad","papa"}, {"stuff","stoff"}, {"going to","gunna"}, {"going","gun"}, {"people","pipple"}, {"little","liddle"}, {"love","luv"}, {"nice","nize"}, {"bug","bog"}, {"let me","lemme"}, {"give me","gimmee"}, {"castle","kestle"}, {"clank","klenk"}, {"better","betta"}, {"darling","dollink"}, {"some","zum"}, {"ouble","ubbel"}, {"bble","bbel"}, {"^ex","hex"}, {"exc","eks"}, {"ex","eks"}, {"accident","exident"}, {"once","vunce"}, {"id$","eed"}, {"^one","vun"}, {"kiss","kees"}, {"^c","k", 0, "h"}, {"am$","em"}, {"^th","t"}, {"th","dd", "!" LETTERS, "!" LETTERS}, {"unix","yoonix"}, {"^un","on"}, {"^sh","sch", 0, "e"}, {"^sl","schl"}, {"^sm","schm"}, {"^sn","schn"}, {"^sp","schp"}, {"^st","scht"}, {"of","uf"}, {"qu","qv"}, {"ing","ink"}, {"irl","url"}, {"ish","eesh"}, {"^i","hy", 0, ")" LETTERS}, {"ve$","ff", "!" LETTERS}, {"ect$","eck"}, {"and","und"}, {"^all","hall"}, {"mister","herr"}, {"mr.","herr"}, {"^is$","iz"}, {"god damn it","gotterdammerung"}, {"goddammit","gotterdammerung"}, {"goddamnit","gotterdammerung"}, {"damn it","gotterdammerung"}, {"goddam","gotterdammerung"}, {"god damn","gotterdammerung"}, {"god damn it","gotterdammerung"}, {"damn","dem"}, {"god","gott"}, {"okay","hokay"}, {"^ok$","hokay"}, {"just","chust"}, {0} }; static const char* cyrillic[][4] = { {"a", "а"}, {"b", "б"}, {"v", "в"}, {"wr", "р"}, {"w", "у"}, {"g", "г"}, {"d", "д"}, {"ye", "е"}, {"ie", "е"}, {"eu", "ев"}, {"e", "е"}, {"io", "ё"}, {"yo", "ё"}, {"zh", "ж"}, {"z", "з"}, {"j", "й"}, {"x", "кс"}, {"q", "к"}, {"l", "л"}, {"m", "м"}, {"n", "н"}, {"o", "о"}, {"p", "п"}, {"rr", "р"}, {"r", "р"}, {"s", "с"}, {"t", "т"}, {"u", "у"}, {"f", "ф"}, {"kh", "х"}, {"h", "х"}, {"k", "к"}, {"ts", "ц"}, {"chr", "кр"}, {"ch", "ч"}, {"c", "ц"}, {"shch","щ"}, {"sh", "ш"}, {"e", "э"}, {"yu", "ю"}, {"iu", "ю"}, {"ya", "я"}, {"ia", "я"}, {"i", "и"}, {"y", "ы"}, {0} }; static const char* runes[][4] = { {"ae", "ᛇ"}, {"a", "ᚨ"}, {"b", "ᛒ"}, {"c", "ᚲ"}, {"d", "ᛞ"}, {"e", "ᛖ"}, {"f", "ᚠ"}, {"g", "ᚷ"}, {"h", "ᚺ"}, {"i", "ᛁ"}, {"j", "ᛃ"}, {"k", "ᚲ"}, {"l", "ᛚ"}, {"m", "ᛗ"}, {"ng", "ᛜ"}, {"n", "ᚾ"}, {"o", "ᛟ"}, {"ph", "ᚠ"}, {"p", "ᛈ"}, {"q", "ᛩ"}, {"r", "ᚱ"}, {"s", "ᛊ"}, {"th", "ᚦ"}, {"t", "ᛏ"}, {"u", "ᚢ"}, {"v", "ᚡ"}, {"w", "ᚹ"}, {"x", "ᚲᛊ"}, {"y", "ᚤ"}, // Anglo-Saxon, sometimes medieval {"z", "ᛉ"}, {0} }; static const char* grunt[][4] = { {"^killed$", "annihilated"}, {"^kill$", "annihilate"}, {"!", "..."}, {".", "!", 0, LETTERS "0123456789"}, {0} }; static void _replace_cap_variants(string &str, string a, string b, const char* not_after = 0, const char* not_before = 0) { ASSERT(!a.empty()); if (a[0] == '^') { a.erase(0, 1); ASSERT(!not_after); not_after = LETTERS; } ASSERT(!a.empty()); if (a[a.length() - 1] == '$') { a.erase(a.length() - 1); ASSERT(!not_before); not_before = LETTERS; } ASSERT(!a.empty()); bool yes_after = false; if (not_after && *not_after == '!') yes_after = true, not_after++; bool yes_before = false; if (not_before && *not_before == '!') yes_before = true, not_before++; for (int captype = 0; captype < 3; captype++) { string A; switch (captype) { case 0: A = lowercase(a); break; case 1: A = uppercase_first(a); break; case 2: A = uppercase(a); break; default: die("lolwut?"); } size_t pos = 0; while ((pos = str.find(A, pos)) != string::npos) { if (not_after && pos > 0 && (yes_after == !strchr(not_after, str[pos - 1]))) { pos++; continue; } size_t epos = pos + A.length(); if (not_before && epos < str.length() && (yes_before == !strchr(not_before, str[epos]))) { pos++; continue; } string B; switch (captype) { case 0: B = lowercase(b); break; case 1: B = uppercase_first(b); break; case 2: B = uppercase(b); break; default: die("lolwut?"); } str.erase(pos, A.length()); str.insert(pos, B); pos += B.length(); } } } static void _german(string &txt) { /* The European Commission has just announced an agreement whereby English will be the official language of the European Union rather than German, which was the other possibility. As part of the negotiations with the Germans, the British Government conceded that English spelling had some room for improvement and has accepted a 5-year phase-in plan that would become known as "Euro-English". In the first year, "s" will replace the soft "c". Sertainly, this will make the sivil servants jump with joy. The hard "c" will be dropped in favour of "k". This should klear up konfusion, and keyboards kan have one less letter. */ for (unsigned int i = 0; i < txt.length(); i++) if (txt[i] == 'c' || txt[i] == 'C') { switch (tolower(txt[i+1])) { case 'h': case 'z': break; case 'e': case 'i': case 'y': txt[i] = isalower(txt[i]) ? 's' : 'S'; break; default: txt[i] = isalower(txt[i]) ? 'k' : 'K'; } } /* There will be growing publik enthusiasm in the sekond year when the troublesome "ph" will be replaced with "f". This will make words like fotograf 20% shorter. */ _replace_cap_variants(txt, "ph", "f"); /* In the 3rd year, publik akseptanse of the new spelling kan be expekted to reach the stage where more komplikated changes are possible. Governments will enkourage the removal of double letters which have always ben a deterent to akurate speling. */ for (int i = txt.length() - 2; i > 0; i--) if (txt[i] == txt[i + 1]) txt.erase(i, 1); /* Also, al wil agre that the horibl mes of the silent "e" in the languag is disgrasful and it should go away. */ for (int i = txt.length() - 2; i > 0; i--) if (txt[i]=='e' || txt[i]=='E') { if (i < 3) continue; if (isaalpha(txt[i + 1])) continue; if (txt[i-1]=='e' || txt[i-1]=='E') continue; if (!isaalpha(txt[i-1]) || !isaalpha(txt[i-2]) || !isaalpha(txt[i-3])) { continue; } txt.erase(i, 1); } /* By the 4th yer people wil be reseptiv to steps such as replasing "th" with "z" and "w" with "v". */ _replace_cap_variants(txt, "th", "z"); _replace_cap_variants(txt, "w", "v"); /* During ze fifz yer, ze unesesary "o" kan be dropd from vords containing "ou" and after ziz fifz yer, ve vil hav a reil sensibl riten styl. */ _replace_cap_variants(txt, "ou", "u"); /* Zer vil be no mor trubl or difikultis and evrivun vil find it ezi tu understand ech oza. Ze drem of a united urop vil finali kum tru. Und efter ze fifz yer, ve vil al be speking German like zey vunted in ze forst plas. */ // TODO :p } static const char* german[][4] = { {"sh", "sch"}, {"^is$", "ist"}, {"an$", "ann"}, {"^and$", "und"}, {"^one$", "ein"}, {0} }; static void _wide(string &txt) { string out; for (size_t i = 0; i < txt.length(); i++) { if (txt[i] == ' ') out += " "; // U+3000 rather than U+FF00 else if (txt[i] > 32 && txt[i] < 127) { char buf[4]; int r = wctoutf8(buf, txt[i] + 0xFF00 - 32); for (int j = 0; j < r; j++) out.push_back(buf[j]); } else out.push_back(txt[i]); } txt = out; } static void _grunt(string &txt) { static const char* exact_grunt[][2] = { {"battlesphere", "BATTLESPHERE"}, {"Battlesphere", "BATTLESPHERE"}, {"battlemage", "BATTLEMAGE"}, {"Battlemage", "BATTLEMAGE"}, {"Battle Magician", "BATTLEMAGE"}, {"battleaxe", "BATTLEAXE"}, {"book of Battle", "BATTLEBOOK"}, {"Battlelust", "BATTLELUST"}, {"Kill them all", "RIP AND TEAR"}, {"accepts your kill", "roars: ANNIHILATED"}, {"appreciates your killing of a heretic priest", "smash puny heretic"}, {"appreciates your killing of a magic user", "smash puny caster"}, {"appreciates your killing of a holy being", "smash puny angel"}, {"appreciates your kill", "screams: ANNIHILATED"}, {"You pass out from exhaustion.", "POWER NAP!!!"}, {"You die...", "rip"}, {"LOW HITPOINT WARNING", "don't die"}, {"Ouch! That really hurt!", "DANG"}, {0} }; const char* (*repl)[2] = exact_grunt; for (; **repl; repl++) { size_t pos = 0; string a = (*repl)[0]; string b = (*repl)[1]; while ((pos = txt.find(a, pos)) != string::npos) { txt.erase(pos, a.length()); txt.insert(pos, b); pos += b.length(); } } } void filter_lang(string &str) { const char* (*repl)[4]; switch (Options.lang) { case LANG_DWARVEN: repl = dwarven; break; case LANG_JAGERKIN: repl = jager; break; case LANG_KRAUT: _german(str), repl = german; break; case LANG_CYRILLIC: repl = cyrillic; break; case LANG_WIDE: return _wide(str); case LANG_FUTHARK: repl = runes; break; case LANG_GRUNT: _grunt(str); repl = grunt; break; default: return; } for (; **repl; repl++) _replace_cap_variants(str, (*repl)[0], (*repl)[1], (*repl)[2], (*repl)[3]); } string filtered_lang(string str) { filter_lang(str); return str; }