Skip to content

fix(gt-flask,gt-fastapi): load translations for every configured locale - #37

Open
Kevin-Liu-01 wants to merge 3 commits into
k/gt-i18n-cache-expiryfrom
k/flask-fastapi-configured-locales
Open

Kevin-Liu-01 wants to merge 3 commits into
k/gt-i18n-cache-expiryfrom
k/flask-fastapi-configured-locales

Conversation

@Kevin-Liu-01

@Kevin-Liu-01 Kevin-Liu-01 commented Oct 12, 2026 •

Copy link
Copy Markdown

Summary

This PR is stacked on fix(gt-i18n): keep translations loaded after the cache expires (#36 (k/gt-i18n-cache-expiry)). Open it against that branch, or against main once that PR merges.

gt-flask's initialize_gt eager-loaded only the locales argument (if eager_loading and locales:). An app whose locales come from gt.config.json, like the quickstart's, never loaded any translations. Neither integration loaded a locale on its first request, so eager_loading=False needed a hand-written hook, and both example apps had one. After startup, nothing called the loader again.

Changes in gt-flask and gt-fastapi:

  • Flask eager-loads the locales resolved from the argument or gt.config.json, through load_all_translations_blocking(), which also works when an event loop is already running.
  • Flask's before_request hook and FastAPI's HTTP middleware load the request's locale before the route runs, when that locale is configured and needs translation. After expiry the call returns the cached translations and starts one background reload.
  • A locale outside the configuration is never loaded. When only es is configured and a custom get_locale returns es-MX, nothing loads.
  • "cacheUrl": null (or "") in gt.config.json turns CDN loading off. A cache_url= argument still takes precedence.
  • The _branchId from gt.config.json goes to the CDN loader, unless version_id= is passed explicitly, because the file's branch belongs to the file's version.
  • The flask-lazy and fastapi-lazy examples drop their hand-written loading hooks.
  • gt-flask and gt-fastapi require gt-i18n>=0.3.1 and generaltranslation>=0.1.0.

Size: 11 files, +353/-60 on top of the gt-i18n PR. Product code is +26/-5, tests are +312/-18, example apps are +5/-33, pyproject.toml files are +4/-4 and the changeset is +6.

Decisions

  • The gt-i18n floor is 0.3.1. Once the release workflow can publish 0.3.0, a >=0.3.0 floor would accept a gt-i18n with the 60 s bug.
  • uv still resolves the workspace's gt-i18n, which stays at 0.3.0 until the release PR, against the new floor. uv lock --check passes and uv.lock does not change.
  • A reviewer checked on the combined fix that an upgrade from the 0.1.0 set on PyPI pulls compatible versions.

Overlap with #20

#20 (open since 2026-03-08, last updated 2026-03-20) moves the four example apps to gt.config.json and _gt/<locale>.json, and it makes the same one-line change in gt_flask/_setup.py (if eager_loading and resolved_locales:). This PR also fixes gt-fastapi and adds tests for both packages. The two PRs conflict in gt_flask/_setup.py and the two lazy example apps. Whichever merges second keeps this PR's _setup.py, which contains #20's line, and merges the example apps by hand.

Test plan

After review, on b98c0b3, which merges #36's review fix (3b065a2) in 2ac6e44 (origin/main is still c3fe22b on 2026-10-17), with Python 3.12.13:

  • uv lock --check, uv sync --all-packages --locked, ruff check ., ruff format --check . and mypy .: clean.
  • pytest: 1,443 passed. The new tests are now 17 cases: 9 in test_flask_integration.py and 8 in test_fastapi_integration.py. Both files run the same five CDN settings cases, which replace Flask's two single-case tests.
  • With the two _setup.py files reverted to fix(gt-i18n): keep translations loaded after the cache expires #36's versions, 12 of the 17 cases fail, and so do the 8 example-app tests. Five pass on both. Two are the Flask cases that turn the CDN off, since the old Flask code never loaded those locales. Three are FastAPI cases the old code already handled: an empty cacheUrl, an explicit cache_url, and an explicit version, for which it sent no branch.

Before review, on this branch (k/flask-fastapi-configured-locales at 29580f4, on 0332c94, which is on origin/main c3fe22b, still the latest main on 2026-10-11), with Python 3.12.13:

  • uv lock --check and uv sync --all-packages --locked: the lockfile does not change
  • ruff check ., ruff format --check . and mypy .: clean
  • pytest: 1,429 passed. That includes 9 new tests: 6 in test_flask_integration.py and 3 in test_fastapi_integration.py.
  • With the two _setup.py files reverted to the gt-i18n PR's versions, 8 of the 9 new tests fail, and so do the 8 example-app tests, whose hooks this PR removes. The "cacheUrl": null test passes on both, because the old code never loaded that locale at all.

Earlier, on the combined SDK fix: the quickstart's app, run exactly as printed against a stand-in CDN, answered in Spanish, while the published 0.1.0 answered in English. Two requests 65 s apart on real wall-clock time were both answered in Spanish.

Merge order

  1. fix(release): publish every package version missing from PyPI (fix(release): publish every package version missing from PyPI #35 (k/publish-missing-versions)), which must merge before the release PR.
  2. fix(gt-i18n): keep translations loaded after the cache expires (fix(gt-i18n): keep translations loaded after the cache expires #36 (k/gt-i18n-cache-expiry)), then this PR.
  3. sampo's release PR, chore(release): bump versions and changelogs. It bumps gt-i18n, gt-flask and gt-fastapi to 0.3.1 and publishes them with the catch-up versions generaltranslation 0.1.0, generaltranslation-intl-messageformat 0.0.5 and generaltranslation-supported-locales 0.0.6.
  4. The docs PR (generaltranslation/content, docs(python): match the quickstart to the fixed SDK content#622 (k/python-quickstart-loads-translations)), after 0.3.1 is on PyPI.

Ernest McCarter wrote most of gt-python, so he should review this PR.

🤖 Generated with Claude Code

RetriggerConfidence Score: 4/5 Tier: plus

The PR appears safe to merge, with non-blocking improvements to configuration tests and the reload comment.

Fix All in Claude CodeFindings

  1. P2 CDN config paths lack tests ▶
  2. P2 Reload comment names wrong thread ▶
Fix with agent prompt
### Issue 1
packages/gt-fastapi/src/gt_fastapi/_setup.py:58-62
FastAPI's new CDN configuration rules have no integration tests. The Flask tests cannot catch mistakes in FastAPI's separate `cache_url` resolution or `branch_id` forwarding, so the two integrations could drift without a test failing.

Add FastAPI cases for null cache settings and file-provided version/branch values. Also test explicit `cache_url` and `version_id` overrides in both integrations, including that an explicit version drops the file's branch.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

### Issue 2
packages/gt-fastapi/tests/test_fastapi_integration.py:137
For expired entries, `get_translations()` starts a daemon thread, not work on the app's event loop. This comment could mislead a maintainer into reusing resources tied to that loop. Change it to say “background thread.”

```suggestion
        # The reload runs on a background thread.
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Both integrations now load configured request locales before routes run. Flask also eager-loads locales from gt.config.json.

  • Flask and FastAPI load configured translations before routes run.
  • CDN requests follow the cache and branch settings in config.

Author acknowledgments:

  • Kevin-Liu-01 intentionally excludes es-MX when only es is configured and intentionally drops the file's branch when version_id is explicit.
  • Kevin-Liu-01 acknowledges the workspace's gt-i18n version remains 0.3.0; the stated release order supplies the required 0.3.1 version and publishing prerequisites.
  • Kevin-Liu-01 notes the null-cache test also passes on the base revision and acknowledges conflicts with PR chore: use gt.config.json and _gt/<locale>.json for example translations #20 that require a manual merge.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Read arguments and config] --> B[Create translation manager]
    B --> C{Eager loading enabled?}
    C -->|Yes| D[Load configured locales]
    C -->|No| E[Wait for requests]
    D --> F[Select request locale]
    E --> F
    F --> G{Configured locale needs translation?}
    G -->|No| K[Run route]
    G -->|Yes| H{Cache state}
    H -->|Missing| I[Wait for first load]
    H -->|Fresh| K
    H -->|Expired| J[Return cached text and start background reload]
    I --> K
    J --> K
Loading

Reviews (1) · Last reviewed commit: "fix(gt-flask,gt-fastapi): load translati..." · Reviewed by Greptile

Flask eager-loaded only the locales passed to initialize_gt, so an app
that lists its locales in gt.config.json, as the quickstart does, never
loaded any translations. Neither integration loaded a locale on its
first request, and nothing reloaded expired translations.

Flask now eager-loads the resolved locales, also inside a running event
loop. Both integrations load each configured locale before its first
request, which also starts the background reload once it expires.
Locales outside the configuration are never loaded. "cacheUrl": null in
gt.config.json turns CDN loading off, and the file's _branchId goes to
the CDN loader unless version_id is passed explicitly. The example apps
drop their hand-written loading hooks.

The floors move to gt-i18n>=0.3.1, the release with the cache fix, and
generaltranslation>=0.1.0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Comment on lines +58 to +62
if cache_url is not None:
resolved_cache_url: str | None = cache_url
elif "cache_url" in file_config:
# "cacheUrl": null (or "") in gt.config.json turns CDN loading off.
resolved_cache_url = file_config.get("cache_url") or ""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 CDN config paths lack tests

FastAPI's new CDN configuration rules have no integration tests. The Flask tests cannot catch mistakes in FastAPI's separate cache_url resolution or branch_id forwarding, so the two integrations could drift without a test failing.

Add FastAPI cases for null cache settings and file-provided version/branch values. Also test explicit cache_url and version_id overrides in both integrations, including that an explicit version drops the file's branch.

Knowledge Base Used: Web framework integrations

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/gt-fastapi/src/gt_fastapi/_setup.py
Line: 58-62

Comment:
**CDN config paths lack tests**

FastAPI's new CDN configuration rules have no integration tests. The Flask tests cannot catch mistakes in FastAPI's separate `cache_url` resolution or `branch_id` forwarding, so the two integrations could drift without a test failing.

Add FastAPI cases for null cache settings and file-provided version/branch values. Also test explicit `cache_url` and `version_id` overrides in both integrations, including that an explicit version drops the file's branch.

**Knowledge Base Used:** [Web framework integrations](https://app.greptile.com/generaltranslation/-/custom-context/knowledge-base/generaltranslation/gt-python/-/docs/web-framework-integrations.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b98c0b3. Both integration test files now run the same table of five cases: the file's version and branch reach the CDN, an explicit version_id drops the file's branch, a null or empty cacheUrl turns CDN loading off, and an explicit cache_url replaces the file's. Reverting FastAPI's null cache_url handling or its branch_id rule makes the matching case fail.

now[0] += 120 # two minutes later, past the 60 s cache expiry
later = client.get("/hello", headers={"Accept-Language": "es"})

# The reload runs in the background on the app's event loop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Reload comment names wrong thread

For expired entries, get_translations() starts a daemon thread, not work on the app's event loop. This comment could mislead a maintainer into reusing resources tied to that loop. Change it to say “background thread.”

Suggested change
# The reload runs in the background on the app's event loop.
# The reload runs on a background thread.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/gt-fastapi/tests/test_fastapi_integration.py
Line: 137

Comment:
**Reload comment names wrong thread**

For expired entries, `get_translations()` starts a daemon thread, not work on the app's event loop. This comment could mislead a maintainer into reusing resources tied to that loop. Change it to say “background thread.”

```suggestion
        # The reload runs on a background thread.
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b98c0b3 with the suggested wording. This test's loader is synchronous, so its reload runs on a background thread. Since 3b065a2 (merged here in 2ac6e44), only an async loader reloads on the app's event loop.

Kevin-Liu-01 and others added 2 commits October 17, 2026 00:14
FastAPI had no tests for its cache_url and branch_id handling, which it
implements apart from Flask's, so the two could drift without a test
failing. Both integration test files now run one table of cases: the
file's version and branch reach the CDN, an explicit version_id drops
the file's branch, a null or empty cacheUrl turns CDN loading off, and
an explicit cache_url replaces the file's. The table replaces Flask's
two single-case tests.

The FastAPI expiry test's comment now says that the reload of its
synchronous loader runs on a background thread.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant