[FIX] base: lang should fallback on english instead of arab
Install a database with many langs, arab, french, english, ... Keep english as the default lang. Start a shell and validate a sale-order using the superuser. On the web client, the sale order has been validated in arab instead of in english. In 16.0 the `context_get` method was changed to ensure there was always a lang set in the returned context. It used the following fallback order: context > request. The solution was partial because in case there was no request to extract a lang from, no lang was set on the context. In a recent 16.0 fix (f2523c4a), the mechanism was changed to fix the previous problem. The fallback order became: context > request > first installed lang. This solution is sub-optimal because the first installed lang isn't always the best pick. e.g. when you have a mostly english company but that arab is installed for some website pages, arab is selected instead of english (the langs are alphabetically sorted) In this work, the fallback order is changed once again: 1. The lang set on the user's profile if activated 2. The best lang extracted from the user's browser if activated 3. (new) The lang of the user's current company if activated 4. (new) English if activated 5. The first lang (ordered by ISO code) if any 6. English The 3rd should cover most of ill-cases. For the 4th step, we assume that english is prioritaty to other installed langs when no lang standout. closes odoo/odoo#113186 X-original-commit: 03134bf7cb1e3d63f3be435fbc734e6198ca029b Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
This commit is contained in:
@@ -52,7 +52,7 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
batch_size = 20
|
||||
|
||||
# simple without type involved + website
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=3236): # tef 3173 / com 3172
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=3377): # tef 3275 / com 3274
|
||||
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||
event_values = [
|
||||
dict(self.event_base_vals,
|
||||
@@ -60,7 +60,7 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
)
|
||||
for x in range(batch_size)
|
||||
]
|
||||
self.env['event.event'].create(event_values)
|
||||
self.env['event.event'].with_context(lang='en_US').create(event_values)
|
||||
|
||||
@users('event_user')
|
||||
@warmup
|
||||
@@ -89,7 +89,7 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||
|
||||
# complex with type + website
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=3340): # tef 3277 / com 3276
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=3481): # tef 3379 / com 3378
|
||||
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||
event_values = [
|
||||
dict(self.event_base_vals,
|
||||
@@ -97,7 +97,7 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
)
|
||||
for x in range(batch_size)
|
||||
]
|
||||
self.env['event.event'].create(event_values)
|
||||
self.env['event.event'].with_context(lang='en_US').create(event_values)
|
||||
|
||||
|
||||
@users('event_user')
|
||||
@@ -128,7 +128,7 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise
|
||||
|
||||
# no type, website
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=427): # tef 379 / com 380
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=447): # tef 379 / com 427
|
||||
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||
# Require for `website_menu` to be visible
|
||||
# <div name="event_menu_configuration" groups="base.group_no_one">
|
||||
@@ -150,7 +150,7 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise
|
||||
|
||||
# type and website
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=450): # tef 402 / com 408
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=470): # tef 402 / com 450
|
||||
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||
# Require for `website_menu` to be visible
|
||||
# <div name="event_menu_configuration" groups="base.group_no_one">
|
||||
@@ -181,13 +181,13 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
def test_event_create_single_notype_website(self):
|
||||
""" Test a single event creation """
|
||||
# simple without type involved + website
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=234): # tef 228 / com 227
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=242): # tef 228 / com 234
|
||||
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||
event_values = dict(
|
||||
self.event_base_vals,
|
||||
website_menu=True
|
||||
)
|
||||
self.env['event.event'].create([event_values])
|
||||
self.env['event.event'].with_context(lang='en_US').create([event_values])
|
||||
|
||||
@users('event_user')
|
||||
@warmup
|
||||
@@ -212,13 +212,13 @@ class TestEventPerformance(EventPerformanceCase):
|
||||
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||
|
||||
# complex with type + website
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=262): # tef 256 / com 255
|
||||
with freeze_time(self.reference_now), self.assertQueryCount(event_user=271): # tef 256 / com 262
|
||||
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||
event_values = dict(
|
||||
self.event_base_vals,
|
||||
event_type_id=event_type.id,
|
||||
)
|
||||
self.env['event.event'].create([event_values])
|
||||
self.env['event.event'].with_context(lang='en_US').create([event_values])
|
||||
|
||||
|
||||
@tagged('event_performance', 'registration_performance', 'post_install', '-at_install')
|
||||
|
||||
@@ -693,12 +693,19 @@ class Users(models.Model):
|
||||
for name, key in name_to_key.items()
|
||||
}
|
||||
|
||||
# ensure the language is set and is compatible with the web client
|
||||
lang = context.get('lang') or (request and request.default_lang()) or DEFAULT_LANG
|
||||
if lang == 'ar_AR':
|
||||
context['lang'] = 'ar'
|
||||
if lang in babel.core.LOCALE_ALIASES:
|
||||
context['lang'] = babel.core.LOCALE_ALIASES[lang]
|
||||
# ensure lang is set and available
|
||||
# context > request > company > english > any lang installed
|
||||
langs = [code for code, _ in self.env['res.lang'].get_installed()]
|
||||
lang = context.get('lang')
|
||||
if lang not in langs:
|
||||
lang = request.best_lang if request else None
|
||||
if lang not in langs:
|
||||
lang = self.env.user.company_id.partner_id.lang
|
||||
if lang not in langs:
|
||||
lang = DEFAULT_LANG
|
||||
if lang not in langs:
|
||||
lang = langs[0] if langs else DEFAULT_LANG
|
||||
context['lang'] = lang
|
||||
|
||||
# ensure uid is set
|
||||
context['uid'] = self.env.uid
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
from odoo.addons.base.models.res_users import is_selection_groups, get_selection_groups, name_selection_groups
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import TransactionCase, Form, tagged
|
||||
from odoo.tests.common import Form, TransactionCase, new_test_user, tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@@ -188,6 +190,37 @@ class TestUsers(TransactionCase):
|
||||
self.assertTrue(portal_partner_2.exists(), 'Should have kept the partner')
|
||||
self.assertEqual(asked_deletion_2.state, 'fail', 'Should have marked the deletion as failed')
|
||||
|
||||
def test_context_get_lang(self):
|
||||
self.env['res.lang'].with_context(active_test=False).search([
|
||||
('code', 'in', ['fr_FR', 'es_ES', 'de_DE', 'en_US'])
|
||||
]).write({'active': True})
|
||||
|
||||
user = new_test_user(self.env, 'jackoneill')
|
||||
user = user.with_user(user)
|
||||
user.lang = 'fr_FR'
|
||||
|
||||
company = user.company_id.partner_id.sudo()
|
||||
company.lang = 'de_DE'
|
||||
|
||||
request = SimpleNamespace()
|
||||
request.best_lang = 'es_ES'
|
||||
request_patch = patch('odoo.addons.base.models.res_users.request', request)
|
||||
self.addCleanup(request_patch.stop)
|
||||
request_patch.start()
|
||||
|
||||
self.assertEqual(user.context_get()['lang'], 'fr_FR')
|
||||
self.env.registry.clear_caches()
|
||||
user.lang = False
|
||||
|
||||
self.assertEqual(user.context_get()['lang'], 'es_ES')
|
||||
self.env.registry.clear_caches()
|
||||
request_patch.stop()
|
||||
|
||||
self.assertEqual(user.context_get()['lang'], 'de_DE')
|
||||
self.env.registry.clear_caches()
|
||||
company.lang = False
|
||||
|
||||
self.assertEqual(user.context_get()['lang'], 'en_US')
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestUsers2(TransactionCase):
|
||||
|
||||
+17
-13
@@ -1372,6 +1372,22 @@ class Request:
|
||||
|
||||
_cr = cr
|
||||
|
||||
@lazy_property
|
||||
def best_lang(self):
|
||||
lang = self.httprequest.accept_languages.best
|
||||
if not lang:
|
||||
return None
|
||||
|
||||
try:
|
||||
code, territory, _, _ = babel.core.parse_locale(lang, sep='-')
|
||||
if territory:
|
||||
lang = f'{code}_{territory}'
|
||||
else:
|
||||
lang = babel.core.LOCALE_ALIASES[code]
|
||||
return lang
|
||||
except (ValueError, KeyError):
|
||||
return None
|
||||
|
||||
# =====================================================
|
||||
# Helpers
|
||||
# =====================================================
|
||||
@@ -1434,19 +1450,7 @@ class Request:
|
||||
:returns: Preferred language if specified or 'en_US'
|
||||
:rtype: str
|
||||
"""
|
||||
lang = self.httprequest.accept_languages.best
|
||||
if not lang:
|
||||
return DEFAULT_LANG
|
||||
|
||||
try:
|
||||
code, territory, _, _ = babel.core.parse_locale(lang, sep='-')
|
||||
if territory:
|
||||
lang = f'{code}_{territory}'
|
||||
else:
|
||||
lang = babel.core.LOCALE_ALIASES[code]
|
||||
return lang
|
||||
except (ValueError, KeyError):
|
||||
return DEFAULT_LANG
|
||||
return self.best_lang or DEFAULT_LANG
|
||||
|
||||
def get_http_params(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user