Cleanup
[jmdict-cli.git] / kana2romaji.cpp
1 /*
2 jmdict, a frontend to the JMdict file. http://mandrill.fuxx0r.net/jmdict.php
3 Copyright (C) 2004 Florian Bluemel (florian.bluemel@uni-dortmund.de)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19 // encoding: utf-8
20 #include "kana2romaji.h"
21 #include <map>
22 #include <iostream>
23 #include <ostream>
24 #include <string>
25
26 using namespace std;
27
28 namespace  {
29 void utfchar(const string& from, string::size_type pos, string& to) {
30     unsigned first = from[pos];
31     if ((first & 0x80) == 0)
32         to = from[pos];
33     else {
34         string::size_type len = 0;
35         while (first & 0x80) {
36             ++len;
37             first <<= 1;
38         }
39         to = from.substr(pos, len);
40     }
41 }
42 }
43
44 typedef map<string, string> romaji_map;
45 romaji_map romaji;
46
47 void initRomaji() {
48     // -- hiragana -----
49     romaji["あ"] = "a";
50     romaji["い"] = "i";
51     romaji["う"] = "u";
52     romaji["え"] = "e";
53     romaji["お"] = "o";    
54     romaji["か"] = "ka";
55     romaji["き"] = "ki";
56     romaji["く"] = "ku";
57     romaji["け"] = "ke";
58     romaji["こ"] = "ko";
59     romaji["さ"] = "sa";
60     romaji["し"] = "shi";
61     romaji["す"] = "su";
62     romaji["せ"] = "se";
63     romaji["そ"] = "so";
64     romaji["た"] = "ta";
65     romaji["ち"] = "chi";
66     romaji["つ"] = "tsu";
67     romaji["て"] = "te";
68     romaji["と"] = "to";
69     romaji["な"] = "na";
70     romaji["に"] = "ni";
71     romaji["ぬ"] = "nu";
72     romaji["ね"] = "ne";
73     romaji["の"] = "no";
74     romaji["は"] = "ha";
75     romaji["ひ"] = "hi";
76     romaji["ふ"] = "fu";
77     romaji["へ"] = "he";
78     romaji["ほ"] = "ho";
79     romaji["ま"] = "ma";
80     romaji["み"] = "mi";
81     romaji["む"] = "mu";
82     romaji["め"] = "me";
83     romaji["も"] = "mo";
84     romaji["や"] = "ya";
85     romaji["ゆ"] = "yu";
86     romaji["よ"] = "yo";
87     romaji["ら"] = "ra";
88     romaji["り"] = "ri";
89     romaji["る"] = "ru";
90     romaji["れ"] = "re";
91     romaji["ろ"] = "ro";
92     romaji["わ"] = "wa";
93     romaji["ゐ"] = "wi";
94     romaji["ゑ"] = "we";
95     romaji["を"] = "wo";
96     romaji["ん"] = "n";
97     
98     romaji["ぁ"] = "\1a";
99     romaji["ぃ"] = "\1i";
100     romaji["ぇ"] = "\1e";
101     romaji["ぉ"] = "\1o";
102     romaji["ゃ"] = "\1ya";
103     romaji["ゅ"] = "\1yu";
104     romaji["ょ"] = "\1yo";
105     romaji["っ"] = "\2";
106
107     romaji["ゔ"] = "vu";
108     romaji["が"] = "ga";
109     romaji["ぎ"] = "gi";
110     romaji["ぐ"] = "gu";
111     romaji["げ"] = "ge";
112     romaji["ご"] = "go";
113     romaji["ざ"] = "za";
114     romaji["じ"] = "ji";
115     romaji["ず"] = "zu";
116     romaji["ぜ"] = "ze";
117     romaji["ぞ"] = "zo";
118     romaji["だ"] = "da";
119     romaji["ぢ"] = "dzi";
120     romaji["づ"] = "dzu";
121     romaji["で"] = "de";
122     romaji["ど"] = "do";
123     romaji["ば"] = "ba";
124     romaji["び"] = "bi";
125     romaji["ぶ"] = "bu";
126     romaji["べ"] = "be";
127     romaji["ぼ"] = "bo";
128     romaji["ぱ"] = "pa";
129     romaji["ぴ"] = "pi";
130     romaji["ぷ"] = "pu";
131     romaji["ぺ"] = "pe";
132     romaji["ぽ"] = "po";
133
134     // -- katakana -----
135     romaji["ア"] = "a";
136     romaji["イ"] = "i";
137     romaji["ウ"] = "u";
138     romaji["エ"] = "e";
139     romaji["オ"] = "o";    
140     romaji["カ"] = "ka";
141     romaji["キ"] = "ki";
142     romaji["ク"] = "ku";
143     romaji["ケ"] = "ke";
144     romaji["コ"] = "ko";
145     romaji["サ"] = "sa";
146     romaji["シ"] = "shi";
147     romaji["ス"] = "su";
148     romaji["セ"] = "se";
149     romaji["ソ"] = "so";
150     romaji["タ"] = "ta";
151     romaji["チ"] = "chi";
152     romaji["ツ"] = "tsu";
153     romaji["テ"] = "te";
154     romaji["ト"] = "to";
155     romaji["ナ"] = "na";
156     romaji["ニ"] = "ni";
157     romaji["ヌ"] = "nu";
158     romaji["ネ"] = "ne";
159     romaji["ノ"] = "no";
160     romaji["ハ"] = "ha";
161     romaji["ヒ"] = "hi";
162     romaji["フ"] = "fu";
163     romaji["ヘ"] = "he";
164     romaji["ホ"] = "ho";
165     romaji["マ"] = "ma";
166     romaji["ミ"] = "mi";
167     romaji["ム"] = "mu";
168     romaji["メ"] = "me";
169     romaji["モ"] = "mo";
170     romaji["ヤ"] = "ya";
171     romaji["ユ"] = "yu";
172     romaji["ヨ"] = "yo";
173     romaji["ラ"] = "ra";
174     romaji["リ"] = "ri";
175     romaji["ル"] = "ru";
176     romaji["レ"] = "re";
177     romaji["ロ"] = "ro";
178     romaji["ワ"] = "wa";
179     romaji["ヰ"] = "wi";
180     romaji["ヱ"] = "we";
181     romaji["ヲ"] = "wo";
182     romaji["ン"] = "n";
183     
184     romaji["ァ"] = "\1a";
185     romaji["ィ"] = "\1i";
186     romaji["ゥ"] = "\1u";
187     romaji["ェ"] = "\1e";
188     romaji["ォ"] = "\1o";
189     romaji["ヮ"] = "\1wa";
190     romaji["ャ"] = "\1ya";
191     romaji["ュ"] = "\1yu";
192     romaji["ョ"] = "\1yo";
193     romaji["ッ"] = "\2";
194
195     romaji["ヴ"] = "vu";
196     romaji["ガ"] = "ga";
197     romaji["ギ"] = "gi";
198     romaji["グ"] = "gu";
199     romaji["ゲ"] = "ge";
200     romaji["ゴ"] = "go";
201     romaji["ザ"] = "za";
202     romaji["ジ"] = "ji";
203     romaji["ズ"] = "zu";
204     romaji["ゼ"] = "ze";
205     romaji["ゾ"] = "zo";
206     romaji["ダ"] = "da";
207     romaji["ヂ"] = "dzi";
208     romaji["ヅ"] = "dzu";
209     romaji["デ"] = "de";
210     romaji["ド"] = "do";
211     romaji["バ"] = "ba";
212     romaji["ビ"] = "bi";
213     romaji["ブ"] = "bu";
214     romaji["ベ"] = "be";
215     romaji["ボ"] = "bo";
216     romaji["パ"] = "pa";
217     romaji["ピ"] = "pi";
218     romaji["プ"] = "pu";
219     romaji["ペ"] = "pe";
220     romaji["ポ"] = "po";
221     romaji["・"] = " ";
222     
223     // -- double width letters ------
224     romaji["A"] = "A";
225     romaji["B"] = "B";
226     romaji["C"] = "C";
227     romaji["D"] = "D";
228     romaji["E"] = "E";
229     romaji["F"] = "F";
230     romaji["G"] = "G";
231     romaji["H"] = "H";
232     romaji["I"] = "I";
233     romaji["J"] = "J";
234     romaji["K"] = "K";
235     romaji["L"] = "L";
236     romaji["M"] = "M";
237     romaji["N"] = "N";
238     romaji["O"] = "O";
239     romaji["P"] = "P";
240     romaji["Q"] = "Q";
241     romaji["R"] = "R";
242     romaji["S"] = "S";
243     romaji["T"] = "T";
244     romaji["U"] = "U";
245     romaji["V"] = "V";
246     romaji["W"] = "W";
247     romaji["X"] = "X";
248     romaji["Y"] = "Y";
249     romaji["Z"] = "Z";
250
251     romaji["a"] = "a";
252     romaji["b"] = "b";
253     romaji["c"] = "c";
254     romaji["d"] = "d";
255     romaji["e"] = "e";
256     romaji["f"] = "f";
257     romaji["g"] = "g";
258     romaji["h"] = "h";
259     romaji["i"] = "i";
260     romaji["j"] = "j";
261     romaji["k"] = "k";
262     romaji["l"] = "l";
263     romaji["m"] = "m";
264     romaji["n"] = "n";
265     romaji["o"] = "o";
266     romaji["p"] = "p";
267     romaji["q"] = "q";
268     romaji["r"] = "r";
269     romaji["s"] = "s";
270     romaji["t"] = "t";
271     romaji["u"] = "u";
272     romaji["v"] = "v";
273     romaji["w"] = "w";
274     romaji["x"] = "x";
275     romaji["y"] = "y";
276     romaji["z"] = "z";
277
278     romaji["0"] = "0";
279     romaji["1"] = "1";
280     romaji["2"] = "2";
281     romaji["3"] = "3";
282     romaji["4"] = "4";
283     romaji["5"] = "5";
284     romaji["6"] = "6";
285     romaji["7"] = "7";
286     romaji["8"] = "8";
287     romaji["9"] = "9";
288    
289     romaji["!"] = "!";
290     romaji["""] = "\"";
291     romaji["#"] = "#";
292     romaji["$"] = "$";
293     romaji["%"] = "%";
294     romaji["&"] = "&";
295     romaji["'"] = "'";
296     romaji["*"] = "*";
297     romaji["+"] = "+";
298     romaji[","] = ",";
299     romaji["-"] = "-";
300     romaji["."] = ".";
301     romaji["/"] = "/";
302
303     romaji[":"] = ":";
304     romaji[";"] = ";";
305     romaji["<"] = "<";
306     romaji["="] = "=";
307     romaji[">"] = ">";
308     romaji["?"] = "?";
309     romaji["@"] = "@";
310
311     romaji["〔"] = "(";
312     romaji["〕"] = ")";
313     romaji["("] = "(";
314     romaji[")"] = ")";
315     romaji["["] = "[";
316     romaji["]"] = "]";
317     romaji["【"] = "[";
318     romaji["】"] = "]";
319     romaji["{"] = "{";
320     romaji["}"] = "}";
321     romaji["\"] = "\\";
322     romaji["^"] = "^";
323     romaji["_"] = "_";
324     romaji["`"] = "`";
325     romaji["|"] = "|";
326     romaji["~"] = "~";
327     romaji["ー"] = "";
328     romaji["。"] = ".";
329     romaji["、"] = ",";
330     romaji["〜"] = "~";
331     romaji["−"] = "-";
332     romaji["―"] = "-";
333     romaji[" "] = " ";
334
335     romaji["ー"] = "\3";
336 }
337
338 void remove_quote_1(
339   string::size_type const pos,
340   string &rom)
341 {
342   // if we encounter something like
343   // "ki" + '\1' 
344   // remove the previous character of \1 and the \1
345   // if \1 is followed by an 'y' remove that also
346   rom.erase(
347     pos - 1,
348     (pos + 1 < rom.size()
349      && rom[pos + 1] == 'y'
350     )
351       ? 3
352       : 2);
353 }
354
355 void kana2romaji(const string& kana, string& rom) {
356     rom.clear();
357     for (string::size_type pos = 0; pos < kana.size(); ) {
358         string ch;
359         utfchar(kana, pos, ch);
360         romaji_map::const_iterator trans = romaji.find(ch);
361         if (trans == romaji.end()) {
362             rom += ch;
363             if (ch.size() > 1)
364                 cout << "Don't know how to translate '" << ch << "' in '" << kana << "' to romaji.\n";
365         }
366         else
367             rom += trans->second;
368         pos += ch.size();
369     }
370     for (string::size_type pos = 0; pos < rom.size(); ++pos)
371         if (rom[pos] == '\1') {
372
373             if (pos > 2) {
374                string const pred = rom.substr(pos - 3, 3);
375                if(pred == "chi" ||
376                   pred == "shi" ||
377                   pred == "dzi" ||
378                   pred == "tsu" ||
379                   pred == "shi"
380                ) {
381                  remove_quote_1(pos, rom);
382                  pos -= 2;
383                  continue;
384                }
385             }
386             if (pos > 1) {
387              
388                string const pred = rom.substr(pos - 2, 2);
389                if(pred == "ki" ||
390                   pred == "ni" ||
391                   pred == "mi" ||
392                   pred == "ri" ||
393                   pred == "gi" ||
394                   pred == "ji" ||
395                   pred == "hi" ||
396                   pred == "bi" ||
397                   pred == "pi"
398                 )
399                {
400                  // shorten "ji\1y" to "j"
401                  // otherwise remove "\1" and the preceding character
402                  // but not the y
403                  rom.erase(
404                    pos - 1,
405                    (pos + 1 < rom.size()
406                     && rom[pos + 1] == 'y'
407                     && pred[0] == 'j')
408                       ? 3
409                       : 2);
410                   pos -= 2;
411                  continue;
412                }
413                else if(
414                   pred == "fu" ||
415                   pred == "de" ||
416                   pred == "te" ||
417                   pred == "vu")
418                {
419                   remove_quote_1(pos, rom);
420                   pos -= 2;
421                  continue;
422                }
423                else if(
424                  pred == "su" ||
425                  pred == "zu" ||
426                  pred == "te" ||
427                  pred == "de" ||
428                  pred == "ku" ||
429                  pred == "gu" ||
430                  pred == "mu"
431                )
432                {
433                  rom[pos - 1] = 'w'; 
434                  rom.erase(pos, 1);
435                  --pos;
436                  continue;
437                }
438                else if(
439                  pred == "to" ||
440                  pred == "do"
441                )
442                {
443                  rom[pos - 1] = 'h';
444                  rom.erase(pos, 1);
445                  --pos;
446                  continue;
447                }
448             }
449
450             if (pos > 0) {
451               char const pred = rom[pos - 1];
452
453               switch(pred)
454               {
455               case 'a':
456               case 'i':
457               case 'u':
458               case 'e':
459               case 'o':
460                 rom.erase(pos, 1);
461                 --pos;
462                 break;
463               default:
464                 cout << "Encountered a special character in " << kana << " but don't know what to do with it.\n";
465               }
466             }
467             else
468             {
469               rom.erase(pos, 1);
470               --pos;
471             }
472         }
473         else if (rom[pos] == '\2')
474         {
475           // two tsu may follow each other, so just remove them
476           if(pos + 1 < rom.size() && rom[pos + 1] != '\2')
477             rom[pos] = rom[pos + 1];
478           else
479           {
480             rom.erase(pos, 1);
481             --pos;
482           }
483         }
484         else if (rom[pos] == '\3')
485         {
486           if(pos == 0)
487           {
488             if(rom.size() == 1)
489               rom = "chouon";
490             else
491             {
492               cout << "ー is the first letter of " << kana << ". Don't know how to translate this.\n";
493               rom.erase(pos, 1);
494               --pos;
495             }
496           }
497           else
498             rom[pos] = rom[pos-1];
499         }
500
501   for (string::size_type pos = 0; pos < rom.size(); ++pos)
502     switch(rom[pos])
503     {
504     case '\1':
505     case '\2':
506     case '\3':
507       cout << "Failed to translate " << kana << '\n';
508       return;
509     }
510 }