diff --git a/Dockerfile b/Dockerfile index 39e6b16..6c6f195 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,8 @@ WORKDIR /app COPY uv.lock pyproject.toml ./ COPY app ./app/ +RUN rm -rf ./app/lang +COPY resources/lang ./app/lang RUN uv pip install . --system diff --git a/Makefile b/Makefile index 12ae82a..c689782 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,8 @@ ifeq (,$(shell which podman)) endif DOCKER ?= podman +DOCKER_TAG ?= tout-doux +PORT ?= 5000 APP_EXEC ?= $(DOCKER) compose exec app APP_RUN ?= $(DOCKER) compose run --rm app @@ -147,6 +149,14 @@ docker-compose-down-%: ## docker compose down (container) docker-compose-logs: ## docker compose logs $(DOCKER) compose logs -f || true +.PHONY: docker-build +docker-build: ## docker build + $(DOCKER) build . -t $(DOCKER_TAG) + +.PHONY: docker-run +docker-run: ## docker run + $(DOCKER) run -p $(PORT):5000 $(DOCKER_TAG) + .PHONY: tortoise-% tortoise-%: .venv ## tortoise (command) $(TORTOISE) --config-file tortoise-dev.json $* diff --git a/README.md b/README.md index 232f7fb..611481f 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,6 @@ - [x] lang in cookies - [x] Dockerfile - [ ] html cards +- [ ] custom background - [ ] favicon - [ ] README diff --git a/app/lang b/app/lang new file mode 120000 index 0000000..438c878 --- /dev/null +++ b/app/lang @@ -0,0 +1 @@ +../resources/lang \ No newline at end of file diff --git a/app/lang.py b/app/lang.py new file mode 100644 index 0000000..15f2e8e --- /dev/null +++ b/app/lang.py @@ -0,0 +1,35 @@ +from collections.abc import MutableMapping +from json import load +from pathlib import Path + +LANG_DIR = Path(__file__).parent / "lang" +FALLBACK_LANG = "en" +LANG_CACHE: dict[str, dict[str, str]] = {} + + +# https://stackoverflow.com/questions/6027558/flatten-nested-dictionaries-compressing-keys +def __flatten(dictionary: dict, parent_key: str = "") -> dict[str, str]: + items = [] + for key, value in dictionary.items(): + new_key = parent_key + "." + key if parent_key else key + if isinstance(value, MutableMapping): + items.extend(__flatten(value, new_key).items()) + else: + items.append((new_key, value)) + return dict(items) + + +def __get_lang_data(lang: str) -> dict[str, str]: + if lang not in LANG_CACHE: + file = LANG_DIR / f"{lang}.json" + if not file.exists(): + return __get_lang_data(FALLBACK_LANG) + with file.open() as f: + raw_data: dict = load(f) + LANG_CACHE[lang] = __flatten(raw_data) + return LANG_CACHE[lang] + + +def get_lang(lang: str, key: str) -> str: + data = __get_lang_data(lang) + return data.get(key, key) diff --git a/app/params.py b/app/params.py index 4445f40..1de1667 100644 --- a/app/params.py +++ b/app/params.py @@ -13,6 +13,7 @@ class Parameters: env: str = "production" bind: str = "0.0.0.0" port: int = 5000 + app_url: str = "http://localhost:5000" workers: int = 4 timeout: int = 120 db_uri: str = "sqlite://db.sqlite" @@ -87,6 +88,13 @@ def parse_parameters(args: typing.Sequence[str]) -> Parameters: default=default_values.env, help_txt="environnement", ) + __add_arg_str( + parser, + "--app-url", + env_var="APP_URL", + default=default_values.app_url, + help_txt="app external url", + ) __add_arg_str( parser, "--bind", diff --git a/app/server.py b/app/server.py index 9f1f669..d26d16e 100644 --- a/app/server.py +++ b/app/server.py @@ -8,6 +8,7 @@ import gunicorn.app.base import tortoise from app import PKG_NAME +from app.lang import get_lang from app.models import Task if typing.TYPE_CHECKING: @@ -22,6 +23,7 @@ class Server(gunicorn.app.base.BaseApplication): self.debug = params.debug self.workers = params.workers self.timeout = params.timeout + self.app_url = params.app_url self.app = flask.Flask( PKG_NAME, static_folder="dist/assets", template_folder="dist" ) @@ -36,11 +38,23 @@ class Server(gunicorn.app.base.BaseApplication): super().__init__() def register_routes(self) -> typing.Self: - @self.app.route("/", defaults={"_": ""}) - @self.app.route("/<_>") + @self.app.route("/", defaults={"path": ""}) + @self.app.route("/") @self.limiter.exempt - def index(_: str) -> str: - return flask.render_template("index.html") + def index(path: str) -> str: + locale = "en" + if "lang" in flask.request.cookies: + locale = flask.request.cookies["lang"] + if "lang" in flask.request.args: + locale = flask.request.args["lang"] + return flask.render_template( + "index.html", + title=("Tout Doux" if not len(path) else f"Tout Doux - {path}"), + app_url=self.app_url, + path=path, + locale=locale, + lang=lambda key: get_lang(locale, key), + ) @self.app.route("/api/lists//tasks", methods=["GET"]) async def get_tasks(list_name: str) -> list: diff --git a/index.html b/index.html index 5bc66e1..ecbc483 100644 --- a/index.html +++ b/index.html @@ -1,24 +1,22 @@ - + - Tout Doux + {{ title }} - + + + + - + data-website-id="e2536336-73f8-4de1-87bf-d95ae19d85c9" + >
diff --git a/resources/lang/en.json b/resources/lang/en.json index f8d509f..6085750 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -74,7 +74,8 @@ }, "not_found": "Looks like there's nothing here...", "not_found_button": "Go back to familiar territory", - "home": "A shareable task list with auto resetting items.
Create your list below.", + "home": "A shareable task list with auto resetting items.", + "call_to_action": "Create your list below:", "home_button": "Let's go !", "share_button": "Share my list", "share_text": "Check my list on Tout Doux!" diff --git a/resources/lang/fr.json b/resources/lang/fr.json index 7a76806..a7d62c7 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -74,7 +74,8 @@ }, "not_found": "On dirait qu'il n'y a rien ici...", "not_found_button": "Retour en terrain connu", - "home": "Une liste de tâches avec réinitialisation automatique des éléments.
Créez ta liste ci-dessous.", + "home": "Une liste de tâches avec réinitialisation automatique des éléments.", + "call_to_action": "Créez ta liste ci-dessous :", "home_button": "C'est parti !", "share_button": "Partager ma liste", "share_text": "Regarde ma liste sur Tout Doux!" diff --git a/resources/ts/views/HomeView.vue b/resources/ts/views/HomeView.vue index ddf2b61..bccefb8 100644 --- a/resources/ts/views/HomeView.vue +++ b/resources/ts/views/HomeView.vue @@ -30,8 +30,7 @@ onBeforeRouteUpdate(updateTitle);
+ >{{ $t('home') }}
{{ $t('call_to_action') }}