Babel locale
This commit is contained in:
parent
08e68e35b8
commit
426d1fe531
3
babel.cfg
Normal file
3
babel.cfg
Normal file
|
@ -0,0 +1,3 @@
|
|||
[python: **.py]
|
||||
[jinja2: **/templates/**.html]
|
||||
extensions=jinja2.ext.autoescape,jinja2.ext.with_
|
|
@ -1,8 +0,0 @@
|
|||
from flask import render_template
|
||||
from objects import glob
|
||||
|
||||
@glob.app.context_processor
|
||||
def locale():
|
||||
def _locale(lang, key):
|
||||
return glob.langs[lang][key] if key in glob.langs[lang] else key
|
||||
return dict(locale = _locale)
|
|
@ -6,14 +6,14 @@ from flask_login import UserMixin
|
|||
from objects import glob
|
||||
|
||||
class BillForm(Form):
|
||||
payment_to = StringField("lbl_to", [validators.DataRequired()])
|
||||
description = TextAreaField("lbl_desc", render_kw = {
|
||||
payment_to = StringField("Payment to", [validators.DataRequired()])
|
||||
description = TextAreaField("Description", render_kw = {
|
||||
"cols": 55,
|
||||
"rows": 8
|
||||
})
|
||||
sum = DecimalField("lbl_sum")
|
||||
kid = IntegerField("lbl_id")
|
||||
date_due = DateField("lbl_date")
|
||||
sum = DecimalField("Sum")
|
||||
kid = IntegerField("KID")
|
||||
date_due = DateField("Date due")
|
||||
|
||||
class LoginForm(Form):
|
||||
email = StringField("Email", [
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"lbl_to": "Payment to",
|
||||
"lbl_desc": "Description",
|
||||
"lbl_sum": "Sum",
|
||||
"lbl_id": "KID",
|
||||
"lbl_date": "Date due",
|
||||
"lbl_status": "Payment status"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"add": "Add",
|
||||
"close": "Close"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"lnk_dashboard": "Dashboard",
|
||||
"lnk_bills": "Bills",
|
||||
"lnk_receipts": "Receipts",
|
||||
"lnk_warranties": "Warranties",
|
||||
"ttl_economical": "Economical"
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"lbl_to": "Betaling til",
|
||||
"lbl_desc": "Tekst",
|
||||
"lbl_sum": "Sum",
|
||||
"lbl_id": "KID",
|
||||
"lbl_date": "Forfallsdato",
|
||||
"lbl_status": "Betalingsstatus"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"add": "Legg til",
|
||||
"close": "Lukk"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"lnk_dashboard": "Dashbord",
|
||||
"lnk_bills": "Regninger",
|
||||
"lnk_receipts": "Kvitteringer",
|
||||
"lnk_warranties": "Garantier",
|
||||
"ttl_economical": "Økonomisk"
|
||||
}
|
17
localizer.py
Normal file
17
localizer.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from flask import g, request, session
|
||||
from flask_babel import Babel
|
||||
|
||||
from objects import glob
|
||||
|
||||
babel = Babel(glob.app)
|
||||
|
||||
LANGUAGES = {
|
||||
"en": "English",
|
||||
"no": "Norwegian"
|
||||
}
|
||||
|
||||
@babel.localeselector
|
||||
def get_locale():
|
||||
if request.args.get("lang"):
|
||||
session["lang"] = request.args.get("lang") if request.args.get("lang") in LANGUAGES.keys() else "en"
|
||||
return session.get("lang", "en")
|
3
main.py
3
main.py
|
@ -5,8 +5,9 @@ from objects import glob # Global sharing of python objects in a manageable way
|
|||
glob.app = Flask(__name__)
|
||||
glob.app.secret_key = "E2FGrJXLtOxPh70Q"
|
||||
|
||||
import localizer # Initialize localization (Babel)
|
||||
import routes # All flask app routes
|
||||
import filters # All flask app filters
|
||||
# import filters # All flask app filters
|
||||
|
||||
if glob.config["git"]["auto_pull_and_restart"]: # Only used on the VPS (Do not enable in config)
|
||||
@glob.app.route(glob.config["git"]["webhook_endpoint"], methods = ["POST"])
|
||||
|
|
|
@ -10,8 +10,6 @@ import bcrypt
|
|||
app = None # main.py -> Flask App
|
||||
sql_conn = None
|
||||
|
||||
langs = {}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Global variables that initializes on first load of module
|
||||
|
||||
|
@ -22,19 +20,6 @@ if not os.path.isfile("config.json"):
|
|||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
|
||||
path = "locale"
|
||||
for l in os.listdir(path):
|
||||
langs[l] = {}
|
||||
_path = "%s/%s" % (path, l)
|
||||
for f in os.listdir(_path):
|
||||
_f = "%s/%s" % (_path, f)
|
||||
with open(_f, "r", encoding = "UTF-8") as j:
|
||||
pnd = json.load(j)
|
||||
for k in pnd.keys():
|
||||
if k in langs[l]:
|
||||
raise Exception("Duplicate localization entries found in file %s" % _f)
|
||||
langs[l] = {**langs[l], **pnd}
|
||||
|
||||
def make_sql_connection():
|
||||
return mysql.connector.connect(**config["mysql"])
|
||||
|
||||
|
|
19
routes.py
19
routes.py
|
@ -6,6 +6,8 @@ from forms.login import LoginForm, RegisterForm, BillForm, User, register_accoun
|
|||
|
||||
from objects import glob # Global sharing of python objects in a manageable way
|
||||
|
||||
from flask_babel import gettext
|
||||
|
||||
login_manager = flask_login.LoginManager()
|
||||
login_manager.init_app(glob.app)
|
||||
login_manager.login_view = "login"
|
||||
|
@ -65,7 +67,7 @@ def receipts():
|
|||
@glob.app.route("/login", methods = ["GET", "POST"])
|
||||
def login():
|
||||
if flask_login.current_user.is_authenticated:
|
||||
flash("Already logged in", "info")
|
||||
flash(gettext("Already logged in"), "info")
|
||||
return redirect(url_for("dashboard"))
|
||||
|
||||
form = LoginForm(request.form)
|
||||
|
@ -73,20 +75,20 @@ def login():
|
|||
try:
|
||||
user = User((form.email.data, form.password.data))
|
||||
except Exception as e:
|
||||
flash(str(e), "danger")
|
||||
flash(gettext(str(e)), "danger")
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
flask_login.login_user(user)
|
||||
logged_in_users.append(user)
|
||||
|
||||
flash("Logged in", "success")
|
||||
flash(gettext("Logged in"), "success")
|
||||
return redirect(url_for("dashboard"))
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
@glob.app.route("/register", methods = ["GET", "POST"])
|
||||
def register():
|
||||
if flask_login.current_user.is_authenticated:
|
||||
flash("Already logged in", "info")
|
||||
flash(gettext("Already logged in"), "info")
|
||||
return redirect(url_for("dashboard"))
|
||||
|
||||
form = RegisterForm(request.form)
|
||||
|
@ -94,10 +96,10 @@ def register():
|
|||
try:
|
||||
register_account(form.email.data, form.password.data, form.firstname.data, form.surname.data)
|
||||
except Exception as e:
|
||||
flash(str(e), "danger")
|
||||
flash(gettext(str(e)), "danger")
|
||||
return render_template("register.html", form=form)
|
||||
|
||||
flash("User registered", "success")
|
||||
flash(gettext("User registered"), "success")
|
||||
return redirect(url_for("login"))
|
||||
return render_template("register.html", form=form)
|
||||
|
||||
|
@ -105,12 +107,12 @@ def register():
|
|||
@flask_login.login_required
|
||||
def logout():
|
||||
flask_login.logout_user()
|
||||
flash("Logged out", "success")
|
||||
flash(gettext("Logged out"), "success")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
@glob.app.errorhandler(401)
|
||||
def unauthorized_handler_err():
|
||||
flash("Login is required", "danger")
|
||||
flash(gettext("Login is required"), "danger")
|
||||
unauthorized_handler()
|
||||
|
||||
@login_manager.user_loader
|
||||
|
@ -122,4 +124,3 @@ def load_user(uuid):
|
|||
@login_manager.unauthorized_handler
|
||||
def unauthorized_handler():
|
||||
return redirect(url_for("login"))
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
{% set LANG = "no" %} <!-- TODO: Dynamic lang swap -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% include 'layout/includes/boot-head.html' %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/custom.css') }}">
|
||||
{% if title %}
|
||||
<title>Husstanden - {{ locale(LANG, title) }}</title>
|
||||
<title>Husstanden - {{ _(title) | title }}</title>
|
||||
{% else %}
|
||||
<title>Husstanden</title>
|
||||
{% endif %}
|
||||
|
|
|
@ -105,29 +105,29 @@
|
|||
<div class="flex-column">
|
||||
<div> <!-- Collection -->
|
||||
<a class="item" href="{{ url_for('dashboard') }}">
|
||||
<i class="far fa-calendar-alt"></i><span>{{ locale(LANG, "lnk_dashboard") }}</span>
|
||||
<i class="far fa-calendar-alt"></i><span>{{ _("Dashboard") }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h4>{{ locale(LANG, "ttl_economical") }}</h4>
|
||||
<h4>{{ _("Economical") }}</h4>
|
||||
</div>
|
||||
<div>
|
||||
<a class="item" href="{{ url_for('bills') }}">
|
||||
<i class="fas fa-money-check-alt"></i><span>{{ locale(LANG, "lnk_bills") }}</span>
|
||||
<i class="fas fa-money-check-alt"></i><span>{{ _("Bills") }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class="item" onclick="toggleCategory(this)">
|
||||
<i class="far fa-list-alt"></i><span>{{ locale(LANG, "lnk_receipts") }}</span>
|
||||
<i class="far fa-list-alt"></i><span>{{ _("Receipts") }}</span>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</a>
|
||||
<div class="downtab hidden">
|
||||
<div class="flex-column">
|
||||
<div class="page">
|
||||
<a href="{{ url_for('receipts') }}">{{ locale(LANG, "lnk_receipts") }}</a>
|
||||
<a href="{{ url_for('receipts') }}">{{ _("Receipts") }}</a>
|
||||
</div>
|
||||
<div class="page">
|
||||
<a href="{{ url_for('warranties') }}">{{ locale(LANG, "lnk_warranties") }}</a>
|
||||
<a href="{{ url_for('warranties') }}">{{ _("Warranties") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<style>
|
||||
.rndblock {
|
||||
border-radius: 100%;
|
||||
background: #506EE4;
|
||||
background: #506EE4;
|
||||
background-position: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
@ -15,11 +15,24 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.languk {
|
||||
.lang-icon {
|
||||
background-position: left;
|
||||
background-size: contain;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
vertical-align: middle;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.lang-en {
|
||||
background-image: url("{{ url_for('static', filename='const/img/flags/gb.svg') }}");
|
||||
}
|
||||
|
||||
.langno {
|
||||
.lang-no {
|
||||
background-image: url("{{ url_for('static', filename='const/img/flags/no.svg') }}");
|
||||
}
|
||||
|
||||
|
@ -34,7 +47,7 @@
|
|||
</style>
|
||||
|
||||
<nav class="navbar topnav" style="margin-bottom: 10px;">
|
||||
<h3>{{ locale(LANG, title) }}</h3>
|
||||
<h3>{{ _(title) | title }}</h3>
|
||||
<div>
|
||||
{% with messages = get_flashed_messages(with_categories = true) %}
|
||||
{% if messages %}
|
||||
|
@ -49,7 +62,25 @@
|
|||
</div>
|
||||
<div class="my-2 my-lg-0 d-flex icon-buttons">
|
||||
<div class="col">
|
||||
<div class="rndblock languk"></div>
|
||||
<div class="rndblock lang-{{ session.lang }}" data-toggle="dropdown" aria-expanded="false"></div>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<div class="row">
|
||||
<a class="col d-flex m-1" href="?lang=en">
|
||||
<div class="lang-icon lang-en"></div>
|
||||
<div class="flex-grow-1"></div>
|
||||
<span>{{ _("english") | title }}</span>
|
||||
<div class="flex-grow-1"></div>
|
||||
</a>
|
||||
<a class="col d-flex m-1" href="?lang=no">
|
||||
<div class="lang-icon lang-no"></div>
|
||||
<div class="flex-grow-1"></div>
|
||||
<span>{{ _("norwegian") | title }}</span>
|
||||
<div class="flex-grow-1"></div>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="rndblock">
|
||||
|
@ -64,7 +95,7 @@
|
|||
<li>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a href="{{ url_for('logout') }}">Sign out</a>
|
||||
<a href="{{ url_for('logout') }}">{{ _("Sign out") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% set title = "lnk_bills" %}
|
||||
{% set title = "Bills" %}
|
||||
|
||||
{% extends "layout/dash.html" %}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
|||
|
||||
<div class="container module">
|
||||
|
||||
<button type="button" class="btn btn-primary" style="margin:10px;" data-toggle="modal" data-target="#myModal">{{ locale(LANG, "add") }}</button>
|
||||
<button type="button" class="btn btn-primary" style="margin:10px;" data-toggle="modal" data-target="#myModal">{{ _("Add") }}</button>
|
||||
<div class="modal fade" id="myModal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<!-- Modal content-->
|
||||
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
{% macro render_field(field) %}
|
||||
<dt><label for="{{ field.label.field_id }}">{{ locale(LANG, field.label.text) }}</label>
|
||||
<dt><label for="{{ field.label.field_id }}">{{ _(field.label.text) }}</label>
|
||||
<dd>{{ field(**kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
|
@ -42,11 +42,11 @@
|
|||
{{ render_field(form.kid) }}
|
||||
{{ render_field(form.date_due) }}
|
||||
</dl>
|
||||
<input type=submit value="{{ locale(LANG, 'add') }}">
|
||||
<input type=submit value="{{ _('Add') }}">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ locale(LANG, "close") }}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ _("Close") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -55,12 +55,12 @@
|
|||
<table class="table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col">{{ locale(LANG, "lbl_to") }}</th>
|
||||
<th scope="col">{{ locale(LANG, "lbl_desc") }}</th>
|
||||
<th scope="col" style="width: 120px">{{ locale(LANG, "lbl_sum") }}</th>
|
||||
<th scope="col" style="width: 220px">{{ locale(LANG, "lbl_id") }}</th>
|
||||
<th scope="col" style="width: 120px">{{ locale(LANG, "lbl_date") }}</th>
|
||||
<th scope="col" style="width: 150px">{{ locale(LANG, "lbl_status") }}</th>
|
||||
<th scope="col">{{ _("Payment to") }}</th>
|
||||
<th scope="col">{{ _("Description") }}</th>
|
||||
<th scope="col" style="width: 120px">{{ _("Sum") }}</th>
|
||||
<th scope="col" style="width: 220px">{{ _("KID") }}</th>
|
||||
<th scope="col" style="width: 120px">{{ _("Date due") }}</th>
|
||||
<th scope="col" style="width: 150px">{{ _("Payment status") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% set title = "lnk_dashboard" %}
|
||||
{% set title = "Dashboard" %}
|
||||
|
||||
{% extends "layout/dash.html" %}
|
||||
|
||||
|
|
102
translations/nb/LC_MESSAGES/messages.po
Normal file
102
translations/nb/LC_MESSAGES/messages.po
Normal file
|
@ -0,0 +1,102 @@
|
|||
# Norwegian text and messages
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.4\n"
|
||||
"Report-Msgid-Bugs-To: noreply@osufx.com\n"
|
||||
"POT-Creation-Date: 2019-05-12 15:57+0200\n"
|
||||
"PO-Revision-Date: 2015-05-12 09:47+1000\n"
|
||||
"Last-Translator: Emily Steinsvik <emily@osufx.com>\n"
|
||||
"Language: no\n"
|
||||
"Language-Team: no <emily@osufx.com>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.6.0\n"
|
||||
|
||||
#: routes.py:70 routes.py:91
|
||||
msgid "Already logged in"
|
||||
msgstr ""
|
||||
|
||||
#: routes.py:84
|
||||
msgid "Logged in"
|
||||
msgstr "Logget inn"
|
||||
|
||||
#: routes.py:102
|
||||
msgid "User registered"
|
||||
msgstr "Bruker registrert"
|
||||
|
||||
#: routes.py:110
|
||||
msgid "Logged out"
|
||||
msgstr "Logget ut"
|
||||
|
||||
#: routes.py:115
|
||||
msgid "Login is required"
|
||||
msgstr "Innlogging kreves"
|
||||
|
||||
#: templates/layout/includes/side_nav.html:108
|
||||
msgid "Dashboard"
|
||||
msgstr "Dashbord"
|
||||
|
||||
#: templates/layout/includes/side_nav.html:112
|
||||
msgid "Economical"
|
||||
msgstr "Økonomisk"
|
||||
|
||||
#: templates/layout/includes/side_nav.html:116
|
||||
msgid "Bills"
|
||||
msgstr "Regninger"
|
||||
|
||||
#: templates/layout/includes/side_nav.html:121
|
||||
#: templates/layout/includes/side_nav.html:127
|
||||
msgid "Receipts"
|
||||
msgstr "Kvitteringer"
|
||||
|
||||
#: templates/layout/includes/side_nav.html:130
|
||||
msgid "Warranties"
|
||||
msgstr "Garantier"
|
||||
|
||||
#: templates/layout/includes/top_nav.html:72
|
||||
msgid "english"
|
||||
msgstr "engelsk"
|
||||
|
||||
#: templates/layout/includes/top_nav.html:78
|
||||
msgid "norwegian"
|
||||
msgstr "norsk"
|
||||
|
||||
#: templates/layout/includes/top_nav.html:98
|
||||
msgid "Sign out"
|
||||
msgstr "Logg ut"
|
||||
|
||||
#: templates/pages/bills.html:15 templates/pages/bills.html:45
|
||||
msgid "Add"
|
||||
msgstr "Legg til"
|
||||
|
||||
#: templates/pages/bills.html:49
|
||||
msgid "Close"
|
||||
msgstr "Lukk"
|
||||
|
||||
#: templates/pages/bills.html:58
|
||||
msgid "Payment to"
|
||||
msgstr "Betaling til"
|
||||
|
||||
#: templates/pages/bills.html:59
|
||||
msgid "Description"
|
||||
msgstr "Tekst"
|
||||
|
||||
#: templates/pages/bills.html:60
|
||||
msgid "Sum"
|
||||
msgstr "Sum"
|
||||
|
||||
#: templates/pages/bills.html:61
|
||||
msgid "KID"
|
||||
msgstr "KID"
|
||||
|
||||
#: templates/pages/bills.html:62
|
||||
msgid "Date due"
|
||||
msgstr "Forfallsdato"
|
||||
|
||||
#: templates/pages/bills.html:63
|
||||
msgid "Payment status"
|
||||
msgstr "Betalingsstatus"
|
||||
|
Loading…
Reference in New Issue
Block a user