Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

translate.py 12 KiB

vor 12 Jahren
vor 12 Jahren
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. """
  5. Contributing:
  6. 1. Add the .csv file
  7. 2. Run import
  8. 3. Then run translate
  9. """
  10. import webnotes
  11. import os
  12. import codecs
  13. import json
  14. import re
  15. from csv import reader
  16. from webnotes.modules import get_doc_path
  17. from webnotes.utils import get_base_path, cstr
  18. messages = {}
  19. def translate(lang=None):
  20. languages = [lang]
  21. if lang=="all" or lang==None:
  22. languages = get_all_languages()
  23. print "Extracting / updating translatable strings..."
  24. build_message_files()
  25. print "Compiling messages in one file..."
  26. export_messages(lang, '_lang_tmp.csv')
  27. for lang in languages:
  28. if lang != "en":
  29. filename = 'app/translations/'+lang+'.csv'
  30. print "For " + lang + ":"
  31. print "Translating via Google Translate..."
  32. google_translate(lang, '_lang_tmp.csv', filename)
  33. print "Updating language files..."
  34. import_messages(lang, filename)
  35. print "Deleting temp file..."
  36. os.remove('_lang_tmp.csv')
  37. def get_all_languages():
  38. try:
  39. return [f[:-4] for f in os.listdir("app/translations") if f.endswith(".csv")]
  40. except OSError, e:
  41. if e.args[0]==2:
  42. return []
  43. else:
  44. raise e
  45. def get_lang_dict():
  46. languages_path = os.path.join(get_base_path(), "app", "translations", "languages.json")
  47. if os.path.exists(languages_path):
  48. with open(languages_path, "r") as langfile:
  49. return json.loads(langfile.read())
  50. else: return {}
  51. def update_translations():
  52. """
  53. compare language file timestamps with last updated timestamps in `.wnf-lang-status`
  54. if timestamps are missing / changed, build new `.json` files in the `lang folders`
  55. """
  56. langstatus = {}
  57. languages = get_all_languages()
  58. message_updated = False
  59. status_file_path = "app/.wnf-lang-status"
  60. if not os.path.exists(os.path.join('app', 'translations')):
  61. return
  62. if os.path.exists(status_file_path):
  63. with open(status_file_path, "r") as langstatusfile:
  64. langstatus = eval(langstatusfile.read())
  65. for lang in languages:
  66. filename = os.path.join('app', 'translations', lang + '.csv')
  67. if langstatus.get(lang, None)!=os.path.getmtime(filename):
  68. print "Setting up lang files for " + lang + "..."
  69. if not message_updated:
  70. print "Extracting / updating translatable strings..."
  71. build_message_files()
  72. message_updated = True
  73. print "Writing translations..."
  74. import_messages(lang, filename)
  75. langstatus[lang] = os.path.getmtime(filename)
  76. with open(status_file_path, "w") as langstatusfile:
  77. langstatus = langstatusfile.write(str(langstatus))
  78. def build_message_files():
  79. """build from doctypes, pages, database and framework"""
  80. if not webnotes.conn:
  81. webnotes.connect()
  82. build_for_pages('lib/core')
  83. build_for_pages('app')
  84. build_from_query_report('lib/core')
  85. build_from_query_report('app')
  86. build_from_doctype_code('lib/core')
  87. build_from_doctype_code('app')
  88. # doctype
  89. build_from_database()
  90. build_for_framework('lib/webnotes', 'py', with_doctype_names=True)
  91. build_for_framework('lib/public/js/wn', 'js')
  92. build_for_framework('app/public/js', 'js', with_doctype_names=True)
  93. def build_for_pages(path):
  94. """make locale files for framework py and js (all)"""
  95. messages = []
  96. for (basepath, folders, files) in os.walk(path):
  97. if os.path.basename(os.path.dirname(basepath))=="page":
  98. messages_js, messages_py = [], []
  99. for fname in files:
  100. fname = cstr(fname)
  101. if fname.endswith('.js'):
  102. messages_js += get_message_list(os.path.join(basepath, fname))
  103. if fname.endswith('.py'):
  104. messages_py += get_message_list(os.path.join(basepath, fname))
  105. if messages_js:
  106. write_messages_file(basepath, messages_js, "js")
  107. if messages_py:
  108. write_messages_file(basepath, messages_py, "py")
  109. def build_from_query_report(path):
  110. """make locale files for reports py and js (all)"""
  111. messages = []
  112. for (basepath, folders, files) in os.walk(path):
  113. if os.path.basename(os.path.dirname(basepath))=="page":
  114. messages_js, messages_py = [], []
  115. for fname in files:
  116. fname = cstr(fname)
  117. if fname.endswith('.js'):
  118. messages_js += get_message_list(os.path.join(basepath, fname))
  119. if fname.endswith('.py'):
  120. messages_py += get_message_list(os.path.join(basepath, fname))
  121. if messages_js:
  122. write_messages_file(basepath, messages_js, "js")
  123. if messages_py:
  124. write_messages_file(basepath, messages_py, "py")
  125. def build_from_database():
  126. """make doctype labels, names, options, descriptions"""
  127. def get_select_options(doc):
  128. if doc.doctype=="DocField" and doc.fieldtype=='Select' and doc.options \
  129. and not doc.options.startswith("link:") \
  130. and not doc.options.startswith("attach_files:"):
  131. return doc.options.split('\n')
  132. else:
  133. return []
  134. build_for_doc_from_database(webnotes._dict({
  135. "doctype": "DocType",
  136. "module_field": "module",
  137. "DocType": ["name", "description", "module"],
  138. "DocField": ["label", "description"],
  139. "custom": get_select_options
  140. }))
  141. def build_for_doc_from_database(fields):
  142. for item in webnotes.conn.sql("""select name from `tab%s`""" % fields.doctype, as_dict=1):
  143. messages = []
  144. doclist = webnotes.bean(fields.doctype, item.name).doclist
  145. for doc in doclist:
  146. if doc.doctype in fields:
  147. messages += map(lambda x: x in fields[doc.doctype] and doc.fields.get(x) or None,
  148. doc.fields.keys())
  149. if fields.custom:
  150. messages += fields.custom(doc)
  151. doc = doclist[0]
  152. if doc.fields.get(fields.module_field):
  153. doctype_path = get_doc_path(doc.fields[fields.module_field],
  154. doc.doctype, doc.name)
  155. write_messages_file(doctype_path, messages, 'doc')
  156. def build_for_framework(path, mtype, with_doctype_names = False):
  157. """make locale files for framework py and js (all)"""
  158. messages = []
  159. for (basepath, folders, files) in os.walk(path):
  160. for fname in files:
  161. fname = cstr(fname)
  162. if fname.endswith('.' + mtype):
  163. messages += get_message_list(os.path.join(basepath, fname))
  164. # append module & doctype names
  165. if with_doctype_names:
  166. for m in webnotes.conn.sql("""select name, module from `tabDocType`"""):
  167. messages.append(m[0])
  168. messages.append(m[1])
  169. # append labels from config.json
  170. config = webnotes.get_config()
  171. for moduleinfo in config["modules"].values():
  172. if moduleinfo.get("label"):
  173. messages.append(moduleinfo["label"])
  174. if messages:
  175. write_messages_file(path, messages, mtype)
  176. def build_from_doctype_code(path):
  177. """walk and make locale files in all folders"""
  178. for (basepath, folders, files) in os.walk(path):
  179. messagespy = []
  180. messagesjs = []
  181. for fname in files:
  182. fname = cstr(fname)
  183. if fname.endswith('py'):
  184. messagespy += get_message_list(os.path.join(basepath, fname))
  185. if fname.endswith('js'):
  186. messagesjs += get_message_list(os.path.join(basepath, fname))
  187. if messagespy:
  188. write_messages_file(basepath, messagespy, 'py')
  189. if messagespy:
  190. write_messages_file(basepath, messagesjs, 'js')
  191. def get_message_list(path):
  192. """get list of messages from a code file"""
  193. import re
  194. messages = []
  195. with open(path, 'r') as sourcefile:
  196. txt = sourcefile.read()
  197. messages += re.findall('_\("([^"]*)"\)', txt)
  198. messages += re.findall("_\('([^']*)'\)", txt)
  199. messages += re.findall('_\("{3}([^"]*)"{3}\)', txt, re.S)
  200. return messages
  201. def write_messages_file(path, messages, mtype):
  202. """write messages to translation file"""
  203. if not os.path.exists(path):
  204. return
  205. if not os.path.exists(os.path.join(path, 'locale')):
  206. os.makedirs(os.path.join(path, 'locale'))
  207. fname = os.path.join(path, 'locale', '_messages_' + mtype + '.json')
  208. messages = list(set(messages))
  209. filtered = []
  210. for m in messages:
  211. if m and re.search('[a-zA-Z]+', m):
  212. filtered.append(m)
  213. with open(fname, 'w') as msgfile:
  214. msgfile.write(json.dumps(filtered, indent=1))
  215. def export_messages(lang, outfile):
  216. """get list of all messages"""
  217. messages = {}
  218. # extract messages
  219. for (basepath, folders, files) in os.walk('.'):
  220. def _get_messages(messages, basepath, mtype):
  221. mlist = get_messages(basepath, mtype)
  222. if not mlist:
  223. return
  224. # update messages with already existing translations
  225. langdata = get_lang_data(basepath, lang, mtype)
  226. for m in mlist:
  227. if not messages.get(m):
  228. messages[m] = langdata.get(m, "")
  229. if os.path.basename(basepath)=='locale':
  230. _get_messages(messages, basepath, 'doc')
  231. _get_messages(messages, basepath, 'py')
  232. _get_messages(messages, basepath, 'js')
  233. # remove duplicates
  234. if outfile:
  235. from csv import writer
  236. with open(outfile, 'w') as msgfile:
  237. w = writer(msgfile)
  238. keys = messages.keys()
  239. keys.sort()
  240. for m in keys:
  241. w.writerow([m.encode('utf-8'), messages.get(m, '').encode('utf-8')])
  242. def import_messages(lang, infile):
  243. """make individual message files for each language"""
  244. data = dict(get_all_messages_from_file(infile))
  245. for (basepath, folders, files) in os.walk('.'):
  246. def _update_lang_file(mtype):
  247. """create a langauge file for the given message type"""
  248. messages = get_messages(basepath, mtype)
  249. if not messages: return
  250. # read existing
  251. langdata = get_lang_data(basepath, lang, mtype)
  252. # update fresh
  253. for m in messages:
  254. if data.get(m):
  255. langdata[m] = data.get(m)
  256. if langdata:
  257. # write new langfile
  258. langfilename = os.path.join(basepath, lang + '-' + mtype + '.json')
  259. with open(langfilename, 'w') as langfile:
  260. langfile.write(json.dumps(langdata, indent=1, sort_keys=True).encode('utf-8'))
  261. #print 'wrote ' + langfilename
  262. if os.path.basename(basepath)=='locale':
  263. # make / update lang files for each type of message file (doc, js, py)
  264. # example: hi-doc.json, hi-js.json, hi-py.json
  265. _update_lang_file('doc')
  266. _update_lang_file('js')
  267. _update_lang_file('py')
  268. docs_loaded = []
  269. def load_doc_messages(module, doctype, name):
  270. if webnotes.lang=="en":
  271. return {}
  272. global docs_loaded
  273. doc_path = get_doc_path(module, doctype, name)
  274. # don't repload the same doc again
  275. if (webnotes.lang + ":" + doc_path) in docs_loaded:
  276. return
  277. docs_loaded.append(webnotes.lang + ":" + doc_path)
  278. global messages
  279. messages.update(get_lang_data(doc_path, None, 'doc'))
  280. def get_lang_data(basepath, lang, mtype):
  281. """get language dict from langfile"""
  282. # add "locale" folder if reqd
  283. if os.path.basename(basepath) != 'locale':
  284. basepath = os.path.join(basepath, 'locale')
  285. if not lang: lang = webnotes.lang
  286. path = os.path.join(basepath, lang + '-' + mtype + '.json')
  287. langdata = {}
  288. if os.path.exists(path):
  289. with codecs.open(path, 'r', 'utf-8') as langfile:
  290. langdata = json.loads(langfile.read())
  291. return langdata
  292. def get_messages(basepath, mtype):
  293. """load list of messages from _message files"""
  294. # get message list
  295. path = os.path.join(basepath, '_messages_' + mtype + '.json')
  296. messages = []
  297. if os.path.exists(path):
  298. with open(path, 'r') as msgfile:
  299. messages = json.loads(msgfile.read())
  300. return messages
  301. def update_lang_js(jscode, path):
  302. return jscode + "\n\n$.extend(wn._messages, %s)" % \
  303. json.dumps(get_lang_data(path, webnotes.lang, 'js'))
  304. def get_all_messages_from_file(path):
  305. with codecs.open(path, 'r', 'utf-8') as msgfile:
  306. data = msgfile.read()
  307. data = reader([r.encode('utf-8') for r in data.splitlines()])
  308. newdata = []
  309. for row in data:
  310. newrow = []
  311. for val in row:
  312. newrow.append(unicode(val, 'utf-8'))
  313. newdata.append(newrow)
  314. return newdata
  315. def google_translate(lang, infile, outfile):
  316. """translate objects using Google API. Add you own API key for translation"""
  317. data = get_all_messages_from_file(infile)
  318. import requests, conf
  319. old_translations = {}
  320. # update existing translations
  321. if os.path.exists(outfile):
  322. with codecs.open(outfile, "r", "utf-8") as oldfile:
  323. old_data = oldfile.read()
  324. old_translations = dict(reader([r.encode('utf-8').strip() for r in old_data.splitlines()]))
  325. with open(outfile, 'w') as msgfile:
  326. from csv import writer
  327. w = writer(msgfile)
  328. for row in data:
  329. if row[0] and row[0].strip():
  330. if old_translations.get(row[0].strip()):
  331. row[1] = old_translations[row[0].strip()]
  332. else:
  333. print 'translating: ' + row[0]
  334. response = requests.get("""https://www.googleapis.com/language/translate/v2""",
  335. params = {
  336. "key": conf.google_api_key,
  337. "source": "en",
  338. "target": lang,
  339. "q": row[0]
  340. })
  341. if "error" in response.json:
  342. print response.json
  343. continue
  344. row[1] = response.json["data"]["translations"][0]["translatedText"]
  345. if not row[1]:
  346. row[1] = row[0] # google unable to translate!
  347. row[1] = row[1].encode('utf-8')
  348. row[0] = row[0].encode('utf-8')
  349. w.writerow(row)