Skip to content

feat(showcase): i18n_demo app — LocaleMiddleware + URL-prefix (Phase 5)#527

Open
ujeenet wants to merge 3 commits into
developfrom
feat/e2e-showcase-phase5-i18n
Open

feat(showcase): i18n_demo app — LocaleMiddleware + URL-prefix (Phase 5)#527
ujeenet wants to merge 3 commits into
developfrom
feat/e2e-showcase-phase5-i18n

Conversation

@ujeenet
Copy link
Copy Markdown
Owner

@ujeenet ujeenet commented May 23, 2026

Summary

Phase 5 of the E2E showcase plan. Adds the `i18n_demo` sub-app exercising:

  • `LocaleMiddleware` cookie / Accept-Language pick order (`cookie > Accept-Language > default`)
  • Documented `Router::nest("/", router.layer(LocaleMiddleware::new(&[lang])))` pattern for URL-prefix locales — the framework's axum-shape answer to Django's `i18n_patterns`
  • Per-locale greeting handler reading `ActiveLocale` from request extensions

Routes

Method Path Behavior
GET `/i18n/greeting` Cookie/header-driven locale
GET `//i18n/greeting` URL-prefix-pinned (`en` / `fr` / `es`)

Playwright

9 new tests under `e2e/tests/i18n_demo/`:

  • default → en
  • Accept-Language matches available locale (fr-FR → fr)
  • Accept-Language no-match → falls back to default
  • cookie overrides Accept-Language (`django_language=es` beats `fr`)
  • unknown cookie value falls through to Accept-Language
  • URL prefix overrides headers (3 tests, one per language)
  • unknown locale prefix → 404

Suite is now 32/32 passing locally (2 smoke + 5 blog + 6 shop + 10 accounts + 9 i18n_demo).

CI matrix

Same shape — runs the 32 tests against postgres / mysql / sqlite per matrix row.

ujeenet added 3 commits May 23, 2026 09:59
… (Phase 5)

Adds the i18n_demo sub-app exercising:

- LocaleMiddleware cookie / Accept-Language pick order:
  cookie > Accept-Language > configured default
- Documented Router::nest("/<lang>", router.layer(LocaleMiddleware::new(\&[lang])))
  pattern for URL-prefix locales — same shape as Django's
  i18n_patterns
- Per-locale greeting handler that reads ActiveLocale from the
  request extensions

## Routes

GET /i18n/greeting             — cookie/header-driven
GET /<lang>/i18n/greeting      — URL-prefix-pinned (en/fr/es)

## Playwright

9 new tests under e2e/tests/i18n_demo/:
- default → en
- Accept-Language matches available locale
- Accept-Language no-match → default
- cookie overrides Accept-Language
- unknown cookie value falls through to Accept-Language
- URL prefix overrides headers (fr / es / en)
- unknown locale prefix → 404

Suite is now 32/32 passing locally (2 smoke + 5 blog + 6 shop +
10 accounts + 9 i18n_demo).
ujeenet/rustango-cms#260)

The bare admin's `AdminError::TableMissing` variant rendered a
friendly "run migrations" page when the underlying SQL table was
absent — but only for Postgres. The detection lived inside
`pg_undefined_table_error`, which matched PG SQLSTATE `42P01`. On
sqlite and mysql the error flowed through `AdminError::Internal`
and surfaced as a 500 with a correlation id and an opaque
"internal server error" body. Anyone clicking around the
auto-generated admin sidebar after a fresh tenant provisioning on
a non-PG build hit this.

Add dialect-specific parsers:

- `sqlite_undefined_table_error` matches sqlx's message prefix
  `no such table: <name>` and extracts the identifier.
- `mysql_undefined_table_error` matches MySQL error code 1146
  (`ER_NO_SUCH_TABLE`) and pulls the unqualified name out of the
  `Table 'db.name' doesn't exist` message shape.
- New `undefined_table_error` umbrella tries PG → sqlite → mysql in
  order; the two `From` impls (`sqlx::Error` and `ExecError`) now
  call the umbrella so every entry point gets parity.

Two unit tests pin the sqlite + mysql message-shape parsing.

The friendly HTML page and its existing test are unchanged — same
URL surface, same response shape, just reached on more backends.
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