# The Mia! Accounting Flask Demonstration Website. # Author: imacat@mail.imacat.idv.tw (imacat), 2023/1/2 # Copyright (c) 2023 imacat. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """The localization for the Mia! Accounting Flask demonstration website. """ from babel import Locale from flask import request, session, current_app, Blueprint, Response, \ redirect, url_for, Flask from flask_babel import Babel from werkzeug.datastructures import LanguageAccept bp: Blueprint = Blueprint("locale", __name__, url_prefix="/") def get_locale(): """Returns the locale of the user :return: The locale of the user. """ all_linguas: dict[str, str] = get_all_linguas() if "locale" in session and session["locale"] in all_linguas: return session["locale"] return __fix_accept_language(request.accept_languages)\ .best_match(all_linguas.keys()) def __fix_accept_language(accept: LanguageAccept) -> LanguageAccept: """Fixes the accept-language so that territory variants may be matched to script variants. For example, zh_TW, zh_HK to zh_Hant, and zh_CN, zh_SG to zh_Hans. This is to solve the issue that Flask only recognizes the script variants, like zh_Hant and zh_Hans. :param accept: The original HTTP accept languages. :return: The fixed HTTP accept languages """ accept_list: list[tuple[str, float]] = list(accept) to_add: list[tuple[str, float]] = [] for pair in accept_list: locale: Locale = Locale.parse(pair[0].replace("-", "_")) if locale.script is not None: tag: str = f"{locale.language}-{locale.script}" if tag not in accept: to_add.append((tag, pair[1])) accept_list.extend(to_add) return LanguageAccept(accept_list) @bp.post("/locale", endpoint="set-locale") def set_locale() -> Response: """Sets the locale for the user. :return: The response. """ all_linguas: dict[str, str] = get_all_linguas() if "locale" in request.form and request.form["locale"] in all_linguas: session["locale"] = request.form["locale"] if "next" in request.form: return redirect(request.form["next"]) return redirect(url_for("home.home")) def get_all_linguas() -> dict[str, str]: """Returns all the available languages. :return: All the available languages, as a dictionary of the language code and their local names. """ return {y[0]: y[1] for y in [x.split("|") for x in current_app.config["ALL_LINGUAS"].split(",")]} def init_app(app: Flask) -> None: """Initialize the localization. :param app: The Flask application. :return: None. """ babel = Babel() babel.init_app(app, locale_selector=get_locale) app.register_blueprint(bp) app.jinja_env.globals["get_locale"] = get_locale app.jinja_env.globals["get_all_linguas"] = get_all_linguas