Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
369 lines (335 sloc) 8.49 KB
import string, japanese.euc_jp, re, unicodedata, shelve
# Maximum length of a dictionary entry
kanji_dict ='kanjidic.dat')
edict_dict ='edict.dat')
romaji_dict = {
u'\u3041': 'small a',
u'\u3042': 'a',
u'\u3043': 'small i',
u'\u3044': 'i',
u'\u3045': 'small u',
u'\u3046': 'u',
u'\u3047': 'small e',
u'\u3048': 'e',
u'\u3049': 'small o',
u'\u304a': 'o',
u'\u304b': 'ka',
u'\u304c': 'ga',
u'\u304d': 'ki',
u'\u304e': 'gi',
u'\u304f': 'ku',
u'\u3050': 'gu',
u'\u3051': 'ke',
u'\u3052': 'ge',
u'\u3053': 'ko',
u'\u3054': 'go',
u'\u3055': 'sa',
u'\u3056': 'za',
u'\u3057': 'shi',
u'\u3058': 'zi',
u'\u3059': 'su',
u'\u305a': 'zu',
u'\u305b': 'se',
u'\u305c': 'ze',
u'\u305d': 'so',
u'\u305e': 'zo',
u'\u305f': 'ta',
u'\u3060': 'da',
u'\u3061': 'chi',
u'\u3062': 'di',
u'\u3063': 'small tsu',
u'\u3064': 'tsu',
u'\u3065': 'du',
u'\u3066': 'te',
u'\u3067': 'de',
u'\u3068': 'to',
u'\u3069': 'do',
u'\u306a': 'na',
u'\u306b': 'ni',
u'\u306c': 'nu',
u'\u306d': 'ne',
u'\u306e': 'no',
u'\u306f': 'ha',
u'\u3070': 'ba',
u'\u3071': 'pa',
u'\u3072': 'hi',
u'\u3073': 'bi',
u'\u3074': 'pi',
u'\u3075': 'fu',
u'\u3076': 'bu',
u'\u3077': 'pu',
u'\u3078': 'he',
u'\u3079': 'be',
u'\u307a': 'pe',
u'\u307b': 'ho',
u'\u307c': 'bo',
u'\u307d': 'po',
u'\u307e': 'ma',
u'\u307f': 'mi',
u'\u3080': 'mu',
u'\u3081': 'me',
u'\u3082': 'mo',
u'\u3083': 'small ya',
u'\u3084': 'ya',
u'\u3085': 'small yu',
u'\u3086': 'yu',
u'\u3087': 'small yo',
u'\u3088': 'yo',
u'\u3089': 'ra',
u'\u308a': 'ri',
u'\u308b': 'ru',
u'\u308c': 're',
u'\u308d': 'ro',
u'\u308e': 'small wa',
u'\u308f': 'wa',
u'\u3090': 'wi',
u'\u3091': 'we',
u'\u3092': 'wo',
u'\u3093': 'n',
u'\u3094': 'vu',
u'\u30a1': 'SMALL A',
u'\u30a2': 'A',
u'\u30a3': 'SMALL I',
u'\u30a4': 'I',
u'\u30a5': 'SMALL U',
u'\u30a6': 'U',
u'\u30a7': 'SMALL E',
u'\u30a8': 'E',
u'\u30a9': 'SMALL O',
u'\u30aa': 'O',
u'\u30ab': 'KA',
u'\u30ac': 'GA',
u'\u30ad': 'KI',
u'\u30ad\u30e3': 'KYA',
u'\u30ad\u30e5': 'KYU',
u'\u30ad\u30e7': 'KYO',
u'\u30ae': 'GI',
u'\u30ae\u30e3': 'GYA',
u'\u30ae\u30e5': 'GYU',
u'\u30ae\u30e7': 'GYO',
u'\u30af': 'KU',
u'\u30b0': 'GU',
u'\u30b1': 'KE',
u'\u30b2': 'GE',
u'\u30b3': 'KO',
u'\u30b4': 'GO',
u'\u30b5': 'SA',
u'\u30b6': 'ZA',
u'\u30b7': 'SHI',
u'\u30b7\u30e3': 'JA',
u'\u30b7\u30e5': 'JU',
u'\u30b7\u30e7': 'JO',
u'\u30b8': 'ZI',
u'\u30b9': 'SU',
u'\u30ba': 'ZU',
u'\u30bb': 'SE',
u'\u30bc': 'ZE',
u'\u30bd': 'SO',
u'\u30be': 'ZO',
u'\u30bf': 'TA',
u'\u30c0': 'DA',
u'\u30c1': 'CHI',
u'\u30d2\u30e3': 'CHA',
u'\u30d2\u30e5': 'CHU',
u'\u30d2\u30e7': 'CHO',
u'\u30c2': 'DI',
u'\u30c3': 'SMALL TSU',
u'\u30c4': 'TSU',
u'\u30c5': 'DU',
u'\u30c6': 'TE',
u'\u30c7': 'DE',
u'\u30c8': 'TO',
u'\u30c9': 'DO',
u'\u30ca': 'NA',
u'\u30cb': 'NI',
u'\u30cc': 'NU',
u'\u30cd': 'NE',
u'\u30ce': 'NO',
u'\u30cf': 'HA',
u'\u30d0': 'BA',
u'\u30d1': 'PA',
u'\u30d2': 'HI',
u'\u30d2\u30e3': 'HYA',
u'\u30d2\u30e5': 'HYU',
u'\u30d2\u30e7': 'HYO',
u'\u30d3': 'BI',
u'\u30d3\u30e3': 'BYA',
u'\u30d3\u30e5': 'BYU',
u'\u30d3\u30e7': 'BYO',
u'\u30d4': 'PI',
u'\u30d4\u30e3': 'BYA',
u'\u30d4\u30e5': 'BYU',
u'\u30d4\u30e7': 'BYO',
u'\u30d5': 'FU',
u'\u30d6': 'BU',
u'\u30d7': 'PU',
u'\u30d8': 'HE',
u'\u30d9': 'BE',
u'\u30da': 'PE',
u'\u30db': 'HO',
u'\u30dc': 'BO',
u'\u30dd': 'PO',
u'\u30de': 'MA',
u'\u30df': 'MI',
u'\u30e0': 'MU',
u'\u30e1': 'ME',
u'\u30e2': 'MO',
u'\u30e3': 'SMALL YA',
u'\u30e4': 'YA',
u'\u30e5': 'SMALL YU',
u'\u30e6': 'YU',
u'\u30e7': 'SMALL YO',
u'\u30e8': 'YO',
u'\u30e9': 'RA',
u'\u30ea': 'RI',
u'\u30eb': 'RU',
u'\u30ec': 'RE',
u'\u30ed': 'RO',
u'\u30ee': 'SMALL WA',
u'\u30ef': 'WA',
u'\u30f0': 'WI',
u'\u30f1': 'WE',
u'\u30f2': 'WO',
u'\u30f3': 'N',
u'\u30f4': 'VU',
u'\u30f5': 'SMALL KA',
u'\u30f6': 'SMALL KE',
u'\u30f7': 'VA',
u'\u30f8': 'VI',
u'\u30f9': 'VE',
u'\u30fa': 'VO',
u'\u30fc': '-'
class Kanji:
def __init__(self, code, readings, meanings):
self.code = code
self.readings = readings
self.meanings = meanings
decode = japanese.euc_jp.getregentry()[1]
meaning_regex = re.compile('}\s*{')
def parse_kanji(line):
if not line or line[0] == '#': return None
line = decode(line)[0].split()
i = 2
while i < len(line) and line[i][0] in fields:
if line[i][0] == 'U': code = int(line[i][1:], 16)
i = i + 1
readings = []
while i < len(line) and line[i][0] not in 'T{':
i = i + 1
if not readings: return None
# Skip over the special readings for now
while i < len(line) and line[i][0] != '{':
i = i + 1
meanings = meaning_regex.split(' '.join(line[i:])[1:-1])
return Kanji(code, readings, meanings)
def make_kanjidat(filename):
dict =, 'c')
infile = open('kanjidic')
while 1:
line = infile.readline()
if not line: break
line = line.strip()
kanji = parse_kanji(line)
if not kanji: continue
dict[unichr(kanji.code).encode('UTF16')] = kanji
class Definition:
def __init__(self, kana, meaning):
self.kana = kana
self.meaning = meaning
edict_regex = re.compile('(\S+)(\s+\[([^\]]*)\])?\s+/([^/]*)/')
def make_edictdat(file, filename):
dict =, 'c')
maxlen = 0
while 1:
line = file.readline()
if not line: break
line = line.strip()
if not line: continue
line = decode(line)[0]
m = edict_regex.match(line)
key ='UTF16')
kana =
meaning =
if dict.has_key(key): dict[key] = dict[key] + [Definition(kana, meaning)]
else: dict[key] = [Definition(kana, meaning)]
if len(key) > maxlen: maxlen = len(key)
return maxlen
def edict_lookup(text):
"""Look up the longest match we can find"""
l = min(len(text), MAXLEN)
while l > 0:
key = text[:l].encode('UTF16')
if edict_dict.has_key(key):
return l, edict_dict[key]
l = l - 1
return 0, None
def kanji_lookup(kanji):
key = kanji.encode('UTF16')
try: return kanji_dict[key]
except KeyError: return None
KANA_LOW = 0x3040
def kana_romaji(text):
global romaji_dict
lastchar = None
r = ''
for char in text:
if ord(char) < KANA_LOW:
r = r + char
if char == u'\u30fc':
# Extend the sound
r = r + r[-1]
try: romaji = romaji_dict[char]
except KeyError:
print 'Cannot translate %r' % char
r = r + char
if romaji[:6].lower() == 'small ':
if romaji[6].lower() == 'y':
# Back up by one and write the character
r = r[:-1] + romaji[6:]
elif romaji[6:].lower() == 'tsu': r = r + r[-1]
else: r = r + romaji
else: r = r + romaji
return r
def romajify(text):
r = ''
i = 0
while i < len(text):
# Find Japanese sections to convert
while i < len(text) and ord(text[i]) < KANA_LOW:
r = r + text[i]
i = i + 1
if i >= len(text): break
# Try to find the longest definition in the dictionary
matchlen, definitions = edict_lookup(text[i:])
if definitions is not None:
r = r + '[' + ' | '.join(map(lambda d, t=text[i:i+matchlen]: '%s: %s' % (d.kana or t, d.meaning), definitions)) + ']'
i = i + matchlen
kanji = kanji_lookup(text[i])
if kanji is None:
r = r + text[i]
i = i + 1
r = r + '{' + kana_romaji('|'.join(kanji.readings))
meanings = kanji.meanings
if meanings:
r = r + ': ' + ', '.join(meanings)
r = r + ']'
return kana_romaji(r)
romaji_regex = re.compile('(HIRAGANA|KATAKANA) LETTER (.+)')
def make_dict():
for i in range(65535):
char = unichr(i)
m = romaji_regex.match(, ''))
if m:
if == 'HIRAGANA': print ' %r: %r,' % (char,
else: print ' %r: %r,' % (char,
Something went wrong with that request. Please try again.