fix(markets): map country names, localised dropdown + avg/top score tooltip
- Expand dim_countries.sql CASE to cover 22 missing countries (PL, RO, CO, HU, ZA, KE, BR, CZ, QA, NZ, HR, LV, MT, CR, CY, PA, SV, DO, PE, VE, EE, ID) that fell through to bare ISO codes - Add 19 missing entries to COUNTRY_LABELS (i18n.py) + both locale files (EN + DE dir_country_* keys) including IE which was in SQL but not i18n - Localise map tooltips: routes.py injects country_name via get_country_name(), JS uses c.country_name instead of c.country_name_en - Localise dropdown: apply country_name filter to option labels - Show avg + top score in map tooltip with separate color dots and new map_score_avg / map_score_top i18n keys (EN: "Avg. Score" / "Top City", DE: "Ø Score" / "Top-Stadt") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ from ..core import (
|
||||
fetch_all,
|
||||
fetch_one,
|
||||
)
|
||||
from ..i18n import get_translations
|
||||
from ..i18n import get_country_name, get_translations
|
||||
|
||||
bp = Blueprint(
|
||||
"content",
|
||||
@@ -208,10 +208,14 @@ async def markets():
|
||||
SELECT country_code, country_name_en, country_slug,
|
||||
city_count, total_venues,
|
||||
avg_market_score, avg_opportunity_score,
|
||||
top_opportunity_score,
|
||||
lat, lon
|
||||
FROM serving.pseo_country_overview
|
||||
ORDER BY total_venues DESC
|
||||
""")
|
||||
lang = g.get("lang", "en")
|
||||
for c in map_countries:
|
||||
c["country_name"] = get_country_name(c["country_code"], lang)
|
||||
# Sort so user's country renders last (on top in Leaflet z-order)
|
||||
user_country = g.get("user_country", "")
|
||||
if user_country and map_countries:
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
hx-include="#market-q, #market-region">
|
||||
<option value="">{{ t.mkt_all_countries }}</option>
|
||||
{% for c in countries %}
|
||||
<option value="{{ c }}" {% if c == current_country %}selected{% endif %}>{{ c }}</option>
|
||||
<option value="{{ c }}" {% if c == current_country %}selected{% endif %}>{{ c | country_name(lang) }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -86,7 +86,7 @@
|
||||
<script src="{{ url_for('static', filename='vendor/leaflet/leaflet.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/map-markers.js') }}"></script>
|
||||
<script>
|
||||
window.__MAP_T = {score_label:"{{ t.map_score_label }}",venues:"{{ t.map_venues }}",cities:"{{ t.map_cities }}"};
|
||||
window.__MAP_T = {score_label:"{{ t.map_score_label }}",venues:"{{ t.map_venues }}",cities:"{{ t.map_cities }}",score_avg:"{{ t.map_score_avg }}",score_top:"{{ t.map_score_top }}"};
|
||||
(function() {
|
||||
var sc = PNMarkers.scoreColor;
|
||||
var T = window.__MAP_T;
|
||||
@@ -105,9 +105,13 @@ window.__MAP_T = {score_label:"{{ t.map_score_label }}",venues:"{{ t.map_venues
|
||||
var size = 12 + 44 * Math.sqrt(c.total_venues / maxV);
|
||||
var score = c.avg_opportunity_score || 0;
|
||||
var hex = sc(score);
|
||||
var tip = '<strong>' + c.country_name_en + '</strong><br>'
|
||||
var topScore = c.top_opportunity_score || 0;
|
||||
var topHex = sc(topScore);
|
||||
var tip = '<strong>' + c.country_name + '</strong><br>'
|
||||
+ '<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:' + hex + ';vertical-align:middle;margin-right:4px;"></span>'
|
||||
+ '<span style="color:' + hex + ';font-weight:600;">' + T.score_label + ': ' + score + '/100</span><br>'
|
||||
+ '<span style="color:' + hex + ';font-weight:600;">' + T.score_avg + ': ' + score + '/100</span><br>'
|
||||
+ '<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:' + topHex + ';vertical-align:middle;margin-right:4px;"></span>'
|
||||
+ '<span style="color:' + topHex + ';font-weight:600;">' + T.score_top + ': ' + topScore + '/100</span><br>'
|
||||
+ '<span style="color:#94A3B8;font-size:0.75rem;">' + c.total_venues + ' ' + T.venues + ' · ' + c.city_count + ' ' + T.cities + '</span>';
|
||||
L.marker([c.lat, c.lon], { icon: PNMarkers.makeIcon({ size: size, color: hex }) })
|
||||
.bindTooltip(tip, { className: 'map-tooltip', direction: 'top', offset: [0, -Math.round(size / 2)] })
|
||||
|
||||
@@ -49,6 +49,25 @@ COUNTRY_LABELS: dict[str, str] = {
|
||||
"AU": "Australia",
|
||||
"ZA": "South Africa",
|
||||
"EG": "Egypt",
|
||||
"PL": "Poland",
|
||||
"RO": "Romania",
|
||||
"CO": "Colombia",
|
||||
"HU": "Hungary",
|
||||
"KE": "Kenya",
|
||||
"CZ": "Czech Republic",
|
||||
"QA": "Qatar",
|
||||
"NZ": "New Zealand",
|
||||
"HR": "Croatia",
|
||||
"LV": "Latvia",
|
||||
"MT": "Malta",
|
||||
"CR": "Costa Rica",
|
||||
"CY": "Cyprus",
|
||||
"PA": "Panama",
|
||||
"SV": "El Salvador",
|
||||
"DO": "Dominican Republic",
|
||||
"PE": "Peru",
|
||||
"VE": "Venezuela",
|
||||
"IE": "Ireland",
|
||||
}
|
||||
|
||||
_LOCALES_DIR = Path(__file__).parent / "locales"
|
||||
|
||||
@@ -345,6 +345,25 @@
|
||||
"dir_country_AU": "Australien",
|
||||
"dir_country_ZA": "Südafrika",
|
||||
"dir_country_EG": "Ägypten",
|
||||
"dir_country_PL": "Polen",
|
||||
"dir_country_RO": "Rumänien",
|
||||
"dir_country_CO": "Kolumbien",
|
||||
"dir_country_HU": "Ungarn",
|
||||
"dir_country_KE": "Kenia",
|
||||
"dir_country_CZ": "Tschechien",
|
||||
"dir_country_QA": "Katar",
|
||||
"dir_country_NZ": "Neuseeland",
|
||||
"dir_country_HR": "Kroatien",
|
||||
"dir_country_LV": "Lettland",
|
||||
"dir_country_MT": "Malta",
|
||||
"dir_country_CR": "Costa Rica",
|
||||
"dir_country_CY": "Zypern",
|
||||
"dir_country_PA": "Panama",
|
||||
"dir_country_SV": "El Salvador",
|
||||
"dir_country_DO": "Dominikanische Republik",
|
||||
"dir_country_PE": "Peru",
|
||||
"dir_country_VE": "Venezuela",
|
||||
"dir_country_IE": "Irland",
|
||||
"sp_back": "Zurück zum Verzeichnis",
|
||||
"sp_verified": "Verifiziert ✓",
|
||||
"sp_request_quote": "Angebot anfragen →",
|
||||
@@ -620,6 +639,8 @@
|
||||
"map_existing_venues": "bestehende Anlagen",
|
||||
"map_km_nearest": "km zur nächsten Anlage",
|
||||
"map_no_nearby": "Keine Anlagen in der Nähe",
|
||||
"map_score_avg": "Ø Score",
|
||||
"map_score_top": "Top-Stadt",
|
||||
"waitlist_markets_title": "Marktdaten — Demnächst verfügbar",
|
||||
"waitlist_markets_sub": "Detaillierte Marktberichte für Padel-Investoren: Baukosten, Umsatz-Benchmarks, Auslastungsdaten und ROI-Analysen nach Stadt und Region.",
|
||||
"waitlist_markets_feature1": "Echte Kostendaten aus laufenden Anlagen in über 30 Ländern",
|
||||
|
||||
@@ -345,6 +345,25 @@
|
||||
"dir_country_AU": "Australia",
|
||||
"dir_country_ZA": "South Africa",
|
||||
"dir_country_EG": "Egypt",
|
||||
"dir_country_PL": "Poland",
|
||||
"dir_country_RO": "Romania",
|
||||
"dir_country_CO": "Colombia",
|
||||
"dir_country_HU": "Hungary",
|
||||
"dir_country_KE": "Kenya",
|
||||
"dir_country_CZ": "Czech Republic",
|
||||
"dir_country_QA": "Qatar",
|
||||
"dir_country_NZ": "New Zealand",
|
||||
"dir_country_HR": "Croatia",
|
||||
"dir_country_LV": "Latvia",
|
||||
"dir_country_MT": "Malta",
|
||||
"dir_country_CR": "Costa Rica",
|
||||
"dir_country_CY": "Cyprus",
|
||||
"dir_country_PA": "Panama",
|
||||
"dir_country_SV": "El Salvador",
|
||||
"dir_country_DO": "Dominican Republic",
|
||||
"dir_country_PE": "Peru",
|
||||
"dir_country_VE": "Venezuela",
|
||||
"dir_country_IE": "Ireland",
|
||||
"sp_back": "Back to Directory",
|
||||
"sp_verified": "Verified ✓",
|
||||
"sp_request_quote": "Request Quote →",
|
||||
@@ -620,6 +639,8 @@
|
||||
"map_existing_venues": "existing venues",
|
||||
"map_km_nearest": "km to nearest court",
|
||||
"map_no_nearby": "No nearby courts",
|
||||
"map_score_avg": "Avg. Score",
|
||||
"map_score_top": "Top City",
|
||||
"waitlist_markets_title": "Markets Intelligence — Coming Soon",
|
||||
"waitlist_markets_sub": "Deep-dive market reports for padel investors: construction costs, revenue benchmarks, occupancy data, and ROI analysis by city and region.",
|
||||
"waitlist_markets_feature1": "Real cost data from operating venues across 30+ countries",
|
||||
|
||||
Reference in New Issue
Block a user