From f7b21f249480342144f406287985e8efb911a154 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Wed, 13 May 2026 17:03:08 +0200 Subject: [PATCH 1/7] use current language if Language is not passed --- CHANGES.md | 6 ++++++ src/plone/pgcatalog/query.py | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 8950b98..a15c0a9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changelog +## 1.0.0b65 + +### Fixed + +Use site language to mark the search language if `Language` parameter is not used #166 + ## 1.0.0b64 ### Fixed diff --git a/src/plone/pgcatalog/query.py b/src/plone/pgcatalog/query.py index 7b5b7af..efc257e 100644 --- a/src/plone/pgcatalog/query.py +++ b/src/plone/pgcatalog/query.py @@ -13,13 +13,13 @@ from plone.pgcatalog.columns import get_registry from plone.pgcatalog.columns import IndexType from plone.pgcatalog.columns import validate_identifier +from plone import api from psycopg.types.json import Json from typing import ClassVar import logging import re - log = logging.getLogger(__name__) # Keys in the query dict that are NOT index names @@ -567,6 +567,10 @@ def _handle_text(self, name, idx_key, spec): lang_val = self._query.get("Language") if isinstance(lang_val, dict): lang_val = lang_val.get("query", "") + if not lang_val: + # Try getting the current language from the environment + lang_val = api.portal.get_current_language() + lang_val = _bool_to_lower_str(lang_val) if lang_val else "" clause, params, rank_expr = get_backend().build_search_clause( From 2341d3e3324bfda737e2569acc6008ca77f1fb3f Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 16 Jun 2026 11:48:53 +0200 Subject: [PATCH 2/7] remove dependency on plone.api --- src/plone/pgcatalog/query.py | 4 ++-- src/plone/pgcatalog/utils.py | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/plone/pgcatalog/utils.py diff --git a/src/plone/pgcatalog/query.py b/src/plone/pgcatalog/query.py index efc257e..8259758 100644 --- a/src/plone/pgcatalog/query.py +++ b/src/plone/pgcatalog/query.py @@ -13,7 +13,7 @@ from plone.pgcatalog.columns import get_registry from plone.pgcatalog.columns import IndexType from plone.pgcatalog.columns import validate_identifier -from plone import api +from plone.pgcatalog.utils import get_current_language from psycopg.types.json import Json from typing import ClassVar @@ -569,7 +569,7 @@ def _handle_text(self, name, idx_key, spec): lang_val = lang_val.get("query", "") if not lang_val: # Try getting the current language from the environment - lang_val = api.portal.get_current_language() + lang_val = get_current_language() lang_val = _bool_to_lower_str(lang_val) if lang_val else "" diff --git a/src/plone/pgcatalog/utils.py b/src/plone/pgcatalog/utils.py new file mode 100644 index 0000000..b7469ac --- /dev/null +++ b/src/plone/pgcatalog/utils.py @@ -0,0 +1,38 @@ +# Implementation taken from plone.api.portal to avoid +# having plone.api as a dependency + +from Acquisition import aq_inner +from zope.globalrequest import getRequest +from zope.component import getUtility +from plone.registry.interfaces import IRegistry + + +def get_default_language(): + """Return the default language. + + :returns: language identifier + :rtype: string + :Example: :ref:`portal-get-default-language-example` + """ + from plone.i18n.interfaces import ILanguageSchema + + registry = getUtility(IRegistry) + settings = registry.forInterface(ILanguageSchema, prefix="plone") + return settings.default_language + + +def get_current_language(context=None): + """Return the current negotiated language. + + :param context: context object + :type context: object + :returns: language identifier + :rtype: string + :Example: :ref:`portal-get-current-language-example` + """ + request = getRequest() + return ( + request.get("LANGUAGE", None) + or (context and aq_inner(context).Language()) + or get_default_language() + ) From 04bc7953297e39a712bb7cbffe259289f72164c9 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 16 Jun 2026 11:54:56 +0200 Subject: [PATCH 3/7] lint --- src/plone/pgcatalog/query.py | 1 + src/plone/pgcatalog/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plone/pgcatalog/query.py b/src/plone/pgcatalog/query.py index 8259758..1f9f36b 100644 --- a/src/plone/pgcatalog/query.py +++ b/src/plone/pgcatalog/query.py @@ -20,6 +20,7 @@ import logging import re + log = logging.getLogger(__name__) # Keys in the query dict that are NOT index names diff --git a/src/plone/pgcatalog/utils.py b/src/plone/pgcatalog/utils.py index b7469ac..9be3321 100644 --- a/src/plone/pgcatalog/utils.py +++ b/src/plone/pgcatalog/utils.py @@ -2,9 +2,9 @@ # having plone.api as a dependency from Acquisition import aq_inner -from zope.globalrequest import getRequest -from zope.component import getUtility from plone.registry.interfaces import IRegistry +from zope.component import getUtility +from zope.globalrequest import getRequest def get_default_language(): From 4ec14bcd5d5d442d860fcd901e7a830134f086c6 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 16 Jun 2026 12:00:55 +0200 Subject: [PATCH 4/7] fix changelog --- CHANGES.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b06b7e6..4e9a77e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changelog +## UNRELEASED + +### Fixed + +- Use site language to mark the search language if `Language` parameter is not used #166 + + ## 1.0.0b65 ### Added @@ -42,8 +49,6 @@ ### Fixed -- Use site language to mark the search language if `Language` parameter is not used #166 - - Fix automatic language detection for BM25 ranking #164 - PG-backed test layer: reset zodb_pgjsonb's process-wide From 7012def3db8f06c967f98cbb2c0659e237354a28 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 16 Jun 2026 12:20:21 +0200 Subject: [PATCH 5/7] add error control --- src/plone/pgcatalog/query.py | 5 +++-- src/plone/pgcatalog/utils.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/plone/pgcatalog/query.py b/src/plone/pgcatalog/query.py index 1f9f36b..b0a6382 100644 --- a/src/plone/pgcatalog/query.py +++ b/src/plone/pgcatalog/query.py @@ -20,7 +20,6 @@ import logging import re - log = logging.getLogger(__name__) # Keys in the query dict that are NOT index names @@ -570,7 +569,9 @@ def _handle_text(self, name, idx_key, spec): lang_val = lang_val.get("query", "") if not lang_val: # Try getting the current language from the environment - lang_val = get_current_language() + current_language = get_current_language() + if current_language is not None: + lang_val = current_language lang_val = _bool_to_lower_str(lang_val) if lang_val else "" diff --git a/src/plone/pgcatalog/utils.py b/src/plone/pgcatalog/utils.py index 9be3321..cba1c8b 100644 --- a/src/plone/pgcatalog/utils.py +++ b/src/plone/pgcatalog/utils.py @@ -31,8 +31,11 @@ def get_current_language(context=None): :Example: :ref:`portal-get-current-language-example` """ request = getRequest() - return ( - request.get("LANGUAGE", None) - or (context and aq_inner(context).Language()) - or get_default_language() - ) + if request is not None: + return ( + request.get("LANGUAGE", None) + or (context and aq_inner(context).Language()) + or get_default_language() + ) + + return None From 224501883928407f5e45f7932b54556f502af553 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 16 Jun 2026 12:27:08 +0200 Subject: [PATCH 6/7] lint --- src/plone/pgcatalog/query.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plone/pgcatalog/query.py b/src/plone/pgcatalog/query.py index b0a6382..6995451 100644 --- a/src/plone/pgcatalog/query.py +++ b/src/plone/pgcatalog/query.py @@ -20,6 +20,7 @@ import logging import re + log = logging.getLogger(__name__) # Keys in the query dict that are NOT index names From 97ef39cdeb2e2b770a847da2dc5fec6a4f2943da Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 29 Jun 2026 14:30:05 +0200 Subject: [PATCH 7/7] test(query): cover current-language fallback for SearchableText (#166) Adds regression tests for erral's fix: when a query has no Language, _handle_text falls back to get_current_language() (request/site/default), and an explicit Language still wins. Plus unit tests for the vendored get_current_language helper. Co-authored-by: Mikel Larreategi Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/test_query.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_query.py b/tests/test_query.py index 07e91f7..5ccef27 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -1378,3 +1378,47 @@ def test_empty_when_only_unknown_keys(self): assert ( strip_non_index_keys({"metadata_fields": ["Title"]}, ["portal_type"]) == {} ) + + +class TestSearchableTextCurrentLanguage: + """#166: when the query has no Language, fall back to the current/site + language for the search text-search configuration instead of 'simple'.""" + + def test_uses_current_language_when_language_absent(self, monkeypatch): + import plone.pgcatalog.query as q + + monkeypatch.setattr(q, "get_current_language", lambda *a, **k: "de") + qr = build_query({"SearchableText": "hallo"}) + assert [v for v in qr["params"].values() if v == "de"], qr["params"] + + def test_explicit_language_wins_over_current_language(self, monkeypatch): + import plone.pgcatalog.query as q + + monkeypatch.setattr(q, "get_current_language", lambda *a, **k: "de") + qr = build_query({"SearchableText": "bonjour", "Language": "fr"}) + assert [v for v in qr["params"].values() if v == "fr"], qr["params"] + assert not [v for v in qr["params"].values() if v == "de"], qr["params"] + + def test_no_request_falls_back_to_simple(self, monkeypatch): + """No current language available → empty string ('simple' config).""" + import plone.pgcatalog.query as q + + monkeypatch.setattr(q, "get_current_language", lambda *a, **k: None) + qr = build_query({"SearchableText": "hello"}) + assert [v for v in qr["params"].values() if v == ""], qr["params"] + + +class TestGetCurrentLanguage: + """Unit tests for the vendored get_current_language helper (#166).""" + + def test_returns_none_without_request(self, monkeypatch): + import plone.pgcatalog.utils as u + + monkeypatch.setattr(u, "getRequest", lambda: None) + assert u.get_current_language() is None + + def test_returns_request_language(self, monkeypatch): + import plone.pgcatalog.utils as u + + monkeypatch.setattr(u, "getRequest", lambda: {"LANGUAGE": "eu"}) + assert u.get_current_language() == "eu"