pgadmin-org / pgadmin-org/pgadmin4

Server mode: selected UI language doesn't persist beyond the request that set it

Open Beginner friendly
#10,347 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug
Dominant language
Python
Stars
3.8k
Forks
891
Avg merge
4d 7h
Merged PRs (30d)
8

Description

Describe the bug

In server mode, get_locale() (web/pgadmin/__init__.py:308-339) only resolves the correct language on the single request that includes a language field in the POSTed form (e.g. the login form, or the "Miscellaneous > User language" preferences save). On every other request it falls through to English, because its session/cookie fallback is broken:

data = request.form
if 'language' in data:
    language = data['language'] or language
    setattr(session, 'PGADMIN_LANGUAGE', language)
elif hasattr(session, 'PGADMIN_LANGUAGE'):
    language = getattr(session, 'PGADMIN_LANGUAGE', language)
elif hasattr(request.cookies, 'PGADMIN_LANGUAGE'):
    language = getattr(
        request.cookies, 'PGADMIN_LANGUAGE', language
    )
  • session (Flask's SecureCookieSession) and request.cookies (a MultiDict) are dict-likes, not objects that expose their keys as attributes. setattr(session, 'PGADMIN_LANGUAGE', language) sets a plain Python instance attribute that is never merged into the session's actual dict contents, so it's never serialised into the session cookie and doesn't survive past the current request object.
  • hasattr(request.cookies, 'PGADMIN_LANGUAGE') and the matching getattr(...) never succeed either, for the same reason — cookie values live in the dict, not as attributes.

Confirmed with a quick REPL check against flask.sessions.SecureCookieSession and werkzeug.datastructures.ImmutableMultiDict: both hasattr checks are always False, and the setattr value never appears in dict(session).

Net effect: elsewhere in the app (e.g. web/pgadmin/preferences/__init__.py:292-296 and web/pgadmin/browser/__init__.py:454-457) a PGADMIN_LANGUAGE cookie is correctly written to the browser via response.set_cookie(...), but get_locale() can never read it back on subsequent requests, so the UI silently reverts to English as soon as the request no longer includes the language form field.

To Reproduce

  1. Run pgAdmin in server mode with more than one configured LANGUAGES entry.
  2. Log in choosing a non-English language on the login page (or set a non-English language in Preferences > Miscellaneous > User language and save).
  3. Navigate to any other page / reload.
  4. The UI reverts to English rather than staying in the selected language.

Expected behavior

The selected language should persist for the session/browser (via the PGADMIN_LANGUAGE cookie that is already being set correctly elsewhere) across requests, not just the one request that submitted it.

Suggested fix

Read the cookie/session properly, e.g.:

elif 'PGADMIN_LANGUAGE' in session:
    language = session.get('PGADMIN_LANGUAGE', language)
elif 'PGADMIN_LANGUAGE' in request.cookies:
    language = request.cookies.get('PGADMIN_LANGUAGE', language)

and set the session value with session['PGADMIN_LANGUAGE'] = language rather than setattr.

Found while re-verifying #5692 (login page language selection) — the login page itself reads the cookie correctly via request.cookies.get(...) in login_user.html, but this get_locale() path is what governs the language of every other page, and it doesn't work.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in web/pgadmin/init.py at get_locale() and compare its session and cookie handling with web/pgadmin/preferences/init.py and web/pgadmin/browser/init.py. Reproduce the language selection flow in server mode, then verify that navigating or reloading retains the selected language instead of reverting to English.

Written by the indexing model from the issue text.

Assessment

Tech stack
flask, python
Domain
backend, localization
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.