Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

406 строки
12 KiB

  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
  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_doctype_code('lib/core')
  85. build_from_doctype_code('app')
  86. # doctype
  87. build_from_database()
  88. build_for_framework('lib/webnotes', 'py', with_doctype_names=True)
  89. build_for_framework('lib/public/js/wn', 'js')
  90. build_for_framework('app/public/js', 'js', with_doctype_names=True)
  91. def build_for_pages(path):
  92. """make locale files for framework py and js (all)"""
  93. messages = []
  94. for (basepath, folders, files) in os.walk(path):
  95. if os.path.basename(os.path.dirname(basepath))=="page":
  96. messages_js, messages_py = [], []
  97. for fname in files:
  98. if fname.endswith('.js'):
  99. messages_js += get_message_list(os.path.join(basepath, fname))
  100. if fname.endswith('.py'):
  101. messages_py += get_message_list(os.path.join(basepath, fname))
  102. if messages_js:
  103. write_messages_file(basepath, messages_js, "js")
  104. if messages_py:
  105. write_messages_file(basepath, messages_py, "py")
  106. def build_from_database():
  107. """make doctype labels, names, options, descriptions"""
  108. def get_select_options(doc):
  109. if doc.doctype=="DocField" and doc.fieldtype=='Select' and doc.options \
  110. and not doc.options.startswith("link:") \
  111. and not doc.options.startswith("attach_files:"):
  112. return doc.options.split('\n')
  113. else:
  114. return []
  115. build_for_doc_from_database(webnotes._dict({
  116. "doctype": "DocType",
  117. "module_field": "module",
  118. "DocType": ["name", "description", "module"],
  119. "DocField": ["label", "description"],
  120. "custom": get_select_options
  121. }))
  122. def build_for_doc_from_database(fields):
  123. for item in webnotes.conn.sql("""select name from `tab%s`""" % fields.doctype, as_dict=1):
  124. messages = []
  125. doclist = webnotes.bean(fields.doctype, item.name).doclist
  126. for doc in doclist:
  127. if doc.doctype in fields:
  128. messages += map(lambda x: x in fields[doc.doctype] and doc.fields.get(x) or None,
  129. doc.fields.keys())
  130. if fields.custom:
  131. messages += fields.custom(doc)
  132. doc = doclist[0]
  133. if doc.fields.get(fields.module_field):
  134. doctype_path = get_doc_path(doc.fields[fields.module_field],
  135. doc.doctype, doc.name)
  136. write_messages_file(doctype_path, messages, 'doc')
  137. def build_for_framework(path, mtype, with_doctype_names = False):
  138. """make locale files for framework py and js (all)"""
  139. messages = []
  140. for (basepath, folders, files) in os.walk(path):
  141. for fname in files:
  142. if fname.endswith('.' + mtype):
  143. messages += get_message_list(os.path.join(basepath, fname))
  144. # append module & doctype names
  145. if with_doctype_names:
  146. for m in webnotes.conn.sql("""select name, module from `tabDocType`"""):
  147. messages.append(m[0])
  148. messages.append(m[1])
  149. # append labels from config.json
  150. config = webnotes.get_config()
  151. for moduleinfo in config["modules"].values():
  152. if moduleinfo.get("label"):
  153. messages.append(moduleinfo["label"])
  154. if messages:
  155. write_messages_file(path, messages, mtype)
  156. def build_from_doctype_code(path):
  157. """walk and make locale files in all folders"""
  158. for (basepath, folders, files) in os.walk(path):
  159. messagespy = []
  160. messagesjs = []
  161. for fname in files:
  162. if fname.endswith('py'):
  163. messagespy += get_message_list(os.path.join(basepath, fname))
  164. if fname.endswith('js'):
  165. messagesjs += get_message_list(os.path.join(basepath, fname))
  166. if messagespy:
  167. write_messages_file(basepath, messagespy, 'py')
  168. if messagespy:
  169. write_messages_file(basepath, messagesjs, 'js')
  170. def get_message_list(path):
  171. """get list of messages from a code file"""
  172. import re
  173. messages = []
  174. with open(path, 'r') as sourcefile:
  175. txt = sourcefile.read()
  176. messages += re.findall('_\("([^"]*)"\)', txt)
  177. messages += re.findall("_\('([^']*)'\)", txt)
  178. messages += re.findall('_\("{3}([^"]*)"{3}\)', txt, re.S)
  179. return messages
  180. def write_messages_file(path, messages, mtype):
  181. """write messages to translation file"""
  182. if not os.path.exists(path):
  183. return
  184. if not os.path.exists(os.path.join(path, 'locale')):
  185. os.makedirs(os.path.join(path, 'locale'))
  186. fname = os.path.join(path, 'locale', '_messages_' + mtype + '.json')
  187. messages = list(set(messages))
  188. filtered = []
  189. for m in messages:
  190. if m and re.search('[a-zA-Z]+', m):
  191. filtered.append(m)
  192. with open(fname, 'w') as msgfile:
  193. msgfile.write(json.dumps(filtered, indent=1))
  194. def export_messages(lang, outfile):
  195. """get list of all messages"""
  196. messages = {}
  197. # extract messages
  198. for (basepath, folders, files) in os.walk('.'):
  199. def _get_messages(messages, basepath, mtype):
  200. mlist = get_messages(basepath, mtype)
  201. if not mlist:
  202. return
  203. # update messages with already existing translations
  204. langdata = get_lang_data(basepath, lang, mtype)
  205. for m in mlist:
  206. if not messages.get(m):
  207. messages[m] = langdata.get(m, "")
  208. if os.path.basename(basepath)=='locale':
  209. _get_messages(messages, basepath, 'doc')
  210. _get_messages(messages, basepath, 'py')
  211. _get_messages(messages, basepath, 'js')
  212. # remove duplicates
  213. if outfile:
  214. from csv import writer
  215. with open(outfile, 'w') as msgfile:
  216. w = writer(msgfile)
  217. keys = messages.keys()
  218. keys.sort()
  219. for m in keys:
  220. w.writerow([m.encode('utf-8'), messages.get(m, '').encode('utf-8')])
  221. def import_messages(lang, infile):
  222. """make individual message files for each language"""
  223. data = dict(get_all_messages_from_file(infile))
  224. for (basepath, folders, files) in os.walk('.'):
  225. def _update_lang_file(mtype):
  226. """create a langauge file for the given message type"""
  227. messages = get_messages(basepath, mtype)
  228. if not messages: return
  229. # read existing
  230. langdata = get_lang_data(basepath, lang, mtype)
  231. # update fresh
  232. for m in messages:
  233. if data.get(m):
  234. langdata[m] = data.get(m)
  235. if langdata:
  236. # write new langfile
  237. langfilename = os.path.join(basepath, lang + '-' + mtype + '.json')
  238. with open(langfilename, 'w') as langfile:
  239. langfile.write(json.dumps(langdata, indent=1, sort_keys=True).encode('utf-8'))
  240. #print 'wrote ' + langfilename
  241. if os.path.basename(basepath)=='locale':
  242. # make / update lang files for each type of message file (doc, js, py)
  243. # example: hi-doc.json, hi-js.json, hi-py.json
  244. _update_lang_file('doc')
  245. _update_lang_file('js')
  246. _update_lang_file('py')
  247. docs_loaded = []
  248. def load_doc_messages(module, doctype, name):
  249. if webnotes.lang=="en":
  250. return {}
  251. global docs_loaded
  252. doc_path = get_doc_path(module, doctype, name)
  253. # don't repload the same doc again
  254. if (webnotes.lang + ":" + doc_path) in docs_loaded:
  255. return
  256. docs_loaded.append(webnotes.lang + ":" + doc_path)
  257. global messages
  258. messages.update(get_lang_data(doc_path, None, 'doc'))
  259. def get_lang_data(basepath, lang, mtype):
  260. """get language dict from langfile"""
  261. # add "locale" folder if reqd
  262. if os.path.basename(basepath) != 'locale':
  263. basepath = os.path.join(basepath, 'locale')
  264. if not lang: lang = webnotes.lang
  265. path = os.path.join(basepath, lang + '-' + mtype + '.json')
  266. langdata = {}
  267. if os.path.exists(path):
  268. with codecs.open(path, 'r', 'utf-8') as langfile:
  269. langdata = json.loads(langfile.read())
  270. return langdata
  271. def get_messages(basepath, mtype):
  272. """load list of messages from _message files"""
  273. # get message list
  274. path = os.path.join(basepath, '_messages_' + mtype + '.json')
  275. messages = []
  276. if os.path.exists(path):
  277. with open(path, 'r') as msgfile:
  278. messages = json.loads(msgfile.read())
  279. return messages
  280. def update_lang_js(jscode, path):
  281. return jscode + "\n\n$.extend(wn._messages, %s)" % \
  282. json.dumps(get_lang_data(path, webnotes.lang, 'js'))
  283. def get_all_messages_from_file(path):
  284. with codecs.open(path, 'r', 'utf-8') as msgfile:
  285. data = msgfile.read()
  286. data = reader([r.encode('utf-8') for r in data.splitlines()])
  287. newdata = []
  288. for row in data:
  289. newrow = []
  290. for val in row:
  291. newrow.append(unicode(val, 'utf-8'))
  292. newdata.append(newrow)
  293. return newdata
  294. def google_translate(lang, infile, outfile):
  295. """translate objects using Google API. Add you own API key for translation"""
  296. data = get_all_messages_from_file(infile)
  297. import requests, conf
  298. old_translations = {}
  299. # update existing translations
  300. if os.path.exists(outfile):
  301. with codecs.open(outfile, "r", "utf-8") as oldfile:
  302. old_data = oldfile.read()
  303. old_translations = dict(reader([r.encode('utf-8').strip() for r in old_data.splitlines()]))
  304. with open(outfile, 'w') as msgfile:
  305. from csv import writer
  306. w = writer(msgfile)
  307. for row in data:
  308. if row[0] and row[0].strip():
  309. if old_translations.get(row[0].strip()):
  310. row[1] = old_translations[row[0].strip()]
  311. else:
  312. print 'translating: ' + row[0]
  313. response = requests.get("""https://www.googleapis.com/language/translate/v2""",
  314. params = {
  315. "key": conf.google_api_key,
  316. "source": "en",
  317. "target": lang,
  318. "q": row[0]
  319. })
  320. if "error" in response.json:
  321. print response.json
  322. continue
  323. row[1] = response.json["data"]["translations"][0]["translatedText"]
  324. if not row[1]:
  325. row[1] = row[0] # google unable to translate!
  326. row[1] = row[1].encode('utf-8')
  327. row[0] = row[0].encode('utf-8')
  328. w.writerow(row)