diff --git a/padelnomics/src/padelnomics/app.py b/padelnomics/src/padelnomics/app.py index 8397540..4cd4508 100644 --- a/padelnomics/src/padelnomics/app.py +++ b/padelnomics/src/padelnomics/app.py @@ -54,6 +54,15 @@ def _fmt_n(n) -> str: return f"{round(float(n)):,}".replace(",", ".") +def _tformat(s: str, **kwargs) -> str: + """Format a translation string with named placeholders. + + Usage: {{ t.some_key | tformat(count=total, name=supplier.name) }} + JSON value: "Browse {count}+ suppliers from {name}" + """ + return s.format_map(kwargs) + + def create_app() -> Quart: """Create and configure the Quart application.""" @@ -67,12 +76,13 @@ def create_app() -> Quart: app.secret_key = config.SECRET_KEY - # Jinja2 filters for financial formatting (used in planner templates) + # Jinja2 filters app.jinja_env.filters["fmt_currency"] = _fmt_currency app.jinja_env.filters["fmt_k"] = _fmt_k app.jinja_env.filters["fmt_pct"] = _fmt_pct app.jinja_env.filters["fmt_x"] = _fmt_x app.jinja_env.filters["fmt_n"] = _fmt_n + app.jinja_env.filters["tformat"] = _tformat # translate with placeholders: {{ t.key | tformat(count=n) }} # Session config app.config["SESSION_COOKIE_SECURE"] = not config.DEBUG @@ -186,7 +196,8 @@ def create_app() -> Quart: @app.context_processor def inject_globals(): from datetime import datetime - lang = g.get("lang", "en") + lang = g.get("lang") or _detect_lang() + g.lang = lang # ensure g.lang is always set (e.g. for dashboard/billing routes) effective_lang = lang if lang in SUPPORTED_LANGS else "en" return { "config": config, diff --git a/padelnomics/src/padelnomics/billing/routes.py b/padelnomics/src/padelnomics/billing/routes.py index d089406..8a45453 100644 --- a/padelnomics/src/padelnomics/billing/routes.py +++ b/padelnomics/src/padelnomics/billing/routes.py @@ -14,6 +14,7 @@ from quart import Blueprint, flash, g, jsonify, redirect, render_template, reque from ..auth.routes import login_required from ..core import config, execute, fetch_one, get_paddle_price +from ..i18n import get_translations def _paddle_client() -> PaddleClient: @@ -177,7 +178,8 @@ async def manage(): """Redirect to Paddle customer portal.""" sub = await get_subscription(g.user["id"]) if not sub or not sub.get("provider_subscription_id"): - await flash("No active subscription found.", "error") + t = get_translations(g.get("lang") or "en") + await flash(t["billing_no_subscription"], "error") return redirect(url_for("dashboard.settings")) paddle = _paddle_client() diff --git a/padelnomics/src/padelnomics/billing/templates/pricing.html b/padelnomics/src/padelnomics/billing/templates/pricing.html index 7f9a5f1..85c7ce5 100644 --- a/padelnomics/src/padelnomics/billing/templates/pricing.html +++ b/padelnomics/src/padelnomics/billing/templates/pricing.html @@ -1,51 +1,51 @@ {% extends "base.html" %} -{% block title %}Free Padel Court Financial Planner - {{ config.APP_NAME }}{% endblock %} +{% block title %}{{ t.billing_pricing_title }} - {{ config.APP_NAME }}{% endblock %} {% block head %} - - - + + + {% endblock %} {% block content %}
-

100% Free. No Catch.

-

The most sophisticated padel court financial planner available — completely free. Plan your investment with 60+ variables, sensitivity analysis, and professional-grade projections.

+

{{ t.billing_pricing_h1 }}

+

{{ t.billing_pricing_subtitle }}

-

Financial Planner

-

Free — forever

+

{{ t.billing_planner_card }}

+

{{ t.billing_planner_free }} {{ t.billing_planner_forever }}

    -
  • 60+ adjustable variables
  • -
  • 6 analysis tabs (CAPEX, Operating, Cash Flow, Returns, Metrics)
  • -
  • Sensitivity analysis (utilization + pricing)
  • -
  • Save unlimited scenarios
  • -
  • Interactive charts
  • -
  • Indoor/outdoor & rent/buy models
  • +
  • {{ t.billing_feature_1 }}
  • +
  • {{ t.billing_feature_2 }}
  • +
  • {{ t.billing_feature_3 }}
  • +
  • {{ t.billing_feature_4 }}
  • +
  • {{ t.billing_feature_5 }}
  • +
  • {{ t.billing_feature_6 }}
{% if user %} - Open Planner + {{ t.billing_open_planner }} {% else %} - Create Free Account + {{ t.billing_create_account }} {% endif %}
-

Need Help Building?

-

We connect you with verified partners

+

{{ t.billing_help_card }}

+

{{ t.billing_help_subtitle }}

    -
  • Court supplier quotes
  • -
  • Financing & bank connections
  • -
  • Construction planning
  • -
  • Equipment sourcing
  • +
  • {{ t.billing_help_feature_1 }}
  • +
  • {{ t.billing_help_feature_2 }}
  • +
  • {{ t.billing_help_feature_3 }}
  • +
  • {{ t.billing_help_feature_4 }}
{% if user %} - Get Supplier Quotes + {{ t.billing_get_quotes }} {% else %} - Sign Up to Get Started + {{ t.billing_signup }} {% endif %}
diff --git a/padelnomics/src/padelnomics/billing/templates/success.html b/padelnomics/src/padelnomics/billing/templates/success.html index 82dcd56..e718d55 100644 --- a/padelnomics/src/padelnomics/billing/templates/success.html +++ b/padelnomics/src/padelnomics/billing/templates/success.html @@ -1,15 +1,15 @@ {% extends "base.html" %} -{% block title %}Welcome - {{ config.APP_NAME }}{% endblock %} +{% block title %}{{ t.billing_success_title }} - {{ config.APP_NAME }}{% endblock %} {% block content %}
-

Welcome to Padelnomics!

+

{{ t.billing_success_h1 }}

-

Your account is ready. Start planning your padel court investment with our financial planner.

+

{{ t.billing_success_body }}

- Open Planner + {{ t.billing_success_btn }}
{% endblock %} diff --git a/padelnomics/src/padelnomics/businessplan.py b/padelnomics/src/padelnomics/businessplan.py index c1d1388..1bf9f61 100644 --- a/padelnomics/src/padelnomics/businessplan.py +++ b/padelnomics/src/padelnomics/businessplan.py @@ -8,6 +8,7 @@ import json from pathlib import Path from .core import fetch_one +from .i18n import get_translations from .planner.calculator import calc, validate_state TEMPLATE_DIR = Path(__file__).parent / "templates" / "businessplan" @@ -27,57 +28,66 @@ def _fmt_pct(n) -> str: return f"{n * 100:.1f}%" -def _fmt_months(idx: int) -> str: +def _fmt_months(idx: int, t: dict) -> str: """Format payback month index as readable string.""" if idx < 0: - return "Not reached in 60 months" + return t["bp_payback_not_reached"] months = idx + 1 if months <= 12: - return f"{months} months" + return t["bp_months"].format(n=months) years = months / 12 - return f"{years:.1f} years" + return t["bp_years"].format(n=f"{years:.1f}") def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: """Extract and format all business plan sections from planner data.""" s = state - is_en = language == "en" + t = get_translations(language) - venue_type = "Indoor" if s["venue"] == "indoor" else "Outdoor" - own_type = "Own" if s["own"] == "buy" else "Rent" + venue_type = t["bp_indoor"] if s["venue"] == "indoor" else t["bp_outdoor"] + own_type = t["bp_own"] if s["own"] == "buy" else t["bp_rent"] + + payback_str = _fmt_months(d["paybackIdx"], t) + irr_str = _fmt_pct(d["irr"]) + total_capex_str = _fmt_eur(d["capex"]) + equity_str = _fmt_eur(d["equity"]) + loan_str = _fmt_eur(d["loanAmount"]) + per_court_str = _fmt_eur(d["capexPerCourt"]) + per_sqm_str = _fmt_eur(d["capexPerSqm"]) sections = { - "title": "Padel Business Plan" if is_en else "Padel Businessplan", + "lang": language, + "title": t["bp_title"], "subtitle": f"{venue_type} ({own_type}) \u2014 {s.get('country', 'DE')}", - "courts": f"{s['dblCourts']} double + {s['sglCourts']} single ({d['totalCourts']} total)", + "courts": t["bp_courts_desc"].format(dbl=s["dblCourts"], sgl=s["sglCourts"], total=d["totalCourts"]), # Executive Summary "executive_summary": { - "heading": "Executive Summary" if is_en else "Zusammenfassung", + "heading": t["bp_exec_summary"], "facility_type": f"{venue_type} ({own_type})", "courts": d["totalCourts"], "sqm": d["sqm"], - "total_capex": _fmt_eur(d["capex"]), - "equity": _fmt_eur(d["equity"]), - "loan": _fmt_eur(d["loanAmount"]), + "total_capex": total_capex_str, + "equity": equity_str, + "loan": loan_str, "y1_revenue": _fmt_eur(d["annuals"][0]["revenue"]) if d["annuals"] else "-", "y3_ebitda": _fmt_eur(d["stabEbitda"]), - "irr": _fmt_pct(d["irr"]), - "payback": _fmt_months(d["paybackIdx"]), + "irr": irr_str, + "payback": payback_str, }, # Investment Plan (CAPEX) "investment": { - "heading": "Investment Plan" if is_en else "Investitionsplan", + "heading": t["bp_investment"], "items": d["capexItems"], - "total": _fmt_eur(d["capex"]), - "per_court": _fmt_eur(d["capexPerCourt"]), - "per_sqm": _fmt_eur(d["capexPerSqm"]), + "total": total_capex_str, + "per_court": per_court_str, + "per_sqm": per_sqm_str, }, # Operating Costs "operations": { - "heading": "Operating Costs" if is_en else "Betriebskosten", + "heading": t["bp_operations"], "items": d["opexItems"], "monthly_total": _fmt_eur(d["opex"]), "annual_total": _fmt_eur(d["annualOpex"]), @@ -85,7 +95,7 @@ def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: # Revenue Model "revenue": { - "heading": "Revenue & Profitability" if is_en else "Umsatz & Rentabilit\u00e4t", + "heading": t["bp_revenue"], "weighted_rate": _fmt_eur(d["weightedRate"]), "utilization": _fmt_pct(s["utilTarget"] / 100), "gross_monthly": _fmt_eur(d["grossRevMonth"]), @@ -96,7 +106,7 @@ def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: # 5-Year P&L "annuals": { - "heading": "5-Year Projection" if is_en else "5-Jahres-Projektion", + "heading": t["bp_annuals"], "years": [ { "year": a["year"], @@ -111,12 +121,12 @@ def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: # Financing "financing": { - "heading": "Financing Structure" if is_en else "Finanzierungsstruktur", + "heading": t["bp_financing"], "loan_pct": _fmt_pct(s["loanPct"] / 100), - "equity": _fmt_eur(d["equity"]), - "loan": _fmt_eur(d["loanAmount"]), + "equity": equity_str, + "loan": loan_str, "interest_rate": f"{s['interestRate']}%", - "term": f"{s['loanTerm']} years", + "term": t["bp_years"].format(n=s["loanTerm"]), "monthly_payment": _fmt_eur(d["monthlyPayment"]), "annual_debt_service": _fmt_eur(d["annualDebtService"]), "ltv": _fmt_pct(d["ltv"]), @@ -124,11 +134,11 @@ def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: # Key Metrics "metrics": { - "heading": "Key Metrics" if is_en else "Kennzahlen", - "irr": _fmt_pct(d["irr"]), + "heading": t["bp_metrics"], + "irr": irr_str, "moic": f"{d['moic']:.2f}x", "cash_on_cash": _fmt_pct(d["cashOnCash"]), - "payback": _fmt_months(d["paybackIdx"]), + "payback": payback_str, "break_even_util": _fmt_pct(d["breakEvenUtil"]), "ebitda_margin": _fmt_pct(d["ebitdaMargin"]), "dscr_y3": f"{d['dscr'][2]['dscr']:.2f}x" if len(d["dscr"]) >= 3 else "-", @@ -137,7 +147,7 @@ def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: # 12-Month Cash Flow "cashflow_12m": { - "heading": "12-Month Cash Flow" if is_en else "12-Monats-Liquidit\u00e4tsplan", + "heading": t["bp_cashflow_12m"], "months": [ { "month": m["m"], @@ -151,6 +161,66 @@ def get_plan_sections(state: dict, d: dict, language: str = "en") -> dict: for m in d["months"][:12] ], }, + + # Template labels + "labels": { + "scenario": t["bp_lbl_scenario"], + "generated_by": t["bp_lbl_generated_by"], + "exec_paragraph": t["bp_exec_paragraph"].format( + facility_type=f"{venue_type} ({own_type})", + courts=d["totalCourts"], + sqm=d["sqm"], + total_capex=total_capex_str, + equity=equity_str, + loan=loan_str, + irr=irr_str, + payback=payback_str, + ), + "total_investment": t["bp_lbl_total_investment"], + "equity_required": t["bp_lbl_equity_required"], + "year3_ebitda": t["bp_lbl_year3_ebitda"], + "irr": t["bp_lbl_irr"], + "payback_period": t["bp_lbl_payback_period"], + "year1_revenue": t["bp_lbl_year1_revenue"], + "item": t["bp_lbl_item"], + "amount": t["bp_lbl_amount"], + "notes": t["bp_lbl_notes"], + "total_capex": t["bp_lbl_total_capex"], + "capex_stats": t["bp_lbl_capex_stats"].format(per_court=per_court_str, per_sqm=per_sqm_str), + "equity": t["bp_lbl_equity"], + "loan": t["bp_lbl_loan"], + "interest_rate": t["bp_lbl_interest_rate"], + "loan_term": t["bp_lbl_loan_term"], + "monthly_payment": t["bp_lbl_monthly_payment"], + "annual_debt_service": t["bp_lbl_annual_debt_service"], + "ltv": t["bp_lbl_ltv"], + "monthly": t["bp_lbl_monthly"], + "total_monthly_opex": t["bp_lbl_total_monthly_opex"], + "annual_opex": t["bp_lbl_annual_opex"], + "weighted_hourly_rate": t["bp_lbl_weighted_hourly_rate"], + "target_utilization": t["bp_lbl_target_utilization"], + "gross_monthly_revenue": t["bp_lbl_gross_monthly_revenue"], + "net_monthly_revenue": t["bp_lbl_net_monthly_revenue"], + "monthly_ebitda": t["bp_lbl_monthly_ebitda"], + "monthly_net_cf": t["bp_lbl_monthly_net_cf"], + "year": t["bp_lbl_year"], + "revenue": t["bp_lbl_revenue"], + "ebitda": t["bp_lbl_ebitda"], + "debt_service": t["bp_lbl_debt_service"], + "net_cf": t["bp_lbl_net_cf"], + "moic": t["bp_lbl_moic"], + "cash_on_cash": t["bp_lbl_cash_on_cash"], + "payback": t["bp_lbl_payback"], + "break_even_util": t["bp_lbl_break_even_util"], + "ebitda_margin": t["bp_lbl_ebitda_margin"], + "dscr_y3": t["bp_lbl_dscr_y3"], + "yield_on_cost": t["bp_lbl_yield_on_cost"], + "month": t["bp_lbl_month"], + "opex": t["bp_lbl_opex"], + "debt": t["bp_lbl_debt"], + "cumulative": t["bp_lbl_cumulative"], + "disclaimer": t["bp_lbl_disclaimer"], + }, } return sections diff --git a/padelnomics/src/padelnomics/content/routes.py b/padelnomics/src/padelnomics/content/routes.py index 1032153..98ab76e 100644 --- a/padelnomics/src/padelnomics/content/routes.py +++ b/padelnomics/src/padelnomics/content/routes.py @@ -10,6 +10,7 @@ from markupsafe import Markup from quart import Blueprint, abort, g, render_template, request from ..core import capture_waitlist_email, config, csrf_protect, fetch_all, fetch_one, waitlist_gate +from ..i18n import get_translations bp = Blueprint( "content", @@ -37,9 +38,20 @@ SECTION_TEMPLATES = { } # Standalone Jinja2 env for baking scenario cards into static HTML. -# Does not require a Quart app context. +# Does not use a Quart request context, so url_for and t are injected +# explicitly. Baked content is always EN (admin operation). _TEMPLATE_DIR = Path(__file__).parent / "templates" _bake_env = Environment(loader=FileSystemLoader(str(_TEMPLATE_DIR)), autoescape=True) +_bake_env.filters["tformat"] = lambda s, **kw: s.format_map(kw) + +# Hardcoded EN URL stubs — the bake env has no request context so Quart's +# url_for cannot be used. Only endpoints referenced by scenario card templates +# need to be listed here. +_BAKE_URLS: dict[str, str] = { + "planner.index": "/en/planner/", + "directory.index": "/en/directory/", +} +_bake_env.globals["url_for"] = lambda endpoint, **kw: _BAKE_URLS.get(endpoint, f"/{endpoint}") def is_reserved_path(url_path: str) -> bool: @@ -79,7 +91,12 @@ async def bake_scenario_cards(html: str) -> str: state_data = json.loads(scenario["state_json"]) tmpl = _bake_env.get_template(template_name) - card_html = tmpl.render(scenario=scenario, d=calc_data, s=state_data) + # Baking is always in the EN admin context; t and lang are required + # by scenario card templates for translated labels. + card_html = tmpl.render( + scenario=scenario, d=calc_data, s=state_data, + lang="en", t=get_translations("en"), + ) html = html[:match.start()] + card_html + html[match.end():] return html diff --git a/padelnomics/src/padelnomics/content/templates/article_detail.html b/padelnomics/src/padelnomics/content/templates/article_detail.html index b11cf24..7df25ad 100644 --- a/padelnomics/src/padelnomics/content/templates/article_detail.html +++ b/padelnomics/src/padelnomics/content/templates/article_detail.html @@ -40,7 +40,7 @@

{{ article.title }}

- {% if article.published_at %}{% if lang == 'de' %}Veröffentlicht{% else %}Published{% endif %} {{ article.published_at[:10] }} · {% endif %}{% if lang == 'de' %}Padelnomics Forschung{% else %}Padelnomics Research{% endif %} + {% if article.published_at %}{{ t.article_detail_published_label }} {{ article.published_at[:10] }} · {% endif %}{{ t.article_detail_research_label }}

diff --git a/padelnomics/src/padelnomics/content/templates/markets.html b/padelnomics/src/padelnomics/content/templates/markets.html index e6337ff..1acb1aa 100644 --- a/padelnomics/src/padelnomics/content/templates/markets.html +++ b/padelnomics/src/padelnomics/content/templates/markets.html @@ -1,11 +1,11 @@ {% extends "base.html" %} -{% block title %}{% if lang == 'de' %}Padel-Märkte - {{ config.APP_NAME }}{% else %}Padel Markets - {{ config.APP_NAME }}{% endif %}{% endblock %} +{% block title %}{{ t.markets_page_title }} - {{ config.APP_NAME }}{% endblock %} {% block head %} - - - + + + {% endblock %} {% block content %} @@ -19,7 +19,7 @@
- +
- +
- + -

Email cannot be changed

+

{{ t.dash_email_hint }}

- - + +
- +
-

Danger Zone

+

{{ t.dash_danger_zone }}

-

Once you delete your account, there is no going back.

+

{{ t.dash_delete_warning }}

- Delete Account -

This will delete all your scenarios and data permanently.

+ {{ t.dash_delete_account }} +

{{ t.dash_delete_confirm }}

- +
diff --git a/padelnomics/src/padelnomics/directory/templates/directory.html b/padelnomics/src/padelnomics/directory/templates/directory.html index 87bad7f..bd5dd65 100644 --- a/padelnomics/src/padelnomics/directory/templates/directory.html +++ b/padelnomics/src/padelnomics/directory/templates/directory.html @@ -1,17 +1,11 @@ {% extends "base.html" %} -{% block title %}{% if lang == 'de' %}Padel-Platz Anbieterverzeichnis - {{ config.APP_NAME }}{% else %}Padel Court Supplier Directory - {{ config.APP_NAME }}{% endif %}{% endblock %} +{% block title %}{{ t.dir_page_title }} - {{ config.APP_NAME }}{% endblock %} {% block head %} -{% if lang == 'de' %} - - - -{% else %} - - - -{% endif %} + + +
-

Lead Feed

+

{{ t.sd_leads_h2 }}

- {{ supplier.credit_balance }} credits + {{ supplier.credit_balance }} {{ t.sd_leads_credits }} Buy More + hx-push-url="{{ url_for('suppliers.dashboard', tab='boosts') }}">{{ t.sd_leads_buy_more }}
- All + hx-get="{{ url_for('suppliers.dashboard_leads', country=current_country, timeline=current_timeline) }}">{{ t.sd_leads_filter_all }} Hot + hx-get="{{ url_for('suppliers.dashboard_leads', heat='hot', country=current_country, timeline=current_timeline) }}">{{ t.sd_leads_filter_hot }} Warm + hx-get="{{ url_for('suppliers.dashboard_leads', heat='warm', country=current_country, timeline=current_timeline) }}">{{ t.sd_leads_filter_warm }} Cool + hx-get="{{ url_for('suppliers.dashboard_leads', heat='cool', country=current_country, timeline=current_timeline) }}">{{ t.sd_leads_filter_cool }}
@@ -70,7 +70,7 @@ hx-target="#dashboard-content" hx-include="[name='heat'],[name='timeline']" name="country"> - + {% for c in countries %} {% endfor %} @@ -82,13 +82,13 @@ Any + hx-get="{{ url_for('suppliers.dashboard_leads', heat=current_heat, country=current_country) }}">{{ t.sd_leads_filter_any }} ASAP + hx-get="{{ url_for('suppliers.dashboard_leads', heat=current_heat, country=current_country, timeline='asap') }}">{{ t.sd_leads_filter_asap }} 3-6mo + hx-get="{{ url_for('suppliers.dashboard_leads', heat=current_heat, country=current_country, timeline='3_6_months') }}">{{ t.sd_leads_filter_3_6mo }} 6-12mo + hx-get="{{ url_for('suppliers.dashboard_leads', heat=current_heat, country=current_country, timeline='6_12_months') }}">{{ t.sd_leads_filter_6_12mo }}
{% if leads %} @@ -103,27 +103,27 @@
{{ (lead.heat_score or 'cool') | upper }} {% if lead.country in service_area %} - Your region + {{ t.sd_leads_region_badge }} {% endif %}
-
Facility
{{ lead.facility_type or '-' }}
-
Courts
{{ lead.court_count or '-' }}
-
Country
{{ lead.country or '-' }}
-
Timeline
{{ lead.timeline or '-' }}
-
Budget
{% if lead.budget_estimate %}€{{ lead.budget_estimate }}{% else %}-{% endif %}
+
{{ t.sd_leads_facility }}
{{ lead.facility_type or '-' }}
+
{{ t.sd_leads_courts }}
{{ lead.court_count or '-' }}
+
{{ t.sd_leads_country }}
{{ lead.country or '-' }}
+
{{ t.sd_leads_timeline }}
{{ lead.timeline or '-' }}
+
{{ t.sd_leads_budget }}
{% if lead.budget_estimate %}€{{ lead.budget_estimate }}{% else %}-{% endif %}
{# Bidder count messaging #} {% if lead.bidder_count == 0 %} -
No other suppliers yet — be first!
+
{{ t.sd_leads_be_first }}
{% else %} -
{{ lead.bidder_count }} supplier{{ 's' if lead.bidder_count != 1 }} already unlocked
+
{{ lead.bidder_count }} {{ t.sd_leads_already_unlocked }}
{% endif %}
-
{{ lead.credit_cost or '?' }} credits to unlock
+
{{ lead.credit_cost or '?' }} {{ t.sd_leads_credits_to_unlock }}
- +
@@ -133,8 +133,8 @@ {% else %}
-

No leads match your filters

-

Try adjusting your filters, or check back later for new leads.

+

{{ t.sd_leads_no_match }}

+

{{ t.sd_leads_no_match_hint }}

{% endif %} diff --git a/padelnomics/src/padelnomics/suppliers/templates/suppliers/partials/dashboard_listing.html b/padelnomics/src/padelnomics/suppliers/templates/suppliers/partials/dashboard_listing.html index 3339b08..9d7be97 100644 --- a/padelnomics/src/padelnomics/suppliers/templates/suppliers/partials/dashboard_listing.html +++ b/padelnomics/src/padelnomics/suppliers/templates/suppliers/partials/dashboard_listing.html @@ -43,12 +43,12 @@ {% if saved is defined and saved %} -
Listing saved successfully.
+
{{ t.sd_lst_saved }}
{% endif %}
-

Your Directory Card Preview

+

{{ t.sd_lst_preview_title }}

{% include "suppliers/partials/dashboard_listing_preview.html" %}
@@ -56,53 +56,53 @@
-

Edit Company Info

+

{{ t.sd_lst_edit_title }}

- +
- - {{ t.sd_lst_tagline }} +
- +
- +
- +
- +
-