diff --git a/examples/fastapi-eager/_gt/es.json b/examples/fastapi-eager/_gt/es.json new file mode 100644 index 0000000..84a3870 --- /dev/null +++ b/examples/fastapi-eager/_gt/es.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Hola, mundo!", + "9b323e35e1a80c51": "Hola, {name}!" +} diff --git a/examples/fastapi-eager/_gt/fr.json b/examples/fastapi-eager/_gt/fr.json new file mode 100644 index 0000000..fba7e6d --- /dev/null +++ b/examples/fastapi-eager/_gt/fr.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Bonjour, le monde!", + "9b323e35e1a80c51": "Bonjour, {name}!" +} diff --git a/examples/fastapi-eager/app.py b/examples/fastapi-eager/app.py index 8a1fae5..cacef98 100644 --- a/examples/fastapi-eager/app.py +++ b/examples/fastapi-eager/app.py @@ -1,42 +1,30 @@ """FastAPI example with eager translation loading. -Translations are loaded at startup for all configured locales. +Translations are loaded from _gt/.json at startup. +Configuration is read from gt.config.json. Run: uv run uvicorn app:app --port 8000 """ +import json +from pathlib import Path + from fastapi import FastAPI from gt_fastapi import initialize_gt, t app = FastAPI(title="FastAPI Eager Example") -# Pre-built translation dictionaries keyed by hash. -# hash_message("Hello, world!") -> "8042e0a3d395c1fb" -# hash_message("Hello, {name}!") -> "9b323e35e1a80c51" -TRANSLATIONS: dict[str, dict[str, str]] = { - "es": { - "8042e0a3d395c1fb": "Hola, mundo!", - "9b323e35e1a80c51": "Hola, {name}!", - }, - "fr": { - "8042e0a3d395c1fb": "Bonjour, le monde!", - "9b323e35e1a80c51": "Bonjour, {name}!", - }, -} +GT_DIR = Path(__file__).parent / "_gt" def load_translations(locale: str) -> dict[str, str]: - """Return translations for a locale from the in-memory dictionary.""" - print(f"[eager] Loading translations for '{locale}'") - return TRANSLATIONS.get(locale, {}) - - -initialize_gt( - app, - default_locale="en", - locales=["en", "es", "fr"], - load_translations=load_translations, - eager_loading=True, -) + path = GT_DIR / f"{locale}.json" + if path.exists(): + with open(path) as f: + return json.load(f) + return {} + + +initialize_gt(app, load_translations=load_translations) @app.get("/") diff --git a/examples/fastapi-eager/gt.config.json b/examples/fastapi-eager/gt.config.json new file mode 100644 index 0000000..9940de2 --- /dev/null +++ b/examples/fastapi-eager/gt.config.json @@ -0,0 +1,4 @@ +{ + "defaultLocale": "en", + "locales": ["es", "fr"] +} diff --git a/examples/fastapi-lazy/_gt/es.json b/examples/fastapi-lazy/_gt/es.json new file mode 100644 index 0000000..84a3870 --- /dev/null +++ b/examples/fastapi-lazy/_gt/es.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Hola, mundo!", + "9b323e35e1a80c51": "Hola, {name}!" +} diff --git a/examples/fastapi-lazy/_gt/fr.json b/examples/fastapi-lazy/_gt/fr.json new file mode 100644 index 0000000..fba7e6d --- /dev/null +++ b/examples/fastapi-lazy/_gt/fr.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Bonjour, le monde!", + "9b323e35e1a80c51": "Bonjour, {name}!" +} diff --git a/examples/fastapi-lazy/app.py b/examples/fastapi-lazy/app.py index 64d91c3..2fc013d 100644 --- a/examples/fastapi-lazy/app.py +++ b/examples/fastapi-lazy/app.py @@ -1,54 +1,40 @@ """FastAPI example with lazy translation loading. -Translations are loaded on first request per locale, not at startup. +Translations are loaded from _gt/.json on first request per locale. +Configuration is read from gt.config.json. Run: uv run uvicorn app:app --port 8001 """ +import json +from pathlib import Path + from fastapi import Depends, FastAPI, Request from gt_fastapi import initialize_gt, t app = FastAPI(title="FastAPI Lazy Example") -TRANSLATIONS: dict[str, dict[str, str]] = { - "es": { - "8042e0a3d395c1fb": "Hola, mundo!", - "9b323e35e1a80c51": "Hola, {name}!", - }, - "fr": { - "8042e0a3d395c1fb": "Bonjour, le monde!", - "9b323e35e1a80c51": "Bonjour, {name}!", - }, -} +GT_DIR = Path(__file__).parent / "_gt" async def load_translations(locale: str) -> dict[str, str]: - """Simulate loading translations from a remote source.""" - print(f"[lazy] Loading translations for '{locale}'") - return TRANSLATIONS.get(locale, {}) - + path = GT_DIR / f"{locale}.json" + if path.exists(): + with open(path) as f: + return json.load(f) + return {} -manager = initialize_gt( - app, - default_locale="en", - locales=["en", "es", "fr"], - load_translations=load_translations, - eager_loading=False, -) +manager = initialize_gt(app, load_translations=load_translations, eager_loading=False) -async def _ensure_translations(request: Request) -> None: - """Load translations for the current locale if not already cached. - t() only reads from cache (get_translations_sync), so we must - explicitly trigger a load for the current locale before t() runs. - """ +async def ensure_translations(request: Request) -> None: + """Load translations for the request locale before t() runs.""" locale = manager.get_locale() if manager.requires_translation(locale): await manager.get_translations(locale) -# Add dependency to all routes — runs inside route context after middleware -app.router.dependencies = [Depends(_ensure_translations)] +app.router.dependencies = [Depends(ensure_translations)] @app.get("/") diff --git a/examples/fastapi-lazy/gt.config.json b/examples/fastapi-lazy/gt.config.json new file mode 100644 index 0000000..9940de2 --- /dev/null +++ b/examples/fastapi-lazy/gt.config.json @@ -0,0 +1,4 @@ +{ + "defaultLocale": "en", + "locales": ["es", "fr"] +} diff --git a/examples/flask-eager/_gt/es.json b/examples/flask-eager/_gt/es.json new file mode 100644 index 0000000..84a3870 --- /dev/null +++ b/examples/flask-eager/_gt/es.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Hola, mundo!", + "9b323e35e1a80c51": "Hola, {name}!" +} diff --git a/examples/flask-eager/_gt/fr.json b/examples/flask-eager/_gt/fr.json new file mode 100644 index 0000000..fba7e6d --- /dev/null +++ b/examples/flask-eager/_gt/fr.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Bonjour, le monde!", + "9b323e35e1a80c51": "Bonjour, {name}!" +} diff --git a/examples/flask-eager/app.py b/examples/flask-eager/app.py index bfe8948..ac61549 100644 --- a/examples/flask-eager/app.py +++ b/examples/flask-eager/app.py @@ -1,39 +1,30 @@ """Flask example with eager translation loading. -Translations are loaded at startup for all configured locales. +Translations are loaded from _gt/.json at startup. +Configuration is read from gt.config.json. Run: uv run python app.py (serves on port 5050) """ -from flask import Flask +import json +from pathlib import Path + +from flask import Flask, request from gt_flask import initialize_gt, t app = Flask(__name__) -TRANSLATIONS: dict[str, dict[str, str]] = { - "es": { - "8042e0a3d395c1fb": "Hola, mundo!", - "9b323e35e1a80c51": "Hola, {name}!", - }, - "fr": { - "8042e0a3d395c1fb": "Bonjour, le monde!", - "9b323e35e1a80c51": "Bonjour, {name}!", - }, -} +GT_DIR = Path(__file__).parent / "_gt" def load_translations(locale: str) -> dict[str, str]: - """Return translations for a locale from the in-memory dictionary.""" - print(f"[eager] Loading translations for '{locale}'") - return TRANSLATIONS.get(locale, {}) + path = GT_DIR / f"{locale}.json" + if path.exists(): + with open(path) as f: + return json.load(f) + return {} -initialize_gt( - app, - default_locale="en", - locales=["en", "es", "fr"], - load_translations=load_translations, - eager_loading=True, -) +initialize_gt(app, load_translations=load_translations) @app.get("/") @@ -43,8 +34,6 @@ def index() -> dict[str, str]: @app.get("/greet") def greet() -> dict[str, str]: - from flask import request - name = request.args.get("name", "World") return {"message": t("Hello, {name}!", name=name)} diff --git a/examples/flask-eager/gt.config.json b/examples/flask-eager/gt.config.json new file mode 100644 index 0000000..9940de2 --- /dev/null +++ b/examples/flask-eager/gt.config.json @@ -0,0 +1,4 @@ +{ + "defaultLocale": "en", + "locales": ["es", "fr"] +} diff --git a/examples/flask-lazy/_gt/es.json b/examples/flask-lazy/_gt/es.json new file mode 100644 index 0000000..84a3870 --- /dev/null +++ b/examples/flask-lazy/_gt/es.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Hola, mundo!", + "9b323e35e1a80c51": "Hola, {name}!" +} diff --git a/examples/flask-lazy/_gt/fr.json b/examples/flask-lazy/_gt/fr.json new file mode 100644 index 0000000..fba7e6d --- /dev/null +++ b/examples/flask-lazy/_gt/fr.json @@ -0,0 +1,4 @@ +{ + "8042e0a3d395c1fb": "Bonjour, le monde!", + "9b323e35e1a80c51": "Bonjour, {name}!" +} diff --git a/examples/flask-lazy/app.py b/examples/flask-lazy/app.py index 4f87f9d..e80beb2 100644 --- a/examples/flask-lazy/app.py +++ b/examples/flask-lazy/app.py @@ -1,51 +1,36 @@ """Flask example with lazy translation loading. -Translations are loaded on first request per locale, not at startup. +Translations are loaded from _gt/.json on first request per locale. +Configuration is read from gt.config.json. Run: uv run python app.py (serves on port 5051) """ import asyncio +import json +from pathlib import Path -from flask import Flask +from flask import Flask, request from gt_flask import initialize_gt, t app = Flask(__name__) -TRANSLATIONS: dict[str, dict[str, str]] = { - "es": { - "8042e0a3d395c1fb": "Hola, mundo!", - "9b323e35e1a80c51": "Hola, {name}!", - }, - "fr": { - "8042e0a3d395c1fb": "Bonjour, le monde!", - "9b323e35e1a80c51": "Bonjour, {name}!", - }, -} +GT_DIR = Path(__file__).parent / "_gt" def load_translations(locale: str) -> dict[str, str]: - """Simulate loading translations from a remote source.""" - print(f"[lazy] Loading translations for '{locale}'") - return TRANSLATIONS.get(locale, {}) + path = GT_DIR / f"{locale}.json" + if path.exists(): + with open(path) as f: + return json.load(f) + return {} -manager = initialize_gt( - app, - default_locale="en", - locales=["en", "es", "fr"], - load_translations=load_translations, - eager_loading=False, -) +manager = initialize_gt(app, load_translations=load_translations, eager_loading=False) @app.before_request -def _ensure_translations() -> None: - """Load translations for the current locale if not already cached. - - Registered after initialize_gt(), so this runs after the locale is set. - t() only reads from cache (get_translations_sync), so we must - explicitly trigger a load for the current locale before t() runs. - """ +def ensure_translations() -> None: + """Load translations for the request locale before t() runs.""" locale = manager.get_locale() if manager.requires_translation(locale): asyncio.run(manager.get_translations(locale)) @@ -58,8 +43,6 @@ def index() -> dict[str, str]: @app.get("/greet") def greet() -> dict[str, str]: - from flask import request - name = request.args.get("name", "World") return {"message": t("Hello, {name}!", name=name)} diff --git a/examples/flask-lazy/gt.config.json b/examples/flask-lazy/gt.config.json new file mode 100644 index 0000000..9940de2 --- /dev/null +++ b/examples/flask-lazy/gt.config.json @@ -0,0 +1,4 @@ +{ + "defaultLocale": "en", + "locales": ["es", "fr"] +} diff --git a/packages/gt-flask/src/gt_flask/_setup.py b/packages/gt-flask/src/gt_flask/_setup.py index 004ee73..f82570c 100644 --- a/packages/gt-flask/src/gt_flask/_setup.py +++ b/packages/gt-flask/src/gt_flask/_setup.py @@ -71,7 +71,7 @@ def initialize_gt( ) set_i18n_manager(manager) - if eager_loading and locales: + if eager_loading and resolved_locales: asyncio.run(manager.load_all_translations()) @app.before_request