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)