From 65dc9babb1604c22ff3c413655e6524660b05e48 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 21:44:51 +0000 Subject: [PATCH 1/3] refactor(seo): import display-name maps from core.constants From cabbd5fe14e6d5ad4e3682ccfc9c2275ce5acaaf Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 21:54:06 +0000 Subject: [PATCH 2/3] refactor(seo): import display-name maps from core.constants --- api/routers/seo.py | 21 ++++++++------------- changelog.d/seo-shared-name-maps.md | 5 +++++ tests/unit/api/test_seo_helpers.py | 12 ++++++++---- 3 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 changelog.d/seo-shared-name-maps.md diff --git a/api/routers/seo.py b/api/routers/seo.py index a91a7dfaa27..637afdab3e1 100644 --- a/api/routers/seo.py +++ b/api/routers/seo.py @@ -16,7 +16,7 @@ from api.schemas import StatsResponse from core import palette from core.config import settings -from core.constants import LANGUAGES_METADATA, LIBRARIES_METADATA +from core.constants import LANGUAGE_NAMES, LANGUAGES_METADATA, LIBRARIES_METADATA, LIBRARY_NAMES from core.database import ImplRepository, SpecRepository from core.database.connection import get_db_context from core.utils import strip_noqa_comments @@ -169,16 +169,11 @@ async def _refresh_sitemap() -> str: DEFAULT_PLOTS_IMAGE = "https://api.anyplot.ai/og/plots.png" DEFAULT_DESCRIPTION = "library-agnostic, ai-powered plotting." -# Display names derived from the canonical registry — never hand-maintain -# name maps in routers (that is how the 9-library-era drift happened). -_LANGUAGE_NAMES = {lang["id"]: str(lang["name"]) for lang in LANGUAGES_METADATA} -_LIBRARY_NAMES = {lib["id"]: str(lib["name"]) for lib in LIBRARIES_METADATA} - # Counts and lists for homepage copy, derived from the registry so the text # can never drift from the actual catalog. _LIBRARY_COUNT = len(LIBRARIES_METADATA) -_LANGUAGE_LIST = ", ".join(_LANGUAGE_NAMES.values()) -_LIBRARY_LIST = ", ".join(_LIBRARY_NAMES.values()) +_LANGUAGE_LIST = ", ".join(LANGUAGE_NAMES.values()) +_LIBRARY_LIST = ", ".join(LIBRARY_NAMES.values()) # Homepage copy sized for SERP display: title ~50-60 chars, description # ~150 chars. The previous 10-char title / 39-char description read as a @@ -213,8 +208,8 @@ async def _refresh_sitemap() -> str: "name": "Markus Neusinger", "url": "https://www.linkedin.com/in/markus-neusinger/", }, - "keywords": [name.lower() for name in _LANGUAGE_NAMES.values()] - + [name.lower() for name in _LIBRARY_NAMES.values()] + "keywords": [name.lower() for name in LANGUAGE_NAMES.values()] + + [name.lower() for name in LIBRARY_NAMES.values()] + ["plotting", "data visualization", "charts", "code examples", "colorblind-safe", "imprint palette"], "isAccessibleForFree": True, }, @@ -360,7 +355,7 @@ def _render_bot_html( def _impl_display_names(impl) -> tuple[str, str]: """(library display name, language display name) for an impl, falling back to raw ids.""" language_id = impl.library.language if impl.library else "" - return _LIBRARY_NAMES.get(impl.library_id, impl.library_id), _LANGUAGE_NAMES.get(language_id, language_id) + return LIBRARY_NAMES.get(impl.library_id, impl.library_id), LANGUAGE_NAMES.get(language_id, language_id) # Registry order (Python, R, Julia, JavaScript), not alphabetical: the language @@ -399,7 +394,7 @@ def _library_groups(impls: list, first_language: str | None = None) -> str: lib_name, lang_name = _impl_display_names(impl) groups.setdefault(impl.library.language, []).append(lib_name) order = sorted(groups, key=lambda language: language != first_language) - return "; ".join(f"{_LANGUAGE_NAMES.get(language, language)}: {', '.join(groups[language])}" for language in order) + return "; ".join(f"{LANGUAGE_NAMES.get(language, language)}: {', '.join(groups[language])}" for language in order) def _hub_heading(spec) -> str: @@ -413,7 +408,7 @@ def _hub_heading(spec) -> str: languages = list(dict.fromkeys(i.library.language for i in _sorted_impls(spec))) if not languages: return str(spec.title) - return f"{spec.title} in {_join_en([_LANGUAGE_NAMES.get(language, language) for language in languages])}" + return f"{spec.title} in {_join_en([LANGUAGE_NAMES.get(language, language) for language in languages])}" def _impl_heading(spec, lib_name: str, lang_name: str) -> str: diff --git a/changelog.d/seo-shared-name-maps.md b/changelog.d/seo-shared-name-maps.md new file mode 100644 index 00000000000..452c0bbf893 --- /dev/null +++ b/changelog.d/seo-shared-name-maps.md @@ -0,0 +1,5 @@ +### Changed + +- **SEO router reuses the shared display-name maps.** `api/routers/seo.py` + imports `LANGUAGE_NAMES` and `LIBRARY_NAMES` from `core.constants` instead + of deriving its own copies, so exactly one derived name map exists. diff --git a/tests/unit/api/test_seo_helpers.py b/tests/unit/api/test_seo_helpers.py index f2bef9e4a49..fe4f5f63d88 100644 --- a/tests/unit/api/test_seo_helpers.py +++ b/tests/unit/api/test_seo_helpers.py @@ -669,14 +669,18 @@ class TestDisplayNameMaps: """The registry-derived name maps must cover the full canonical registry.""" def test_language_names_cover_registry(self) -> None: - from api.routers.seo import _LANGUAGE_NAMES + import core.constants + from api.routers.seo import LANGUAGE_NAMES - assert set(_LANGUAGE_NAMES) == {lang["id"] for lang in LANGUAGES_METADATA} + assert LANGUAGE_NAMES is core.constants.LANGUAGE_NAMES + assert set(LANGUAGE_NAMES) == {lang["id"] for lang in LANGUAGES_METADATA} def test_library_names_cover_registry(self) -> None: - from api.routers.seo import _LIBRARY_NAMES + import core.constants + from api.routers.seo import LIBRARY_NAMES - assert set(_LIBRARY_NAMES) == {lib["id"] for lib in LIBRARIES_METADATA} + assert LIBRARY_NAMES is core.constants.LIBRARY_NAMES + assert set(LIBRARY_NAMES) == {lib["id"] for lib in LIBRARIES_METADATA} class TestMetaDescription: From 1a6f5ce9e73fbe878b43d7fac8e635fefd20c9c5 Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Thu, 29 Oct 2026 09:47:08 +0100 Subject: [PATCH 3/3] docs(changelog): reference #12419 in the fragment Co-Authored-By: Claude Opus 5.5 --- changelog.d/seo-shared-name-maps.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.d/seo-shared-name-maps.md b/changelog.d/seo-shared-name-maps.md index 452c0bbf893..f3ae55062f6 100644 --- a/changelog.d/seo-shared-name-maps.md +++ b/changelog.d/seo-shared-name-maps.md @@ -3,3 +3,4 @@ - **SEO router reuses the shared display-name maps.** `api/routers/seo.py` imports `LANGUAGE_NAMES` and `LIBRARY_NAMES` from `core.constants` instead of deriving its own copies, so exactly one derived name map exists. + (#12419)