How to change language of flask babel on a button click? - python

I have the function in Flask, which returns the website in english. Moreover, i want to be able to use german in the website at a button push from html. How can I change the language at a button push from english to german and from german to english? Also, is it possible to use the function get_locale only at call, not running automatically?
#babel.localeselector
def get_locale():
return 'en'

At first You need to store the address at the main app file:
#app.route('/language=<language>')
def set_language(language=None):
session['language'] = language
return redirect(url_for('home'))
Than You need to change to the same addess to get_locale function in main app file:
#babel.localeselector
def get_locale():
if request.args.get('language'):
session['language'] = request.args.get('language')
return session.get('language', 'en')
To access current language from template:
app.config['LANGUAGES'] = {
'en': 'English',
'ge': 'German',
}
app.secret_key = "super secret key"
#app.context_processor
def inject_conf_var():
return dict(AVAILABLE_LANGUAGES=app.config['LANGUAGES'], CURRENT_LANGUAGE=session.get('language', request.accept_languages.best_match(app.config['LANGUAGES'].keys())))
Here the template file:
{% for language in AVAILABLE_LANGUAGES.items() %}
{% if CURRENT_LANGUAGE == language[0] %}
{{ language[1] }}
{% else %}
<a href="{{ url_for('set_language', language=language[0]) }}" >{{ language[1] }}</a>
{% endif %}
{% endfor %}

Related

Accessing flask methods in Jinaj2.Template.render()

Say we have a flask template as such:
{% extends "layout.html" %}
{% block body %}
<div class="container page-container">
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
{% endblock %}
We can render this template using the flask.render_template() function, and thus display a flash message using code like this:
from flask import flash, render_template
#timesheet_billing.route('/timesheet-billing')
def timesheet_billing_select_job():
jobs = get_open_jobs()
flash('A job was not selected!')
return render_template('timesheet_billing/index.html', jobs = jobs)
However if we render it using Jinja2's template class function jinja2.Template.render() with code like this:
from flask import flash
import jinja2
env = jinja2.Environment(loader=jinja2.PackageLoader('templates'))
index_temp = env.get_template('index.html')
#timesheet_billing.route('/timesheet-billing')
def timesheet_billing_select_job():
jobs = get_open_jobs()
flash('A job was not selected!')
return index_temp.render(jobs = jobs)
We get the following error when trying to load the page:
jinja2.exceptions.UndefinedError: 'get_flashed_messages' is undefined
What is the difference here? The answer in this question suggests that they should be the same. However it seems in one we do not have access to flask methods.
I believe the difference here is how flask.get_flashed_messages works.
This webpage https://flask.palletsprojects.com/en/1.1.x/templating/ explains the scope of jinja2 context variables:
The Jinja Context Behavior:
These variables are added to the context of variables, they are not global variables. The difference is that by default these will not show up in the context of imported templates.
This is partially caused by performance considerations, partially to
keep things explicit.
Here is where flask's render_template makes a difference, when comparing it to jinja2.render (from the question link you referred to):
def render_template(template_name_or_list, **context):
ctx = _app_ctx_stack.top
ctx.app.update_template_context(context)
return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
context, ctx.app)
def _render(template, context, app):
before_render_template.send(app, template=template, context=context)
rv = template.render(context)
template_rendered.send(app, template=template, context=context)
return rv
by calling render directly, you're missing the application context update call, which will inject all the information a template context processor will need to make functions (like get_flashed_messages, in this case) available to templates.

How to render new line from python string to HTML template using FLASK?

message=""
#app.route("/", methods=["GET", "POST"])
def upload_file():
global message
if request.method == "POST":
if request.files:
data=request.files["file"]
if data.filename == "":
message="File doesn't have a name! <br>"
elif allowed_file(data.filename):
message+="File Allowed <br>"
data.save(os.path.join(app.config["FILE_UPLOAD"], data.filename))
message+="File Saved"
if(validate()):
message+="File validated! <br>"
else: message+="Failed validation <br>"
else:
message+="File extension not allowed! <br>"
return render_template("ui.html",message=message)
I'm trying to validate the file uploaded on my ui.html template using flask and I want to send a "message" string back to ui.html about the status of verification and to show it nicely I'm trying to add new line whenever a new string gets added to "message" string so that when I render it in my ui.html, new line is added where I wanted it to be.
This is how I'm rendering the "message" string in ui.html:
{% if message %}
<p>{{ message }}</p>
{% endif %}
But ui.html is not rendering <br> and it is printing it as a string on ui.html template. How can I resolve this? I have tried <br /> as well.
Also mentioned in render html strings in flask templates
flask's template engine (jinja2) assumes that input inside of {{ }} is unsafe and will not allow js or html to be rendered inside of it. The easiest way is to use safe filter in order to do such thing.
{{ message | safe }}
According to flask's documentation https://flask.palletsprojects.com/en/1.1.x/templating/ there are two other ways to control autoescaping behaviour which is either wrap the HTML string in a Markup object or disabling autoescaping altogether like this:
{% autoescape false %}
<p>autoescaping is disabled here
<p>{{ will_not_be_escaped }}
{% endautoescape %}
I tackled it with flash function provided by flask. It prints each message separately so I can add <p> in my HTML File only.
The changes made in ui.html file for rendering are:
{% for message in get_flashed_messages() %}
<p>{{ message }}</p>
{% endfor %}

How can I choose the language, using Flask + Babel?

Now I'm developing a project, which should support two languages: English, as default, and Russian. It's pretty easy to do, using HTTP_ACCEPT_LANGUAGE header, the code is bellow:
babel = Babel(app)
#babel.localeselector
def get_locale():
return request.accept_languages.best_match(app.config["LANGUAGES"].keys())
Languages are hardcoded in application config file:
LANGUAGES = {
'en': 'English',
'ru': 'Russian'
}
But I also want to add a button, like Switch language to English. What is the best practice to realise it?
This is the solution I came across:
First you set a route that will handle the language change and will store the selected language on the session:
#app.route('/language/<language>')
def set_language(language=None):
session['language'] = language
return redirect(url_for('index'))
Secondly, you have to modify a little the code you have to get the selected language from the session:
#babel.localeselector
def get_locale():
# if the user has set up the language manually it will be stored in the session,
# so we use the locale from the user settings
try:
language = session['language']
except KeyError:
language = None
if language is not None:
return language
return request.accept_languages.best_match(app.config['LANGUAGES'].keys())
You have also to be able to access the CURRENT_LANGUAGE from the templates, so you can inject it:
#app.context_processor
def inject_conf_var():
return dict(
AVAILABLE_LANGUAGES=app.config['LANGUAGES'],
CURRENT_LANGUAGE=session.get('language',request.accept_languages.best_match(app.config['LANGUAGES'].keys())))
Finally, on the template you can choose the the language you want:
{% for language in AVAILABLE_LANGUAGES.items() %}
{% if CURRENT_LANGUAGE == language[0] %}
{{ language[1] }}
{% else %}
<a href="{{ url_for('set_language', language=language[0]) }}" >{{ language[1] }}</a>
{% endif %}
{% endfor %}
Application config.py includes the following constant:
LANGUAGES = {
'en': 'English',
'es': 'Spanish'
}
Hope this helps!

Flask-Babel Multiple Language URL Routing

I am creating a multi-language site (EN and FR) and I need it to toggle back and forth on click if the user so chooses. I am using Flask-Babel and the translations and toggle are working correctly on click, however, I need the URLs to be translated as well. I have currently wrapped my URL routes like so, with both the English and French URLs:
#main.route('/accueil')
#main.route('/home')
def index():
return render('index.html', {})
#main.route('/a-propos-de-nous')
#main.route('/about-us')
def about():
return render('about.html', {})
The rest of the code that is grabbing the language and toggling is as follows:
app = Flask(__name__, static_folder=settings.STATIC_ROOT)
main = Blueprint('main', __name__, url_prefix='/language/<lang_code>')
#app.url_defaults
def set_language_code(endpoint, values):
if 'lang_code' in values or not session['lang_code']:
return
if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
values['lang_code'] = session['lang_code']
#app.url_value_preprocessor
def get_lang_code(endpoint, values):
if values is not None:
session['lang_code'] = values.pop('lang_code', None)
#app.before_request
def ensure_lang_support():
lang_code = session['lang_code']
if lang_code and lang_code not in app.config['SUPPORTED_LANGUAGES'].keys():
return abort(404)
#babel.localeselector
def get_locale():
if session.get('lang_code') is None:
session['lang_code'] = request.accept_languages.best_match(app.config['SUPPORTED_LANGUAGES'].keys())
return session['lang_code']
The template looks like this where the user clicks on the link to change languages:
{% if session['lang_code']=='en' %}
{% set new_lang_code='fr' %}
{% else %}
{% set new_lang_code='en' %}
{% endif %}
<li>{{ _('Fr') }}</li>
As I have little experience with Python/Flask...I am struggling with the best way to switch to the translated URL. How would I go about doing this? Any information would be appreciated. Thanks in advance.
I have found a solution! I had to add endpoints to the URL routes like so:
#main.route('accueil', endpoint="index_fr")
#main.route('home', endpoint="index_en")
def index():
return render('index.html', {})
#main.route('a-propos-de-nous', endpoint="about_fr")
#main.route('about-us', endpoint="about_en")
def about():
return render('about.html', {})
This allowed me to use Babel to translate the URL endpoints like it did for the rest of the text, and grab the correct URL ending along with the language code from the session. The toggle works like this now:
{% if session['lang_code']=='en' %}
{% set new_lang_code='fr' %}
{% else %}
{% set new_lang_code='en' %}
{% endif %}
<li>{{ _('Fr') }}</li>

Python Flask cannot get element from form

Im having trouble getting anything from the shown HTML form
I always get "ValueError: View function did not return a response"
Can somebody help me out here please? I have tried every variation of request.get that I can find on the web. Also if I specify my form should use post it uses get anyway - anybody know why this is?
Im new to flask so forgive my ignorance!
Thanks in advance.
The python file (routes.py)
from flask import Flask, render_template, request
import os
app = Flask(__name__)
musicpath = os.listdir(r"C:\Users\Oscar\Music\iTunes\iTunes Media\Music")
lsize = str(len(musicpath))
looper = len(musicpath)
#app.route('/')
def home():
return render_template('home.html', lsize=20, looper=looper, musicpath=musicpath)
#app.route('/pop', methods=['POST', 'GET'])
def pop():
if request.method == "GET":
text = request.args.get('som')
return text
#Have tried every variation of request.get
#app.route('/about')
def about():
name = "Hello!"
return render_template('about.html', name=name)
if __name__ == '__main__':
app.run(debug=True)
The html file (home.html)
{% extends "layout.html" %}
{% block content %}
<div class="jumbo">
<h2>A Music app!<h2>
</div>
<div>
{% if lsize %}
<form action="/pop">
<select id="som" size="20">
{% for i in range(looper):%}
<option value="{{i}}">{{ musicpath[i] }}</option>
{% endfor %}
</select>
</form>
{% endif %}
</div>
Select,
{% endblock %}
You don't have a name attribute on your select element. That is the attribute that browsers use to send information in forms; without it no data will be sent.
Note also that your pop handler does not do anything if the method is POST, even though you explicitly say you accept that method.

Categories