diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 0000000..04f564e --- /dev/null +++ b/babel.cfg @@ -0,0 +1,2 @@ +[python: pyproger/**.py] +[jinja2: pyproger/templates/**.html] diff --git a/poetry.lock b/poetry.lock index c73d362..d953eec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -19,6 +19,17 @@ typing-extensions = ">=4" [package.extras] tz = ["python-dateutil"] +[[package]] +name = "babel" +version = "2.12.1" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, + {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, +] + [[package]] name = "blinker" version = "1.6.2" @@ -130,6 +141,23 @@ wtforms = "*" aws = ["boto"] azure = ["azure-storage-blob"] +[[package]] +name = "flask-babel" +version = "3.1.0" +description = "Adds i18n/l10n support for Flask applications." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "flask_babel-3.1.0-py3-none-any.whl", hash = "sha256:deb3ee272d5adf97f5974ed09ab501243d63e7fb4a047501a00de4bd4aca4830"}, + {file = "flask_babel-3.1.0.tar.gz", hash = "sha256:be015772c5d7f046f3b99c508dcf618636eb93d21b713b356db79f3e79f69f39"}, +] + +[package.dependencies] +Babel = ">=2.12" +Flask = ">=2.0" +Jinja2 = ">=3.1" +pytz = ">=2022.7" + [[package]] name = "flask-ckeditor" version = "0.4.6" @@ -538,6 +566,17 @@ files = [ {file = "psycopg2-2.9.7.tar.gz", hash = "sha256:f00cc35bd7119f1fed17b85bd1007855194dde2cbd8de01ab8ebb17487440ad8"}, ] +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + [[package]] name = "sqlalchemy" version = "2.0.20" @@ -664,4 +703,4 @@ email = ["email-validator"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "478d9db5ee4ac55f1f1c92cac45605f713462b70072ca9125c6bb88fa15a98d0" +content-hash = "63169951638948867f57f4680c69b6f55c929f9ab903261bfd1fa16ed7a5f2e2" diff --git a/pyproger/app.py b/pyproger/app.py index 162d795..75c5429 100644 --- a/pyproger/app.py +++ b/pyproger/app.py @@ -16,6 +16,13 @@ def create_app(test_config=None): else: app.config.from_mapping(test_config) + from .translations import babel + from .translations import bp as bp_translate + from .translations import get_locale + + babel.init_app(app, locale_selector=get_locale) + app.register_blueprint(bp_translate) + db.init_app(app) ckeditor = CKEditor() @@ -50,6 +57,12 @@ def create_app(test_config=None): get_url=url_for, ) + @app.context_processor + def utility_processor(): + return dict( + page_lang=get_locale(), + ) + @app.route("/ping") def hello(): return render_template_string("pong") diff --git a/pyproger/config.py b/pyproger/config.py index b480ab1..2bb620e 100755 --- a/pyproger/config.py +++ b/pyproger/config.py @@ -1,3 +1,5 @@ +import os + FLASK_ADMIN_SWATCH = "slate" # Create secret key so we can use sessions # python3: secrets.token_urlsafe() @@ -35,3 +37,7 @@ SECURITY_SEND_REGISTER_EMAIL = False SECURITY_SEND_PASSWORD_CHANGE_EMAIL = False SECURITY_SEND_PASSWORD_RESET_EMAIL = False SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL = False + +# Flask-Babel +LANGUAGES = ["ru"] +BABEL_TRANSLATION_DIRECTORIES = os.path.join(os.path.curdir, "translations") diff --git a/pyproger/translations/__init__.py b/pyproger/translations/__init__.py new file mode 100644 index 0000000..dbbda0d --- /dev/null +++ b/pyproger/translations/__init__.py @@ -0,0 +1 @@ +from .translation import babel, bp, get_locale diff --git a/pyproger/translations/ru_RU/LC_MESSAGES/messages.po b/pyproger/translations/ru_RU/LC_MESSAGES/messages.po new file mode 100644 index 0000000..8a3788d --- /dev/null +++ b/pyproger/translations/ru_RU/LC_MESSAGES/messages.po @@ -0,0 +1,51 @@ +# Russian translations for PROJECT. +# Copyright (C) 2023 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2023-07-03 23:52+0300\n" +"PO-Revision-Date: 2023-07-02 22:55+0300\n" +"Last-Translator: FULL NAME \n" +"Language: ru\n" +"Language-Team: ru \n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.12.1\n" + +#: pi3code/admin/views.py:64 +msgid "Tags" +msgstr "Тэги" + +#: pi3code/admin/views.py:65 +msgid "Title" +msgstr "Заголовок" + +#: pi3code/admin/views.py:66 +msgid "Author" +msgstr "Автор" + +#: pi3code/admin/views.py:67 +msgid "Published" +msgstr "Опубликован" + +#: pi3code/admin/views.py:68 +msgid "Publication day" +msgstr "Дата публикации" + +#: pi3code/templates/admin/index.html:29 +msgid "Back" +msgstr "Назад" + +#~ msgid "Name" +#~ msgstr "тема" + +#~ msgid "\"\"Translation and localization commands.\"\"" +#~ msgstr "\"\"Команды для перевода и локализации\"\"" + diff --git a/pyproger/translations/translation.py b/pyproger/translations/translation.py new file mode 100644 index 0000000..5b722a7 --- /dev/null +++ b/pyproger/translations/translation.py @@ -0,0 +1,35 @@ +import os + +from flask import Blueprint, request +from flask_babel import Babel + + +def get_locale(): + translations = ["en", "ru"] + return request.accept_languages.best_match(translations) + + +babel = Babel() + +bp = Blueprint( + "bp_translation", + __name__, + cli_group="translate", +) + + +@bp.cli.command("update") +def update(): + """Update all languages.""" + if os.system("pybabel extract -F babel.cfg -k _l -o messages.pot ."): + raise RuntimeError("extract command failed") + if os.system("pybabel update -i messages.pot -d pi3code/translations"): + raise RuntimeError("update command failed") + os.remove("messages.pot") + + +@bp.cli.command("compile") +def compile(): + """Compile all languages.""" + if os.system("pybabel compile -d pi3code/translations"): + raise RuntimeError("compile command failed") diff --git a/pyproject.toml b/pyproject.toml index 8b566e0..4d63930 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ flask-admin = "^1.6.1" psycopg2 = "^2.9.7" click = "^8.1.7" flask-ckeditor = "^0.4.6" +flask-babel = "^3.1.0" [build-system]