Make language selectable in UI (#568)

This commit is contained in:
Daniel Markstedt 2021-12-27 13:21:56 -08:00 committed by GitHub
parent e209345f1c
commit 13187fc2fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 24 deletions

View File

@ -5,7 +5,7 @@ Module for methods reading from and writing to the file system
import os import os
import logging import logging
from pathlib import PurePath from pathlib import PurePath
from flask import current_app from flask import current_app, session
from flask_babel import _ from flask_babel import _
from ractl_cmds import ( from ractl_cmds import (
@ -66,7 +66,7 @@ def list_images():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -123,7 +123,7 @@ def create_new_image(file_name, file_type, size):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.CREATE_IMAGE command.operation = proto.PbOperation.CREATE_IMAGE
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
command.params["file"] = file_name + "." + file_type command.params["file"] = file_name + "." + file_type
command.params["size"] = str(size) command.params["size"] = str(size)
@ -144,7 +144,7 @@ def delete_image(file_name):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DELETE_IMAGE command.operation = proto.PbOperation.DELETE_IMAGE
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
command.params["file"] = file_name command.params["file"] = file_name
@ -163,7 +163,7 @@ def rename_image(file_name, new_file_name):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.RENAME_IMAGE command.operation = proto.PbOperation.RENAME_IMAGE
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
command.params["from"] = file_name command.params["from"] = file_name
command.params["to"] = new_file_name command.params["to"] = new_file_name

View File

@ -4,7 +4,7 @@ Module for commands sent to the RaSCSI backend service.
from settings import REMOVABLE_DEVICE_TYPES from settings import REMOVABLE_DEVICE_TYPES
from socket_cmds import send_pb_command from socket_cmds import send_pb_command
from flask import current_app from flask import current_app, session
from flask_babel import _ from flask_babel import _
import rascsi_interface_pb2 as proto import rascsi_interface_pb2 as proto
@ -25,7 +25,7 @@ def get_server_info():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.SERVER_INFO command.operation = proto.PbOperation.SERVER_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -84,7 +84,7 @@ def get_reserved_ids():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.RESERVED_IDS_INFO command.operation = proto.PbOperation.RESERVED_IDS_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -106,7 +106,7 @@ def get_network_info():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -125,7 +125,7 @@ def get_device_types():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICE_TYPES_INFO command.operation = proto.PbOperation.DEVICE_TYPES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -148,7 +148,7 @@ def get_image_files_info():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -177,7 +177,7 @@ def attach_image(scsi_id, **kwargs):
""" """
command = proto.PbCommand() command = proto.PbCommand()
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
devices = proto.PbDeviceDefinition() devices = proto.PbDeviceDefinition()
devices.id = int(scsi_id) devices.id = int(scsi_id)
@ -253,7 +253,7 @@ def detach_by_id(scsi_id, unit=None):
command.operation = proto.PbOperation.DETACH command.operation = proto.PbOperation.DETACH
command.devices.append(devices) command.devices.append(devices)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -269,7 +269,7 @@ def detach_all():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DETACH_ALL command.operation = proto.PbOperation.DETACH_ALL
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -292,7 +292,7 @@ def eject_by_id(scsi_id, unit=None):
command.operation = proto.PbOperation.EJECT command.operation = proto.PbOperation.EJECT
command.devices.append(devices) command.devices.append(devices)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -312,7 +312,7 @@ def list_devices(scsi_id=None, unit=None):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICES_INFO command.operation = proto.PbOperation.DEVICES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
# If method is called with scsi_id parameter, return the info on those devices # If method is called with scsi_id parameter, return the info on those devices
# Otherwise, return the info on all attached devices # Otherwise, return the info on all attached devices
@ -390,7 +390,7 @@ def reserve_scsi_ids(reserved_scsi_ids):
command.operation = proto.PbOperation.RESERVE_IDS command.operation = proto.PbOperation.RESERVE_IDS
command.params["ids"] = ",".join(reserved_scsi_ids) command.params["ids"] = ",".join(reserved_scsi_ids)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -408,7 +408,7 @@ def set_log_level(log_level):
command.operation = proto.PbOperation.LOG_LEVEL command.operation = proto.PbOperation.LOG_LEVEL
command.params["level"] = str(log_level) command.params["level"] = str(log_level)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -426,7 +426,7 @@ def shutdown_pi(mode):
command.operation = proto.PbOperation.SHUT_DOWN command.operation = proto.PbOperation.SHUT_DOWN
command.params["mode"] = str(mode) command.params["mode"] = str(mode)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -443,7 +443,7 @@ def is_token_auth():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.CHECK_AUTHENTICATION command.operation = proto.PbOperation.CHECK_AUTHENTICATION
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = current_app.config["LOCALE"] command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()

View File

@ -30,3 +30,6 @@ RESERVATIONS = ["" for x in range(0, 8)]
# The user group that is used for webapp authentication # The user group that is used for webapp authentication
AUTH_GROUP = "rascsi" AUTH_GROUP = "rascsi"
# The language locales supported by RaSCSI
LANGUAGES = ["en", "de", "sv"]

View File

@ -612,6 +612,34 @@
<hr/> <hr/>
<details>
<summary class="heading">
{{ _("Language") }}
</summary>
<ul>
<li>{{ _("Change the Web Interface language.") }}</li>
</ul>
</details>
<table style="border: none">
<tr style="border: none">
<td style="border: none; vertical-align:top;">
<form action="/language" method="post">
<label for="language">{{ _("Language:") }}</label>
<select name="locale">
{% for locale in locales %}
<option value="{{ locale.language }}">
{{ locale.language }} - {{ locale.display_name }}
</option>
{% endfor %}
</select>
<input type="submit" value="{{ _("Change Language") }}">
</form>
</td>
</tr>
</table>
<hr/>
<details> <details>
<summary class="heading"> <summary class="heading">
{{ _("Raspberry Pi Operations") }} {{ _("Raspberry Pi Operations") }}

View File

@ -21,7 +21,7 @@ from flask import (
session, session,
abort, abort,
) )
from flask_babel import Babel, _ from flask_babel import Babel, Locale, refresh, _
from file_cmds import ( from file_cmds import (
list_images, list_images,
@ -79,6 +79,7 @@ from settings import (
REMOVABLE_DEVICE_TYPES, REMOVABLE_DEVICE_TYPES,
RESERVATIONS, RESERVATIONS,
AUTH_GROUP, AUTH_GROUP,
LANGUAGES,
) )
APP = Flask(__name__) APP = Flask(__name__)
@ -86,7 +87,27 @@ BABEL = Babel(APP)
@BABEL.localeselector @BABEL.localeselector
def get_locale(): def get_locale():
return request.accept_languages.best_match(["en", "de", "sv"]) """
Uses the session language, or tries to detect based on accept-languages header
"""
try:
language = session["language"]
except KeyError:
language = None
if language is not None:
return language
return request.accept_languages.best_match(LANGUAGES)
def get_supported_locales():
"""
Returns a list of Locale objects that the Web Interfaces supports
"""
locales = BABEL.list_translations()
locales.append(Locale("en"))
sorted_locales = sorted(locales, key=lambda x: x.language)
return sorted_locales
@APP.route("/") @APP.route("/")
def index(): def index():
@ -96,6 +117,7 @@ def index():
if not is_token_auth()["status"] and not APP.config["TOKEN"]: if not is_token_auth()["status"] and not APP.config["TOKEN"]:
abort(403, _(u"RaSCSI is password protected. Start the Web Interface with the --password parameter.")) abort(403, _(u"RaSCSI is password protected. Start the Web Interface with the --password parameter."))
locales = get_supported_locales()
server_info = get_server_info() server_info = get_server_info()
disk = disk_space() disk = disk_space()
devices = list_devices() devices = list_devices()
@ -133,6 +155,7 @@ def index():
return render_template( return render_template(
"index.html", "index.html",
locales=locales,
bridge_configured=is_bridge_setup(), bridge_configured=is_bridge_setup(),
netatalk_configured=running_proc("afpd"), netatalk_configured=running_proc("afpd"),
macproxy_configured=running_proc("macproxy"), macproxy_configured=running_proc("macproxy"),
@ -948,6 +971,16 @@ def unzip():
return redirect(url_for("index")) return redirect(url_for("index"))
@APP.route("/language", methods=["POST"])
def change_language():
locale = request.form.get("locale")
session["language"] = locale
refresh()
flash(_(u"Changed Web Interface language to %(locale)s", locale=locale))
return redirect(url_for("index"))
@APP.before_first_request @APP.before_first_request
def load_default_config(): def load_default_config():
""" """
@ -955,8 +988,7 @@ def load_default_config():
- Get the detected locale to use for localizations - Get the detected locale to use for localizations
- Load the default configuration file, if found - Load the default configuration file, if found
""" """
APP.config["LOCALE"] = get_locale() session["language"] = get_locale()
logging.info("Detected locale: " + APP.config["LOCALE"])
if Path(f"{CFG_DIR}/{DEFAULT_CONFIG}").is_file(): if Path(f"{CFG_DIR}/{DEFAULT_CONFIG}").is_file():
read_config(DEFAULT_CONFIG) read_config(DEFAULT_CONFIG)