Wie wählt man die richtige Pluralform für verschiedene Sprachen?

Nutzen Sie JavaScripts Intl.PluralRules, um je nach sprachspezifischen Regeln zwischen einem, zwei, wenigen oder vielen Elementen zu unterscheiden.

Einführung

Wenn Sie Texte mit Mengenangaben anzeigen, benötigen Sie unterschiedliche Nachrichten für verschiedene Anzahlen. Im Englischen schreibt man "1 file", aber "2 files". Der einfachste Ansatz ist, eine Zahl mit einem Wort zu verknüpfen und bei Bedarf ein "s" anzuhängen.

function formatFileCount(count) {
  return count === 1 ? `${count} file` : `${count} files`;
}

Dieser Ansatz scheitert in drei Punkten. Erstens ergibt sich im Englischen für Null eine unpassende Formulierung ("0 files" sollte eigentlich "no files" heißen). Zweitens funktioniert er nicht bei komplexen Pluralformen wie "1 child, 2 children" oder "1 person, 2 people". Drittens – und am wichtigsten – haben andere Sprachen völlig andere Pluralregeln, die dieser Code nicht abbilden kann.

JavaScript stellt Intl.PluralRules bereit, um dieses Problem zu lösen. Diese API bestimmt für jede Zahl und jede Sprache die richtige Pluralform gemäß dem Unicode CLDR-Standard, der weltweit in professionellen Übersetzungssystemen verwendet wird.

Warum verschiedene Sprachen unterschiedliche Pluralformen benötigen

Englisch verwendet zwei Pluralformen. Man schreibt "1 book" und "2 books". Das Wort ändert sich, wenn die Anzahl genau eins ist, im Vergleich zu allen anderen Zahlen.

Andere Sprachen funktionieren anders. Polnisch verwendet drei Formen auf Basis komplexer Regeln. Russisch verwendet vier Formen. Arabisch verwendet sechs Formen. Manche Sprachen nutzen für alle Mengen nur eine Form.

Hier sind Beispiele, wie sich das Wort für "Apfel" je nach Anzahl in verschiedenen Sprachen verändert:

Englisch: 1 apple, 2 apples, 5 apples, 0 apples

Polnisch: 1 jabłko, 2 jabłka, 5 jabłek, 0 jabłek

Russisch: 1 яблоко, 2 яблока, 5 яблок, 0 яблок

Arabisch: Verwendet sechs verschiedene Formen, abhängig davon, ob Sie null, eins, zwei, einige, viele oder andere Mengen haben

Der Unicode CLDR definiert die genauen Regeln dafür, wann jede Form in jeder Sprache verwendet werden soll. Sie können diese Regeln nicht auswendig lernen oder in Ihre Anwendung hartcodieren. Sie benötigen eine API, die diese Regeln kennt.

Was sind CLDR-Pluralkategorien

Der Unicode CLDR-Standard definiert sechs Pluralkategorien, die alle Sprachen abdecken:

  • zero: Wird in einigen Sprachen für genau null Elemente verwendet
  • one: Wird für Singularformen verwendet
  • two: Wird in Sprachen mit einer Dualform verwendet
  • few: Wird für kleine Mengen in einigen Sprachen verwendet
  • many: Wird für größere Mengen oder Brüche in einigen Sprachen verwendet
  • other: Die Standardform, die verwendet wird, wenn keine andere Kategorie zutrifft

Jede Sprache verwendet die Kategorie other. Die meisten Sprachen verwenden insgesamt nur zwei oder drei Kategorien. Die Kategorien entsprechen nicht direkt Mengen. Zum Beispiel verwendet im Polnischen die Zahl 5 die Kategorie many, aber auch 0, 25 und 1,5.

Die spezifischen Regeln dafür, welche Zahlen welchen Kategorien zugeordnet werden, unterscheiden sich je nach Sprache. JavaScript handhabt diese Komplexität über die API Intl.PluralRules.

Wie man bestimmt, welche Pluralform verwendet werden soll

Das Objekt Intl.PluralRules bestimmt, zu welcher Pluralkategorie eine Zahl in einer bestimmten Sprache gehört. Sie erstellen ein Objekt PluralRules mit einem Locale und rufen dann dessen Methode select() mit einer Zahl auf.

const rules = new Intl.PluralRules('en-US');
console.log(rules.select(0));  // "other"
console.log(rules.select(1));  // "one"
console.log(rules.select(2));  // "other"
console.log(rules.select(5));  // "other"

Im Englischen gibt select() für die Zahl 1 "one" zurück und für alles andere "other".

Polnisch verwendet drei Kategorien mit komplexeren Regeln:

const rules = new Intl.PluralRules('pl-PL');
console.log(rules.select(0));   // "many"
console.log(rules.select(1));   // "one"
console.log(rules.select(2));   // "few"
console.log(rules.select(5));   // "many"
console.log(rules.select(22));  // "few"
console.log(rules.select(25));  // "many"

Arabisch verwendet sechs Kategorien:

const rules = new Intl.PluralRules('ar-EG');
console.log(rules.select(0));   // "zero"
console.log(rules.select(1));   // "one"
console.log(rules.select(2));   // "two"
console.log(rules.select(3));   // "few"
console.log(rules.select(11));  // "many"
console.log(rules.select(100)); // "other"

Die Methode select() gibt einen String zurück, der die Kategorie identifiziert. Sie verwenden diesen String, um die entsprechende Nachricht für die Anzeige auszuwählen.

Wie man Pluralkategorien auf Nachrichten abbildet

Nachdem Sie die Pluralkategorie bestimmt haben, müssen Sie die korrekte Nachricht auswählen, die dem Benutzer angezeigt werden soll. Erstellen Sie ein Objekt, das jede Kategorie ihrer Nachricht zuordnet, und verwenden Sie dann den Kategorie-String, um die Nachricht nachzuschlagen.

const messages = {
  one: '{count} file',
  other: '{count} files'
};

function formatFileCount(count, locale) {
  const rules = new Intl.PluralRules(locale);
  const category = rules.select(count);
  const message = messages[category];
  return message.replace('{count}', count);
}

console.log(formatFileCount(1, 'en-US'));  // "1 file"
console.log(formatFileCount(5, 'en-US'));  // "5 files"

Dieses Muster funktioniert für jede Sprache. Für Polnisch stellen Sie Nachrichten für alle drei Kategorien bereit, die die Sprache verwendet:

const messages = {
  one: '{count} plik',
  few: '{count} pliki',
  many: '{count} plików'
};

function formatFileCount(count, locale) {
  const rules = new Intl.PluralRules(locale);
  const category = rules.select(count);
  const message = messages[category];
  return message.replace('{count}', count);
}

console.log(formatFileCount(1, 'pl-PL'));   // "1 plik"
console.log(formatFileCount(2, 'pl-PL'));   // "2 pliki"
console.log(formatFileCount(5, 'pl-PL'));   // "5 plików"
console.log(formatFileCount(22, 'pl-PL'));  // "22 pliki"

Die Code-Struktur bleibt über alle Sprachen hinweg identisch. Nur das messages-Objekt ändert sich. Diese Trennung ermöglicht es Übersetzern, die korrekten Nachrichten für ihre Sprache bereitzustellen, ohne Code zu modifizieren.

Wie man fehlende Pluralkategorien behandelt

Ihr messages-Objekt enthält möglicherweise nicht alle sechs möglichen Kategorien. Die meisten Sprachen verwenden nur zwei oder drei. Wenn select() eine Kategorie zurückgibt, die nicht in Ihrem messages-Objekt enthalten ist, greifen Sie auf die Kategorie other zurück.

function formatFileCount(count, locale, messages) {
  const rules = new Intl.PluralRules(locale);
  const category = rules.select(count);
  const message = messages[category] || messages.other;
  return message.replace('{count}', count);
}

const englishMessages = {
  one: '{count} file',
  other: '{count} files'
};

console.log(formatFileCount(1, 'en-US', englishMessages));  // "1 file"
console.log(formatFileCount(5, 'en-US', englishMessages));  // "5 files"

Dieses Muster stellt sicher, dass Ihr Code auch dann funktioniert, wenn das messages-Objekt unvollständig ist. Die Kategorie other existiert immer in jeder Sprache, was sie zu einem sicheren Fallback macht.

Wie man Pluralregeln mit Ordinalzahlen verwendet

Der Konstruktor Intl.PluralRules akzeptiert eine Option type, die ändert, wie Kategorien bestimmt werden. Der Standardtyp ist "cardinal", der zum Zählen von Elementen verwendet wird. Setzen Sie type: "ordinal", um Pluralformen für Ordinalzahlen wie "1.", "2.", "3." zu bestimmen.

const cardinalRules = new Intl.PluralRules('en-US', { type: 'cardinal' });
console.log(cardinalRules.select(1));  // "one"
console.log(cardinalRules.select(2));  // "other"
console.log(cardinalRules.select(3));  // "other"

const ordinalRules = new Intl.PluralRules('en-US', { type: 'ordinal' });
console.log(ordinalRules.select(1));   // "one"
console.log(ordinalRules.select(2));   // "two"
console.log(ordinalRules.select(3));   // "few"
console.log(ordinalRules.select(4));   // "other"

Kardinalregeln bestimmen "1 Artikel, 2 Artikel". Ordinalregeln bestimmen "1. Platz, 2. Platz, 3. Platz, 4. Platz". Die zurückgegebenen Kategorien unterscheiden sich, weil die grammatikalischen Muster unterschiedlich sind.

Wie man Bruchzahlen formatiert

Die select()-Methode funktioniert mit Dezimalzahlen. Verschiedene Sprachen haben spezifische Regeln dafür, wie Brüche auf Pluralkategorien abgebildet werden.

const rules = new Intl.PluralRules('en-US');
console.log(rules.select(1));    // "one"
console.log(rules.select(1.0));  // "one"
console.log(rules.select(1.5));  // "other"
console.log(rules.select(0.5));  // "other"

Im Englischen verwendet 1.0 die Singularform, aber 1.5 verwendet den Plural. Einige Sprachen haben unterschiedliche Regeln für Brüche und behandeln sie als separate Kategorie.

const messages = {
  one: '{count} file',
  other: '{count} files'
};

const rules = new Intl.PluralRules('en-US');
const count = 1.5;
const category = rules.select(count);
const message = messages[category];
console.log(message.replace('{count}', count));  // "1.5 files"

Übergeben Sie die Dezimalzahl direkt an select(). Die Methode wendet die korrekten Sprachregeln automatisch an.

Wie man einen wiederverwendbaren Plural-Formatierer erstellt

Anstatt dasselbe Muster in Ihrer gesamten Anwendung zu wiederholen, erstellen Sie eine wiederverwendbare Funktion, die die Pluralauswahllogik kapselt.

class PluralFormatter {
  constructor(locale) {
    this.locale = locale;
    this.rules = new Intl.PluralRules(locale);
  }

  format(count, messages) {
    const category = this.rules.select(count);
    const message = messages[category] || messages.other;
    return message.replace('{count}', count);
  }
}

const formatter = new PluralFormatter('en-US');

const fileMessages = {
  one: '{count} file',
  other: '{count} files'
};

const itemMessages = {
  one: '{count} item',
  other: '{count} items'
};

console.log(formatter.format(1, fileMessages));  // "1 file"
console.log(formatter.format(5, fileMessages));  // "5 files"
console.log(formatter.format(1, itemMessages));  // "1 item"
console.log(formatter.format(3, itemMessages));  // "3 items"

Diese Klasse erstellt das PluralRules-Objekt einmal und verwendet es für mehrere Formatierungsoperationen wieder. Sie können sie erweitern, um erweiterte Funktionen zu unterstützen, wie z. B. die Formatierung der Anzahl mit Intl.NumberFormat, bevor sie in die Nachricht eingefügt wird.

Wie man Pluralregeln mit Übersetzungssystemen integriert

Professionelle Übersetzungssysteme speichern Nachrichten mit Platzhaltern für Pluralkategorien. Wenn Sie Text übersetzen, stellen Sie alle Pluralformen bereit, die Ihre Sprache benötigt.

const translations = {
  'en-US': {
    fileCount: {
      one: '{count} file',
      other: '{count} files'
    },
    downloadComplete: {
      one: 'Download of {count} file complete',
      other: 'Download of {count} files complete'
    }
  },
  'pl-PL': {
    fileCount: {
      one: '{count} plik',
      few: '{count} pliki',
      many: '{count} plików'
    },
    downloadComplete: {
      one: 'Pobieranie {count} pliku zakończone',
      few: 'Pobieranie {count} plików zakończone',
      many: 'Pobieranie {count} plików zakończone'
    }
  }
};

class Translator {
  constructor(locale, translations) {
    this.locale = locale;
    this.translations = translations[locale] || {};
    this.rules = new Intl.PluralRules(locale);
  }

  translate(key, count) {
    const messages = this.translations[key];
    if (!messages) return key;

    const category = this.rules.select(count);
    const message = messages[category] || messages.other;
    return message.replace('{count}', count);
  }
}

const translator = new Translator('en-US', translations);
console.log(translator.translate('fileCount', 1));         // "1 file"
console.log(translator.translate('fileCount', 5));         // "5 files"
console.log(translator.translate('downloadComplete', 1));  // "Download of 1 file complete"
console.log(translator.translate('downloadComplete', 5));  // "Download of 5 files complete"

const polishTranslator = new Translator('pl-PL', translations);
console.log(polishTranslator.translate('fileCount', 1));   // "1 plik"
console.log(polishTranslator.translate('fileCount', 2));   // "2 pliki"
console.log(polishTranslator.translate('fileCount', 5));   // "5 plików"

Dieses Muster trennt Übersetzungsdaten von Code-Logik. Übersetzer stellen die Nachrichten für jede Pluralkategorie bereit, die ihre Sprache verwendet. Ihr Code wendet die Regeln automatisch an.

Wie man prüft, welche Pluralkategorien ein Gebietsschema verwendet

Die resolvedOptions()-Methode gibt Informationen über das PluralRules-Objekt zurück, listet jedoch nicht auf, welche Kategorien das Gebietsschema verwendet. Um alle Kategorien zu finden, die ein Gebietsschema verwendet, testen Sie einen Zahlenbereich und sammeln Sie die zurückgegebenen eindeutigen Kategorien.

function getPluralCategories(locale) {
  const rules = new Intl.PluralRules(locale);
  const categories = new Set();

  for (let i = 0; i <= 100; i++) {
    categories.add(rules.select(i));
    categories.add(rules.select(i + 0.5));
  }

  return Array.from(categories).sort();
}

console.log(getPluralCategories('en-US'));  // ["one", "other"]
console.log(getPluralCategories('pl-PL'));  // ["few", "many", "one"]
console.log(getPluralCategories('ar-EG'));  // ["few", "many", "one", "other", "two", "zero"]

Diese Technik testet Ganzzahlen und Halbwerte über einen Bereich hinweg. Sie erfasst die Kategorien, die Ihr Nachrichten-Objekt für ein bestimmtes Gebietsschema enthalten muss.