[FIX] *: remove en_US as fallback for lang code

en_US may not be activated as it is possible to create a database in
another language using the database manager.

When trying to install a chart of account, the tax return entry tried
to format a date at the installation of the module, with no lang in
the context. The fallback was made on en_US but an error is raised if
that language is not activated.

As it is a very common scenario to retrieve a language from the
context, add a generic tool method to do it.

Replace and closes odoo/odoo#37629

closes odoo/odoo#37568

Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
This commit is contained in:
wan
2019-10-01 10:05:17 +00:00
committed by Martin Trigaux
parent f28f787c10
commit 63de98b9b4
18 changed files with 62 additions and 39 deletions
@@ -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
@@ -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)
@@ -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)
@@ -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([
+2 -1
View File
@@ -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
+3 -2
View File
@@ -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
+2 -1
View File
@@ -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
+3 -2
View File
@@ -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,
@@ -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')]
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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')
+2 -1
View File
@@ -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)
+2 -1
View File
@@ -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
+3 -2
View File
@@ -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:
+3 -4
View File
@@ -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):
+2 -2
View File
@@ -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
+2 -1
View File
@@ -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,
)
+21 -9
View File
@@ -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}')