Format canonical URL structure correctly with URL Processors Format canonical URL structure correctly with URL Processors flask flask

Format canonical URL structure correctly with URL Processors


Ok, you already have language prefix. So you need have several translations for next part.

The easiest way it use several routes or converters:

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

or

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

But it hard to support and add new languages.

Second way is change route method to translate special words or add special translate processor converter, last more interesting and implicit:

from werkzeug.routing import AnyConverterlanguages = ['en', 'pt']def translate_url(word, language):    if language == 'pt' and word == 'about':        return 'sobre'    return wordclass 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

But this example have next issue:

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

is valid urls, but you can also try use own Rule class (Flask.url_rule_class) where in match method you can process this cases:

from werkzeug.routing import AnyConverter, Ruleclass 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 resultapp.url_map.converters['tr'] = TranslateConverterapp.url_rule_class = TranslateCorrelationRule

If you will simplify url_for usage for this examples you can use next sample:

@app.url_value_preprocessordef 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)