diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 629ad917748..e0a72234808 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -6,7 +6,7 @@ from odoo import models, api, _, fields from odoo.osv import expression from odoo.release import version from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF, safe_eval -from odoo.tools.misc import formatLang, format_date as odoo_format_date +from odoo.tools.misc import formatLang, format_date as odoo_format_date, get_lang import random import ast @@ -90,7 +90,7 @@ class account_journal(models.Model): data = [] today = datetime.today() last_month = today + timedelta(days=-30) - locale = self._context.get('lang') or 'en_US' + locale = get_lang(self.env).code #starting point of the graph is the last statement last_stmt = BankStatement.search([('journal_id', '=', self.id), ('date', '<=', today.strftime(DF))], order='date desc, id desc', limit=1) @@ -139,7 +139,7 @@ class account_journal(models.Model): data = [] today = fields.Datetime.now(self) data.append({'label': _('Due'), 'value':0.0, 'type': 'past'}) - day_of_week = int(format_datetime(today, 'e', locale=self._context.get('lang') or 'en_US')) + day_of_week = int(format_datetime(today, 'e', locale=get_lang(self.env).code)) first_day_of_week = today + timedelta(days=-day_of_week+1) for i in range(-1,4): if i==0: @@ -150,9 +150,9 @@ class account_journal(models.Model): start_week = first_day_of_week + timedelta(days=i*7) end_week = start_week + timedelta(days=6) if start_week.month == end_week.month: - label = str(start_week.day) + '-' +str(end_week.day)+ ' ' + format_date(end_week, 'MMM', locale=self._context.get('lang') or 'en_US') + label = str(start_week.day) + '-' + str(end_week.day) + ' ' + format_date(end_week, 'MMM', locale=get_lang(self.env).code) else: - label = format_date(start_week, 'd MMM', locale=self._context.get('lang') or 'en_US')+'-'+format_date(end_week, 'd MMM', locale=self._context.get('lang') or 'en_US') + label = format_date(start_week, 'd MMM', locale=get_lang(self.env).code) + '-' + format_date(end_week, 'd MMM', locale=get_lang(self.env).code) data.append({'label':label,'value':0.0, 'type': 'past' if i<0 else 'future'}) # Build SQL query to find amount aggregated by week diff --git a/addons/account/wizard/account_invoice_send.py b/addons/account/wizard/account_invoice_send.py index 2b98c37e9a2..99d745ff94b 100644 --- a/addons/account/wizard/account_invoice_send.py +++ b/addons/account/wizard/account_invoice_send.py @@ -4,6 +4,7 @@ from odoo import api, fields, models, _ from odoo.addons.mail.wizard.mail_compose_message import _reopen from odoo.exceptions import UserError +from odoo.tools.misc import get_lang class AccountInvoiceSend(models.TransientModel): @@ -103,7 +104,7 @@ class AccountInvoiceSend(models.TransientModel): active_ids = self.env.context.get('active_ids', self.res_id) active_records = self.env[self.model].browse(active_ids) langs = active_records.mapped('partner_id.lang') - default_lang = self.env.context.get('lang', 'en_US') + default_lang = get_lang(self.env) for lang in (set(langs) or [default_lang]): active_ids_lang = active_records.filtered(lambda r: r.partner_id.lang == lang).ids self_lang = self.with_context(active_ids=active_ids_lang, lang=lang) diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py index 3d113ff58bf..e6317daea8c 100644 --- a/addons/account/wizard/account_report_common.py +++ b/addons/account/wizard/account_report_common.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from odoo import api, fields, models, _ +from odoo.tools.misc import get_lang class AccountCommonReport(models.TransientModel): @@ -43,5 +44,5 @@ class AccountCommonReport(models.TransientModel): data['model'] = self.env.context.get('active_model', 'ir.ui.menu') data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0] used_context = self._build_contexts(data) - data['form']['used_context'] = dict(used_context, lang=self.env.context.get('lang') or 'en_US') + data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code) return self.with_context(discard_logo_check=True)._print_report(data) diff --git a/addons/base_gengo/wizard/base_gengo_translations.py b/addons/base_gengo/wizard/base_gengo_translations.py index 24a9d060bdd..898ef0a0656 100644 --- a/addons/base_gengo/wizard/base_gengo_translations.py +++ b/addons/base_gengo/wizard/base_gengo_translations.py @@ -8,6 +8,7 @@ import uuid from odoo import api, fields, models, tools, _ from odoo.exceptions import UserError, ValidationError +from odoo.tools.misc import get_lang _logger = logging.getLogger(__name__) @@ -31,9 +32,7 @@ class BaseGengoTranslations(models.TransientModel): res = super(BaseGengoTranslations, self).default_get(fields) res['authorized_credentials'], gengo = self.gengo_authentication() if 'lang_id' in fields: - res['lang_id'] = self.env['res.lang']._lang_get_id( - self.env.context.get('lang') or 'en_US' - ) + res['lang_id'] = get_lang(self.env).id return res sync_type = fields.Selection([ diff --git a/addons/calendar/controllers/main.py b/addons/calendar/controllers/main.py index 69bd2b9ade7..64b8812c7c2 100644 --- a/addons/calendar/controllers/main.py +++ b/addons/calendar/controllers/main.py @@ -9,6 +9,7 @@ import odoo.http as http from odoo.http import request from odoo import SUPERUSER_ID from odoo import registry as registry_get +from odoo.tools.misc import get_lang class CalendarController(http.Controller): @@ -43,7 +44,7 @@ class CalendarController(http.Controller): if not attendee: return request.not_found() timezone = attendee.partner_id.tz - lang = attendee.partner_id.lang or 'en_US' + lang = attendee.partner_id.lang or get_lang(request.env).code event = env['calendar.event'].with_context(tz=timezone, lang=lang).browse(int(id)) # If user is internal and logged, redirect to form view of event diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index 74073286b36..12f64409f2d 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -20,6 +20,7 @@ from odoo import tools from odoo.addons.base.models.res_partner import _tz_get from odoo.osv import expression from odoo.tools.translate import _ +from odoo.tools.misc import get_lang from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, pycompat from odoo.exceptions import UserError, ValidationError @@ -1352,11 +1353,11 @@ class Meeting(models.Model): elif interval == 'month': # Localized month name and year - result = babel.dates.format_date(date=date, format='MMMM y', locale=self._context.get('lang') or 'en_US') + result = babel.dates.format_date(date=date, format='MMMM y', locale=get_lang(self.env).code) elif interval == 'dayname': # Localized day name - result = babel.dates.format_date(date=date, format='EEEE', locale=self._context.get('lang') or 'en_US') + result = babel.dates.format_date(date=date, format='EEEE', locale=get_lang(self.env).code) elif interval == 'time': # Localized time diff --git a/addons/product/models/product_pricelist.py b/addons/product/models/product_pricelist.py index 25552049c02..754a456d7d1 100644 --- a/addons/product/models/product_pricelist.py +++ b/addons/product/models/product_pricelist.py @@ -5,6 +5,7 @@ from itertools import chain from odoo import api, fields, models, tools, _ from odoo.exceptions import UserError, ValidationError +from odoo.tools.misc import get_lang class Pricelist(models.Model): @@ -40,7 +41,7 @@ class Pricelist(models.Model): if name and operator == '=' and not args: # search on the name of the pricelist and its currency, opposite of name_get(), # Used by the magic context filter in the product search view. - query_args = {'name': name, 'limit': limit, 'lang': self._context.get('lang') or 'en_US'} + query_args = {'name': name, 'limit': limit, 'lang': get_lang(self.env).code} query = """SELECT p.id FROM (( SELECT pr.id, pr.name diff --git a/addons/rating/controllers/main.py b/addons/rating/controllers/main.py index ac585f7de56..7cbeeb85c72 100644 --- a/addons/rating/controllers/main.py +++ b/addons/rating/controllers/main.py @@ -6,6 +6,7 @@ import werkzeug from odoo import http from odoo.http import request from odoo.tools.translate import _ +from odoo.tools.misc import get_lang class Rating(http.Controller): @@ -22,7 +23,7 @@ class Rating(http.Controller): 10: _("satisfied") } rating.write({'rating': rate, 'consumed': True}) - lang = rating.partner_id.lang or 'en_US' + lang = rating.partner_id.lang or get_lang(request.env).code return request.env['ir.ui.view'].with_context(lang=lang).render_template('rating.rating_external_page_submit', { 'rating': rating, 'token': token, 'rate_name': rate_names[rate], 'rate': rate @@ -35,7 +36,7 @@ class Rating(http.Controller): return request.not_found() record_sudo = request.env[rating.res_model].sudo().browse(rating.res_id) record_sudo.rating_apply(rate, token=token, feedback=kwargs.get('feedback')) - lang = rating.partner_id.lang or 'en_US' + lang = rating.partner_id.lang or get_lang(request.env).code return request.env['ir.ui.view'].with_context(lang=lang).render_template('rating.rating_external_page_view', { 'web_base_url': request.env['ir.config_parameter'].sudo().get_param('web.base.url'), 'rating': rating, diff --git a/addons/sale_timesheet/models/project_overview.py b/addons/sale_timesheet/models/project_overview.py index afa3e5cc642..1230ce8c6fa 100644 --- a/addons/sale_timesheet/models/project_overview.py +++ b/addons/sale_timesheet/models/project_overview.py @@ -6,6 +6,7 @@ import json from odoo import fields, _, models from odoo.tools import float_round +from odoo.tools.misc import get_lang from odoo.addons.web.controllers.main import clean_action @@ -256,7 +257,7 @@ class Project(models.Model): def _to_short_month_name(date): month_index = fields.Date.from_string(date).month - return babel.dates.get_month_names('abbreviated', locale=self.env.context.get('lang', 'en_US'))[month_index] + return babel.dates.get_month_names('abbreviated', locale=get_lang(self.env).code)[month_index] header_names = [_('Name'), _('Before')] + [_to_short_month_name(date) for date in ts_months] + [_('Total'), _('Sold'), _('Remaining')] diff --git a/addons/web/models/models.py b/addons/web/models/models.py index e85de4f6c45..3ef871cabe0 100644 --- a/addons/web/models/models.py +++ b/addons/web/models/models.py @@ -5,6 +5,7 @@ import pytz from odoo import _, api, fields, models from odoo.osv.expression import AND from odoo.tools import lazy +from odoo.tools.misc import get_lang from odoo.exceptions import UserError @@ -142,7 +143,7 @@ class Base(models.AbstractModel): # Again, imitating what _read_group_format_result and _read_group_prepare_data do if group_by_value and field_type in ['date', 'datetime']: - locale = self._context.get('lang') or 'en_US' + locale = get_lang(self.env).code group_by_value = fields.Datetime.to_datetime(group_by_value) group_by_value = pytz.timezone('UTC').localize(group_by_value) tz_info = None diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 42a7b946506..0d691bc427c 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -13,6 +13,7 @@ from odoo.addons.http_routing.models.ir_http import slug, unslug from odoo.addons.website.controllers.main import QueryURL from odoo.http import request from odoo.tools import html2plaintext +from odoo.tools.misc import get_lang class WebsiteBlog(http.Controller): @@ -43,7 +44,7 @@ class WebsiteBlog(http.Controller): group['date_begin'] = start group['date_end'] = end - locale = request.context.get('lang') or 'en_US' + locale = get_lang(request.env).code start = pytz.UTC.localize(fields.Datetime.from_string(start)) tzinfo = pytz.timezone(request.context.get('tz', 'utc') or 'utc') diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index 1476c02da8a..20031a67781 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -12,6 +12,7 @@ from odoo import fields, http, _ from odoo.addons.http_routing.models.ir_http import slug from odoo.addons.website.controllers.main import QueryURL from odoo.http import request +from odoo.tools.misc import get_lang class WebsiteEventController(http.Controller): @@ -229,7 +230,7 @@ class WebsiteEventController(http.Controller): def get_formated_date(self, event): start_date = fields.Datetime.from_string(event.date_begin).date() end_date = fields.Datetime.from_string(event.date_end).date() - month = babel.dates.get_month_names('abbreviated', locale=event.env.context.get('lang') or 'en_US')[start_date.month] + month = babel.dates.get_month_names('abbreviated', locale=get_lang(event.env).code)[start_date.month] return ('%s %s%s') % (month, start_date.strftime("%e"), (end_date != start_date and ("-" + end_date.strftime("%e")) or "")) @http.route('/event/get_country_event_list', type='json', auth='public', website=True) diff --git a/addons/website_sale/controllers/backend.py b/addons/website_sale/controllers/backend.py index 6c72971749d..f4f7f8c1210 100644 --- a/addons/website_sale/controllers/backend.py +++ b/addons/website_sale/controllers/backend.py @@ -7,6 +7,7 @@ from datetime import datetime, timedelta, time from odoo import fields, http, _ from odoo.addons.website.controllers.backend import WebsiteBackend from odoo.http import request +from odoo.tools.misc import get_lang class WebsiteSaleBackend(WebsiteBackend): @@ -175,7 +176,7 @@ class WebsiteSaleBackend(WebsiteBackend): sales_graph = [{ '0': fields.Date.to_string(d) if not previous else fields.Date.to_string(d + timedelta(days=days_between)), # Respect read_group format in models.py - '1': daily_sales_dict.get(babel.dates.format_date(d, format='dd MMM yyyy', locale=request.env.context.get('lang') or 'en_US'), 0) + '1': daily_sales_dict.get(babel.dates.format_date(d, format='dd MMM yyyy', locale=get_lang(request.env).code), 0) } for d in date_list] return sales_graph diff --git a/odoo/addons/base/models/ir_qweb.py b/odoo/addons/base/models/ir_qweb.py index 0ddb06744e0..07fe2d1592f 100644 --- a/odoo/addons/base/models/ir_qweb.py +++ b/odoo/addons/base/models/ir_qweb.py @@ -13,6 +13,7 @@ from werkzeug import urls from odoo import api, models, tools from odoo.tools.safe_eval import assert_valid_codeobj, _BUILTINS, _SAFE_OPCODES +from odoo.tools.misc import get_lang from odoo.http import request from odoo.modules.module import get_resource_path @@ -112,7 +113,7 @@ class IrQWeb(models.AbstractModel, QWeb): return super(IrQWeb, self).compile(id_or_xml_id, options=options) def load(self, name, options): - lang = options.get('lang', 'en_US') + lang = options.get('lang', get_lang(self.env).code) env = self.env if lang != env.context.get('lang'): env = env(context=dict(env.context, lang=lang)) @@ -148,7 +149,7 @@ class IrQWeb(models.AbstractModel, QWeb): # compile directives def _compile_directive_lang(self, el, options): - lang = el.attrib.pop('t-lang', 'en_US') + lang = el.attrib.pop('t-lang', get_lang(self.env).code) if el.get('t-call-options'): el.set('t-call-options', el.get('t-call-options')[0:-1] + u', "lang": %s}' % lang) else: diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py index c275707377b..329d9b3c55b 100644 --- a/odoo/addons/base/models/ir_qweb_fields.py +++ b/odoo/addons/base/models/ir_qweb_fields.py @@ -10,6 +10,7 @@ from lxml import etree import math from odoo.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils, format_date, format_duration, pycompat +from odoo.tools.misc import get_lang import logging _logger = logging.getLogger(__name__) @@ -130,13 +131,11 @@ class FieldConverter(models.AbstractModel): """ user_lang() Fetches the res.lang record corresponding to the language code stored - in the user's context. Fallbacks to en_US if no lang is present in the - context *or the language code is not valid*. + in the user's context. :returns: Model[res.lang] """ - lang_code = self._context.get('lang') or 'en_US' - return self.env['res.lang']._lang_get(lang_code) + return get_lang(self.env) class IntegerConverter(models.AbstractModel): diff --git a/odoo/models.py b/odoo/models.py index 0a076effedd..479b86bbf01 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -56,7 +56,7 @@ from .tools import frozendict, lazy_classproperty, lazy_property, ormcache, \ groupby from .tools.config import config from .tools.func import frame_codeinfo -from .tools.misc import CountingStream, clean_context, DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT +from .tools.misc import CountingStream, clean_context, DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT, get_lang from .tools.safe_eval import safe_eval from .tools.translate import _ from .tools import date_utils @@ -1991,7 +1991,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): if ftype == 'many2one': value = value[0] elif ftype in ('date', 'datetime'): - locale = self._context.get('lang') or 'en_US' + locale = get_lang(self.env).code fmt = DEFAULT_SERVER_DATETIME_FORMAT if ftype == 'datetime' else DEFAULT_SERVER_DATE_FORMAT tzinfo = None range_start = value diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py index e15ea963abd..38d503af0f9 100644 --- a/odoo/osv/expression.py +++ b/odoo/osv/expression.py @@ -123,6 +123,7 @@ from zlib import crc32 from datetime import date, datetime, time import odoo.modules from odoo.tools import pycompat +from odoo.tools.misc import get_lang from ..models import MAGIC_COLUMNS, BaseModel import odoo.tools as tools @@ -1145,7 +1146,7 @@ class expression(object): params = ( model._name + ',' + left, - model.env.lang or 'en_US', + get_lang(model.env).code, 'model', right, ) diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py index a9268db1eed..b65cd23cf6f 100644 --- a/odoo/tools/misc.py +++ b/odoo/tools/misc.py @@ -1162,6 +1162,21 @@ else: def html_escape(text): return werkzeug.utils.escape(text) + +def get_lang(env, lang_code=False): + """ + Retrieve the first lang object installed, by checking the parameter lang_code, + the context and then the company. If no lang is installed from those variables, + fallback on the first lang installed in the system. + :param str lang_code: the locale (i.e. en_US) + :return res.lang: the first lang found that is installed on the system. + """ + langs = [code for code, _ in env['res.lang'].get_installed()] + for code in [lang_code, env.context.get('lang'), env.user.company_id.partner_id.lang, langs[0]]: + if code in langs: + return env['res.lang']._lang_get(code) + + def formatLang(env, value, digits=None, grouping=True, monetary=False, dp=False, currency_obj=False): """ Assuming 'Account' decimal.precision=3: @@ -1182,9 +1197,7 @@ def formatLang(env, value, digits=None, grouping=True, monetary=False, dp=False, if isinstance(value, str) and not value: return '' - langs = [code for code, _ in env['res.lang'].get_installed()] - lang_code = env.context['lang'] if env.context.get('lang') in langs else (env.user.company_id.partner_id.lang or langs[0]) - lang_obj = env['res.lang']._lang_get(lang_code) + lang_obj = get_lang(env) res = lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary) @@ -1221,7 +1234,7 @@ def format_date(env, value, lang_code=False, date_format=False): else: value = odoo.fields.Datetime.from_string(value) - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code) if not date_format: date_format = posix_to_ldml(lang.date_format, locale=locale) @@ -1240,7 +1253,7 @@ def parse_date(env, value, lang_code=False): :return: date object from the localized string :rtype: datetime.date ''' - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code) try: return babel.dates.parse_date(value, locale=locale) @@ -1271,8 +1284,7 @@ def format_datetime(env, value, tz=False, dt_format='medium', lang_code=False): except Exception: localized_datetime = utc_datetime - lang_code = lang_code or env.context.get('lang') or 'en_US' - lang = env['res.lang']._lang_get(lang_code) + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code or lang_code) # lang can be inactive, so `lang`is empty if not dt_format: @@ -1302,7 +1314,7 @@ def format_time(env, value, tz=False, time_format='medium', lang_code=False): if not value: return '' - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) locale = babel.Locale.parse(lang.code) if not time_format: time_format = posix_to_ldml(lang.time_format, locale=locale) @@ -1320,7 +1332,7 @@ def _format_time_ago(env, time_delta, lang_code=False, add_direction=True): def format_amount(env, amount, currency, lang_code=False): fmt = "%.{0}f".format(currency.decimal_places) - lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US') + lang = get_lang(env, lang_code) formatted_amount = lang.format(fmt, currency.round(amount), grouping=True, monetary=True)\ .replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}')