diff --git a/web/src/padelnomics/admin/templates/admin/partials/seo_scorecard.html b/web/src/padelnomics/admin/templates/admin/partials/seo_scorecard.html
index 49071e8..715ce89 100644
--- a/web/src/padelnomics/admin/templates/admin/partials/seo_scorecard.html
+++ b/web/src/padelnomics/admin/templates/admin/partials/seo_scorecard.html
@@ -69,7 +69,7 @@
{% for a in scorecard %}
- {{ a.title or a.url_path }}
+ {{ a.title or a.url_path }}
{% if a.template_slug %}
{{ a.template_slug }}
{% endif %}
diff --git a/web/src/padelnomics/content/__init__.py b/web/src/padelnomics/content/__init__.py
index 6b07976..f5d98cb 100644
--- a/web/src/padelnomics/content/__init__.py
+++ b/web/src/padelnomics/content/__init__.py
@@ -308,8 +308,8 @@ async def generate_articles(
# Build render context: row data + language
ctx = {**row, "language": lang}
- # Render URL pattern
- url_path = f"/{lang}" + _render_pattern(config["url_pattern"], ctx)
+ # Render URL pattern (no lang prefix — blueprint provides /)
+ url_path = _render_pattern(config["url_pattern"], ctx)
if is_reserved_path(url_path):
continue
@@ -375,8 +375,8 @@ async def generate_articles(
# Extract FAQ pairs for structured data
faq_pairs = _extract_faq_pairs(body_md)
- # Build SEO metadata
- full_url = base_url + url_path
+ # Build SEO metadata (full_url includes lang prefix for canonical/OG)
+ full_url = f"{base_url}/{lang}{url_path}"
publish_dt = datetime(
publish_date.year, publish_date.month, publish_date.day,
8, 0, 0,
@@ -397,7 +397,7 @@ async def generate_articles(
)
# JSON-LD
- breadcrumbs = _build_breadcrumbs(url_path, base_url)
+ breadcrumbs = _build_breadcrumbs(f"/{lang}{url_path}", base_url)
jsonld_objects = build_jsonld(
config["schema_type"],
title=title,
@@ -499,7 +499,7 @@ async def preview_article(
ctx = {**row, "language": lang}
- url_path = f"/{lang}" + _render_pattern(config["url_pattern"], ctx)
+ url_path = _render_pattern(config["url_pattern"], ctx)
title = _render_pattern(config["title_pattern"], ctx)
meta_desc = _render_pattern(config["meta_description_pattern"], ctx)
diff --git a/web/src/padelnomics/content/routes.py b/web/src/padelnomics/content/routes.py
index f7f669c..fcf9203 100644
--- a/web/src/padelnomics/content/routes.py
+++ b/web/src/padelnomics/content/routes.py
@@ -23,7 +23,7 @@ BUILD_DIR = Path("data/content/_build")
RESERVED_PREFIXES = (
"/admin", "/auth", "/planner", "/billing", "/dashboard",
"/directory", "/leads", "/suppliers", "/health",
- "/sitemap", "/static", "/markets", "/features", "/feedback",
+ "/sitemap", "/static", "/features", "/feedback",
)
SCENARIO_RE = re.compile(r'\[scenario:([a-z0-9_-]+)(?::([a-z]+))?\]')
diff --git a/web/tests/test_content.py b/web/tests/test_content.py
index bcc238f..015b3ff 100644
--- a/web/tests/test_content.py
+++ b/web/tests/test_content.py
@@ -322,8 +322,9 @@ class TestReservedPaths:
def test_planner_reserved(self):
assert is_reserved_path("/planner/") is True
- def test_markets_reserved(self):
- assert is_reserved_path("/markets") is True
+ def test_markets_not_reserved(self):
+ # /markets sub-paths are article URLs; explicit /markets route takes priority
+ assert is_reserved_path("/markets/germany/berlin") is False
def test_custom_path_allowed(self):
assert is_reserved_path("/padel-court-cost-miami") is False
@@ -456,7 +457,7 @@ class TestGenerationPipeline:
miami = await fetch_one("SELECT * FROM articles WHERE slug = 'test-city-en-miami'")
assert miami is not None
- assert miami["url_path"] == "/en/markets/us/miami"
+ assert miami["url_path"] == "/markets/us/miami"
assert miami["title"] == "Padel in Miami"
assert miami["template_slug"] == "test-city"
assert miami["language"] == "en"
@@ -761,7 +762,7 @@ class TestPreviewArticle:
from padelnomics.content import preview_article
result = await preview_article("test-city", "miami")
assert result["title"] == "Padel in Miami"
- assert result["url_path"] == "/en/markets/us/miami"
+ assert result["url_path"] == "/markets/us/miami"
assert result["meta_description"] == "Padel costs in Miami"
assert "" in result["html"]
@@ -773,7 +774,7 @@ class TestPreviewArticle:
async def test_preview_with_language(self, db, pseo_env):
from padelnomics.content import preview_article
result = await preview_article("test-city", "miami", lang="de")
- assert result["url_path"] == "/de/markets/us/miami"
+ assert result["url_path"] == "/markets/us/miami"
async def test_preview_unknown_template_raises(self, db, pseo_env):
from padelnomics.content import preview_article
|