Prompt: Add multilingual SEO + GEO the right way Copy everything below the line into Cursor / Claude / ChatGPT (Agent mode with repo access). Edit the Variables block first. ═══════════════════════════════════════════════════════════════════ VARIABLES — edit these before running ═══════════════════════════════════════════════════════════════════ PRODUCT_NAME: SendRad SITE_URL: https://www.sendrad.com DEFAULT_LOCALE: en TARGET_LOCALES: ar, de, fr, es STACK: Next.js App Router + next-intl HOSTING: Vercel (use x-vercel-ip-country for geo) RTL_LOCALES: ar CURRENCY_NOTE: Currency follows country, NOT language (keep USD catalog; optional presentment currencies) BILLING_PROVIDER: Polar (omit checkout locale for Arabic if provider has no ar) PERSIST_LOCALE_IN_DB: yes — profiles.ui_locale (or equivalent user preference column) ANALYTICS: PostHog — set ui_locale (+ presentment_currency if billing) on product events LEGAL_POLICY: English-governs until a lawyer reviews native translations (localized chrome + banner OK) COMPETITOR_COMPARE: ManyChat (replace with your main competitor, or "none") PRIMARY_MARKETS: US/UK (EN), MENA (AR), DACH (DE), FR, ES (Spain) DO_NOT_CLAIM: fake certifications, Meta Tech Provider, GDPR/SOC unless audited AVOID_VENDOR_NAMES: never put internal vendor/integration brand names in titles, metas, or H1s ═══════════════════════════════════════════════════════════════════ GOAL ═══════════════════════════════════════════════════════════════════ Ship production-ready multilingual routing, copy, and SEO/GEO for PRODUCT_NAME so each language has its own indexable URL, correct hreflang, crawler-safe geo, message parity, and market-native commercial pages — not Google Translate bolted on. Match these outcomes (proven on a real SaaS shipping en/ar/de/fr/es): 1. One language per URL 2. Default locale unprefixed; others prefixed (localePrefix: as-needed) 3. Localized public slugs for money pages (native keywords in the path) 4. Reciprocal hreflang for every TARGET_LOCALE + x-default → English 5. Humans can be geo-redirected; crawlers must NEVER be 6. Cookie / language switcher overrides geo 7. Shared UI strings in message catalogs with automated key-parity tests 8. Marketing long-form pages written per market (not word-for-word translation) 9. Sitemap includes language alternates; robots + llms.txt stay healthy 10. RTL, fonts, dir="rtl", and bidi for RTL_LOCALES 11. Analytics + optional DB preference remember the chosen UI locale 12. No regional duplicate sites (en-US vs en-GB, ar-EG vs ar-SA) unless content is truly unique ═══════════════════════════════════════════════════════════════════ NON-NEGOTIABLE RULES ═══════════════════════════════════════════════════════════════════ URL & indexing - Unique URL per locale. Never serve different languages on the same URL. - Do NOT reuse English slugs under a locale prefix for the same commercial intent (e.g. /de/pricing). Use native Latin slugs (/de/preise, /ar/as3ar, /fr/tarifs). - Internal app pathnames stay English (next-intl pathnames map). Public paths localize. - Canonical = the absolute URL of THAT locale page. - hreflang must be reciprocal and include x-default pointing at DEFAULT_LOCALE. - Sharing the default-locale URL must still work (e.g. /pricing). Geo may 307 redirect humans to the localized path; crawlers keep the English URL. Geo detection - Resolve country server-side only (HOSTING header, e.g. x-vercel-ip-country). - Never trust client-reported country/currency for billing or SEO redirects. - Map allowlisted countries → TARGET_LOCALES; fall back to Accept-Language; then DEFAULT_LOCALE. - Skip geo redirect when: locale already in path, locale cookie set, or User-Agent is a known crawler (Googlebot, Bingbot, GPTBot, ClaudeBot, PerplexityBot, social preview bots, etc.). - Use 307 for temporary geo redirects. Set locale cookie after redirect. - Language switcher writes cookie + navigates to the localized pathname for the same intent. Content & SEO - Translate UI chrome (nav, buttons, errors, toasts). For commercial SEO pages: localize intent (H1, title, FAQ, compare angles) for PRIMARY_MARKETS. - One job per page to avoid cannibalization: home = category+CTA; feature = mechanism; integration = platform+setup; compare = commercial; guides = informational. - Keep legal Terms/Privacy English-governs until lawyer-reviewed (LEGAL_POLICY). - Never invent certifications or compliance badges (DO_NOT_CLAIM). - Never put AVOID_VENDOR_NAMES in public SEO surfaces. Quality gates - Message key parity test across all locales (fail CI if keys drift). - No hardcoded user-visible English strings left in localized UI surfaces. - setRequestLocale / generateStaticParams for [locale] where applicable. - Build must pass. Add focused unit tests for country→locale, path helpers, hreflang. ═══════════════════════════════════════════════════════════════════ IMPLEMENTATION PLAN (do in order) ═══════════════════════════════════════════════════════════════════ Phase 0 — Inventory - Detect current framework, router, existing i18n, sitemap, metadata helpers. - List all public marketing routes and app/dashboard routes. - Confirm STACK; if not next-intl + App Router, adapt the same architecture (prefix routing, localized path map, server geo, hreflang) to the stack in use. Phase 1 — Routing foundation - Add TARGET_LOCALES + DEFAULT_LOCALE config. - Move public + app pages under app/[locale]/… (or equivalent). - Wire next-intl plugin, request config, navigation helpers, middleware/proxy. - localeDetection: false (you own geo + cookie; do not let the library fight you). - Skip i18n middleware for /api, /auth, webhooks, sitemap.xml, robots.txt, static assets. - pathnames: map high-intent marketing routes to native slugs per locale. Leave dashboard/auth paths untranslated (stable product URLs). Phase 2 — Message catalogs - Split namespaces: marketing, app, auth, emails, errors (adjust to product). - Extract hardcoded strings into messages/{locale}/*.json. - Add messages-parity test: every locale must have the exact same flattened keys as en. - Server actions / toasts / a11y labels must use getTranslations / useTranslations — no English leftovers. Phase 3 — SEO metadata + sitemap + GEO surfaces - Central buildPageMetadata({ title, description, path, locale }) that sets: absolute title, description, canonical, alternates.languages (hreflang + x-default), openGraph.locale / alternateLocale, twitter card, robots. - Sitemap: one entry per internal path (default-locale URL) with alternates.languages covering all locales via the same pathname map. - robots.txt: allow marketing; disallow private app if needed; point to sitemap. - Optional but recommended for GEO: /llms.txt curated Markdown index of authoritative pages (auto-generated from your content registry). It does NOT replace sitemap or schema. - JSON-LD Organization/WebSite: stable entity name + SITE_URL; sameAs for real profiles only. - Keep entity one-liner identical across site + social (GEO authority consistency). Phase 4 — Geo + preference - country→locale allowlist for PRIMARY_MARKETS. - Cookie NEXT_LOCALE (or equivalent) + optional LOCALE_SOURCE=geo|user. - Signed-in: persist ui_locale in DB if PERSIST_LOCALE_IN_DB=yes (first create from cookie; later respect stored preference + language switcher updates). - Language switcher in header; updates cookie, path, and preference. Phase 5 — RTL / fonts / direction - For RTL_LOCALES: html lang + dir=rtl, Arabic-capable font, bidi utilities for mixed LTR tokens (brand names, URLs, Instagram/WhatsApp). - Mirror spacing logically (logical CSS properties), not with hard-coded left/right where possible. Phase 6 — Market content (write, don’t machine-translate) - For each TARGET_LOCALE, produce native titles/H1s/FAQs for: pricing, top feature, top integration, main compare page, top use-case. - Research native commercial phrases (not English calques). Example pattern: EN “AI sales agent” / AR “وكيل مبيعات” / DE “KI-Vertriebsagent” / FR “agent IA” / ES “agente IA”. - Add market-only pages only when demand exists; leave weak intents in DEFAULT_LOCALE. - Body copy: same facts as English; natural length; local spelling/orthography rules (FR accents + narrow no-break spaces before ;:?! ; ES Spain wording; DE uses KI not AI in titles). Phase 7 — Billing / analytics (if applicable) - Never trust client country for price or currency. - If BILLING_PROVIDER lacks a locale, fall back gracefully (e.g. Arabic UI + English checkout). - If ANALYTICS set: $set / event props for ui_locale; never identify by email; no PII in events. - Presentment currencies independent of UI language (CURRENCY_NOTE). Phase 8 — Verify - npm run build (or project equivalent). - Visit /pricing as crawler UA → stays English; as geo country → redirects to localized slug. - View-source: hreflang set reciprocal; canonical correct; lang/dir correct. - Sitemap lists alternates; GSC-ready. - Message parity tests green. - Spot-check RTL layout on mobile + desktop. - Document slug map + measurement (GSC per locale path for 3 commercial clusters). ═══════════════════════════════════════════════════════════════════ DELIVERABLES ═══════════════════════════════════════════════════════════════════ 1. Working multilingual site matching the rules above 2. Short LOCALE SEO doc: URL architecture, locked slug map, cannibalization notes, primary commercial phrases per locale, market-only page backlog 3. Optional GEO note: entity one-liner, llms.txt, weekly AI-citation prompt tests 4. Summary of files changed and how to add a new locale later (config + messages + pathnames + content overrides + parity test) ═══════════════════════════════════════════════════════════════════ CONSTRAINTS ═══════════════════════════════════════════════════════════════════ - Production-ready, secure, server-authoritative. No dual/legacy routing paths — migrate callers and delete old unprefixed app trees in the same change set. - Prefer one metadata helper, one pathname helper, one geo resolver — no page copies. - Do not geo-park crawlers on localized URLs. - Do not auto-create thin translated doorway pages for every English URL. - Ask clarifying questions only if a variable above is impossible on this repo; otherwise implement with sensible defaults from the Variables block.