Codificación GSM vs UCS-2 y caracteres
Por qué una tilde puede duplicar el costo de tu campaña y cómo evitarlo.
Un SMS transporta 140 bytes de carga útil. Cuántos caracteres caben en esos bytes depende de la codificación, y es la razón por la que dos mensajes de apariencia idéntica pueden costar distinto.
Dos alfabetos
- GSM-7: 7 bits por carácter → 160 caracteres por segmento. Incluye letras latinas básicas y algunas acentuadas.
- UCS-2: 16 bits por carácter → 70 caracteres por segmento. Soporta cualquier carácter Unicode del plano básico, incluidos emojis simples.
Basta un carácter fuera del alfabeto GSM —una á, una comilla tipográfica “, un emoji— para que el mensaje completo se codifique en UCS-2.
Mensajes concatenados
Cuando el texto excede un segmento, se divide en partes con una cabecera UDH de 6 bytes que permite reensamblarlo. Eso reduce la capacidad: 153 caracteres por parte en GSM-7 y 67 en UCS-2. Cada parte se cobra como un SMS.
+const normalize = (s) => s+ .replace(/[“”]/g, '"')+ .replace(/[—–]/g, "-")+ .normalize("NFD").replace(/[\u0300-\u036f]/g, "");+await mx.sms.send({to,− text: "Tu código es 4821 — válido por 5 minutos “no lo compartas”",+ text: normalize("Tu código es 4821 — válido por 5 minutos “no lo compartas”"),});
Calcula antes de enviar
Implementa una función segments(text) que detecte la codificación y devuelva el número de partes. Muéstrala en el editor de plantillas de tu equipo: es la forma más barata de evitar sorpresas en la factura.
Calcula segmentos antes de enviar
Este playground cuenta segmentos con la misma regla que aplican los operadores: si el texto cabe en el alfabeto GSM-7, cada segmento admite 160 caracteres (153 si el mensaje se divide); si contiene un solo carácter fuera de él, todo pasa a UCS-2 con 70 caracteres (67 concatenados). Cambia el texto y mira cómo un emoji duplica el costo.
// Count SMS segments the way carriers do
const GSM = "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";
function segments(text) {
const gsm = [...text].every((ch) => GSM.includes(ch));
const len = [...text].length;
const single = gsm ? 160 : 70;
const multi = gsm ? 153 : 67;
const n = len <= single ? 1 : Math.ceil(len / multi);
return { encoding: gsm ? "GSM-7" : "UCS-2", len, n };
}
const texto = "Tu despacho llegará mañana entre 9 y 13 h. Gracias por comprar 🙌";
const s = segments(texto);
console.log(s.encoding, "·", s.len, "caracteres ·", s.n, "segmento(s)");
const limpio = texto.replace(" 🙌", "").replace("á", "a");
console.log("Sin emoji:", JSON.stringify(segments(limpio)));
await mx.sms.send({ to: "+56912345678", text: limpio });Fíjate en que la ñ sí pertenece a GSM-7, pero la á no: el alfabeto se diseñó en Europa y cubre solo algunas vocales acentuadas. Por eso muchos equipos normalizan tildes en mensajes transaccionales y las conservan solo cuando la marca lo exige.