</> Cómo funciona

silabear

GET
/v1/silabear

Función pura del español.

Parámetros

parámetrotipopor defectoqué hace
palabrastring— (obligatorio)la palabra a silabear

Cómo funciona (por dentro)

</>Este endpoint es parte de un módulo con más funciones. Ver el módulo completo →

Mapa de funciones

flowchart TD
  MOD[("repositorio")]
  is_vowel["is_vowel()"]
  has_text["has_text()"]
  get_position_first_vowel["get_position_first_vowel()"]
  get_position_last_vowel["get_position_last_vowel()"]
  get_num_consonantes_antes_de_vocal["get_num_consonantes_antes_de_vocal()"]
  get_letters["get_letters()"]
  _compara["_compara()"]
  desacentuar_vocal["desacentuar_vocal()"]
  desacentuar_palabra["desacentuar_palabra()"]
  _cumple_condicion_y["_cumple_condicion_y()"]
  _get_separador_vocales["_get_separador_vocales()"]
  _get_condicion_q["_get_condicion_q()"]
  _get_accion_grupos_separados["_get_accion_grupos_separados()"]
  _get_numero_silabas["_get_numero_silabas()"]
  _get_condicion_h["_get_condicion_h()"]
  _get_condicion_vocales["_get_condicion_vocales()"]
  _get_condicion_psicol["_get_condicion_psicol()"]
  _get_condicion_vuv["_get_condicion_vuv()"]
  silabear["silabear()"]
  int_silaba_tonica["int_silaba_tonica()"]
  numero_silabas["numero_silabas()"]
  _compara --> desacentuar_palabra
  _cumple_condicion_y --> has_text
  _cumple_condicion_y --> is_vowel
  _get_accion_grupos_separados --> get_num_consonantes_antes_de_vocal
  _get_accion_grupos_separados --> has_text
  _get_accion_grupos_separados --> is_vowel
  _get_condicion_h --> _get_numero_silabas
  _get_condicion_q --> _compara
  _get_condicion_vuv --> is_vowel
  _get_separador_vocales --> get_letters
  _get_separador_vocales --> is_vowel
  desacentuar_palabra --> desacentuar_vocal
  get_num_consonantes_antes_de_vocal --> is_vowel
  get_position_first_vowel --> is_vowel
  get_position_last_vowel --> is_vowel
  int_silaba_tonica --> has_text
  int_silaba_tonica --> is_vowel
  silabear --> _compara
  silabear --> _cumple_condicion_y
  silabear --> _get_accion_grupos_separados
  silabear --> _get_condicion_h
  silabear --> _get_condicion_psicol
  silabear --> _get_condicion_q
  silabear --> _get_condicion_vocales
  silabear --> _get_condicion_vuv
  silabear --> _get_separador_vocales
  silabear --> get_position_first_vowel
  silabear --> get_position_last_vowel
  silabear --> has_text
  silabear --> is_vowel
  is_vowel -. datos .-> MOD
  has_text -. datos .-> MOD
  get_letters -. datos .-> MOD
  desacentuar_vocal -. datos .-> MOD
  _get_numero_silabas -. datos .-> MOD
  _get_condicion_vocales -. datos .-> MOD
  _get_condicion_psicol -. datos .-> MOD
  numero_silabas -. datos .-> MOD
  classDef red fill:#fde7f0,stroke:#c0397b,color:#7a1e4d;
  class MOD red;

El código

# -*- coding: utf-8 -*-
"""
upln.py — Silabeador y acentuador algorítmicos del español.

Sin lookup y sin base de datos: PURO ALGORITMO. Divide en sílabas y coloca la tilde
aplicando las reglas del español (diptongos, hiatos, grupos consonánticos, tonicidad).

Método público principal:
    silabear(palabra)            -> "cam=pe=ón"   (getPalabraSilabeada)
    numero_silabas(silabeada)    -> int
    int_silaba_tonica(silabeada) -> 1=aguda, 2=llana, 3=esdrújula... (desde el final)
    acentuar(palabra)            -> coloca la tilde correcta   (getAcentuacionPalabra)
    acentuar_silabeada(sil, pos) -> idem a partir de silabeada+posición
    desacentuar_palabra(palabra) -> quita tildes
"""

SEPARADOR = "="

# --- listas de vocales del español ---
VOC_ABIERTAS               = set("a,á,A,Á,ä,Ä,e,é,E,É,ë,Ë,o,ó,O,Ó,ö,Ö".split(","))
VOC_ABIERTAS_SIN_ACENTUAR  = set("a,A,e,E,o,O".split(","))
VOC_CERRADAS               = set("i,í,I,Í,ï,Ï,u,ú,U,Ú,ü,Ü".split(","))
VOC_ABIERTAS_ACENTUADAS    = set("á,Á,é,É,ó,Ó".split(","))
VOC_CERRADAS_ACENTUADAS    = set("í,Í,ú,Ú".split(","))
VOC_ABIERTAS_CERRADAS_ACENTUADAS = VOC_ABIERTAS | VOC_CERRADAS_ACENTUADAS
VOC_ACENTUADAS             = VOC_ABIERTAS_ACENTUADAS | VOC_CERRADAS_ACENTUADAS

CONSONANTES_ESPECIALES = set("pr,pl,br,bl,fr,fl,cr,cl,gr,gl,tr,dr,ll,ch,tx,tz,fl,rr,kl".split(","))

_VOWELS_ISVOWEL = set("aeiouáéíóúAEIOUÁÉÍÓÚäëïöüÄËÏÖÜ")
_CONSONANTS = set("bcdfghjklmnñpqrstvwxyzBCDFGHJKLMNÑPQRSTVWXYZ")


# ================= auxiliares de texto =================
def is_vowel(c):
    return c in _VOWELS_ISVOWEL if c else False


def has_text(s):
    return bool(s) and any(not ch.isspace() for ch in s)




def get_position_first_vowel(s):
    for i, c in enumerate(s):
        if is_vowel(c):
            return i
    return -1

def get_position_last_vowel(s):
    for i in range(len(s) - 1, -1, -1):
        if is_vowel(s[i]):
            return i
    return -1

def get_num_consonantes_antes_de_vocal(s):
    s = s.replace("-", "")
    r = 0
    for c in s:
        if not is_vowel(c):
            r += 1
        else:
            break
    return r

def get_letters(s, ini, fin):
    """Trozo de `s` entre `ini` y `fin`; si fin>len, devuelve todo el input."""
    if ini > len(s):
        return ""
    if ini <= fin:
        if fin > len(s):
            return s
        return s[ini:fin]
    return ""

def _compara(a, b, accent_sensitive=False):
    """Compara dos caracteres: por defecto ignora tildes y mayúsculas; sensible a tilde si se pide."""
    if a is None or b is None:
        return False
    if accent_sensitive:
        return a.lower() == b.lower()
    return desacentuar_palabra(a).lower() == desacentuar_palabra(b).lower()


# ================= acentuar / desacentuar vocales =================
_ACENTUAR = {"a": "á", "e": "é", "i": "í", "o": "ó", "u": "ú",
             "A": "Á", "E": "É", "I": "Í", "O": "Ó", "U": "Ú"}
_DESACENTUAR = {"á": "a", "é": "e", "í": "i", "ó": "o", "ú": "u",
                "Á": "A", "É": "E", "Í": "I", "Ó": "O", "Ú": "U"}


def desacentuar_vocal(v):
    return _DESACENTUAR.get(v, v)

def desacentuar_palabra(palabra):
    return "".join(desacentuar_vocal(c) for c in palabra)


# ================= silabeo =================
def _cumple_condicion_y(palabra):
    if not has_text(palabra):
        return False
    result = False
    if len(palabra) > 1:
        i = 0
        while (not result) and i < len(palabra):
            if palabra[i].lower() == "y":
                if i == 0:
                    result = not is_vowel(palabra[i + 1])
                elif i == len(palabra) - 1:
                    result = not is_vowel(palabra[i - 1])
                else:
                    result = (not is_vowel(palabra[i - 1])) and (not is_vowel(palabra[i + 1]))
            i += 1
    return result

_CASOS_RAROS = set("ii,uu,ihi,uhu,ihí,íhi,úhu,uhú,ií,íi,úu,uú".split(","))

def _get_separador_vocales(palabra, posicion):
    result = 1
    cont_abcer = 0
    cont_cerradas = 0
    i = posicion
    caso2 = get_letters(palabra, posicion, posicion + 2)
    caso3 = get_letters(palabra, posicion, posicion + 3)
    if not (caso2 in _CASOS_RAROS or caso3 in _CASOS_RAROS):
        if palabra[posicion] in VOC_CERRADAS:
            cont_cerradas = 1
        if palabra[posicion] in VOC_ABIERTAS_CERRADAS_ACENTUADAS:
            cont_abcer = 1
        while (len(palabra) > i + 1) and (
            ((cont_abcer == 0) or ((cont_abcer == 1) and palabra[i + 1] not in VOC_ABIERTAS_CERRADAS_ACENTUADAS)) and
            ((cont_cerradas < 2) or ((cont_cerradas == 2) and palabra[i + 1] not in VOC_CERRADAS)) and
            (palabra[i + 1].lower() == "h" or is_vowel(palabra[i + 1]))
        ):
            if palabra[i + 1] in VOC_CERRADAS:
                cont_cerradas += 1
            if palabra[i + 1] in VOC_ABIERTAS_CERRADAS_ACENTUADAS:
                cont_abcer += 1
            result += 1
            i += 1
    return result

def _get_condicion_q(palabra, posicion):
    if len(palabra) > posicion + 2:
        if _compara(palabra[posicion], "g", accent_sensitive=True):
            if _compara(palabra[posicion + 1], "u", accent_sensitive=True):
                if _compara(palabra[posicion + 2], "e") or _compara(palabra[posicion + 2], "i"):
                    return True
    return False

def _get_accion_grupos_separados(grupo):
    if not has_text(grupo):
        raise ValueError("grupo vacío")
    if is_vowel(grupo[0]):
        return 1
    n = get_num_consonantes_antes_de_vocal(grupo)
    if n == 0:
        return 1
    if n == 1:
        return 1
    if n == 2:
        return 1 if grupo[0:2] in CONSONANTES_ESPECIALES else 2
    # 3 o más
    if grupo[1:3] in CONSONANTES_ESPECIALES:
        return 3
    if grupo[0:2].lower() in ("ns", "bs", "ds"):
        return 4
    if grupo[1:2].lower() == "s":
        return 4
    return 5

def _get_numero_silabas(silabeada):
    return len(silabeada.split(SEPARADOR))

def _get_condicion_h(palabra):
    result = palabra
    if "h" in palabra:
        if _get_numero_silabas(result) > 1:
            if "h" + SEPARADOR in result:
                result = result.replace("h" + SEPARADOR, SEPARADOR + "h")
    return result

def _get_condicion_vocales(palabra):
    result = palabra
    aux = palabra.replace(SEPARADOR, "")
    replace = None
    replacement = None
    cambiar = False
    for i in range(len(aux)):
        cerradas = False
        hache = False
        j = i
        if aux[j] in VOC_ABIERTAS:
            replace = aux[j]
            replacement = aux[j] + SEPARADOR
            if j + 1 < len(aux):
                if aux[j + 1] in VOC_CERRADAS:
                    cerradas = True
                elif aux[j + 1].lower() == "h":
                    if j + 2 < len(aux):
                        if aux[j + 2] in VOC_CERRADAS:
                            j = j + 1
                            cerradas = True
                            hache = True
                            replace = replace + "h"
                if cerradas:
                    replace = replace + aux[j + 1] + SEPARADOR
                    if hache:
                        replacement = replacement + "h" + aux[j + 1]
                    else:
                        replacement = replacement + aux[j + 1]
                    if j + 2 < len(aux):
                        if aux[j + 2] in VOC_ABIERTAS:
                            replace = replace + aux[j + 2]
                            replacement = replacement + aux[j + 2]
                            cambiar = True
                            break
    if cambiar:
        result = palabra.replace(replace, replacement, 1)
    return result

def _get_condicion_psicol(palabra):
    aux = palabra.replace(SEPARADOR, "").lower()
    if "psicol" in aux:
        return palabra.lower().replace("p" + SEPARADOR + "si", SEPARADOR + "psi")
    return palabra

def _get_condicion_vuv(palabra):
    result = palabra
    aux = palabra.replace(SEPARADOR, "")
    replace = None
    replacement = None
    cambiar = False
    for i in range(len(aux)):
        j = i
        if is_vowel(aux[j]):
            replace = aux[j]
            replacement = aux[j] + SEPARADOR
            if j + 1 < len(aux):
                if aux[j + 1].lower() == "h":
                    replace = replace + "h"
                    replacement = replacement + "h"
                    j += 1
                if j + 1 < len(aux):
                    if aux[j + 1].lower() == "u":
                        replace = replace + aux[j + 1]
                        replacement = replacement + aux[j + 1]
                        if j + 2 < len(aux):
                            if is_vowel(aux[j + 2]):
                                replace = replace + aux[j + 2]
                                replacement = replacement + aux[j + 2]
                                cambiar = True
                                break
    if cambiar:
        if replace in palabra:
            result = palabra.replace(replace, replacement, 1)
        else:
            r2 = replace.replace("u", SEPARADOR + "u", 1)
            r3 = replace.replace("u", "u" + SEPARADOR, 1)
            if r2 in palabra:
                result = palabra.replace(r2, replacement, 1)
            elif r3 in palabra:
                result = palabra.replace(r3, replacement, 1)
    return result

def silabear(palabra):
    """Devuelve la palabra dividida en sílabas, con '=' entre ellas (cam=pe=ón)."""
    if palabra is None:
        raise ValueError("null")
    if not has_text(palabra):
        raise ValueError("sin texto")

    palabra_original = palabra
    if _cumple_condicion_y(palabra_original):
        palabra = palabra.replace("y", "i")

    result = []
    i = 0
    L = len(palabra)
    while i < L:
        if is_vowel(palabra[i]):
            if i < L - 1:
                n = _get_separador_vocales(palabra, i)
                if n in (1, 2, 3, 4):
                    result.append(palabra[i:i + n])
                    result.append(SEPARADOR)
                    i += n
                else:
                    result.append(palabra[i]); i += 1
            else:
                result.append(palabra[i]); i += 1
        else:
            if _compara(palabra[i], "q") or _get_condicion_q(palabra, i):
                result.append(palabra[i])
                if i + 1 < L:
                    result.append(palabra[i + 1]); i += 1
                i += 1
            else:
                result.append(palabra[i]); i += 1
    result = "".join(result)

    if "qu" + SEPARADOR + "i" in result:
        result = result.replace("qu" + SEPARADOR + "i", "qui")
    elif "gu" + SEPARADOR + "i" in result:
        result = result.replace("gu" + SEPARADOR + "i", "gui")

    # borrar SEPARADOR que dejaría consonantes colgando al final
    borrar = -1
    i = len(result) - 1
    while borrar == -1 and i > 0:
        if is_vowel(result[i]):
            break
        else:
            if result[i] == SEPARADOR:
                borrar = i
        i -= 1
    if borrar != -1:
        result = result[:borrar] + result[borrar + 1:]

    # redistribución de consonantes entre sílabas
    lResultados = result.split(SEPARADOR)
    out = []
    for j in range(len(lResultados)):
        if j == 0:
            out.append(lResultados[j])
            if len(lResultados) > 1:
                out.append(SEPARADOR)
        else:
            grupo = lResultados[j]
            accion = _get_accion_grupos_separados(grupo)
            if accion == 1:
                out.append(lResultados[j])
                if j != len(lResultados) - 1:
                    out.append(SEPARADOR)
            elif accion in (2, 3):
                out.pop()  # quita el último SEPARADOR
                out.append(grupo[0]); out.append(SEPARADOR); out.append(grupo[1:])
                if j != len(lResultados) - 1:
                    out.append(SEPARADOR)
            elif accion == 4:
                out.pop()
                out.append(grupo[0]); out.append(grupo[1]); out.append(SEPARADOR); out.append(grupo[2:])
                if j != len(lResultados) - 1:
                    out.append(SEPARADOR)
            elif accion == 5:
                pos_ult = get_position_last_vowel(grupo)
                if pos_ult != -1:
                    palabraAux = grupo[0:pos_ult]
                    buscar = palabraAux[len(palabraAux) - 2:len(palabraAux)]
                    if buscar in CONSONANTES_ESPECIALES:
                        out.pop()
                        out.append(palabraAux[0:len(palabraAux) - 2]); out.append(SEPARADOR)
                        out.append(buscar); out.append(grupo[pos_ult:])
                        if j != len(lResultados) - 1:
                            out.append(SEPARADOR)
                    else:
                        buscar1 = palabraAux[len(palabraAux) - 1:len(palabraAux)]
                        if buscar1.lower() == "h":
                            out.pop()
                            out.append(palabraAux[0:len(palabraAux) - 2]); out.append(SEPARADOR)
                            out.append(palabraAux[len(palabraAux) - 2:len(palabraAux)]); out.append(grupo[pos_ult:])
                            if j != len(lResultados) - 1:
                                out.append(SEPARADOR)
                        else:
                            posicion = get_position_first_vowel(grupo)
                            if posicion != -1:
                                out.pop()
                                out.append(grupo[0:posicion - 1]); out.append(SEPARADOR)
                                out.append(grupo[posicion - 1:])
                                if j != len(lResultados) - 1:
                                    out.append(SEPARADOR)
                            else:
                                out.append("No contemplado")
            elif accion == -1:
                return "No contemplado"
    palabra_resultado = "".join(out)

    # restaurar la 'y' original donde se sustituyó por 'i'
    result2 = []
    j = 0
    for i in range(len(palabra_original)):
        if j < len(palabra_resultado) and palabra_resultado[j] == SEPARADOR:
            result2.append(SEPARADOR); j += 1
        if j < len(palabra_resultado) and palabra_resultado[j].lower() == palabra_original[i].lower():
            result2.append(palabra_resultado[j])
        else:
            result2.append("y")
        j += 1
    aux = "".join(result2)

    aux = _get_condicion_h(aux)
    aux = _get_condicion_vocales(aux)
    aux = _get_condicion_psicol(aux)
    aux = _get_condicion_vuv(aux)
    return aux


# ================= sílaba tónica =================
def int_silaba_tonica(silabeada):
    if not has_text(silabeada):
        raise ValueError("sin texto")
    result = -1
    listaSilabas = silabeada.split(SEPARADOR)
    if len(listaSilabas) == 1:
        result = 1
    else:
        i = len(listaSilabas) - 1
        posicion = 1
        while result == -1 and i >= 0:
            pAux = listaSilabas[i]; i -= 1
            for ch in pAux:
                if ch in VOC_ACENTUADAS:
                    result = posicion
                    break
            posicion += 1
    if result == -1:
        c = silabeada[-1]
        if c.lower() == "s":
            if len(silabeada) > 1 and not is_vowel(silabeada[-2]):
                result = 1
            else:
                result = 2
        elif is_vowel(c) or c.lower() == "n":
            result = 2
    if result == -1:
        result = 1
    return result

def numero_silabas(silabeada):
    return len(silabeada.lower().split(SEPARADOR))



# ================= acentuación =================






if __name__ == "__main__":
    # […pruebas del módulo (batería de palabras y comprobación)…]

Pruébalo

Llámala con el código a la vista.

Ejemplos con el SDK

curl "https://api.wordkers.com/v1/silabear" \
  -H "X-API-Key: TU_CLAVE"

Cómo empezar con el SDK →

¿Quieres que cambie algo, o copiar la lógica para lo tuyo? Sin problema — escríbeme. Lo que ves es el algoritmo; los datos y las claves se quedan en casa.

Wordkers · Catálogo · Empezar · Tu cuenta