Skip to content

Commit e14f599

Browse files
committed
Enhance SEO and metadata: add language-based meta tags, implement hreflang for alternate URLs, and create sitemap and robots.txt for better indexing
1 parent 671f127 commit e14f599

File tree

4 files changed

+197
-27
lines changed

4 files changed

+197
-27
lines changed

main.py

Lines changed: 124 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,11 @@ def get_language(request: Request) -> str:
790790
str
791791
The language code ("fr" or "en"). Defaults to "fr" if none matched.
792792
"""
793+
# 1) Query param takes precedence for SEO-friendly alternate URLs
794+
query_lang = request.query_params.get("lang") or request.query_params.get("hl")
795+
if query_lang in TRANSLATIONS:
796+
return query_lang
797+
# 2) Cookie value
793798
cookie_lang = request.cookies.get("lang")
794799
if cookie_lang in TRANSLATIONS:
795800
return cookie_lang
@@ -1013,7 +1018,15 @@ async def home(request: Request):
10131018
return templates.TemplateResponse(
10141019
name="home.html",
10151020
request=request,
1016-
context=ctx(request, {"partners": PARTNERS, "news_home": news_items}),
1021+
context=ctx(
1022+
request,
1023+
{
1024+
"partners": PARTNERS,
1025+
"news_home": news_items,
1026+
"meta_title": TRANSLATIONS[lang]["home-title"] + " — Python Togo",
1027+
"meta_description": TRANSLATIONS[lang]["footer-about-desc"],
1028+
},
1029+
),
10171030
)
10181031

10191032

@@ -1036,8 +1049,17 @@ async def about(request: Request):
10361049
--------
10371050
``GET /about``
10381051
"""
1052+
lang = get_language(request)
10391053
return templates.TemplateResponse(
1040-
name="about.html", request=request, context=ctx(request)
1054+
name="about.html",
1055+
request=request,
1056+
context=ctx(
1057+
request,
1058+
{
1059+
"meta_title": TRANSLATIONS[lang]["about-title"] + " — Python Togo",
1060+
"meta_description": TRANSLATIONS[lang]["about-blurb"],
1061+
},
1062+
),
10411063
)
10421064

10431065

@@ -1075,7 +1097,16 @@ async def events(request: Request):
10751097
)
10761098
items = sorted(items, key=lambda x: x["date"], reverse=True)
10771099
return templates.TemplateResponse(
1078-
request=request, name="events.html", context=ctx(request, {"events": items})
1100+
request=request,
1101+
name="events.html",
1102+
context=ctx(
1103+
request,
1104+
{
1105+
"events": items,
1106+
"meta_title": TRANSLATIONS[lang]["events-title"] + " — Python Togo",
1107+
"meta_description": TRANSLATIONS[lang]["events-sample-desc"],
1108+
},
1109+
),
10791110
)
10801111

10811112

@@ -1113,7 +1144,16 @@ async def event_detail(event_id: int, request: Request):
11131144
"description": tr.get("description", ""),
11141145
}
11151146
return templates.TemplateResponse(
1116-
request=request, name="event_detail.html", context=ctx(request, {"item": item})
1147+
request=request,
1148+
name="event_detail.html",
1149+
context=ctx(
1150+
request,
1151+
{
1152+
"item": item,
1153+
"meta_title": item["title"] + " — Python Togo",
1154+
"meta_description": item["description"],
1155+
},
1156+
),
11171157
)
11181158

11191159

@@ -1151,7 +1191,16 @@ async def actualities(request: Request):
11511191
}
11521192
)
11531193
return templates.TemplateResponse(
1154-
request=request, name="actualites.html", context=ctx(request, {"news": items})
1194+
request=request,
1195+
name="actualites.html",
1196+
context=ctx(
1197+
request,
1198+
{
1199+
"news": items,
1200+
"meta_title": TRANSLATIONS[lang]["news-title"] + " — Python Togo",
1201+
"meta_description": TRANSLATIONS[lang]["footer-about-desc"],
1202+
},
1203+
),
11551204
)
11561205

11571206

@@ -1190,7 +1239,17 @@ async def news_detail(news_id: int, request: Request):
11901239
or f"https://picsum.photos/seed/news-{found['id']}/1200/680",
11911240
}
11921241
return templates.TemplateResponse(
1193-
request=request, name="news_detail.html", context=ctx(request, {"item": item})
1242+
request=request,
1243+
name="news_detail.html",
1244+
context=ctx(
1245+
request,
1246+
{
1247+
"item": item,
1248+
"meta_title": item["title"] + " — Python Togo",
1249+
"meta_description": item["body"][:160],
1250+
"meta_image": item.get("image"),
1251+
},
1252+
),
11941253
)
11951254

11961255

@@ -1209,10 +1268,18 @@ async def partners(request: Request):
12091268
fastapi.responses.HTMLResponse
12101269
Rendered template response.
12111270
"""
1271+
lang = get_language(request)
12121272
return templates.TemplateResponse(
12131273
request=request,
12141274
name="partners.html",
1215-
context=ctx(request, {"partners": PARTNERS}),
1275+
context=ctx(
1276+
request,
1277+
{
1278+
"partners": PARTNERS,
1279+
"meta_title": TRANSLATIONS[lang]["nav-partners"] + " — Python Togo",
1280+
"meta_description": TRANSLATIONS[lang]["partners-intro"],
1281+
},
1282+
),
12161283
)
12171284

12181285

@@ -1231,8 +1298,17 @@ async def communities(request: Request):
12311298
fastapi.responses.HTMLResponse
12321299
Rendered template response.
12331300
"""
1301+
lang = get_language(request)
12341302
return templates.TemplateResponse(
1235-
request=request, name="communities.html", context=ctx(request)
1303+
request=request,
1304+
name="communities.html",
1305+
context=ctx(
1306+
request,
1307+
{
1308+
"meta_title": TRANSLATIONS[lang]["nav-communities"] + " — Python Togo",
1309+
"meta_description": TRANSLATIONS[lang]["communities-card-desc"],
1310+
},
1311+
),
12361312
)
12371313

12381314

@@ -1251,8 +1327,17 @@ async def join(request: Request):
12511327
fastapi.responses.HTMLResponse
12521328
Rendered template response.
12531329
"""
1330+
lang = get_language(request)
12541331
return templates.TemplateResponse(
1255-
request=request, name="join.html", context=ctx(request)
1332+
request=request,
1333+
name="join.html",
1334+
context=ctx(
1335+
request,
1336+
{
1337+
"meta_title": TRANSLATIONS[lang]["join-title"] + " — Python Togo",
1338+
"meta_description": TRANSLATIONS[lang]["join-intro"],
1339+
},
1340+
),
12561341
)
12571342

12581343

@@ -1271,8 +1356,17 @@ async def contact(request: Request):
12711356
fastapi.responses.HTMLResponse
12721357
Rendered template response.
12731358
"""
1359+
lang = get_language(request)
12741360
return templates.TemplateResponse(
1275-
request=request, name="contact.html", context=ctx(request)
1361+
request=request,
1362+
name="contact.html",
1363+
context=ctx(
1364+
request,
1365+
{
1366+
"meta_title": TRANSLATIONS[lang]["contact-title"] + " — Python Togo",
1367+
"meta_description": TRANSLATIONS[lang]["contact-intro"],
1368+
},
1369+
),
12761370
)
12771371

12781372

@@ -1291,8 +1385,17 @@ async def code_of_conduct(request: Request):
12911385
fastapi.responses.HTMLResponse
12921386
Rendered template response.
12931387
"""
1388+
lang = get_language(request)
12941389
return templates.TemplateResponse(
1295-
request=request, name="code_of_conduct.html", context=ctx(request)
1390+
request=request,
1391+
name="code_of_conduct.html",
1392+
context=ctx(
1393+
request,
1394+
{
1395+
"meta_title": TRANSLATIONS[lang]["coc-title"] + " — Python Togo",
1396+
"meta_description": TRANSLATIONS[lang]["coc-intro"],
1397+
},
1398+
),
12961399
)
12971400

12981401

@@ -1513,8 +1616,17 @@ async def privacy(request: Request):
15131616
fastapi.responses.HTMLResponse
15141617
Rendered template response.
15151618
"""
1619+
lang = get_language(request)
15161620
return templates.TemplateResponse(
1517-
request=request, name="privacy.html", context=ctx(request)
1621+
request=request,
1622+
name="privacy.html",
1623+
context=ctx(
1624+
request,
1625+
{
1626+
"meta_title": TRANSLATIONS[lang]["privacy-title"] + " — Python Togo",
1627+
"meta_description": TRANSLATIONS[lang]["privacy-intro"],
1628+
},
1629+
),
15181630
)
15191631

15201632

static/robots.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
User-agent: *
2+
Allow: /
3+
4+
Sitemap: https://pytogo.org/static/sitemap.xml

static/sitemap.xml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3+
<url>
4+
<loc>https://pytogo.org/</loc>
5+
<priority>1.0</priority>
6+
</url>
7+
<url>
8+
<loc>https://pytogo.org/about</loc>
9+
</url>
10+
<url>
11+
<loc>https://pytogo.org/events</loc>
12+
</url>
13+
<url>
14+
<loc>https://pytogo.org/actualities</loc>
15+
</url>
16+
<url>
17+
<loc>https://pytogo.org/partners</loc>
18+
</url>
19+
<url>
20+
<loc>https://pytogo.org/communities</loc>
21+
</url>
22+
<url>
23+
<loc>https://pytogo.org/gallery</loc>
24+
</url>
25+
<url>
26+
<loc>https://pytogo.org/join</loc>
27+
</url>
28+
<url>
29+
<loc>https://pytogo.org/contact</loc>
30+
</url>
31+
<url>
32+
<loc>https://pytogo.org/privacy</loc>
33+
</url>
34+
<url>
35+
<loc>https://pytogo.org/code-of-conduct</loc>
36+
</url>
37+
</urlset>

templates/base.html

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,46 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
7-
{% set _meta_title = (meta_title if meta_title is defined else t.get('site-title','Python Togo')) %}
8-
{% set _meta_desc = (meta_description if meta_description is defined else t.get('footer-about-desc','Python Togo promeut le langage de programmation Python au Togo.')) %}
6+
{% set meta_title = (meta_title if meta_title is defined else t.get('site-title','Python Togo')) %}
97
{% set _meta_image = (meta_image if meta_image is defined else (request.url.scheme ~ '://' ~ request.url.netloc ~ '/static/images/Py.png')) %}
10-
{% set _meta_type = (meta_type if meta_type is defined else 'website') %}
11-
{% set _meta_robots = (meta_robots if meta_robots is defined else 'index,follow') %}
128
{% set _og_locale = ('fr_FR' if lang == 'fr' else 'en_US') %}
139
{% set _canonical = (meta_canonical if meta_canonical is defined else (request.url.scheme ~ '://' ~ request.url.netloc ~ request.url.path)) %}
1410

15-
<title>{% block title %}{{ _meta_title }}{% endblock %}</title>
16-
<meta name="description" content="{{ _meta_desc }}">
17-
<meta name="robots" content="{{ _meta_robots }}">
18-
<link rel="canonical" href="{{ _canonical }}">
11+
<title>{% block title %}{{ meta_title }}{% endblock %}</title>
12+
<meta name="description" content="{{ meta_description }}">
13+
<meta name="robots" content="index, follow, max-snippet:320, max-image-preview:large, max-video-preview:-1">
14+
<meta name="theme-color" content="#121212">
15+
<meta name="application-name" content="Python Togo">
16+
17+
18+
{# hreflang alternates for SEO #}
19+
<link rel="alternate" hreflang="fr" href="{{ request.url.scheme }}://{{ request.url.netloc }}{{ request.url.path }}?lang=fr">
20+
<link rel="alternate" hreflang="en" href="{{ request.url.scheme }}://{{ request.url.netloc }}{{ request.url.path }}?lang=en">
21+
<link rel="alternate" hreflang="x-default" href="{{ _canonical }}">
1922

2023
<meta property="og:site_name" content="Python Togo">
21-
<meta property="og:title" content="{{ _meta_title }}">
22-
<meta property="og:description" content="{{ _meta_desc }}">
23-
<meta property="og:type" content="{{ _meta_type }}">
24+
<meta property="og:title" content="{{ meta_title }}">
25+
<meta property="og:description" content="{{ meta_description }}">
26+
<meta property="og:type" content="website">
2427
<meta property="og:url" content="{{ _canonical }}">
2528
<meta property="og:image" content="{{ _meta_image }}">
2629
<meta property="og:locale" content="{{ _og_locale }}">
30+
{% if lang == 'fr' %}
31+
<meta property="og:locale:alternate" content="en_US">
32+
{% else %}
33+
<meta property="og:locale:alternate" content="fr_FR">
34+
{% endif %}
35+
<meta property="og:image:alt" content="{{ meta_title }}">
2736

2837
<meta name="twitter:card" content="summary_large_image">
2938
<meta name="twitter:site" content="@pytogo_org">
30-
<meta name="twitter:title" content="{{ _meta_title }}">
31-
<meta name="twitter:description" content="{{ _meta_desc }}">
39+
<meta name="twitter:title" content="{{ meta_title }}">
40+
<meta name="twitter:description" content="{{ meta_description }}">
3241
<meta name="twitter:image" content="{{ _meta_image }}">
42+
<link rel="sitemap" type="application/xml" href="/static/sitemap.xml">
43+
<link rel="apple-touch-icon" href="/static/images/Py.png">
44+
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
45+
<link rel="preconnect" href="https://res.cloudinary.com" crossorigin>
3346
<link rel="stylesheet" href="/static/css/style.css">
3447
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon">
3548
<link rel="preload" href="/static/images/Py.png" as="image" />
@@ -50,7 +63,11 @@
5063
"sameAs": [
5164
"https://x.com/pytogo_org",
5265
"https://github.com/pytogo-org",
53-
"https://www.youtube.com/@PythonTogo"
66+
"https://www.youtube.com/@PythonTogo",
67+
"https://www.instagram.com/pycontg/",
68+
"https://techhub.social/@pytogo_org",
69+
"https://www.pytogo.org/discord"
70+
5471
]
5572
}
5673
</script>

0 commit comments

Comments
 (0)