My own solution:
from flask import Flask, g, render_template, redirect, request
app = Flask(__name__)
default_language = 'en'
language_urls = {
'en': 'mysite.com',
'fr': 'mysite.com/fr',
'ru': 'mysite.ru',
'by': 'mysite.ru/by',
}
languages = ','.join(language_urls.keys())
def get_language_by_request(request_host, request_path):
'''
Looking bad, but work.
I cab't use request.view_args there,
because this can't detect language for 404 pages
like mysite.com/fr/unknown-page
'''
request_host_path = request_host + request_path
request_paths = request_host_path.split('/', 2)
if (len(request_paths) > 1 and request_paths[1] in language_urls.keys()):
request_language_prefix = request_paths[1]
return request_language_prefix
for language, url in language_urls.items():
host_prefix = url.split('/')
if len(host_prefix) == 1:
host, = host_prefix
if request_host == host:
return language
return default_language
def get_language_url_parameter_value(language, request_host):
host_prefix = language_urls[language]
if host_prefix == request_host:
return None
return language
def get_redirection_url_by_request(request_host, request_path, request_url):
'''
Looking bad, but work.
I cab't use request.view_args there,
because this can't detect language for 404 pages
like mysite.com/fr/unknown-page
'''
request_host_path = request_host + request_path
request_paths = request_host_path.split('/', 2)
request_language_prefix = None
if (len(request_paths) > 1 and request_paths[1] in language_urls.keys()):
request_language_prefix = request_paths[1]
hosts = []
for language, url in language_urls.items():
host_prefix = url.split('/')
if len(host_prefix) == 1:
host, = host_prefix
language_prefix = None
else:
host, language_prefix = host_prefix
if request_host == host and request_language_prefix == language_prefix:
return None
hosts.append(host)
if request_host not in hosts:
return None
if request_language_prefix:
request_host_prefix = request_host + '/' + request_language_prefix
host_prefix = language_urls[request_language_prefix]
return request_url.replace(request_host_prefix, host_prefix)
return None
@app.url_defaults
def set_language_in_url(endpoint, values):
if '_lang' not in values and hasattr(g, 'language_url_value'):
values['_lang'] = g.language_url_value
@app.url_value_preprocessor
def get_language_from_url(endpoint, values):
g.language = get_language_by_request(request.host, request.path)
g.language_url_value = get_language_url_parameter_value(g.language, request.host)
if values and '_lang' in values:
del values['_lang']
@app.before_request
def check_language_redirection():
redirection_url = get_redirection_url_by_request(request.host, request.path, request.url)
return redirect(redirection_url) if redirection_url else None
@app.route('/')
@app.route('/<any(%s):_lang>/' % languages)
def home():
return render_template('home.html')
@app.route('/other/')
@app.route('/<any(%s):_lang>/other/' % languages)
def other():
return render_template('other.html')
I don't use blueprints there because I also use flask-login
and I can't set several login pages with different languages for each blueprint. For example if page required login, flask redirect me to login page and I must update language for this page. Also login pages can't be as mysite.com/login
, mysite.com/fr/login
and etc without several redirections.
UPD: I can't use request.view_args
for detect language or redirection, because on this case I can't detect language for error pages as mysite.com/fr/wrong-page-there
(can't detect endpoint
and view_args
). To avoid this problem I can use hask: add url rule as /<lang_code>/<path:path>
and raise 404 error there.
request.view_args
for language detection: https://mcmap.net/q/1778137/-python-flask-web-application-with-multilanguages-support-by-host-and-prefix. – Prearrange