Flask支持多语言站点


Flask的多语言国际化可以使用Flask-Babel插件,在此不再细述,但对于所谓的多语言站点(即形如example.com/zh/uri、example.com/en/uri或者zh.example.com、en.example.com)文档上却未作细述

有一个 Flask URL Processors 需要对所有的uri都额外增加一个lang_code的前缀,路由数较少时没什么问题,但路由数较多时太过麻烦

实现example.com/en/uri可以有多种方式,除了使用Flask URL Processors中介绍的外,还可以

使用nginx重定向uri

这应该是各种方式里最简单的一种

1location ~ ^/en/ {
2    rewrite ^/en/(.*)$ /$1 last;
3}
4location = /en {
5    rewrite ^/(.*)$ /index last;
6}

使用uri获取对应非302响应

即增加一个url为/en/<path:uri>的路由,在此路由func中,根据<path:uri>信息获取已注册路由的view_function,不使用重定向,而是直接调用view_function返回实际响应

 1# https://stackoverflow.com/questions/38488134/get-the-flask-view-function-that-matches-a-url
 2def get_view_function(url, method='GET'):
 3    adapter = current_app.url_map.bind('localhost')
 4    try:
 5        match = adapter.match(url, method=method)
 6    except RequestRedirect as e:
 7        # recursively match redirects
 8        return get_view_function(e.new_url, method)
 9    except (MethodNotAllowed, NotFound):
10        # no match
11        return None
12
13    try:
14        # return the view function and arguments
15        return current_app.view_functions[match[0]], match[1]
16    except KeyError:
17        # no view is associated with the endpoint
18        return None

提高查找对应view-function的性能,增加缓存到内存中

 1FUNCTION = dict()
 2
 3def view_function_cache(func):
 4    @wraps(func)
 5    def _view_function(url, method='GET'):
 6        # 避免故意访问
 7        if len(FUNCTION) > 100:
 8            for k, v in FUNCTION.items():
 9                if v is None:
10                    FUNCTION.pop(k)
11
12        key = method + url
13        key = str(hashlib.md5(key.encode("UTF-8")).hexdigest())
14        if key in FUNCTION:
15            return FUNCTION[key]
16        FUNCTION[key] = func(url, method)
17        return FUNCTION[key]
18
19    return _view_function

这样就可以定义/en/<uri>实际的view_function

 1def redirect_en(uri):
 2    view_function = get_view_function(
 3        "/" + uri,
 4        request.method,
 5    )
 6    if view_function is None:
 7        abort(404)
 8    # 注:因为我使用Flask-Babel是根据accept_language来区别不同语言
 9    request.environ["HTTP_ACCEPT_LANGUAGE"] = "en-US,en;q=0.5"
10    return view_function[0](**view_function[1])

使用url_map复制并alias路由

原理同Flask URL Processors ,为所有的路由都额外增加/en前缀,并在before_request中匹配到以/en开头的请求就修改对应accept_language信息

1@app.before_request
2def before_request():
3    if request.path.startswith("/en/"):
4        request.environ["HTTP_ACCEPT_LANGUAGE"] = "en-US,en;q=0.5"
5
6url_map = list(app.url_map.iter_rules())
7for rule in url_map:
8    app.add_url_rule("/en" + rule.rule, rule.endpoint, alias=True)

咦,感觉这种方式更简单一些,但最好还是能够对一些特殊的路由比如: static, admin, subdomain等进行特殊处理

演示效果

切换成English

作者: honmaple
链接: https://honmaple.me/articles/2019/01/Flask支持多语言站点.html
版权: CC BY-NC-SA 4.0 知识共享署名-非商业性使用-相同方式共享4.0国际许可协议
wechat
alipay

加载评论