Unicode et Emoji : des points de code au rendu
Unicode : la norme d'encodage qui unifie tous les caractères
Unicode attribue un numéro unique (point de code) à chaque caractère de presque tous les systèmes d'écriture du monde.
| Concept | Description | Exemple |
|---|---|---|
| Point de code (Code Point) | Numéro unique du caractère | U+4E2D (中) |
| Plan de code | Chaque 65536 points de code forme un plan | BMP (plan 0) |
| BMP | Plan multilingue de base, U+0000~U+FFFF | La plupart des caractères courants |
| Plan supplémentaire | U+10000~U+10FFFF | Emoji, écritures anciennes, caractères rares |
1. La famille d'encodages UTF
| Encodage | Longueur en octets | Encodage de « 中 » | Caractéristiques |
|---|---|---|---|
| UTF-8 | 1-4 octets | E4 B8 AD | Longueur variable, compatible ASCII, préféré pour le Web |
| UTF-16 | 2 ou 4 octets | 4E2D | Longueur variable, usage interne en JS |
| UTF-32 | 4 octets fixe | 00004E2D | Longueur fixe, consommation mémoire élevée |
Règles d'encodage UTF-8
| Plage de points de code | Octets | Modèle d'encodage |
|---|---|---|
| U+0000 ~ U+007F | 1 | 0xxxxxxx |
| U+0080 ~ U+07FF | 2 | 110xxxxx 10xxxxxx |
| U+0800 ~ U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 ~ U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
Exemple d'encodage
Caractère : 中
Point de code : U+4E2D = 0100 1110 0010 1101
UTF-8 (3 octets) :
1110xxxx 10xxxxxx 10xxxxxx
1110 0100 10 111000 10 101101
= E4 B8 AD
UTF-16 (2 octets) :
4E2D
UTF-32 (4 octets) :
00004E2D
2. Pièges d'Unicode en JavaScript
Longueur de chaîne incorrecte
'中'.length // 1 ✅
'𠮷'.length // 2 ❌ (caractère du plan supplémentaire, paire de substitution)
'👨👩👧👦'.length // 11 ❌ (séquence ZWJ d'Emoji)
'🏳️🌈'.length // 6 ❌ (Emoji de drapeau)
Cause : JS utilise UTF-16
Les caractères du plan supplémentaire (point de code > U+FFFF) nécessitent des paires de substitution (2 unités de code UTF-16) :
Caractère : 𠮷 (U+20BB7)
Paire de substitution UTF-16 : D842 DF87
JS : length = 2 (deux unités de code)
Traitement correct
// Méthode 1 : opérateur de décomposition
[...'𠮷'].length // 1 ✅
[...'👨👩👧👦'].length // 1 ✅ (mais la séquence ZWJ est comptée comme une)
// Méthode 2 : Array.from
Array.from('𠮷').length // 1 ✅
// Méthode 3 : for...of
for (const char of '𠮷👨👩👧👦') {
console.log(char); // itération caractère par caractère
}
Opérations sur les points de code
// Obtenir le point de code
'中'.codePointAt(0) // 20013 (0x4E2D)
'𠮷'.codePointAt(0) // 134047 (0x20BB7)
// Créer un caractère depuis un point de code
String.fromCodePoint(0x4E2D) // '中'
String.fromCodePoint(0x20BB7) // '𠮷'
// Note : charCodeAt ne peut pas traiter le plan supplémentaire
'𠮷'.charCodeAt(0) // 55362 (uniquement le premier substitut)
3. Analyse approfondie de l'encodage Emoji
Quatre types d'Emoji
| Type | Exemple | Point de code | Longueur |
|---|---|---|---|
| Emoji de base | 😀 | U+1F600 | 1 (point de code) |
| Emoji avec modificateur | 👩🏽 | U+1F469 + U+1F3FD | 2 (points de code) |
| Séquence ZWJ | 👨👩👧👦 | Plusieurs points de code + ZWJ | 7 (points de code) |
| Emoji de drapeau | 🇨🇳 | U+1F1E8 + U+1F1F3 | 2 (points de code) |
Connecteur de largeur nulle (ZWJ)
ZWJ (U+200D) relie plusieurs Emoji en un Emoji composé :
👨 (U+1F468) + ZWJ (U+200D) + 👩 (U+1F469) + ZWJ (U+200D) + 👧 (U+1F467) + ZWJ (U+200D) + 👦 (U+1F466)
= 👨👩👧👦 (famille de quatre)
Modificateurs de teinte de peau
Les modificateurs de teinte de peau (U+1F3FB ~ U+1F3FF) changent la teinte de peau de l'Emoji :
👩 (U+1F469) + 🏽 (U+1F3FD) = 👩🏽 (femme avec teinte de peau moyenne)
| Modificateur | Point de code | Teinte de peau |
|---|---|---|
| U+1F3FB | 🏻 | Teinte claire |
| U+1F3FC | 🏼 | Teinte moyen clair |
| U+1F3FD | 🏽 | Teinte moyenne |
| U+1F3FE | 🏾 | Teinte moyen foncé |
| U+1F3FF | 🏿 | Teinte foncée |
Emoji de drapeau
Les drapeaux sont composés de deux symboles indicateurs régionaux (ISO 3166-1 alpha-2) :
🇨 (U+1F1E8) + 🇳 (U+1F1F3) = 🇨🇳 (Chine)
🇺 (U+1F1FA) + 🇸 (U+1F1F8) = 🇺🇸 (États-Unis)
🇯 (U+1F1EF) + 🇵 (U+1F1F5) = 🇯🇵 (Japon)
4. Sélecteurs de variante
VS16 (U+FE0F) convertit la présentation textuelle en présentation Emoji :
❤ (U+2764) → présentation textuelle : ❤ (cœur noir)
❤️ (U+2764 + U+FE0F) → présentation Emoji : ❤️ (cœur rouge)
| Caractère | Sans VS16 | Avec VS16 |
|---|---|---|
| ☕ | ☕ (texte) | ☕️ (Emoji) |
| ⚡ | ⚡ (texte) | ⚡️ (Emoji) |
| ❤ | ❤ (texte) | ❤️ (Emoji) |
5. Utilisation de bibliothèques d'outils pour traiter Unicode
Encodage/décodage Unicode
- Ouvrez l'outil d'encodage/décodage Unicode
- Entrez du texte ou un point de code
- Vérifiez les résultats de conversion :
- Texte → séquence d'échappement Unicode (
\u4E2D) - Point de code → caractère
- Séquence d'octets UTF-8/16/32
- Texte → séquence d'échappement Unicode (
Encodage Emoji
- Ouvrez l'outil d'encodage Emoji
- Entrez un Emoji
- Vérifiez les informations complètes d'encodage :
- Séquence de points de code
- Octets UTF-8
- Décomposition ZWJ/modificateurs
- Nom court CLDR
6. Pièges courants et solutions
Piège 1 : Troncature de chaînes
// Erreur : la troncature peut couper les paires de substitution
text.substring(0, 10) // peut produire des caractères invalides
// Correct : tronquer par point de code
function sliceByCodePoint(str, start, end) {
const chars = [...str];
return chars.slice(start, end).join('');
}
Piège 2 : Inversion de chaînes
// Erreur : l'inversion détruit les caractères combinés
'👨👩👧👦'.split('').reverse().join('') // texte illisible
// Correct : inverser par point de code
[...'👨👩👧👦'].reverse().join('') // ✅
Piège 3 : Correspondance regex
// Erreur : . ne correspond pas aux caractères du plan supplémentaire
/^.$/.test('𠮷') // false
// Correct : utiliser le flag u
/^.$/u.test('𠮷') // true
Piège 4 : Tri
// Tri par défaut selon les unités de code UTF-16
['中', '𠮷', 'a'].sort() // peut ne pas être celui attendu
// Correct : trier par point de code
['中', '𠮷', 'a'].sort((a, b) =>
a.codePointAt(0) - b.codePointAt(0)
)
7. API pratiques
// Normalisation (traiter l'équivalence des caractères combinés)
'café'.normalize('NFC') // café (combiné)
'café'.normalize('NFD') // café (décomposé)
// Détecter les Emoji
function isEmoji(char) {
const code = char.codePointAt(0);
return (code >= 0x1F600 && code <= 0x1F64F) || // Émoticônes
(code >= 0x1F300 && code <= 0x1F5FF) || // Symboles divers
(code >= 0x1F680 && code <= 0x1F6FF) || // Transport
(code >= 0x1F900 && code <= 0x1F9FF); // Supplémentaire
}
// Obtenir la longueur réelle des caractères
function graphemeLength(str) {
const segmenter = new Intl.Segmenter('fr', { granularity: 'grapheme' });
return [...segmenter.segment(str)].length;
}
graphemeLength('👨👩👧👦') // 1 ✅
graphemeLength('🏳️🌈') // 1 ✅
#Unicode#Emoji#UTF-8#字符编码#码点