使用URL处理器正确格式化规范URL结构

时间:2014-05-29 08:35:18

标签: python internationalization flask culture

编辑:您能否仔细阅读我的问题?这个问题是具体的,而不是正如所说的那样duplicated。显然我在帖子之前阅读了question


docs的演示代码中,lan​​g参数后面的片段将是静态的。所以,用英语/en/about,例如,用葡萄牙语/pt/about。好吧,正确的,应该是/pt/sobre

有关使用URL处理器的正确方法的想法吗?

from flask import Flask, g

app = Flask(__name__)

@app.url_defaults
def add_language_code(endpoint, values):
    if 'lang_code' in values or not g.lang_code:
        return
    if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
        values['lang_code'] = g.lang_code

@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
    g.lang_code = values.pop('lang_code', None)

@app.route('/<lang_code>/')
def index():
    ...

@app.route('/<lang_code>/about')
def about():

1 个答案:

答案 0 :(得分:5)

好的,你已经有了语言前缀。因此,您需要为下一部分提供多种翻译。

使用多条路线或转换器的最简单方法:

@app.route('/<lang_code>/about')
@app.route('/<lang_code>/sobre')
def about():
    pass

@app.route('/<lang_code>/<any(about,sobre):about>')
def about(about):
    pass

但很难支持和添加新语言。

第二种方式是改变路由方法来翻译特殊字或添加特殊翻译处理器转换器,最后更有趣和隐含:

from werkzeug.routing import AnyConverter


languages = ['en', 'pt']


def translate_url(word, language):
    if language == 'pt' and word == 'about':
        return 'sobre'
    return word


class TranslateConverter(AnyConverter):

    def __init__(self, map, item):
        AnyConverter.__init__(self, map, *[translate_url(item, language) 
                                           for language in languages])


app.url_map.converters['tr'] = TranslateConverter


@app.route('/<lang_code>/<tr(about):about>')
def about(about):
    pass

但是这个例子有下一个问题:

/en/about
/en/sorbe
/pt/about
/pt/sorbe

是有效的网址,但您也可以尝试使用自己的Rule类(Flask.url_rule_class),在match方法中您可以处理此案例:

from werkzeug.routing import AnyConverter, Rule


class TranslateConverter(AnyConverter):

    def __init__(self, map, item):
        self.language_pairs = {language: translate_url(item, language)
                               for language in languages}
        AnyConverter.__init__(self, map, *tuple(self.language_pairs.values()))


class TranslateCorrelationRule(Rule):

    def match(self, path):
        result = Rule.match(self, path)
        if result is None:
            return result
        lang_code = result.get('lang_code')
        if lang_code is None:
            return result
        for name, value in self._converters.items():
            if not isinstance(value, TranslateConverter):
                continue
            if value.language_pairs[lang_code] != result[name]:
                return
        return result


app.url_map.converters['tr'] = TranslateConverter
app.url_rule_class = TranslateCorrelationRule

如果您要简化此示例的url_for用法,可以使用下一个示例:

@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
    if not values:
        return
    g.lang_code = values.pop('lang_code', None)
    for key, value in values.items():
        if key.startswith('_'):
            values.pop(key)


class TranslateCorrelationRule(Rule):

    def _update_translate_values(self, values):
        lang_code = values.get('lang_code', getattr(g, 'lang_code', None))
        if lang_code is None:
            return values
        values = values.copy()
        for argument in self.arguments:
            if argument in values:
                continue
            converter = self._converters[argument]
            if not isinstance(converter, TranslateConverter):
                continue
            values[argument] = converter.language_pairs[lang_code]
        return values

    def suitable_for(self, values, method=None):
        return Rule.suitable_for(self, self._update_translate_values(values),
                                 method)

    def build(self, values, append_unknown=True):
        return Rule.build(self, self._update_translate_values(values),
                          append_unknown)