Gib jeder Sprache ihre eigene URL und annotiere diese URLs dann mit gegenseitigen hreflang-Tags. Ein Dropdown, das Strings im React-State austauscht, übersetzt die Oberfläche für die Person, die bereits auf deiner Site ist, erzeugt aber nichts, was eine Suchmaschine indexieren kann – deine deutsche und spanische Version bleiben unsichtbar. globalize.now ist KI-gestützte Lokalisierungsinfrastruktur, die hartcodierte Strings in versionierte Lingui-PO-Kataloge extrahiert und bei jedem Git Push synchron hält – das macht Routen pro Locale wartbar statt zu einer einmaligen Fleißarbeit.

Diesen Schritt überspringen die meisten Lovable-Builder. Die App wird übersetzt, der Umschalter funktioniert im Browser – und trotzdem kommt kein Traffic an.


Warum indexiert Google die anderen Sprachen meiner Lovable-App nicht?

Weil diese Sprachen so gut wie sicher keine eigenen Adressen haben.

Das Standardmuster, das ein KI-Builder erzeugt, ist ein useState-Locale-Wert und ein Dropdown, das ihn setzt. Jede Sprache rendert unter derselben URL. Ein Crawler, der diese URL abruft, bekommt eine Antwort in einer Sprache und indexiert eine Seite. Es gibt nichts zu entdecken und kein Signal, das anzeigt, dass es weitere Sprachversionen gibt.

Die Lösung ist strukturell, nicht kosmetisch. Googles eigene Richtlinie zu lokalisierten Versionen ist eindeutig: alternative Sprachseiten werden über die URL identifiziert, und die Annotationen, die sie verknüpfen, müssen von jeder Seite im Set zurückgegeben werden. Wenn /de/pricing nicht als abrufbare Adresse existiert, hilft kein Markup, sie sichtbar zu machen.


Was muss ein echter Sprachumschalter leisten?

Drei Dinge, in dieser Reihenfolge: die URL ändern, die Nutzerin auf derselben Seite halten und die Wahl merken.

Die URL zu ändern ist der Teil, der für die Suche zählt. Die Nutzerin auf derselben Seite zu halten ist der Teil, der für sie zählt – und genau hier scheitern die meisten Implementierungen: Der Wechsel von /en/pricing sollte auf /de/pricing landen, nicht auf /de/. Die Wahl zu merken ist eine Annehmlichkeit und sollte niemals ein explizites Locale in der URL überschreiben.

Ein Anti-Pattern, das man beim Namen nennen sollte: automatische Weiterleitungen anhand der Browsersprache. Wenn ein Crawler in den USA /de/pricing anfragt und auf /en/pricing umgeleitet wird, existiert die deutsche Seite faktisch nicht. Biete den Wechsel an, erzwinge ihn nicht.


Wie füge ich einer Lovable-TanStack-Start-App Locale-Routing hinzu?

Füge oben im Routenbaum ein Locale-Segment ein und aktiviere von dort aus den Katalog.

Lovable hat neue Projekte im Mai 2026 auf serverseitiges Rendering mit TanStack Start umgestellt, sodass eine Route mit Locale-Präfix schon bei der ersten Antwort vollständig übersetztes HTML liefert. Genau das soll ein Crawler bekommen.

Erstelle ein dynamisches Segment, das den Rest deiner Routen umschließt:

src/routes/
  $locale/
    route.tsx
    index.tsx
    pricing.tsx

Aktiviere dann den Lingui-Katalog für dieses Locale, bevor die Kinder gerendert werden:

// src/routes/$locale/route.tsx
import { createFileRoute, Outlet, notFound } from "@tanstack/react-router";
import { i18n } from "@lingui/core";
import { I18nProvider } from "@lingui/react";

const LOCALES = ["en", "de", "es", "fr"] as const;

export const Route = createFileRoute("/$locale")({
  loader: async ({ params }) => {
    if (!LOCALES.includes(params.locale as (typeof LOCALES)[number])) {
      throw notFound();
    }
    const { messages } = await import(
      `../../locales/${params.locale}/messages.po`
    );
    i18n.loadAndActivate({ locale: params.locale, messages });
    return { locale: params.locale };
  },
  component: LocaleLayout,
});

function LocaleLayout() {
  return (
    <I18nProvider i18n={i18n}>
      <Outlet />
    </I18nProvider>
  );
}

Zwei Details leisten hier echte Arbeit. Unbekannte Locales mit notFound() abzulehnen verhindert, dass /xx/pricing für jeden Zufallspfad, den ein Crawler ausprobiert, ein weiches 200 zurückgibt. Den Katalog im Route-Loader zu laden bedeutet, dass die Aktivierung auf dem Server passiert, bevor das HTML serialisiert wird.

Wenn du noch auf dem älteren Stack bist, gilt dieselbe URL-Struktur, aber der Render-Pfad unterscheidet sich. Die Lovable-Vite-i18n-Anleitung deckt diesen Fall ab, und der TanStack-Start-Setup-Guide geht ausführlicher auf die SSR-Seite ein.


Wie baue ich die Sprachumschalter-Komponente?

Navigiere zur selben Route mit einem anderen Locale-Parameter und rendere jede Option als echten Link.

// src/components/LanguageSwitcher.tsx
import { Link, useLocation, useParams } from "@tanstack/react-router";

const LOCALES = {
  en: "English",
  de: "Deutsch",
  es: "Español",
  fr: "Français",
} as const;

export function LanguageSwitcher() {
  const { locale } = useParams({ from: "/$locale" });
  const { pathname } = useLocation();
  const rest = pathname.replace(`/${locale}`, "") || "/";

  return (
    <nav aria-label="Language">
      <ul>
        {Object.entries(LOCALES).map(([code, label]) => (
          <li key={code}>
            <Link
              to={`/${code}${rest}`}
              hrefLang={code}
              aria-current={code === locale ? "true" : undefined}
            >
              {label}
            </Link>
          </li>
        ))}
      </ul>
    </nav>
  );
}

Rendere die Optionen als Anker, nicht als Buttons, die einen Router-Aufruf auslösen. Anker sind crawlbar und geben Suchmaschinen zusätzlich zur Sitemap einen zweiten Weg, deine Locale-Routen zu entdecken. Beschrifte jede Option in ihrer eigenen Sprache statt mit einer Flagge: Flaggen stehen für Länder, und Spanisch ist kein Land.


Wie erzeuge ich hreflang-Tags korrekt?

Gib pro Locale eine Annotation auf jeder Seite im Set aus, inklusive Selbstreferenz, plus ein x-default.

Google prüft, ob alternative URLs zurückverlinken, und ignoriert Annotationen, die nicht gegenseitig sind oder von der eigenen kanonischen Seite wegzeigen. Deshalb ist die programmatische Generierung der einzig vernünftige Ansatz – bei vier Sprachen über zwanzig Seiten hinweg sind das 320 Annotationen, die man von Hand konsistent halten müsste.

In TanStack Start baust du sie im head der Route:

// src/routes/$locale/route.tsx (excerpt)
const SITE = "https://example.com";
const LOCALES = ["en", "de", "es", "fr"] as const;

export const Route = createFileRoute("/$locale")({
  head: ({ params, location }) => {
    const rest = location.pathname.replace(`/${params.locale}`, "") || "/";
    return {
      links: [
        { rel: "canonical", href: `${SITE}/${params.locale}${rest}` },
        ...LOCALES.map((code) => ({
          rel: "alternate",
          hrefLang: code,
          href: `${SITE}/${code}${rest}`,
        })),
        { rel: "alternate", hrefLang: "x-default", href: `${SITE}/en${rest}` },
      ],
    };
  },
});

Weil die Annotationen aus demselben LOCALES-Array abgeleitet werden, das der Router nutzt, aktualisiert das Hinzufügen einer Sprache Routen, Umschalter und hreflang-Set auf einmal. Genau diese Eigenschaft gilt es zu schützen: eine Liste, drei Verbraucher.

Füge dieselben URLs mit xhtml:link-Alternates zu deiner Sitemap hinzu und stelle sicher, dass dein <html lang>-Attribut mit dem aktiven Locale übereinstimmt. Das lang-Attribut beeinflusst hreflang nicht, wirkt sich aber auf Screenreader und Browser-Übersetzungsvorschläge aus.


Warum kann ein Runtime-Übersetzungswidget das nicht leisten?

Weil das Widget erst übersetzt, nachdem die Seite bereits auf Englisch ausgeliefert wurde.

Ein Runtime-Widget tauscht Text im Browser aus, sobald sein Script geladen ist. Die erste Antwort, die ein Crawler erhält, ist das englische DOM, und die übersetzte Version existiert erst, nachdem clientseitiges JavaScript gelaufen ist. Manche Widgets bieten zwar Subdomain- oder Unterverzeichnis-Modi mit generiertem hreflang, aber dann gehören URL-Struktur und Annotationen zur Infrastruktur des Anbieters, nicht zu deinem Repository.

Diese Abhängigkeit hat einen Preis, der längst keine Theorie mehr ist. Lovalingo, ein bei Lovable-Buildern beliebtes Übersetzungs-Widget, schließt am 31. August 2026, und der Anbieter selbst rät seinen Nutzer:innen, auf eine projekteigene Internationalisierung umzusteigen. Verschwindet ein Widget, verschwinden auch die davon generierten Sprach-URLs, und die Seite fällt zurück auf Englisch. Fest eingebundene Kataloge und Routen im eigenen Code kennen so einen Schalter nicht. Die praktische Seite dieser Migration haben wir im Lovalingo-Alternativen-Vergleich aufgeschrieben.


Wie sieht es mit dem klassischen Vite-SPA-Stack aus?

Die URL-Struktur ist identisch, das Rendering nicht.

Lovable-Projekte, die vor dem Wechsel zu TanStack Start entstanden sind, sind Vite-plus-React-Single-Page-Apps, ausgeliefert als statische Dateien und im Browser gerendert. Du kannst weiterhin über /:locale routen, hreflang ausgeben und Kataloge im Repo halten. Was du verlierst, ist die Garantie, dass das erste Byte, das ein Crawler bekommt, bereits übersetzten Inhalt enthält.

Wenn SEO der Grund ist, warum du überhaupt lokalisierst, füge Prerendering für die Locale-Routen hinzu, damit jede echtes HTML ausliefert, oder plane einen Wechsel zum serverseitig gerenderten Stack. Löse das nicht, indem du zusätzlich ein Runtime-Widget auf eine clientseitig gerenderte App packst – das stapelt zwei clientseitige Render-Durchläufe übereinander und verschlechtert den ersten aussagekräftigen Bildaufbau in jeder Sprache.


Wie verhinderst du, dass vier Locales auseinanderdriften?

Automatisiere die Extraktion und behandle die Kataloge als Quellcode.

Der Grund, warum Routing pro Locale wieder aufgegeben wird, ist nicht das Routing selbst. Es liegt daran, dass jedes neue Feature englische Strings hinzufügt, die niemand extrahiert, sodass sich /de/ nach und nach mit englischen Fragmenten füllt und am Ende schlechter aussieht, als gar kein Deutsch zu haben. globalize.now übernimmt genau diese Schicht: Füge den lovable-i18n-Skill in deinem Lovable-Workspace hinzu und prompte Lovable, i18n einzurichten, oder installiere die Skills lokal:

npx skills add globalize-now/globalize-skills

Füge --all hinzu, um es für jeden Agent zu installieren. Neue Strings werden extrahiert, Schlüssel generiert, Kataloge übersetzt und die aktualisierten PO-Dateien bei jedem Git Push zurückcommittet.

Die Preise liegen bei 20 € pro Monat und Workspace, inklusive 20 € Übersetzungsguthaben (rund 200.000 Wörter) pro Monat, danach tokenbasierte Nutzung zum gleichen Preis pro Zeichen. Es gibt keine Kosten pro Sitzplatz und keine pro Sprache – eine fünfte Locale ändert also deine Routing-Konfiguration, nicht deine Rechnung. Bei der Anmeldung gibt es 5 € Übersetzungsguthaben, keine Kreditkarte nötig. Mehr Details für Entwickler und für alle, die KI-gebaute Apps releasen.

Routing und hreflang sind an einem Nachmittag erledigt. Vier Kataloge vollständig zu halten, während sich die App weiterentwickelt, ist die Aufgabe, die nie endet – und genau die automatisiert globalize.now.

globalize.now konvertiert hartcodierte App-Texte in übersetzungsreife Locale-Dateien und hält sie aktuell, während Sie veröffentlichen.

Probiere globalize.now kostenlos