|
- # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
- # MIT License. See license.txt
-
- from __future__ import unicode_literals
-
- """
- Contributing:
- 1. Add the .csv file
- 2. Run import
- 3. Then run translate
- """
-
- # loading
- # doctype, page, report
- # boot(startup)
- # frappe.require
- # frappe._
-
- import frappe, os, re, codecs, json
-
- def guess_language_from_http_header(lang):
- """set frappe.local.lang from HTTP headers at beginning of request"""
- if not lang:
- return frappe.local.lang
-
- guess = None
- lang_list = get_all_languages() or []
-
- if ";" in lang: # not considering weightage
- lang = lang.split(";")[0]
- if "," in lang:
- lang = lang.split(",")
- else:
- lang = [lang]
-
- for l in lang:
- code = l.strip()
- if code in lang_list:
- guess = code
- break
-
- # check if parent language (pt) is setup, if variant (pt-BR)
- if "-" in code:
- code = code.split("-")[0]
- if code in lang_list:
- guess = code
- break
-
- return guess or frappe.local.lang
-
- def get_user_lang(user=None):
- """set frappe.local.lang from user preferences on session beginning or resumption"""
- if not user:
- user = frappe.session.user
-
- # via cache
- lang = frappe.cache().get_value("lang:" + user)
-
- if not lang:
-
- # if defined in user profile
- user_lang = frappe.db.get_value("User", user, "language")
- if user_lang and user_lang!="Loading...":
- lang = get_lang_dict().get(user_lang)
- else:
- default_lang = frappe.db.get_default("lang")
- lang = default_lang or frappe.local.lang
-
- frappe.cache().set_value("lang:" + user, lang)
-
- return lang
-
- def set_default_language(language):
- lang = get_lang_dict()[language]
- frappe.db.set_default("lang", lang)
- frappe.local.lang = lang
-
- def get_all_languages():
- return [a.split()[0] for a in get_lang_info()]
-
- def get_lang_dict():
- return dict([[a[1], a[0]] for a in [a.split() for a in get_lang_info()]])
-
- def get_lang_info():
- return frappe.cache().get_value("langinfo",
- lambda:frappe.get_file_items(os.path.join(frappe.local.sites_path, "languages.txt")))
-
- def get_dict(fortype, name=None):
- fortype = fortype.lower()
- cache = frappe.cache()
- cache_key = "translation_assets:" + frappe.local.lang
- asset_key = fortype + ":" + (name or "-")
- translation_assets = cache.get_value(cache_key) or {}
-
- if not asset_key in translation_assets:
- if fortype=="doctype":
- messages = get_messages_from_doctype(name)
- elif fortype=="page":
- messages = get_messages_from_page(name)
- elif fortype=="report":
- messages = get_messages_from_report(name)
- elif fortype=="include":
- messages = get_messages_from_include_files()
- elif fortype=="jsfile":
- messages = get_messages_from_file(name)
- elif fortype=="boot":
- messages = get_messages_from_include_files()
- messages += frappe.db.sql_list("select name from tabDocType")
- messages += frappe.db.sql_list("select name from `tabModule Def`")
-
- translation_assets[asset_key] = make_dict_from_messages(messages)
- cache.set_value(cache_key, translation_assets)
-
- return translation_assets[asset_key]
-
- def add_lang_dict(code):
- messages = extract_messages_from_code(code)
- code += "\n\n$.extend(frappe._messages, %s)" % json.dumps(make_dict_from_messages(messages))
- return code
-
- def make_dict_from_messages(messages, full_dict=None):
- out = {}
- if full_dict==None:
- full_dict = get_full_dict(frappe.local.lang)
-
- for m in messages:
- if m in full_dict:
- out[m] = full_dict[m]
-
- return out
-
- def get_lang_js(fortype, name):
- return "\n\n$.extend(frappe._messages, %s)" % json.dumps(get_dict(fortype, name))
-
- def get_full_dict(lang):
- if lang == "en": return {}
- return frappe.cache().get_value("lang:" + lang, lambda:load_lang(lang))
-
- def load_lang(lang, apps=None):
- out = {}
- for app in (apps or frappe.get_all_apps(True)):
- path = os.path.join(frappe.get_pymodule_path(app), "translations", lang + ".csv")
- if os.path.exists(path):
- cleaned = dict([item for item in dict(read_csv_file(path)).iteritems() if item[1]])
- out.update(cleaned)
-
- return out
-
- def clear_cache():
- cache = frappe.cache()
- cache.delete_value("langinfo")
- for lang in get_all_languages():
- cache.delete_value("lang:" + lang)
- cache.delete_value("translation_assets:" + lang)
-
- def get_messages_for_app(app):
- messages = []
- modules = ", ".join(['"{}"'.format(m.title().replace("_", " ")) \
- for m in frappe.local.app_modules[app]])
-
- # doctypes
- for name in frappe.db.sql_list("""select name from tabDocType
- where module in ({})""".format(modules)):
- messages.extend(get_messages_from_doctype(name))
-
- # pages
- for name, title in frappe.db.sql("""select name, title from tabPage
- where module in ({})""".format(modules)):
- messages.append(title or name)
- messages.extend(get_messages_from_page(name))
-
- # reports
- for name in frappe.db.sql_list("""select tabReport.name from tabDocType, tabReport
- where tabReport.ref_doctype = tabDocType.name
- and tabDocType.module in ({})""".format(modules)):
- messages.append(name)
- messages.extend(get_messages_from_report(name))
-
- # app_include_files
- messages.extend(get_messages_from_include_files(app))
-
- # server_messages
- messages.extend(get_server_messages(app))
-
- return list(set(messages))
-
-
- def get_messages_from_doctype(name):
- messages = []
- meta = frappe.get_meta(name)
-
- messages = [meta.name, meta.module]
-
- for d in meta.get("fields"):
- messages.extend([d.label, d.description])
-
- if d.fieldtype=='Select' and d.options \
- and not d.options.startswith("link:") \
- and not d.options.startswith("attach_files:"):
- options = d.options.split('\n')
- if not "icon" in options[0]:
- messages.extend(options)
-
- # extract from js, py files
- doctype_file_path = frappe.get_module_path(meta.module, "doctype", meta.name, meta.name)
- messages.extend(get_messages_from_file(doctype_file_path + ".js"))
- return clean(messages)
-
- def get_messages_from_page(name):
- return get_messages_from_page_or_report("Page", name)
-
- def get_messages_from_report(name):
- report = frappe.get_doc("Report", name)
- messages = get_messages_from_page_or_report("Report", name,
- frappe.db.get_value("DocType", report.ref_doctype, "module"))
- if report.query:
- messages.extend(re.findall('"([^:,^"]*):', report.query))
- messages.append(report.report_name)
- return clean(messages)
-
- def get_messages_from_page_or_report(doctype, name, module=None):
- if not module:
- module = frappe.db.get_value(doctype, name, "module")
- file_path = frappe.get_module_path(module, doctype, name, name)
- messages = get_messages_from_file(file_path + ".js")
-
- return clean(messages)
-
- def get_server_messages(app):
- messages = []
- for basepath, folders, files in os.walk(frappe.get_pymodule_path(app)):
- for dontwalk in (".git", "public", "locale"):
- if dontwalk in folders: folders.remove(dontwalk)
-
- for f in files:
- if f.endswith(".py") or f.endswith(".html"):
- messages.extend(get_messages_from_file(os.path.join(basepath, f)))
-
- return clean(messages)
-
- def get_messages_from_include_files(app_name=None):
- messages = []
- for file in (frappe.get_hooks("app_include_js") or []) + (frappe.get_hooks("web_include_js") or []):
- messages.extend(get_messages_from_file(os.path.join(frappe.local.sites_path, file)))
-
- return clean(messages)
-
- def get_messages_from_file(path):
- """get list of messages from a code file"""
- if os.path.exists(path):
- with open(path, 'r') as sourcefile:
- return extract_messages_from_code(sourcefile.read(), path.endswith(".py"))
- else:
- return []
-
- def extract_messages_from_code(code, is_py=False):
- messages = []
- messages += re.findall('_\("([^"]*)"', code)
- messages += re.findall("_\('([^']*)'", code)
- if is_py:
- messages += re.findall('_\("{3}([^"]*)"{3}.*\)', code, re.S)
- return clean(messages)
-
- def clean(messages):
- l = []
- messages = list(set(messages))
- for m in messages:
- if m:
- if re.search("[a-z]", m) and not m.startswith("icon-") and not m.endswith("px") and not m.startswith("eval:"):
- l.append(m)
- return l
-
- def read_csv_file(path):
- from csv import reader
- with codecs.open(path, 'r', 'utf-8') as msgfile:
- data = msgfile.read()
- data = reader([r.encode('utf-8') for r in data.splitlines()])
- newdata = [[unicode(val, 'utf-8') for val in row] for row in data]
- return newdata
-
- def write_csv_file(path, app_messages, lang_dict):
- app_messages.sort()
- from csv import writer
- with open(path, 'w') as msgfile:
- w = writer(msgfile)
- for m in app_messages:
- t = lang_dict.get(m, '')
- # strip whitespaces
- t = re.sub('{\s?([0-9]+)\s?}', "{\g<1>}", t)
- w.writerow([m.encode('utf-8'), t.encode('utf-8')])
-
- def get_untranslated(lang, untranslated_file, get_all=False):
- """translate objects using Google API. Add you own API key for translation"""
- clear_cache()
- apps = frappe.get_all_apps(True)
-
- messages = []
- untranslated = []
- for app in apps:
- messages.extend(get_messages_for_app(app))
-
- if get_all:
- print str(len(messages)) + " messages"
- with open(untranslated_file, "w") as f:
- for m in messages:
- f.write((m + "\n").encode("utf-8"))
- else:
- full_dict = get_full_dict(lang)
-
- for m in messages:
- if not full_dict.get(m):
- untranslated.append(m)
-
- if untranslated:
- print str(len(untranslated)) + " missing translations of " + str(len(messages))
- with open(untranslated_file, "w") as f:
- for m in untranslated:
- f.write((m + "\n").encode("utf-8"))
- else:
- print "all translated!"
-
- def update_translations(lang, untranslated_file, translated_file):
- clear_cache()
- full_dict = get_full_dict(lang)
-
- full_dict.update(dict(zip(frappe.get_file_items(untranslated_file),
- frappe.get_file_items(translated_file))))
-
- for app in frappe.get_all_apps(True):
- write_translations_file(app, lang, full_dict)
-
- def rebuild_all_translation_files():
- for lang in get_all_languages():
- for app in frappe.get_all_apps():
- write_translations_file(app, lang)
-
- def write_translations_file(app, lang, full_dict=None):
- tpath = frappe.get_pymodule_path(app, "translations")
- frappe.create_folder(tpath)
- write_csv_file(os.path.join(tpath, lang + ".csv"),
- get_messages_for_app(app), full_dict or get_full_dict(lang))
|