Ich habe in den vorhandenen Antworten auf Fragen im Zusammenhang mit Codepunkten für Astralebenen oder der Internationalisierung keine Erwähnung gesehen . "Großbuchstaben" bedeuten nicht in jeder Sprache dasselbe, wenn ein bestimmtes Skript verwendet wird.
Anfangs sah ich keine Antworten auf Probleme im Zusammenhang mit Codepunkten für Astralebenen. Es gibt eine , aber sie ist ein bisschen begraben (wie diese, denke ich!)
Die meisten der vorgeschlagenen Funktionen sehen folgendermaßen aus:
function capitalizeFirstLetter(str) {
return str[0].toUpperCase() + str.slice(1);
}
Einige Großbuchstaben liegen jedoch außerhalb des BMP (mehrsprachige Grundebene, Codepunkte U + 0 bis U + FFFF). Nehmen Sie zum Beispiel diesen Deseret-Text:
capitalizeFirstLetter("𐐶𐐲𐑌𐐼𐐲𐑉"); // "𐐶𐐲𐑌𐐼𐐲𐑉"
Das erste Zeichen wird hier nicht groß geschrieben, da die Array-indizierten Eigenschaften von Zeichenfolgen nicht auf "Zeichen" oder Codepunkte * zugreifen. Sie greifen auf UTF-16-Codeeinheiten zu. Dies gilt auch beim Schneiden - die Indexwerte zeigen auf Codeeinheiten.
Es ist zufällig, dass UTF-16-Codeeinheiten 1: 1 mit USV-Codepunkten innerhalb von zwei Bereichen sind, U + 0 bis U + D7FF und U + E000 bis U + FFFF einschließlich. Die meisten Groß- und Kleinschreibung fallen in diese beiden Bereiche, aber nicht alle.
Ab ES2015 wurde der Umgang damit etwas einfacher. String.prototype[@@iterator]
liefert Zeichenfolgen, die Codepunkten ** entsprechen. So können wir zum Beispiel Folgendes tun:
function capitalizeFirstLetter([ first, ...rest ]) {
return [ first.toUpperCase(), ...rest ].join('');
}
capitalizeFirstLetter("𐐶𐐲𐑌𐐼𐐲𐑉") // "𐐎𐐲𐑌𐐼𐐲𐑉"
Für längere Saiten ist dies wahrscheinlich nicht besonders effizient *** - wir müssen den Rest nicht wirklich wiederholen. Wir könnten verwenden String.prototype.codePointAt
, um an diesen ersten (möglichen) Buchstaben zu gelangen, aber wir müssten immer noch bestimmen, wo das Slice beginnen soll. Eine Möglichkeit, das Wiederholen des Restes zu vermeiden, besteht darin, zu testen, ob sich der erste Codepunkt außerhalb des BMP befindet. Ist dies nicht der Fall, beginnt das Slice bei 1, und wenn dies der Fall ist, beginnt das Slice bei 2.
function capitalizeFirstLetter(str) {
const firstCP = str.codePointAt(0);
const index = firstCP > 0xFFFF ? 2 : 1;
return String.fromCodePoint(firstCP).toUpperCase() + str.slice(index);
}
capitalizeFirstLetter("𐐶𐐲𐑌𐐼𐐲𐑉") // "𐐎𐐲𐑌𐐼𐐲𐑉"
Sie könnten stattdessen bitweise Mathematik verwenden > 0xFFFF
, aber es ist wahrscheinlich einfacher, diesen Weg zu verstehen, und beide würden dasselbe erreichen.
Wir können diese Funktion auch in ES5 und darunter ausführen, indem wir diese Logik bei Bedarf etwas weiter ausbauen. In ES5 gibt es keine intrinsischen Methoden für die Arbeit mit Codepunkten. Daher müssen wir manuell testen, ob die erste Codeeinheit eine Ersatzeinheit ist ****:
function capitalizeFirstLetter(str) {
var firstCodeUnit = str[0];
if (firstCodeUnit < '\uD800' || firstCodeUnit > '\uDFFF') {
return str[0].toUpperCase() + str.slice(1);
}
return str.slice(0, 2).toUpperCase() + str.slice(2);
}
capitalizeFirstLetter("𐐶𐐲𐑌𐐼𐐲𐑉") // "𐐎𐐲𐑌𐐼𐐲𐑉"
Zu Beginn habe ich auch Überlegungen zur Internationalisierung erwähnt. Einige davon sind sehr schwer zu erklären, da sie nicht nur Kenntnisse darüber erfordern, was Sprache verwendet wird, sondern möglicherweise auch spezifische Kenntnisse der Wörter in der Sprache erfordern. Beispielsweise wird der irische Digraph "mb" am Anfang eines Wortes als "mB" großgeschrieben. Ein anderes Beispiel, das deutsche Eszett, beginnt nie ein Wort (afaik), hilft aber dennoch, das Problem zu veranschaulichen. Das Kleinbuchstaben-Eszett ("ß") wird in "SS" großgeschrieben, "SS" kann jedoch in "ß" oder "ss" in Kleinbuchstaben geschrieben werden. Sie benötigen Out-of-Band-Kenntnisse der deutschen Sprache, um zu wissen, was richtig ist!
Das bekannteste Beispiel für diese Art von Problemen ist wahrscheinlich das Türkische. Im türkischen Latein ist die Großbuchstabe von i İ, während die Kleinbuchstabe von I ı ist - es sind zwei verschiedene Buchstaben. Glücklicherweise haben wir eine Möglichkeit, dies zu erklären:
function capitalizeFirstLetter([ first, ...rest ], locale) {
return [ first.toLocaleUpperCase(locale), ...rest ].join('');
}
capitalizeFirstLetter("italy", "en") // "Italy"
capitalizeFirstLetter("italya", "tr") // "İtalya"
In einem Browser wird das am meisten bevorzugte Sprach-Tag des Benutzers durch angezeigt navigator.language
, eine Liste in der Reihenfolge seiner Präferenz wird unter gefunden navigator.languages
und die Sprache eines bestimmten DOM-Elements kann (normalerweise) Object(element.closest('[lang]')).lang || YOUR_DEFAULT_HERE
in mehrsprachigen Dokumenten abgerufen werden .
In Agenten, die Unicode-Eigenschaftszeichenklassen in RegExp unterstützen, die in ES2018 eingeführt wurden, können wir die Dinge weiter bereinigen, indem wir direkt ausdrücken, an welchen Zeichen wir interessiert sind:
function capitalizeFirstLetter(str, locale=navigator.language) {
return str.replace(/^\p{CWU}/u, char => char.toLocaleUpperCase(locale));
}
Dies könnte ein wenig optimiert werden, um auch die Großschreibung mehrerer Wörter in einer Zeichenfolge mit ziemlich guter Genauigkeit zu handhaben. Die ZeicheneigenschaftCWU
oder Changes_When_Uppercased stimmt mit allen Codepunkten überein , die sich in Großbuchstaben ändern. Das können wir versuchen , mit einem titlecased digraph Zeichen wie die niederländische ij zum Beispiel:
capitalizeFirstLetter('ijsselmeer'); // "IJsselmeer"
Zum Zeitpunkt des Schreibens (Februar 2020) hat Firefox / Spidermonkey noch keine der in den letzten zwei Jahren eingeführten RegExp-Funktionen implementiert *****. Sie können den aktuellen Status dieser Funktion in der Kangax-Kompatibilitätstabelle überprüfen . Babel ist in der Lage, RegExp-Literale mit Eigenschaftsreferenzen auf äquivalente Muster ohne diese zu kompilieren. Beachten Sie jedoch, dass der resultierende Code enorm sein kann.
Personen, die diese Frage stellen, werden sich aller Wahrscheinlichkeit nach nicht mit der Kapitalisierung oder Internationalisierung von Deseret befassen. Es ist jedoch gut, sich dieser Probleme bewusst zu sein, da es eine gute Chance gibt, dass Sie ihnen irgendwann begegnen, auch wenn sie derzeit keine Bedenken haben. Es handelt sich nicht um "Randfälle", oder vielmehr, sie sind per Definition keine Randfälle - es gibt ein ganzes Land, in dem die meisten Menschen sowieso Türkisch sprechen, und das Zusammenführen von Codeeinheiten mit Codepunkten ist eine ziemlich häufige Fehlerquelle (insbesondere bei in Bezug auf Emoji). Sowohl Zeichenfolgen als auch Sprache sind ziemlich kompliziert!
* Die Codeeinheiten von UTF-16 / UCS2 sind auch Unicode-Codepunkte in dem Sinne, dass z. B. U + D800 technisch gesehen ein Codepunkt ist, aber das bedeutet es hier nicht ... irgendwie ... obwohl es hübsch wird verschwommen. Was die Surrogate definitiv nicht sind, sind USVs (Unicode-Skalarwerte).
** Wenn eine Ersatzcodeeinheit „verwaist“ ist - dh nicht Teil eines logischen Paares -, können Sie auch hier noch Ersatzzeichen erhalten.
*** könnte sein. Ich habe es nicht getestet. Wenn Sie nicht festgestellt haben, dass Kapitalisierung ein bedeutender Engpass ist, würde ich wahrscheinlich nicht ins Schwitzen kommen - wählen Sie, was Ihrer Meinung nach am klarsten und lesbarsten ist.
**** Eine solche Funktion möchte möglicherweise sowohl die erste als auch die zweite Codeeinheit anstatt nur die erste testen, da es möglich ist, dass die erste Einheit eine verwaiste Leihmutter ist. Zum Beispiel würde die Eingabe "\ uD800x" das X so wie es ist großschreiben, was erwartet werden kann oder nicht.
***** Hier ist das Bugzilla-Problem, wenn Sie den Fortschritt direkter verfolgen möchten.