diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d3cb227d7d1..356e2960346 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -227,6 +227,8 @@ def module_installed_bypass_session(dbname): return sorted_modules def module_boot(req): + return [m for m in req.config.server_wide_modules if m in openerpweb.addons_manifest] + # TODO the following will be enabled once we separate the module code and translation loading serverside = [] dbside = [] for i in req.config.server_wide_modules: @@ -531,6 +533,20 @@ def parse_context(context, session): except ValueError: return common.nonliterals.Context(session, context) + +def _local_web_translations(trans_file): + messages = [] + try: + with open(trans_file) as t_file: + po = babel.messages.pofile.read_po(t_file) + except Exception: + return + for x in po: + if x.id and x.string and "openerp-web" in x.auto_comments: + messages.append({'id': x.id, 'string': x.string}) + return messages + + #---------------------------------------------------------- # OpenERP Web web Controllers #---------------------------------------------------------- @@ -576,6 +592,7 @@ class Home(openerpweb.Controller): def login(self, req, db, login, key): return login_and_redirect(req, db, login, key) + class WebClient(openerpweb.Controller): _cp_path = "/web/webclient" @@ -658,41 +675,53 @@ class WebClient(openerpweb.Controller): last_modified, checksum) @openerpweb.jsonrequest - def translations(self, req, mods, lang): - lang_model = req.session.model('res.lang') - ids = lang_model.search([("code", "=", lang)]) - if ids: - lang_obj = lang_model.read(ids[0], ["direction", "date_format", "time_format", - "grouping", "decimal_point", "thousands_sep"]) - else: - lang_obj = None + def bootstrap_translations(self, req, mods): + """ Load local translations from *.po files, as a temporary solution + until we have established a valid session. This is meant only + for translating the login page and db management chrome, using + the browser's language. """ + lang = req.httprequest.accept_languages.best or 'en' + # For performance reasons we only load a single translation, so for + # sub-languages (that should only be partially translated) we load the + # main language PO instead - that should be enough for the login screen. + if '-' in lang: # RFC2616 uses '-' separators for sublanguages + lang = [lang.split('-',1)[0], lang] - if "_" in lang: - separator = "_" - else: - separator = "@" - langs = lang.split(separator) - langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)] - - transs = {} + translations_per_module = {} for addon_name in mods: - transl = {"messages":[]} - transs[addon_name] = transl addons_path = openerpweb.addons_manifest[addon_name]['addons_path'] - for l in langs: - f_name = os.path.join(addons_path, addon_name, "i18n", l + ".po") - if not os.path.exists(f_name): - continue - try: - with open(f_name) as t_file: - po = babel.messages.pofile.read_po(t_file) - except Exception: - continue - for x in po: - if x.id and x.string and "openerp-web" in x.auto_comments: - transl["messages"].append({'id': x.id, 'string': x.string}) - return {"modules": transs, - "lang_parameters": lang_obj} + f_name = os.path.join(addons_path, addon_name, "i18n", lang + ".po") + if not os.path.exists(f_name): + continue + translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)} + + return {"modules": translations_per_module, + "lang_parameters": None} + + @openerpweb.jsonrequest + def translations(self, req, mods, lang): + res_lang = req.session.model('res.lang') + ids = res_lang.search([("code", "=", lang)]) + lang_params = None + if ids: + lang_params = res_lang.read(ids[0], ["direction", "date_format", "time_format", + "grouping", "decimal_point", "thousands_sep"]) + + # Regional languages (ll_CC) must inherit/override their parent lang (ll), but this is + # done server-side when the language is loaded, so we only need to load the user's lang. + ir_translation = req.session.model('ir.translation') + translations_per_module = {} + messages = ir_translation.search_read([('module','in',mods),('lang','=',lang), + ('comments','like','openerp-web'),('value','!=',False), + ('value','!=','')], + ['module','src','value','lang'], order='module') + for mod, msg_group in itertools.groupby(messages, key=operator.itemgetter('module')): + translations_per_module.setdefault(mod,{'messages':[]}) + translations_per_module[mod]['messages'].extend({'id': m['src'], + 'string': m['value']} \ + for m in msg_group) + return {"modules": translations_per_module, + "lang_parameters": lang_params} @openerpweb.jsonrequest def version_info(self, req): @@ -769,7 +798,7 @@ class Database(openerpweb.Controller): {'fileToken': int(token)} ) except xmlrpclib.Fault, e: - return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]]) + return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]]) @openerpweb.httprequest def restore(self, req, db_file, restore_pwd, new_db): diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js index ea160d6faee..d55408ebc83 100644 --- a/addons/web/static/src/js/coresetup.js +++ b/addons/web/static/src/js/coresetup.js @@ -57,7 +57,12 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess if(self.session_is_valid()) { return deferred.pipe(function() { return self.load_modules(); }); } - return deferred; + return $.when( + deferred, + self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).pipe(function(trans) { + instance.web._t.database.set_bundle(trans); + }) + ); }); }, /** @@ -537,13 +542,9 @@ instance.web.qweb.preprocess_node = function() { if (translation && translation.value === 'off') { return; } - var ts = _.str.trim(this.node.data); - if (ts.length === 0) { - return; - } - var tr = instance.web._t(ts); - if (tr !== ts) { - this.node.data = tr; + var match = /^(\s*)(.+?)(\s*)$/.exec(this.node.data); + if (match) { + this.node.data = match[1] + instance.web._t(match[2]) + match[3]; } break; case 1: @@ -600,8 +601,7 @@ var messages_by_seconds = function() { [120, _t("Don't leave yet,
it's still loading...")], [300, _t("You may not believe it,
but the application is actually loading...")], [420, _t("Take a minute to get a coffee,
because it's loading...")], - [600, _t("It's loading...
By the way, did you tried the kitten mode?")], - [3600, _t("Maybe you should consider pressing F5...")], + [3600, _t("Maybe you should consider reloading the application by pressing F5...")], ]; }; diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 01bc0c76c87..71e955e80a6 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1494,7 +1494,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ return $.when(facet_from(this, value)); } assert(value.length <= 1, - _("M2O search fields do not currently handle multiple default values")); + _t("M2O search fields do not currently handle multiple default values")); // there are many cases of {search_default_$m2ofield: [id]}, need // to handle this as if it were a single value. value = value[0]; @@ -1578,7 +1578,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ $filter.unbind('click').click(function () { self.view.query.reset([{ - category: _("Custom Filter"), + category: _t("Custom Filter"), icon: 'M', field: { get_context: function () { return filter.context; }, diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index bf6b869482b..1c32d5ef63e 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2081,7 +2081,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w var self = this; var trans = new instance.web.DataSet(this, 'ir.translation'); return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).then(function(r) { - self.do_action(r.result); + self.do_action(r); }); }, }); @@ -4282,7 +4282,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ this.dataset.create_function = function(data, sup) { var fct = self.options.create_function || sup; return fct.call(this, data).then(function(r) { - self.created_elements.push(r.result); + self.created_elements.push(r); }); }; this.dataset.write_function = function(id, data, options, sup) { diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 2fff0e7b683..7b5fcc70289 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -1190,7 +1190,7 @@ instance.web.View = instance.web.Widget.extend({ var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {}); var handler = function (r) { - var action = r.result; + var action = r; if (action && action.constructor == Object) { var ncontext = new instance.web.CompoundContext(context); if (record_id) { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index ab4f81a1688..bf55db8d7c2 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -2,12 +2,6 @@ - -
- -
-
-
@@ -413,7 +407,7 @@
@@ -1128,7 +1122,7 @@ - width: 83px; + width: 83px;