refactor: Set Language in HTTPHeader

Order of priority for setting language:
1. Form Dict => _lang
2. Cookie => preferred_language
3. Request Header => Accept-Language
4. User document => language
5. System Settings => language

Cookie is placed at #2 since the language picker in the navbar depends
on it. And the Accept-Language header sends values based on the client's locales.

---

Form Dict _lang now accepts language codes too. Previously, language
names were used...for whatever reason.
This commit is contained in:
Gavin D'souza 2021-07-13 17:03:30 +05:30
parent caafd9e2b5
commit c47cbfd2ef
2 changed files with 59 additions and 32 deletions

View file

@ -5,13 +5,13 @@ from frappe import _
import frappe
import frappe.database
import frappe.utils
from frappe.utils import cint, flt, get_datetime, datetime, date_diff, today
from frappe.utils import cint, get_datetime, datetime, date_diff, today
import frappe.utils.user
from frappe import conf
from frappe.sessions import Session, clear_sessions, delete_session
from frappe.modules.patch_handler import check_session_stopped
from frappe.translate import get_lang_code, guess_language
from frappe.utils.password import check_password, delete_login_failed_cache
from frappe.translate import guess_language
from frappe.utils.password import check_password
from frappe.core.doctype.activity_log.activity_log import add_authentication_log
from frappe.twofactor import (should_run_2fa, authenticate_for_2factor,
confirm_otp_token, get_cached_user_pass)
@ -92,12 +92,7 @@ class HTTPRequest:
frappe.throw(_("Invalid Request"), frappe.CSRFTokenError)
def set_lang(self):
if frappe.form_dict._lang:
lang = get_lang_code(frappe.form_dict._lang)
if lang:
frappe.local.lang = lang
else:
frappe.local.lang = guess_language()
frappe.local.lang = guess_language()
def get_db_name(self):
"""get database name from conf"""

View file

@ -11,6 +11,7 @@ import io
import itertools
import json
import operator
import functools
import os
import re
from csv import reader
@ -21,36 +22,64 @@ from frappe.utils import is_html, strip, strip_html_tags
def guess_language(lang_list=None):
"""Set `frappe.local.lang` from HTTP headers at beginning of request"""
"""Set `frappe.local.lang` from HTTP headers at beginning of request
Order of priority for setting language:
1. Form Dict => _lang
2. Cookie => preferred_language
3. Request Header => Accept-Language
4. User document => language
5. System Settings => language
"""
# fetch language from form_dict
if frappe.form_dict._lang:
language = get_lang_code(
frappe.form_dict._lang or get_parent_language(frappe.form_dict._lang)
)
if language:
return language
lang_set = set(lang_list or get_all_languages() or [])
# fetch language from cookie
preferred_language_cookie = frappe.request.cookies.get('preferred_language')
lang_codes = list(frappe.request.accept_languages.values())
if preferred_language_cookie:
lang_codes.append(preferred_language_cookie)
if preferred_language_cookie in lang_set:
return preferred_language_cookie
if not lang_codes:
return frappe.local.lang
parent_language = get_parent_language(language)
if parent_language in lang_set:
return parent_language
guess = None
if not lang_list:
lang_list = get_all_languages() or []
# fetch language from request headers
accept_language = list(frappe.request.accept_languages.values())
for l in lang_codes:
code = l.strip()
if not isinstance(code, str):
code = str(code, 'utf-8')
if code in lang_list or code == "en":
guess = code
break
for language in accept_language:
if language in lang_set:
return language
# check if parent language (pt) is setup, if variant (pt-BR)
if "-" in code:
code = code.split("-")[0]
if code in lang_list:
guess = code
break
parent_language = get_parent_language(language)
if parent_language in lang_set:
return parent_language
# fallback to language set in User or System Settings
return frappe.local.lang
@functools.lru_cache(maxsize=None)
def get_parent_language(lang: str) -> str:
"""If the passed language is a variant, return its parent
Eg:
1. zh-TW -> zh
2. sr-BA -> sr
"""
is_language_variant = "-" in lang
if is_language_variant:
return lang[:lang.index("-")]
return guess or frappe.local.lang
def get_user_lang(user=None):
"""Set frappe.local.lang from user preferences on session beginning or resumption"""
@ -75,7 +104,10 @@ def get_user_lang(user=None):
return lang
def get_lang_code(lang):
return frappe.db.get_value('Language', {'language_name': lang}) or lang
return (
frappe.db.get_value("Language", {"name": lang})
or frappe.db.get_value("Language", {"language_name": lang})
)
def set_default_language(lang):
"""Set Global default language"""