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/xml/base.xml b/addons/web/static/src/xml/base.xml
index 3d979186aa3..dfe86036c45 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -413,7 +413,7 @@