Geteilte Strings

So internationalisieren Sie Strings, die in mehreren Komponenten und files verwendet werden

Geteilte Strings sind Textwerte, die an mehreren Stellen in Ihrer Anwendung verwendet werden – etwa Navigationslabels, Formularmeldungen oder Konfigurationsdaten. Anstatt die Übersetzungslogik überall zu duplizieren, verwenden Sie msg, um Strings für die Übersetzung zu markieren, und useMessages, um sie zu dekodieren.

Das Problem mit gemeinsam genutzten Inhalten

Betrachten Sie diese Navigationskonfiguration, die in Ihrer gesamten App verwendet wird:

// navData.ts
export const navData = [
  {
    label: 'Startseite',
    description: 'Startseite',
    href: '/'
  },
  {
    label: 'Über uns', 
    description: 'Informationen zum Unternehmen',
    href: '/about'
  }
];

Um das zu internationalisieren, müssten Sie typischerweise:

  1. Es in eine Funktion umwandeln, die eine Übersetzungsfunktion entgegennimmt
  2. Jede Verwendung so anpassen, dass die Funktion mit t aufgerufen wird
  3. Die entstehende Komplexität in Ihrer Codebasis verwalten

Das erzeugt Wartungsaufwand und macht Ihren Code schwerer lesbar. Die Funktion msg löst das, indem Sie Zeichenfolgen direkt im Code zur Übersetzung markieren und sie bei Bedarf dekodieren können.

Schnellstart

Verwende msg, um Zeichenketten zu markieren, und useMessages, um sie zu dekodieren:

// navData.ts - Zeichenketten für die Übersetzung markieren
import { msg } from 'gt-next';

export const navData = [
  {
    label: msg('Startseite'),
    description: msg('Startseite'), 
    href: '/'
  },
  {
    label: msg('Über uns'),
    description: msg('Informationen zum Unternehmen'),
    href: '/about'
  }
];
// Komponentenverwendung – Markierte Strings dekodieren
import { useMessages } from 'gt-next';
import { navData } from './navData';

function Navigation() {
  const m = useMessages();
  
  return (
    <nav>
      {navData.map((item) => (
        <a key={item.href} href={item.href} title={m(item.description)}>
          {m(item.label)}
        </a>
      ))}
    </nav>
  );
}

Funktionsweise gemeinsamer Strings

Das System für gemeinsame Strings arbeitet in zwei Phasen:

  1. Markierungsphase: msg codiert Strings mit Übersetzungsmetadaten
  2. Dekodierungsphase: useMessages oder getMessages dekodieren und übersetzen die Strings
// msg() kodiert die Zeichenfolge mit Metadaten
const encoded = msg('Hallo, Welt!');
console.log(encoded); // "Hallo, Welt!:eyIkX2hhc2giOiJkMjA3MDliZGExNjNlZmM2In0="

// useMessages() dekodiert und übersetzt
const m = useMessages();
const translated = m(encoded); // „Hallo, Welt!“ in der Sprache des Benutzers

Kodierte Zeichenfolgen aus msg können nicht direkt verwendet werden – sie müssen mit useMessages oder getMessages decodiert werden.

Client- gegenüber Server-Nutzung

Client-Komponenten

Verwenden Sie den Hook useMessages:

import { useMessages } from 'gt-next';

const encodedString = msg('Hallo, Welt!');

function MyComponent() {
  const m = useMessages();
  return <div>{m(encodedString)}</div>;
}

Server-Komponenten

Verwenden Sie die Funktion getMessages:

import { getMessages } from 'gt-next/server';

const encodedString = msg('Hallo, Welt!');

async function MyServerComponent() {
  const m = await getMessages();
  return <div>{m(encodedString)}</div>;
}

Original-Strings mit decodeMsg abrufen

Manchmal müssen Sie auf den Original-String ohne Übersetzung zugreifen, etwa für Logging, Debugging oder Vergleiche. Verwenden Sie decodeMsg, um den Originaltext zu extrahieren:

import { decodeMsg } from 'gt-next';

const encoded = msg('Hallo, Welt!');
const original = decodeMsg(encoded); // „Hallo, Welt!“ (Original)
const translated = m(encoded); // „Hallo, Welt!“ (in der Sprache des/der Nutzer:in)

// Nützlich fürs Logging oder Debugging
console.log('Originalzeichenfolge:', decodeMsg(encoded));
console.log('Übersetzte Zeichenfolge:', m(encoded));

Anwendungsfälle für decodeMsg

  • Entwicklung & Debugging: Originalstrings zur Fehleranalyse protokollieren
  • Standardwert‑Behandlung: Originaltext verwenden, wenn Übersetzungen fehlschlagen
  • String‑Vergleiche: Mit bekannten Originalwerten vergleichen
  • Analytics: Nutzung der Originalstrings nachverfolgen
// Beispiel: Fallback-Behandlung
function getDisplayText(encodedStr) {
  const m = useMessages();
  try {
    return m(encodedStr);
  } catch (error) {
    console.warn('Übersetzung fehlgeschlagen, Original wird verwendet:', decodeMsg(encodedStr));
    return decodeMsg(encodedStr);
  }
}

Verwendung von Variablen

Für Strings mit dynamischem Inhalt Platzhalter verwenden und Variablen übergeben:

// Zeichenkette mit Variablen markieren
const items = 100;
export const pricing = [
  {
    name: 'Basic',
    price: 100,
    description: msg('Der Basic‑Tarif umfasst {items} Einträge', { items })
  }
];
// Use in component
function PricingCard() {
  const m = useMessages();
  
  return (
    <div>
      <h3>{pricing[0].name}</h3>
      <p>{m(pricing[0].description)}</p>
    </div>
  );
}

ICU Message Format

Für fortgeschrittenes Formatieren verwenden Sie die ICU-Syntax:

const count = 10;
const message = msg('Im Warenkorb {count, plural, =0 {ist kein Artikel} =1 {ist ein Artikel} other {sind {count} Artikel}}', { count });

Erfahren Sie mehr über das ICU Message Format in der Unicode-Dokumentation.

Beispiele

// config/navigation.ts
import { msg } from 'gt-next';

export const mainNav = [
  {
    label: msg('Startseite'),
    href: '/',
    icon: 'home'
  },
  {
    label: msg('Produkte'),
    href: '/products', 
    icon: 'package'
  },
  {
    label: msg('Über uns'),
    href: '/about',
    icon: 'info'
  }
];

export const footerLinks = [
  {
    title: msg('Unternehmen'),
    links: [
      { label: msg('Über uns'), href: '/about' },
      { label: msg('Karriere'), href: '/careers' },
      { label: msg('Kontakt'), href: '/contact' }
    ]
  },
  {
    title: msg('Support'), 
    links: [
      { label: msg('Hilfecenter'), href: '/help' },
      { label: msg('Dokumentation'), href: '/docs' },
      { label: msg('API-Referenz'), href: '/api' }
    ]
  }
];
// components/Navigation.tsx
import { useMessages } from 'gt-next';
import { mainNav } from '../config/navigation';

function Navigation() {
  const m = useMessages();
  
  return (
    <nav>
      {mainNav.map((item) => (
        <a key={item.href} href={item.href}>
          <Icon name={item.icon} />
          {m(item.label)}
        </a>
      ))}
    </nav>
  );
}

Formularkonfiguration

/ / config/forms.ts
import { msg } from 'gt-next';

export const formMessages = {
  placeholders: {
    email: msg('Geben Sie Ihre E‑Mail-Adresse ein'),
    password: msg('Geben Sie Ihr Passwort ein'),
    message: msg('Geben Sie hier Ihre Nachricht ein...')
  },
  actions: {
    send: msg('Nachricht senden'),
    save: msg('Änderungen speichern'),
    cancel: msg('Abbrechen')
  },
  validation: {
    required: msg('Dieses Feld ist erforderlich'),
    email: msg('Bitte geben Sie eine gültige E‑Mail-Adresse ein'),
    minLength: msg('Mindestens {min} Zeichen', { min: 8 }),
    maxLength: msg('Darf nicht mehr als {max} Zeichen enthalten', { max: 100 })
  },
  success: {
    saved: msg('Änderungen erfolgreich gespeichert'),
    sent: msg('Nachricht erfolgreich gesendet'),
    updated: msg('Profil aktualisiert')
  },
  errors: {
    network: msg('Netzwerkfehler – bitte versuchen Sie es erneut'),
    server: msg('Serverfehler – bitte wenden Sie sich an den Support'),
    timeout: msg('Die Anfrage hat ein Zeitlimit überschritten – bitte versuchen Sie es erneut')
  }
};
// components/ContactForm.tsx
import { useMessages } from 'gt-next';
import { formMessages } from '../config/forms';

function ContactForm() {
  const m = useMessages();
  const [errors, setErrors] = useState({});
  
  return (
    <form>
      <input 
        type="email"
        placeholder={m(formMessages.placeholders.email)}
        required
      />
      {errors.email && <span>{m(formMessages.validation.email)}</span>}
      
      <button type="submit">
        {m(formMessages.actions.send)}
      </button>
    </form>
  );
}

Dynamische Inhaltserstellung

// utils/productData.ts
import { msg } from 'gt-next';

function mockProducts() {
  return [
    { name: 'iPhone 15', company: 'Apple', category: 'Electronics' },
    { name: 'Galaxy S24', company: 'Samsung', category: 'Electronics' }
  ];
}

export function getProductData() {
  const products = mockProducts();
  
  return products.map(product => ({
    ...product,
    description: msg('{name} ist ein {category}-Produkt von {company}', {
      name: product.name,
      category: product.category,
      company: product.company
    })
  }));
}
// components/ProductList.tsx
import { useMessages } from 'gt-next';
import { getProductData } from '../utils/productData';

function ProductList() {
  const m = useMessages();
  const products = getProductData();
  
  return (
    <div>
      {products.map(product => (
        <div key={product.name}>
          <h3>{product.name}</h3>
          <p>{m(product.description)}</p>
        </div>
      ))}
    </div>
  );
}

Häufige Probleme

Encoded Strings direkt verwenden

Verwenden Sie die Ausgabe von msg niemals direkt:

// ❌ Falsch – verschlüsselten String direkt verwendet
const encoded = msg('Hello, world!');
return <div>{encoded}</div>; // Zeigt den verschlüsselten String, nicht die Übersetzung

// ✅ Richtig – den String zuerst entschlüsseln
const encoded = msg('Hello, world!');
const m = useMessages();
return <div>{m(encoded)}</div>; // Zeigt die korrekte Übersetzung

Dynamische Inhalte in msg()

Strings müssen zur Buildzeit bekannt sein:

// ❌ Falsch – dynamisches Template-Literal
const name = 'John';
const message = msg(`Hallo, ${name}`); // Buildzeitfehler

// ✅ Richtig – Variablen verwenden  
const name = 'John';
const message = msg('Hallo, {name}', { name });

Vergessen zu decodieren

Jeder msg-String muss decodiert werden:

// ❌ Dekodierung fehlt
const config = {
  title: msg('Dashboard'),
  subtitle: msg('Willkommen zurück')
};

// Später in der Komponente – Dekodierung vergessen
return <h1>{config.title}</h1>; // Zeigt kodierte Zeichenfolge

// ✅ Korrekt – beim Verwenden dekodieren
const m = useMessages();
return <h1>{m(config.title)}</h1>; // Zeigt übersetzten Titel

Nächste Schritte

Wie ist diese Anleitung?