diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index 84791959..a362100b 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -5,7 +5,7 @@ Module for methods reading from and writing to the file system import os import logging from pathlib import PurePath -from flask import current_app +from flask import current_app, session from flask_babel import _ from ractl_cmds import ( @@ -66,7 +66,7 @@ def list_images(): command = proto.PbCommand() command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO 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()) result = proto.PbResult() @@ -123,7 +123,7 @@ def create_new_image(file_name, file_type, size): command = proto.PbCommand() command.operation = proto.PbOperation.CREATE_IMAGE 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["size"] = str(size) @@ -144,7 +144,7 @@ def delete_image(file_name): command = proto.PbCommand() command.operation = proto.PbOperation.DELETE_IMAGE command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = current_app.config["LOCALE"] + command.params["locale"] = session["language"] command.params["file"] = file_name @@ -163,7 +163,7 @@ def rename_image(file_name, new_file_name): command = proto.PbCommand() command.operation = proto.PbOperation.RENAME_IMAGE 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["to"] = new_file_name diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 6ed3b060..79bb5f32 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -4,7 +4,7 @@ Module for commands sent to the RaSCSI backend service. from settings import REMOVABLE_DEVICE_TYPES from socket_cmds import send_pb_command -from flask import current_app +from flask import current_app, session from flask_babel import _ import rascsi_interface_pb2 as proto @@ -25,7 +25,7 @@ def get_server_info(): command = proto.PbCommand() command.operation = proto.PbOperation.SERVER_INFO 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()) result = proto.PbResult() @@ -84,7 +84,7 @@ def get_reserved_ids(): command = proto.PbCommand() command.operation = proto.PbOperation.RESERVED_IDS_INFO 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()) result = proto.PbResult() @@ -106,7 +106,7 @@ def get_network_info(): command = proto.PbCommand() command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO 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()) result = proto.PbResult() @@ -125,7 +125,7 @@ def get_device_types(): command = proto.PbCommand() command.operation = proto.PbOperation.DEVICE_TYPES_INFO 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()) result = proto.PbResult() @@ -148,7 +148,7 @@ def get_image_files_info(): command = proto.PbCommand() command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO 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()) result = proto.PbResult() @@ -177,7 +177,7 @@ def attach_image(scsi_id, **kwargs): """ command = proto.PbCommand() command.params["token"] = current_app.config["TOKEN"] - command.params["locale"] = current_app.config["LOCALE"] + command.params["locale"] = session["language"] devices = proto.PbDeviceDefinition() devices.id = int(scsi_id) @@ -253,7 +253,7 @@ def detach_by_id(scsi_id, unit=None): command.operation = proto.PbOperation.DETACH command.devices.append(devices) 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()) result = proto.PbResult() @@ -269,7 +269,7 @@ def detach_all(): command = proto.PbCommand() command.operation = proto.PbOperation.DETACH_ALL 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()) result = proto.PbResult() @@ -292,7 +292,7 @@ def eject_by_id(scsi_id, unit=None): command.operation = proto.PbOperation.EJECT command.devices.append(devices) 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()) result = proto.PbResult() @@ -312,7 +312,7 @@ def list_devices(scsi_id=None, unit=None): command = proto.PbCommand() command.operation = proto.PbOperation.DEVICES_INFO 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 # 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.params["ids"] = ",".join(reserved_scsi_ids) 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()) result = proto.PbResult() @@ -408,7 +408,7 @@ def set_log_level(log_level): command.operation = proto.PbOperation.LOG_LEVEL command.params["level"] = str(log_level) 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()) result = proto.PbResult() @@ -426,7 +426,7 @@ def shutdown_pi(mode): command.operation = proto.PbOperation.SHUT_DOWN command.params["mode"] = str(mode) 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()) result = proto.PbResult() @@ -443,7 +443,7 @@ def is_token_auth(): command = proto.PbCommand() command.operation = proto.PbOperation.CHECK_AUTHENTICATION 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()) result = proto.PbResult() diff --git a/src/web/settings.py b/src/web/settings.py index 6f4839bc..3095b8f2 100644 --- a/src/web/settings.py +++ b/src/web/settings.py @@ -30,3 +30,6 @@ RESERVATIONS = ["" for x in range(0, 8)] # The user group that is used for webapp authentication AUTH_GROUP = "rascsi" + +# The language locales supported by RaSCSI +LANGUAGES = ["en", "de", "sv"] diff --git a/src/web/templates/index.html b/src/web/templates/index.html index ef323959..283e8b3b 100644 --- a/src/web/templates/index.html +++ b/src/web/templates/index.html @@ -612,6 +612,34 @@
+
+ + {{ _("Language") }} + + +
+ + + + +
+
+ + + +
+
+ +
+
{{ _("Raspberry Pi Operations") }} diff --git a/src/web/web.py b/src/web/web.py index 2559a1c2..57dd356f 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -21,7 +21,7 @@ from flask import ( session, abort, ) -from flask_babel import Babel, _ +from flask_babel import Babel, Locale, refresh, _ from file_cmds import ( list_images, @@ -79,6 +79,7 @@ from settings import ( REMOVABLE_DEVICE_TYPES, RESERVATIONS, AUTH_GROUP, + LANGUAGES, ) APP = Flask(__name__) @@ -86,7 +87,27 @@ BABEL = Babel(APP) @BABEL.localeselector 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("/") def index(): @@ -96,6 +117,7 @@ def index(): 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.")) + locales = get_supported_locales() server_info = get_server_info() disk = disk_space() devices = list_devices() @@ -133,6 +155,7 @@ def index(): return render_template( "index.html", + locales=locales, bridge_configured=is_bridge_setup(), netatalk_configured=running_proc("afpd"), macproxy_configured=running_proc("macproxy"), @@ -948,6 +971,16 @@ def unzip(): 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 def load_default_config(): """ @@ -955,8 +988,7 @@ def load_default_config(): - Get the detected locale to use for localizations - Load the default configuration file, if found """ - APP.config["LOCALE"] = get_locale() - logging.info("Detected locale: " + APP.config["LOCALE"]) + session["language"] = get_locale() if Path(f"{CFG_DIR}/{DEFAULT_CONFIG}").is_file(): read_config(DEFAULT_CONFIG)