- The Event {{ event }} - description ...
+ The Event {{ variables.event }} - description ...
Last updated 3 mins ago
diff --git a/tests/test_utilities.py b/tests/test_utilities.py
new file mode 100644
index 00000000..17a261a2
--- /dev/null
+++ b/tests/test_utilities.py
@@ -0,0 +1,20 @@
+from fastapi import Request
+
+from app.internal import utilities
+from app.main import app
+
+
+class TestUtilities:
+
+ @staticmethod
+ def test_get_template_response():
+ request = Request({
+ "type": "http",
+ "router": app.router,
+ "headers": [(b'host', b'testserver'),
+ (b'user-agent', b'testclient'),
+ (b'accept-encoding', b'gzip, deflate'),
+ (b'accept', b'*/*'), (b'connection', b'keep-alive')],
+ })
+
+ assert utilities.get_template_response("home.html", request)
From abc76414b867d99e19544cca2f23ac11da3fe3e3 Mon Sep 17 00:00:00 2001
From: Gonny <1@1>
Date: Mon, 25 Jan 2021 14:49:57 +0200
Subject: [PATCH 11/53] fix(i18n): fixed CR request to not use global state for
translations
The translations are now handled with @lru_cache and the function is only re-handled when a new language code parameter is used. This should be the last edit (unless any crash) to this version of the translation feature.
---
app/internal/languages.py | 70 +++++++++++++++++++++++----------------
tests/test_language.py | 28 ++++++----------
2 files changed, 53 insertions(+), 45 deletions(-)
diff --git a/app/internal/languages.py b/app/internal/languages.py
index 0b68e347..e4502e6d 100644
--- a/app/internal/languages.py
+++ b/app/internal/languages.py
@@ -1,3 +1,4 @@
+from functools import lru_cache
import glob
import json
from pathlib import PureWindowsPath
@@ -8,30 +9,46 @@
LANGUAGE_FILES_PATH = "app/languages/*.json"
LANGUAGE_FILES_PATH_TEST = "../app/languages/*.json"
-translation_words = {}
+@lru_cache()
+def get_translation_words(display_language: str = None) -> \
+ Dict[str, Union[str, Dict[str, str]]]:
+ """Gets and returns the translation words for a given language.
+ The returned object is a dictionary of the translated words in either
+ the user's language setting, or the default app setting.
-def get_translation_words() -> Dict[str, Union[str, Dict[str, str]]]:
- """Gets and returns the translation_words, which is a dictionary of
- the translated words in either the user's language setting,
- or the default app setting.
+ Using the @lru_cache() decorator makes the function return the same
+ translation for a given language that was previously used, instead of
+ computing it again, executing the code of the function every time.
+
+ Args:
+ display_language (str): a valid code that follows RFC 1766.
+ See also the Language Code Identifier (LCID) Reference for a list of
+ valid codes.
Returns:
- dict[str, Union[str, Dict[str, str]]]: a dictionary of string keys and
+ Dict[str, Union[str, Dict[str, str]]]: a dictionary of string keys and
their translation as their values. The value can either be a string,
or a nested dictionary for plural translations.
+
+ .. _RFC 1766:
+ https://tools.ietf.org/html/rfc1766.html
+
+ .. _Language Code Identifier (LCID) Reference:
+ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c?redirectedfrom=MSDN # noqa: E501
"""
- if translation_words:
- return translation_words
- # TODO: Waiting for user registration. Restore when done.
- # display_language = get_display_language(user_id)
- # populate_with_language(display_language)
- populate_with_language(config.WEBSITE_LANGUAGE)
- return translation_words
+
+ if display_language:
+ return _populate_with_language(display_language)
+ else:
+ # TODO: Waiting for user registration. Restore when done.
+ # display_language = _get_display_language(user_id)
+ # return populate_with_language(display_language)
+ return _populate_with_language(config.WEBSITE_LANGUAGE)
# TODO: Waiting for user registration. Add doc.
-# def get_display_language(user_id: int) -> str:
+# def _get_display_language(user_id: int) -> str:
# # TODO: handle user language setting:
# # If user is logged in, get language setting.
# # If user is not logged in, get default site setting.
@@ -41,7 +58,8 @@ def get_translation_words() -> Dict[str, Union[str, Dict[str, str]]]:
# return config.WEBSITE_LANGUAGE
-def populate_with_language(display_language: str) -> None:
+def _populate_with_language(display_language: str) -> \
+ Dict[str, Union[str, Dict[str, str]]]:
"""Updates the translation_words to the requested language.
If the language code is not supported by the applications, the dictionary
defaults to the config.WEBSITE_LANGUAGE setting.
@@ -51,28 +69,24 @@ def populate_with_language(display_language: str) -> None:
See also the Language Code Identifier (LCID) Reference for a list of
valid codes.
- .. _RFC 1766:
- https://tools.ietf.org/html/rfc1766.html
-
- .. _Language Code Identifier (LCID) Reference:
- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c?redirectedfrom=MSDN # noqa
+ Returns:
+ Dict[str, Union[str, Dict[str, str]]]: a dictionary of string keys and
+ their translation as their values. The value can either be a string,
+ or a nested dictionary for plural translations.
"""
- translation_words_all_languages = get_translations_words_all_languages()
- global translation_words
+ translation_words_all_languages = _get_translation_words_all_languages()
if display_language in translation_words_all_languages:
- translation_words = translation_words_all_languages[display_language]
- else:
- translation_words = translation_words_all_languages[
- config.WEBSITE_LANGUAGE]
+ return translation_words_all_languages[display_language]
+ return translation_words_all_languages[config.WEBSITE_LANGUAGE]
-def get_translations_words_all_languages() -> \
+def _get_translation_words_all_languages() -> \
Dict[str, Dict[str, Union[str, Dict[str, str]]]]:
"""Gets and returns a dictionary of nested language dictionaries from
the language translation files.
Returns:
- dict[str, Dict[str, Union[str, Dict[str, str]]]]: a dictionary of
+ Dict[str, Dict[str, Union[str, Dict[str, str]]]]: a dictionary of
language codes as string keys, and nested dictionaries of translations
as their values.
"""
diff --git a/tests/test_language.py b/tests/test_language.py
index 4d500893..07ece810 100644
--- a/tests/test_language.py
+++ b/tests/test_language.py
@@ -1,6 +1,5 @@
import pytest
-from app import config
import app.internal.languages as languages
@@ -13,41 +12,36 @@ class TestLanguage:
@staticmethod
def test_get_translation_words():
- translations = languages.get_translation_words()
- assert translations
+ assert languages.get_translation_words()
@staticmethod
- def test_get_translations_words_all_languages():
- translations_dicts = languages.get_translations_words_all_languages()
- assert translations_dicts
+ def test_get_translation_words_all_languages():
+ assert languages._get_translation_words_all_languages()
@staticmethod
@pytest.mark.parametrize("language_code, translation, is_valid",
LANGUAGE_TESTS)
- def test_translations_words_all_languages_return_all_supported_languages(
+ def test_translation_words_all_languages_return_all_supported_languages(
language_code, translation, is_valid):
- translations_dicts = languages.get_translations_words_all_languages()
- assert (language_code in translations_dicts and is_valid) or (
- language_code not in translations_dicts and not is_valid)
+ all_translations = languages._get_translation_words_all_languages()
+ assert ((language_code in all_translations and is_valid)
+ or (language_code not in all_translations and not is_valid))
@staticmethod
@pytest.mark.parametrize("language_code, translation, is_valid",
LANGUAGE_TESTS)
- def test_translations_words_all_languages_valid_translations(
+ def test_translation_words_all_languages_valid_translations(
language_code, translation, is_valid):
if is_valid:
- assert languages.get_translations_words_all_languages()[
+ assert languages._get_translation_words_all_languages()[
language_code]["test_word"] == translation
@staticmethod
@pytest.mark.parametrize("language_code, translation, is_valid",
LANGUAGE_TESTS)
def test_populate_with_language(language_code, translation, is_valid):
- # Reset test to default language.
- languages.populate_with_language(config.WEBSITE_LANGUAGE)
-
- languages.populate_with_language(language_code)
- assert languages.get_translation_words()["test_word"] == translation
+ translations = languages._populate_with_language(language_code)
+ assert translations["test_word"] == translation
@staticmethod
def test_get_display_language():
From 6f5728d3055b8e0d4d07efef71434bbe0abac031 Mon Sep 17 00:00:00 2001
From: Gonny <1@1>
Date: Mon, 25 Jan 2021 14:54:47 +0200
Subject: [PATCH 12/53] fix(i18n): add test to complete code coverage
---
tests/test_language.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tests/test_language.py b/tests/test_language.py
index 07ece810..e34531e7 100644
--- a/tests/test_language.py
+++ b/tests/test_language.py
@@ -14,6 +14,10 @@ class TestLanguage:
def test_get_translation_words():
assert languages.get_translation_words()
+ @staticmethod
+ def test_get_translation_words_for_language():
+ assert languages.get_translation_words("en")
+
@staticmethod
def test_get_translation_words_all_languages():
assert languages._get_translation_words_all_languages()
From e1caaa4b3c3ca095010a9d703760039fe503e4bc Mon Sep 17 00:00:00 2001
From: Gonny <1@1>
Date: Mon, 25 Jan 2021 15:03:45 +0200
Subject: [PATCH 13/53] fix(i18n): add missing documentation
---
app/internal/utilities.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/app/internal/utilities.py b/app/internal/utilities.py
index cc005372..825112f1 100644
--- a/app/internal/utilities.py
+++ b/app/internal/utilities.py
@@ -9,6 +9,19 @@
def get_template_response(html_file_name: str, request: Request,
variables: Dict[str, Any] = None) -> Jinja2Templates:
+ """Creates and returns a Jinja2Templates object with a result dictionary
+ containing three parts: the request object, a variables dictionary,
+ and a translation dictionary.
+
+ Args:
+ html_file_name (str): the name of the html file.
+ request (Request): a FastApi Request object.
+ variables (Dict[str, Any]): an optional variables dictionary used
+ in the html file.
+
+ Returns:
+ Jinja2Templates: a Jinja2Templates response object.
+ """
translations = languages.get_translation_words()
result = {"request": request,
"variables": variables,
From ade7c25df77163941972e62567229a380502e8ec Mon Sep 17 00:00:00 2001
From: Gonny <1@1>
Date: Tue, 26 Jan 2021 20:40:08 +0200
Subject: [PATCH 14/53] fix(i18n): i18n v2.0
Changed i18n system as requested in code review.
---
app/babel_mapping.ini | 3 +
app/internal/languages.py | 117 +++++++++++----------------
app/internal/utilities.py | 30 -------
app/languages/en.json | 31 -------
app/languages/he.json | 31 -------
app/locales/base.pot | 124 ++++++++++++++++++++++++++++
app/locales/en/LC_MESSAGES/base.mo | Bin 0 -> 443 bytes
app/locales/en/LC_MESSAGES/base.po | 125 +++++++++++++++++++++++++++++
app/locales/he/LC_MESSAGES/base.mo | Bin 0 -> 551 bytes
app/locales/he/LC_MESSAGES/base.po | 125 +++++++++++++++++++++++++++++
app/main.py | 14 +++-
app/routers/agenda.py | 9 +--
app/routers/event.py | 9 ++-
app/routers/profile.py | 12 ++-
app/templates/agenda.html | 36 ++++-----
app/templates/base.html | 16 ++--
app/templates/profile.html | 78 +++++++++---------
tests/test_language.py | 55 +++++++------
tests/test_utilities.py | 20 -----
19 files changed, 542 insertions(+), 293 deletions(-)
create mode 100644 app/babel_mapping.ini
delete mode 100644 app/internal/utilities.py
delete mode 100644 app/languages/en.json
delete mode 100644 app/languages/he.json
create mode 100644 app/locales/base.pot
create mode 100644 app/locales/en/LC_MESSAGES/base.mo
create mode 100644 app/locales/en/LC_MESSAGES/base.po
create mode 100644 app/locales/he/LC_MESSAGES/base.mo
create mode 100644 app/locales/he/LC_MESSAGES/base.po
delete mode 100644 tests/test_utilities.py
diff --git a/app/babel_mapping.ini b/app/babel_mapping.ini
new file mode 100644
index 00000000..959806a3
--- /dev/null
+++ b/app/babel_mapping.ini
@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **/templates/**.html]
+extensions=jinja2.ext.i18n,jinja2.ext.autoescape,jinja2.ext.with_
\ No newline at end of file
diff --git a/app/internal/languages.py b/app/internal/languages.py
index e4502e6d..6b66dd4b 100644
--- a/app/internal/languages.py
+++ b/app/internal/languages.py
@@ -1,35 +1,34 @@
-from functools import lru_cache
-import glob
-import json
-from pathlib import PureWindowsPath
-from typing import Dict, Union
+import gettext
+import os
+from pathlib import Path
+from typing import List
from app import config
+from app.dependencies import templates
-LANGUAGE_FILES_PATH = "app/languages/*.json"
-LANGUAGE_FILES_PATH_TEST = "../app/languages/*.json"
+LANGUAGE_DIR = "app/locales"
+LANGUAGE_DIR_TEST = "../app/locales"
+TRANSLATION_FILE = "base"
-@lru_cache()
-def get_translation_words(display_language: str = None) -> \
- Dict[str, Union[str, Dict[str, str]]]:
- """Gets and returns the translation words for a given language.
- The returned object is a dictionary of the translated words in either
- the user's language setting, or the default app setting.
+def setup_ui_language():
+ """Set the jinja2 environment on startup to support the i18n
+ and call set_ui_language() to setup an initial language for translations.
+ """
+ templates.env.add_extension('jinja2.ext.i18n')
+ change_ui_language()
- Using the @lru_cache() decorator makes the function return the same
- translation for a given language that was previously used, instead of
- computing it again, executing the code of the function every time.
- Args:
- display_language (str): a valid code that follows RFC 1766.
- See also the Language Code Identifier (LCID) Reference for a list of
- valid codes.
+def change_ui_language(language: str = None):
+ """Set the gettext translations to a given language.
+ If the language requested is not supported, the translations default
+ to the value of config.WEBSITE_LANGUAGE.
- Returns:
- Dict[str, Union[str, Dict[str, str]]]: a dictionary of string keys and
- their translation as their values. The value can either be a string,
- or a nested dictionary for plural translations.
+ Args:
+ language (str, optional): a valid code that follows RFC 1766.
+ Defaults to None.
+ See also the Language Code Identifier (LCID) Reference for a list of
+ valid codes.
.. _RFC 1766:
https://tools.ietf.org/html/rfc1766.html
@@ -38,13 +37,23 @@ def get_translation_words(display_language: str = None) -> \
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c?redirectedfrom=MSDN # noqa: E501
"""
- if display_language:
- return _populate_with_language(display_language)
+ # TODO: Connect when user registration is completed.
+ # if not language:
+ # language = _get_display_language(user_id: int)
+
+ if language not in _get_supported_languages():
+ language = config.WEBSITE_LANGUAGE
+
+ if Path(LANGUAGE_DIR).is_dir():
+ language_dir = LANGUAGE_DIR
else:
- # TODO: Waiting for user registration. Restore when done.
- # display_language = _get_display_language(user_id)
- # return populate_with_language(display_language)
- return _populate_with_language(config.WEBSITE_LANGUAGE)
+ language_dir = LANGUAGE_DIR_TEST
+
+ translations = gettext.translation(TRANSLATION_FILE,
+ localedir=language_dir,
+ languages=[language])
+ translations.install()
+ templates.env.install_gettext_translations(translations, newstyle=True)
# TODO: Waiting for user registration. Add doc.
@@ -58,43 +67,15 @@ def get_translation_words(display_language: str = None) -> \
# return config.WEBSITE_LANGUAGE
-def _populate_with_language(display_language: str) -> \
- Dict[str, Union[str, Dict[str, str]]]:
- """Updates the translation_words to the requested language.
- If the language code is not supported by the applications, the dictionary
- defaults to the config.WEBSITE_LANGUAGE setting.
-
- Args:
- display_language (str): a valid code that follows RFC 1766.
- See also the Language Code Identifier (LCID) Reference for a list of
- valid codes.
-
- Returns:
- Dict[str, Union[str, Dict[str, str]]]: a dictionary of string keys and
- their translation as their values. The value can either be a string,
- or a nested dictionary for plural translations.
- """
- translation_words_all_languages = _get_translation_words_all_languages()
- if display_language in translation_words_all_languages:
- return translation_words_all_languages[display_language]
- return translation_words_all_languages[config.WEBSITE_LANGUAGE]
-
-
-def _get_translation_words_all_languages() -> \
- Dict[str, Dict[str, Union[str, Dict[str, str]]]]:
- """Gets and returns a dictionary of nested language dictionaries from
- the language translation files.
+def _get_supported_languages() -> List[str]:
+ """Get and return a list of supported translation languages codes.
- Returns:
- Dict[str, Dict[str, Union[str, Dict[str, str]]]]: a dictionary of
- language codes as string keys, and nested dictionaries of translations
- as their values.
+ :Returns:
+ List[str]: a list of supported translation languages codes.
"""
- language_translations = {}
- language_files = (glob.glob(LANGUAGE_FILES_PATH)
- or glob.glob(LANGUAGE_FILES_PATH_TEST))
- for language_file in language_files:
- language_code = PureWindowsPath(language_file).stem
- with open(language_file, 'r', encoding='utf8') as file:
- language_translations[language_code] = json.load(file)
- return language_translations
+ try:
+ language_dir = os.scandir(LANGUAGE_DIR)
+ except FileNotFoundError:
+ language_dir = os.scandir(LANGUAGE_DIR_TEST)
+ return [language.name for language in
+ [Path(f.path) for f in language_dir if f.is_dir()]]
diff --git a/app/internal/utilities.py b/app/internal/utilities.py
deleted file mode 100644
index 825112f1..00000000
--- a/app/internal/utilities.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from typing import Any, Dict
-
-from fastapi import Request
-from fastapi.templating import Jinja2Templates
-
-from app.dependencies import templates
-from app.internal import languages
-
-
-def get_template_response(html_file_name: str, request: Request,
- variables: Dict[str, Any] = None) -> Jinja2Templates:
- """Creates and returns a Jinja2Templates object with a result dictionary
- containing three parts: the request object, a variables dictionary,
- and a translation dictionary.
-
- Args:
- html_file_name (str): the name of the html file.
- request (Request): a FastApi Request object.
- variables (Dict[str, Any]): an optional variables dictionary used
- in the html file.
-
- Returns:
- Jinja2Templates: a Jinja2Templates response object.
- """
- translations = languages.get_translation_words()
- result = {"request": request,
- "variables": variables,
- "localized": translations,
- }
- return templates.TemplateResponse(html_file_name, result)
diff --git a/app/languages/en.json b/app/languages/en.json
deleted file mode 100644
index 0f9f5399..00000000
--- a/app/languages/en.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "website_title": "Calendar",
- "calendar_button": "Calendar",
- "home_button": "Home",
- "profile_button": "Profile",
- "sign_in_button": "Sign In",
- "sign_up_button": "Sign Up",
- "agenda_button": "Agenda",
- "edit_full_name_button": "Edit full name",
- "edit_email_button": "Edit email",
- "edit_photo_button": "Edit photo",
- "edit_description_button": "Edit description",
- "update_full_name_title": "Update full name",
- "update_email_title": "Update email",
- "update_photo_title": "Upload photo",
- "update_description_title": "Update description",
- "save_changes_button": "Save changes",
- "settings_button": "Settings",
- "features_title": "Features",
- "export_calendar_button": "Export my calendar",
- "upcoming_event_on_date": "Upcoming event on {date}",
- "from_button": "From",
- "to_button": "to",
- "get_agenda_button": "Get Agenda",
- "agenda_today": "Today",
- "agenda_next_week": "Next week",
- "agenda_next_month": "Next month",
- "agenda_invalid_date": "Start date is greater than end date",
- "no_events_found": "No events found...",
- "test_word": "test"
-}
\ No newline at end of file
diff --git a/app/languages/he.json b/app/languages/he.json
deleted file mode 100644
index 991c1979..00000000
--- a/app/languages/he.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "website_title": "Calendar",
- "calendar_button": "Calendar",
- "home_button": "Home",
- "profile_button": "פרופיל",
- "sign_in_button": "Sign In",
- "sign_up_button": "Sign Up",
- "agenda_button": "Agenda",
- "edit_full_name_button": "ערוך שם מלא",
- "edit_email_button": "ערוך אימייל",
- "edit_photo_button": "ערוך תמונה",
- "edit_description_button": "ערוך תיאור",
- "update_full_name_title": "עדכן שם מלא",
- "update_email_title": "עדכן אימייל",
- "update_photo_title": "עדכן תמונה",
- "update_description_title": "עדכן תיאור",
- "save_changes_button": "שמור שינויים",
- "settings_button": "הגדרות",
- "features_title": "Features",
- "export_calendar_button": "Export my calendar",
- "upcoming_event_on_date": "Upcoming event on {date}",
- "from_button": "מ",
- "to_button": "עד",
- "get_agenda_button": "Get Agenda",
- "agenda_today": "היום",
- "agenda_next_week": "שבוע הבא",
- "agenda_next_month": "חודש הבא",
- "agenda_invalid_date": "Start date is greater than end date",
- "no_events_found": "לא נמצאו אירועים...",
- "test_word": "בדיקה"
-}
\ No newline at end of file
diff --git a/app/locales/base.pot b/app/locales/base.pot
new file mode 100644
index 00000000..ee23369b
--- /dev/null
+++ b/app/locales/base.pot
@@ -0,0 +1,124 @@
+# Translations template for PROJECT.
+# Copyright (C) 2021 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR
, 2021.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2021-01-26 20:17+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.9.0\n"
+
+#: app/routers/profile.py:19
+msgid "Not found"
+msgstr ""
+
+#: app/templates/agenda.html:11
+msgid "From"
+msgstr ""
+
+#: app/templates/agenda.html:13
+msgid "to"
+msgstr ""
+
+#: app/templates/agenda.html:33
+msgid "Start date is greater than end date"
+msgstr ""
+
+#: app/templates/agenda.html:35
+msgid "No events found..."
+msgstr ""
+
+#: app/templates/base.html:18
+msgid "Calendar"
+msgstr ""
+
+#: app/templates/base.html:26
+msgid "Home"
+msgstr ""
+
+#: app/templates/base.html:29
+msgid "Profile"
+msgstr ""
+
+#: app/templates/base.html:32
+msgid "Sign in"
+msgstr ""
+
+#: app/templates/base.html:35
+msgid "Sign up"
+msgstr ""
+
+#: app/templates/base.html:40
+msgid "Agenda"
+msgstr ""
+
+#: app/templates/profile.html:53
+msgid "Update name"
+msgstr ""
+
+#: app/templates/profile.html:60 app/templates/profile.html:82
+#: app/templates/profile.html:103 app/templates/profile.html:127
+msgid "Save changes"
+msgstr ""
+
+#: app/templates/profile.html:74
+msgid "Update email"
+msgstr ""
+
+#: app/templates/profile.html:95
+msgid "Update description"
+msgstr ""
+
+#: app/templates/profile.html:118
+msgid "Update photo"
+msgstr ""
+
+#: app/templates/profile.html:144
+msgid "Settings"
+msgstr ""
+
+#: app/templates/profile.html:161
+msgid "Features"
+msgstr ""
+
+#: app/templates/profile.html:165
+msgid "Export my calendar"
+msgstr ""
+
+#: app/templates/profile.html:168 app/templates/profile.html:171
+msgid "Your feature"
+msgstr ""
+
+#: app/templates/profile.html:192
+msgid "Upcoming event on (date)"
+msgstr ""
+
+#: app/templates/profile.html:203
+msgid "The Event (event)"
+msgstr ""
+
+#: app/templates/profile.html:206
+msgid "Last updated (time) ago"
+msgstr ""
+
+#: app/templates/profile.html:223
+msgid "Explore MeetUps near you"
+msgstr ""
+
+#: app/templates/profile.html:232 app/templates/profile.html:241
+msgid "Your Card"
+msgstr ""
+
+#: tests/test_language.py:28
+msgid "test"
+msgstr ""
+
diff --git a/app/locales/en/LC_MESSAGES/base.mo b/app/locales/en/LC_MESSAGES/base.mo
new file mode 100644
index 0000000000000000000000000000000000000000..6a4ee08edf6ebbc5de895e6ec08ca5699eaf7efa
GIT binary patch
literal 443
zcmZut!A`v
z;v|#2H}f)Y@;*3>~kuZyVCm`Tc2Nwts!CQnfW(_tJy?~irgiYU)(nj1^Te+Xub
zZkq}8qG(VSZ6Wi%m6WxlB~FCgDgwOvrpj}>u!r6ShcgeHODhiJ;1G$^wk=<9&A54L
zR~akK-twk^-QH=+N<>jI|p6!{Dw&k_)4q`o)hRS3vE3x&ZJ4mWVs_SqDxkS
J+rBprtuKQ(eWL&X
literal 0
HcmV?d00001
diff --git a/app/locales/en/LC_MESSAGES/base.po b/app/locales/en/LC_MESSAGES/base.po
new file mode 100644
index 00000000..a41c25ca
--- /dev/null
+++ b/app/locales/en/LC_MESSAGES/base.po
@@ -0,0 +1,125 @@
+# English translations for PROJECT.
+# Copyright (C) 2021 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR , 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2021-01-26 20:17+0200\n"
+"PO-Revision-Date: 2021-01-26 20:18+0200\n"
+"Last-Translator: FULL NAME \n"
+"Language: en\n"
+"Language-Team: en \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.9.0\n"
+
+#: app/routers/profile.py:19
+msgid "Not found"
+msgstr ""
+
+#: app/templates/agenda.html:11
+msgid "From"
+msgstr ""
+
+#: app/templates/agenda.html:13
+msgid "to"
+msgstr ""
+
+#: app/templates/agenda.html:33
+msgid "Start date is greater than end date"
+msgstr ""
+
+#: app/templates/agenda.html:35
+msgid "No events found..."
+msgstr ""
+
+#: app/templates/base.html:18
+msgid "Calendar"
+msgstr ""
+
+#: app/templates/base.html:26
+msgid "Home"
+msgstr ""
+
+#: app/templates/base.html:29
+msgid "Profile"
+msgstr ""
+
+#: app/templates/base.html:32
+msgid "Sign in"
+msgstr ""
+
+#: app/templates/base.html:35
+msgid "Sign up"
+msgstr ""
+
+#: app/templates/base.html:40
+msgid "Agenda"
+msgstr ""
+
+#: app/templates/profile.html:53
+msgid "Update name"
+msgstr ""
+
+#: app/templates/profile.html:60 app/templates/profile.html:82
+#: app/templates/profile.html:103 app/templates/profile.html:127
+msgid "Save changes"
+msgstr ""
+
+#: app/templates/profile.html:74
+msgid "Update email"
+msgstr ""
+
+#: app/templates/profile.html:95
+msgid "Update description"
+msgstr ""
+
+#: app/templates/profile.html:118
+msgid "Update photo"
+msgstr ""
+
+#: app/templates/profile.html:144
+msgid "Settings"
+msgstr ""
+
+#: app/templates/profile.html:161
+msgid "Features"
+msgstr ""
+
+#: app/templates/profile.html:165
+msgid "Export my calendar"
+msgstr ""
+
+#: app/templates/profile.html:168 app/templates/profile.html:171
+msgid "Your feature"
+msgstr ""
+
+#: app/templates/profile.html:192
+msgid "Upcoming event on (date)"
+msgstr ""
+
+#: app/templates/profile.html:203
+msgid "The Event (event)"
+msgstr ""
+
+#: app/templates/profile.html:206
+msgid "Last updated (time) ago"
+msgstr ""
+
+#: app/templates/profile.html:223
+msgid "Explore MeetUps near you"
+msgstr ""
+
+#: app/templates/profile.html:232 app/templates/profile.html:241
+msgid "Your Card"
+msgstr ""
+
+#: tests/test_language.py:28
+msgid "test"
+msgstr ""
+
diff --git a/app/locales/he/LC_MESSAGES/base.mo b/app/locales/he/LC_MESSAGES/base.mo
new file mode 100644
index 0000000000000000000000000000000000000000..31749db695f62af1cdaca0cb267be5a630ec731b
GIT binary patch
literal 551
zcmaJ-!AcxK6l_IE7$QQ>9$s#S(Cf?`5;N|K>+UEkGww1w@su3e-M8b&*u8Z3hWHUV
zh>8dvJP7I+_04bbO#*5zc~DeUQ*=Gv=7>$vyP}xj)i6ettMrK%%ZWiIl__Uy)oZMZAv907-aq3xRb+v
zoDF;ZJukzl(w3*DD93zjRv4bE2;#KeOE%lxZWa%R-k_iJjz(eW2i_GHBMAL4;C{fv
z&m^PZ%O^kdeG2jnf0lLAe{+d2mgibXQwghd1l!+|1oql#49$P4H@rm1VkQczJj7?i
za}>w*3pA5tvnm@(7aQK7nrTt-ZKaP*1akVvnpU_De`RY$!iN?FAHB4f#xExc8ot+2
z(xRlLPNr04@t0klR-%;aFgg_4pl!|Ugs;8i>g6Y>c`Qe2T*@MXwS&@nJ19}ph3J+i
q5p0PAtRQTBr3U7%-G#dZ_uy{aIdiw}p8T!5a@WkAx-$}Y^!@}{zo01q
literal 0
HcmV?d00001
diff --git a/app/locales/he/LC_MESSAGES/base.po b/app/locales/he/LC_MESSAGES/base.po
new file mode 100644
index 00000000..7939bbc6
--- /dev/null
+++ b/app/locales/he/LC_MESSAGES/base.po
@@ -0,0 +1,125 @@
+# Hebrew translations for PROJECT.
+# Copyright (C) 2021 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR , 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2021-01-26 20:17+0200\n"
+"PO-Revision-Date: 2021-01-26 20:17+0200\n"
+"Last-Translator: FULL NAME \n"
+"Language: he\n"
+"Language-Team: he \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.9.0\n"
+
+#: app/routers/profile.py:19
+msgid "Not found"
+msgstr ""
+
+#: app/templates/agenda.html:11
+msgid "From"
+msgstr ""
+
+#: app/templates/agenda.html:13
+msgid "to"
+msgstr ""
+
+#: app/templates/agenda.html:33
+msgid "Start date is greater than end date"
+msgstr ""
+
+#: app/templates/agenda.html:35
+msgid "No events found..."
+msgstr ""
+
+#: app/templates/base.html:18
+msgid "Calendar"
+msgstr "לוח שנה"
+
+#: app/templates/base.html:26
+msgid "Home"
+msgstr ""
+
+#: app/templates/base.html:29
+msgid "Profile"
+msgstr "פרופיל"
+
+#: app/templates/base.html:32
+msgid "Sign in"
+msgstr ""
+
+#: app/templates/base.html:35
+msgid "Sign up"
+msgstr ""
+
+#: app/templates/base.html:40
+msgid "Agenda"
+msgstr ""
+
+#: app/templates/profile.html:53
+msgid "Update name"
+msgstr ""
+
+#: app/templates/profile.html:60 app/templates/profile.html:82
+#: app/templates/profile.html:103 app/templates/profile.html:127
+msgid "Save changes"
+msgstr ""
+
+#: app/templates/profile.html:74
+msgid "Update email"
+msgstr ""
+
+#: app/templates/profile.html:95
+msgid "Update description"
+msgstr ""
+
+#: app/templates/profile.html:118
+msgid "Update photo"
+msgstr ""
+
+#: app/templates/profile.html:144
+msgid "Settings"
+msgstr ""
+
+#: app/templates/profile.html:161
+msgid "Features"
+msgstr ""
+
+#: app/templates/profile.html:165
+msgid "Export my calendar"
+msgstr ""
+
+#: app/templates/profile.html:168 app/templates/profile.html:171
+msgid "Your feature"
+msgstr ""
+
+#: app/templates/profile.html:192
+msgid "Upcoming event on (date)"
+msgstr ""
+
+#: app/templates/profile.html:203
+msgid "The Event (event)"
+msgstr ""
+
+#: app/templates/profile.html:206
+msgid "Last updated (time) ago"
+msgstr ""
+
+#: app/templates/profile.html:223
+msgid "Explore MeetUps near you"
+msgstr ""
+
+#: app/templates/profile.html:232 app/templates/profile.html:241
+msgid "Your Card"
+msgstr ""
+
+#: tests/test_language.py:28
+msgid "test"
+msgstr "בדיקה"
+
diff --git a/app/main.py b/app/main.py
index 9f56372d..f2ed0f90 100644
--- a/app/main.py
+++ b/app/main.py
@@ -3,9 +3,11 @@
from app.database import models
from app.database.database import engine
-from app.dependencies import MEDIA_PATH, STATIC_PATH
-from app.internal.utilities import get_template_response
-from app.routers import agenda, email, event, profile
+from app.dependencies import MEDIA_PATH, STATIC_PATH, templates
+from app.internal.languages import setup_ui_language
+
+# This MUST come before the app.routers import.
+setup_ui_language()
models.Base.metadata.create_all(bind=engine)
@@ -13,6 +15,8 @@
app.mount("/static", StaticFiles(directory=STATIC_PATH), name="static")
app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media")
+from app.routers import agenda, email, event, profile # noqa: E402
+
app.include_router(profile.router)
app.include_router(event.router)
app.include_router(agenda.router)
@@ -21,4 +25,6 @@
@app.get("/")
async def home(request: Request):
- return get_template_response("home.html", request)
+ return templates.TemplateResponse("home.html", {
+ "request": request,
+ })
diff --git a/app/routers/agenda.py b/app/routers/agenda.py
index b59f0cab..076f9b34 100644
--- a/app/routers/agenda.py
+++ b/app/routers/agenda.py
@@ -7,8 +7,8 @@
from sqlalchemy.orm import Session
from app.database.database import get_db
+from app.dependencies import templates
from app.internal import agenda_events
-from app.internal.utilities import get_template_response
router = APIRouter()
@@ -53,10 +53,9 @@ def agenda(
)
events[event_obj.start.date()].append((event_obj, event_duration))
- variables = {
+ return templates.TemplateResponse("agenda.html", {
+ "request": request,
"events": events,
"start_date": start_date,
"end_date": end_date,
- }
-
- return get_template_response("agenda.html", request, variables)
+ })
diff --git a/app/routers/event.py b/app/routers/event.py
index 3f447e53..f2a0b2dc 100644
--- a/app/routers/event.py
+++ b/app/routers/event.py
@@ -1,6 +1,6 @@
from fastapi import APIRouter, Request
-from app.internal.utilities import get_template_response
+from app.dependencies import templates
router = APIRouter(
prefix="/event",
@@ -11,10 +11,11 @@
@router.get("/edit")
async def eventedit(request: Request):
- return get_template_response("event/eventedit.html", request)
+ return templates.TemplateResponse("event/eventedit.html",
+ {"request": request})
@router.get("/view/{id}")
async def eventview(request: Request, id: int):
- variables = {"event_id": id}
- return get_template_response("event/eventview.html", request, variables)
+ return templates.TemplateResponse("event/eventview.html",
+ {"request": request, "event_id": id})
diff --git a/app/routers/profile.py b/app/routers/profile.py
index 3eacacf0..b661ee2e 100644
--- a/app/routers/profile.py
+++ b/app/routers/profile.py
@@ -8,8 +8,7 @@
from app import config
from app.database.database import get_db
from app.database.models import User
-from app.dependencies import MEDIA_PATH
-from app.internal.utilities import get_template_response
+from app.dependencies import MEDIA_PATH, templates
PICTURE_EXTENSION = config.PICTURE_EXTENSION
PICTURE_SIZE = config.AVATAR_SIZE
@@ -17,7 +16,7 @@
router = APIRouter(
prefix="/profile",
tags=["profile"],
- responses={404: {"description": "Not found"}},
+ responses={404: {"description": _("Not found")}},
)
@@ -46,12 +45,11 @@ async def profile(
session.close()
- variables = {
+ return templates.TemplateResponse("profile.html", {
+ "request": request,
"user": user,
"events": upcoming_events,
- }
-
- return get_template_response("profile.html", request, variables)
+ })
@router.post("/update_user_fullname")
diff --git a/app/templates/agenda.html b/app/templates/agenda.html
index c2daee17..9d27bd17 100644
--- a/app/templates/agenda.html
+++ b/app/templates/agenda.html
@@ -7,41 +7,41 @@
{% block content %}