diff --git a/app/babel_mapping.ini b/app/babel_mapping.ini new file mode 100644 index 00000000..6a9a3e5c --- /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_ diff --git a/app/config.py.example b/app/config.py.example index 7af0e1af..429c60cb 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -12,6 +12,9 @@ MEDIA_DIRECTORY = 'media' PICTURE_EXTENSION = '.png' AVATAR_SIZE = (120, 120) +# DEFAULT WEBSITE LANGUAGE +WEBSITE_LANGUAGE = "en" + email_conf = ConnectionConfig( MAIL_USERNAME=os.getenv("MAIL_USERNAME") or "user", MAIL_PASSWORD=os.getenv("MAIL_PASSWORD") or "password", diff --git a/app/database/models.py b/app/database/models.py index 0c92ae94..47210783 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -14,8 +14,8 @@ class User(Base): full_name = Column(String) description = Column(String, default="Happy new user!") avatar = Column(String, default="profile.png") - is_active = Column(Boolean, default=True) + language_id = Column(Integer, ForeignKey("languages.id")) events = relationship( "Event", cascade="all, delete", back_populates="owner") @@ -32,3 +32,10 @@ class Event(Base): owner_id = Column(Integer, ForeignKey("users.id")) owner = relationship("User", back_populates="events") + + +class Language(Base): + __tablename__ = "languages" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, unique=True, nullable=False) diff --git a/app/dependencies.py b/app/dependencies.py index 79ae18c5..f1eee53b 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -4,10 +4,10 @@ from app import config - APP_PATH = os.path.dirname(os.path.realpath(__file__)) MEDIA_PATH = os.path.join(APP_PATH, config.MEDIA_DIRECTORY) STATIC_PATH = os.path.join(APP_PATH, "static") TEMPLATES_PATH = os.path.join(APP_PATH, "templates") templates = Jinja2Templates(directory=TEMPLATES_PATH) +templates.env.add_extension('jinja2.ext.i18n') diff --git a/app/internal/languages.py b/app/internal/languages.py new file mode 100644 index 00000000..ba55dbfb --- /dev/null +++ b/app/internal/languages.py @@ -0,0 +1,89 @@ +import gettext +import os +from pathlib import Path +from typing import Any, Generator + +from app import config +from app.dependencies import templates + +LANGUAGE_DIR = "app/locales" +LANGUAGE_DIR_TEST = "../app/locales" +TRANSLATION_FILE = "base" + + +def set_ui_language(language: str = None) -> 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. + + Args: + language (str, optional): a valid language code that follows RFC 1766. + Defaults to None. + See also the Language Code Identifier (LCID) Reference for a list of + valid language 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 # noqa: E501 + """ + + # TODO: Connect when user registration is completed. + # if not language: + # language = _get_display_language(user_id: int) + + language_dir = _get_language_directory() + + if language not in _get_supported_languages(language_dir): + language = config.WEBSITE_LANGUAGE + + 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. +# 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. +# +# if db_user: +# return db_user.language +# return config.WEBSITE_LANGUAGE + + +def _get_language_directory() -> str: + """Get and return the language directory relative path. + + Returns: + str: the language directory relative path. + """ + language_dir = LANGUAGE_DIR + if Path(LANGUAGE_DIR_TEST).is_dir(): + # If running from test, change dir path. + language_dir = LANGUAGE_DIR_TEST + return language_dir + + +def _get_supported_languages(language_dir: str = None) -> \ + Generator[str, Any, None]: + """Get and return a generator of supported translation languages codes. + + Args: + language_dir (str, optional): the path of the language directory. + Defaults to None. + + Returns: + Generator[str, Any, None]: a generator expression of supported + translation languages codes. + """ + + if not language_dir: + language_dir = _get_language_directory() + + return (language.name for language in + [Path(f.path) for f in os.scandir(language_dir) if f.is_dir()]) diff --git a/app/locales/base.pot b/app/locales/base.pot new file mode 100644 index 00000000..1ffbeab0 --- /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 21:30+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:39 +msgid "test python translation" +msgstr "" + diff --git a/app/locales/en/LC_MESSAGES/base.mo b/app/locales/en/LC_MESSAGES/base.mo new file mode 100644 index 00000000..203ac92e Binary files /dev/null and b/app/locales/en/LC_MESSAGES/base.mo differ diff --git a/app/locales/en/LC_MESSAGES/base.po b/app/locales/en/LC_MESSAGES/base.po new file mode 100644 index 00000000..0d75f1c5 --- /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 21:30+0200\n" +"PO-Revision-Date: 2021-01-26 21:31+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:39 +msgid "test python translation" +msgstr "" + diff --git a/app/locales/he/LC_MESSAGES/base.mo b/app/locales/he/LC_MESSAGES/base.mo new file mode 100644 index 00000000..7e5e4bcb Binary files /dev/null and b/app/locales/he/LC_MESSAGES/base.mo differ diff --git a/app/locales/he/LC_MESSAGES/base.po b/app/locales/he/LC_MESSAGES/base.po new file mode 100644 index 00000000..cf337316 --- /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 21:30+0200\n" +"PO-Revision-Date: 2021-01-26 21:31+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:39 +msgid "test python translation" +msgstr "בדיקת תרגום בפייתון" + diff --git a/app/main.py b/app/main.py index 07861586..91fe2390 100644 --- a/app/main.py +++ b/app/main.py @@ -3,10 +3,11 @@ from app.database import models from app.database.database import engine -from app.dependencies import ( - MEDIA_PATH, STATIC_PATH, templates) -from app.routers import agenda, event, profile, email +from app.dependencies import MEDIA_PATH, STATIC_PATH, templates +from app.internal.languages import set_ui_language +# This MUST come before the app.routers import. +set_ui_language() models.Base.metadata.create_all(bind=engine) @@ -14,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) @@ -24,6 +27,4 @@ async def home(request: Request): return templates.TemplateResponse("home.html", { "request": request, - "message": "Hello, World!" - }) diff --git a/app/routers/agenda.py b/app/routers/agenda.py index f8fd532b..076f9b34 100644 --- a/app/routers/agenda.py +++ b/app/routers/agenda.py @@ -10,7 +10,6 @@ from app.dependencies import templates from app.internal import agenda_events - router = APIRouter() @@ -18,7 +17,7 @@ def calc_dates_range_for_agenda( start: Optional[date], end: Optional[date], days: Optional[int] - ) -> Tuple[date, date]: +) -> Tuple[date, date]: """Create start and end dates eccording to the parameters in the page.""" if days is not None: start = date.today() @@ -36,27 +35,27 @@ def agenda( start_date: Optional[date] = None, end_date: Optional[date] = None, days: Optional[int] = None - ) -> Jinja2Templates: +) -> Jinja2Templates: """Route for the agenda page, using dates range or exact amount of days.""" - user_id = 1 # there is no user session yet, so I use user id- 1. + user_id = 1 # there is no user session yet, so I use user id- 1. start_date, end_date = calc_dates_range_for_agenda( start_date, end_date, days - ) + ) events_objects = agenda_events.get_events_per_dates( db, user_id, start_date, end_date - ) + ) events = defaultdict(list) for event_obj in events_objects: event_duration = agenda_events.get_time_delta_string( event_obj.start, event_obj.end - ) + ) events[event_obj.start.date()].append((event_obj, event_duration)) return templates.TemplateResponse("agenda.html", { "request": request, "events": events, "start_date": start_date, - "end_date": end_date + "end_date": end_date, }) diff --git a/app/routers/profile.py b/app/routers/profile.py index 39724939..c5d0ea6b 100644 --- a/app/routers/profile.py +++ b/app/routers/profile.py @@ -1,23 +1,22 @@ import io from fastapi import APIRouter, Depends, File, Request, UploadFile +from PIL import Image from starlette.responses import RedirectResponse from starlette.status import HTTP_302_FOUND -from PIL import Image from app import config from app.database.database import get_db from app.database.models import User from app.dependencies import MEDIA_PATH, templates - PICTURE_EXTENSION = config.PICTURE_EXTENSION PICTURE_SIZE = config.AVATAR_SIZE router = APIRouter( prefix="/profile", tags=["profile"], - responses={404: {"description": "Not found"}}, + responses={404: {"description": _("Not found")}}, # noqa F821 ) @@ -26,7 +25,8 @@ def get_placeholder_user(): username='new_user', email='my@email.po', password='1a2s3d4f5g6', - full_name='My Name' + full_name='My Name', + language_id=1 ) @@ -35,9 +35,8 @@ async def profile( request: Request, session=Depends(get_db), new_user=Depends(get_placeholder_user)): - # Get relevant data from database - upcouming_events = range(5) + upcoming_events = range(5) user = session.query(User).filter_by(id=1).first() if not user: session.add(new_user) @@ -49,14 +48,13 @@ async def profile( return templates.TemplateResponse("profile.html", { "request": request, "user": user, - "events": upcouming_events + "events": upcoming_events, }) @router.post("/update_user_fullname") async def update_user_fullname( request: Request, session=Depends(get_db)): - user = session.query(User).filter_by(id=1).first() data = await request.form() new_fullname = data['fullname'] @@ -75,7 +73,6 @@ async def update_user_fullname( @router.post("/update_user_email") async def update_user_email( request: Request, session=Depends(get_db)): - user = session.query(User).filter_by(id=1).first() data = await request.form() new_email = data['email'] @@ -93,7 +90,6 @@ async def update_user_email( @router.post("/update_user_description") async def update_profile( request: Request, session=Depends(get_db)): - user = session.query(User).filter_by(id=1).first() data = await request.form() new_description = data['description'] @@ -111,7 +107,6 @@ async def update_profile( @router.post("/upload_user_photo") async def upload_user_photo( file: UploadFile = File(...), session=Depends(get_db)): - user = session.query(User).filter_by(id=1).first() pic = await file.read() diff --git a/app/templates/agenda.html b/app/templates/agenda.html index 873b69e5..c0e25435 100644 --- a/app/templates/agenda.html +++ b/app/templates/agenda.html @@ -7,39 +7,39 @@ {% block content %}
-
-
-
-
-
- +
+
+
+
+
+
{% if start_date > end_date %} -

Start date is greater than end date

+

{{ gettext("Start date is greater than end date") }}

{% elif events | length == 0 %} -

No events found...

+

{{ gettext("No events found...") }}

{% elif start_date == end_date %}

{{ start_date.strftime("%d/%m/%Y") }}

{% else %} -

{{ start_date.strftime("%d/%m/%Y") }} - {{end_date.strftime("%d/%m/%Y") }}

+

{{ start_date.strftime("%d/%m/%Y") }} - {{end_date.strftime("%d/%m/%Y") }}

{% endif %}
- +
{% for events_date, events_list in events.items() %}
{{ events_date.strftime("%d/%m/%Y") }}
@@ -49,4 +49,4 @@

{{ start_date.strftime("%d/%m/%Y") }} - {{end_date.strftime("%d/%m/%Y") }} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index ddc549db..50d89c94 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -15,7 +15,7 @@

@@ -115,7 +115,7 @@
@@ -141,7 +141,7 @@
{{ user.full_name }}

- Settings + {{ gettext("Settings") }}


@@ -158,25 +158,25 @@
{{ user.full_name }}
- + - +
@@ -189,7 +189,7 @@
{{ user.full_name }}
- Upcoming Event On December 01, 2021 + {{ gettext("Upcoming event on (date)", date=event.start) }} @@ -200,19 +200,19 @@
{{ user.full_name }}

- The Event {{ event }} - description ... + {{ gettext("The Event (event)", event=event.title) }}


- Last updated 3 mins ago + {{ gettext("Last updated (time) ago") }}
{% endfor %}
- + - +
@@ -220,7 +220,7 @@
{{ user.full_name }}

- Explore MeetUps near you + {{ gettext("Explore MeetUps near you") }}

@@ -229,7 +229,7 @@
{{ user.full_name }}

- Your Card + {{ gettext("Your Card") }}

@@ -238,7 +238,7 @@
{{ user.full_name }}

- Your Card + {{ gettext("Your Card") }}

@@ -246,4 +246,4 @@
{{ user.full_name }}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index bc7cc2d6..25d32fa9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,36 @@ aiofiles==0.6.0 +aioredis==1.3.1 +aiosmtpd==1.2.2 +aiosmtplib==1.1.4 arrow==0.17.0 +async-timeout==3.0.1 atomicwrites==1.4.0 +atpublic==2.1.2 attrs==20.3.0 +blinker==1.4 certifi==2020.12.5 chardet==4.0.0 click==7.1.2 colorama==0.4.4 coverage==5.3.1 +dnspython==2.1.0 +email-validator==1.1.2 +Faker==5.6.2 +fakeredis==1.4.5 fastapi==0.63.0 -fastapi_mail==0.3.3.1 -faker==5.6.2 -smtpdfix==0.2.6 +fastapi-mail==0.3.3.1 h11==0.12.0 h2==4.0.0 +hiredis==1.1.0 hpack==4.0.0 +httpcore==0.12.2 +httpx==0.16.1 hyperframe==6.0.0 idna==2.10 importlib-metadata==3.3.0 iniconfig==1.1.1 Jinja2==2.11.2 +lazy-object-proxy==1.5.2 MarkupSafe==1.1.1 packaging==20.8 Pillow==8.1.0 @@ -33,10 +45,16 @@ python-dateutil==2.8.1 python-dotenv==0.15.0 python-multipart==0.0.5 PyYAML==5.3.1 +redis==3.5.3 requests==2.25.1 +rfc3986==1.4.0 six==1.15.0 +smtpdfix==0.2.6 +sniffio==1.2.0 +sortedcontainers==2.3.0 SQLAlchemy==1.3.22 starlette==0.13.6 +text-unidecode==1.3 toml==0.10.2 typing-extensions==3.7.4.3 urllib3==1.26.2 diff --git a/tests/conftest.py b/tests/conftest.py index 0631b61d..442a76b5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -67,7 +67,8 @@ def get_test_placeholder_user(): username='fake_user', email='fake@mail.fake', password='123456fake', - full_name='FakeName' + full_name='FakeName', + language_id=1 ) diff --git a/tests/test_app.py b/tests/test_app.py index e69de29b..0b20a980 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -0,0 +1,3 @@ +def test_home(client): + response = client.get("/") + assert response.ok diff --git a/tests/test_language.py b/tests/test_language.py new file mode 100644 index 00000000..6f42c420 --- /dev/null +++ b/tests/test_language.py @@ -0,0 +1,56 @@ +from pathlib import Path + +import pytest + +from app.internal import languages + + +class TestLanguage: + # Empty, invalid, or valid, but unsupported non-'he' language codes, + # are set to the default language setting at config.WEBSITE_LANGUAGE, + # which is currently set to 'en' (English). + LANGUAGE_TESTS = [ + ('en', 'test python translation', 'Profile', True), + ('he', 'בדיקת תרגום בפייתון', 'פרופיל', True), + (None, 'test python translation', 'Profile', False), + ('', 'test python translation', 'Profile', False), + ('de', 'test python translation', 'Profile', False), + (["en"], 'test python translation', 'Profile', False), + (3, 'test python translation', 'Profile', False), + ] + + NUMBER_OF_LANGUAGES = 2 + + @staticmethod + @pytest.mark.parametrize("language_code, translation, __, is_valid", + LANGUAGE_TESTS) + def test_gettext_python(client, language_code, translation, __, is_valid): + languages.set_ui_language(language_code) + gettext_translation = _("test python translation") # noqa F821 + assert ((is_valid and gettext_translation == translation) + or (not is_valid and gettext_translation == translation)) + + @staticmethod + @pytest.mark.parametrize("language_code, __, translation, is_valid", + LANGUAGE_TESTS) + def test_gettext_html(client, language_code, __, translation, is_valid): + languages.set_ui_language(language_code) + text = client.get("/").text + assert ((is_valid and translation in text) + or (not is_valid and translation in text)) + + @staticmethod + def test_get_supported_languages(): + number_of_languages = len(list(languages._get_supported_languages())) + assert number_of_languages == TestLanguage.NUMBER_OF_LANGUAGES + + @staticmethod + def test_get_language_directory(): + pytest.MonkeyPatch().setattr(Path, 'is_dir', lambda x: True) + assert languages._get_language_directory() + + @staticmethod + def test_get_display_language(): + # TODO: Waiting for user registration. + # Test: no user, user not logged in and user with non-english set. + pass