Reputation: 2840
EDIT: Could you read my question more carefully? This question is specific and not duplicated as has been said. Obviously I read this question before post.
In this demo code from the docs, the fragment after the lang parameter will be static. So, in English /en/about
, and for example, in Portuguese /pt/about
. Well, the correct, should be /pt/sobre
.
Any idea about the correct way to make that with URL Processors?
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():
Upvotes: 5
Views: 812
Reputation: 26080
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 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
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, 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
If you will simplify url_for
usage for this examples you can use next sample:
@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)
Upvotes: 5