Documentación de Universally

Guías paso a paso, consejos de SEO multilingüe y mejores prácticas para ayudarte a traducir y escalar tu sitio web de WordPress.

Crea un selector de idioma personalizado en WordPress

Universally ships with a built-in dropdown switcher that you can drop in via auto-placement, the [universally_switcher] shortcode, or the universally_switcher() PHP function. That covers most cases.

Pero si quieres control total sobre cómo se ve y se comporta el selector: una fila de pestañas en línea, botones solo con banderas, una lista vertical en la barra lateral, un selector desplegable personalizado que coincida con tu sistema de diseño, cualquier cosa. Puedes crear el tuyo propio en PHP utilizando los mismos datos que utiliza el plugin internamente.

This guide shows you:

  1. La base: la única función auxiliar que necesitas, los datos que devuelve y un selector mínimo de "Hola Mundo" sobre el que puedes construir.
  2. Un ejemplo completo y funcional: pestañas en línea (banderas + nombres nativos) con opciones de alineación, interruptores y estilos, que puedes usar tal cual o adaptar.

The foundation

Universally exposes a single PHP helper that returns everything you need to render a switcher:

universally_get_switcher_urls();

It returns an array of language entries. Each entry contains:

Campo Type Descripción
name string Nombre de visualización en inglés, con la región o variante entre corchetes (p. ej. Español (México), Inglés (Británico)).
originalName string Solo nombre nativo, sin región (p. ej. Deutsch, English, Français).
flagUrl string URL del icono de la bandera, o una cadena vacía cuando no hay ninguna bandera asignada para la región. Siempre presente, independientemente de la configuración de Banderas de países.
urlPrefix string Prefijo de URL para el idioma (p. ej. de, fr, mx). Vacío para el idioma de origen.
idioma string Código de idioma base (p. ej. es, en).
variant string Variante de traducción, en minúsculas (p. ej. es-419, en-gb). Vacío para el idioma de origen.
region string Código de región completo, en minúsculas (p. ej. es-mx, de-de). No es un código de país base.
isSource bool true for the source language.
deshabilitado bool true cuando el idioma se agregó pero está deshabilitado. Solo para idiomas de destino.
url string Full URL to the current page in this language.
isCurrent bool true for the language the visitor is currently viewing.

Omitir idiomas deshabilitados. Un idioma deshabilitado todavía aparece en esta matriz, pero su URL ya no se sirve como traducción, por lo que un enlace a él es un callejón sin salida. El selector integrado los filtra y tu selector también debería hacerlo: esa es la verificación isDisabled en los ejemplos a continuación.

Usa region cuando necesites un valor de local para un atributo lang o un hreflang, ya que es el código completo en lugar de una abreviatura de país.

Una vez que tengas esta matriz, el resto depende de ti: en qué HTML lo envuelves, qué CSS aplicas, qué comportamiento agregas. A Universally no le importa.

Ejemplo de base mínima

El conmutador personalizado más pequeño posible: recorre los idiomas, genera un enlace para cada uno, expónlo como un shortcode. Sin estilos, sin opciones, sin marcado más allá de lo necesario.

<?php
function custom_language_switcher() {
    if (!function_exists('universally_get_switcher_urls')) return '';
    $langs = universally_get_switcher_urls();
    if (empty($langs)) return '';

    $out = '<ul class="custom-language-switcher">';
    foreach ($langs as $lang) {
        if (empty($lang['url'])) continue;
        if (!empty($lang['isDisabled'])) continue;

        $label = esc_html($lang['originalName'] ?? '');
        $url   = esc_url($lang['url']);
        $flag  = !empty($lang['flagUrl'])
            ? '<img src="' . esc_url($lang['flagUrl']) . '" alt="">'
            : '';

        $out .= '<li><a href="' . $url . '">' . $flag . $label . '</a></li>';
    }
    $out .= '</ul>';

    return $out;
}
add_shortcode('custom_language_switcher', 'custom_language_switcher');

Pega esto en el functions.php de tu tema hijo (o un plugin de fragmentos de código como WPCode) y úsalo donde se acepten shortcodes:

[custom_language_switcher]

...o llámalo desde PHP (por ejemplo, en un archivo de plantilla de tema):

<?php if (function_exists('custom_language_switcher')) echo custom_language_switcher(); ?>

Esa es la base. A partir de aquí puedes:

  • Añadir CSS para diseñarlo como quieras (fila de pestañas, menú vertical, cuadrícula...).
  • Añadir atributos de shortcode para hacerlo configurable.
  • Resaltar el idioma actual usando $lang['isCurrent'].
  • Ocultar el idioma de origen, mostrar solo ciertos idiomas, agruparlos, etc.

El ejemplo práctico a continuación muestra cómo puede ser eso en la práctica.


Ejemplo funcional: pestañas de idioma en línea

Un selector personalizado con todas las funciones construido sobre la base anterior. Renderiza los idiomas como una fila horizontal de pestañas con banderas y nombres nativos, útil para colocar en un encabezado, junto a una barra de búsqueda o en cualquier otro lugar donde desees un selector que no sea desplegable.

Añade:

  • Tres opciones de alineación: izquierda, derecha, centro.
  • Interruptores para banderas y nombres de forma independiente (por ejemplo, solo banderas).
  • CSS incorporado para que se vea bien sin que tengas que escribir ningún estilo.
  • Resalta el idioma actual con aria-current="page" y una clase --current para estilizar.
  • Clases con espacio de nombres (custom-uni-tabs*) para que no colisione con nada más.

⚠️ Usa este fragmento O el fragmento de base anterior, no ambos al mismo tiempo, porque ambos definirían un shortcode. (Puedes renombrar cualquiera de los dos para usarlos uno al lado del otro).

Añadir el fragmento

Pega esto en el functions.php de tu tema hijo (o WPCode):

<?php
function custom_uni_tabs($atts = []) {
    if (!function_exists('universally_get_switcher_urls')) return '';
    $langs = universally_get_switcher_urls();
    if (empty($langs)) return '';

    $atts = shortcode_atts([
        'align'      => 'center',
        'show_flags' => 'true',
        'show_names' => 'true',
    ], $atts);

    $align     = in_array($atts['align'], ['left', 'right', 'center'], true) ? $atts['align'] : 'center';
    $showFlags = filter_var($atts['show_flags'], FILTER_VALIDATE_BOOLEAN);
    $showNames = filter_var($atts['show_names'], FILTER_VALIDATE_BOOLEAN);

    if (!$showFlags && !$showNames) {
        $showNames = true; // never render an empty link
    }

    static $printed_styles = false;
    $styles = '';
    if (!$printed_styles) {
        $printed_styles = true;
        $styles = '<style>
            .custom-uni-tabs { display: flex; align-items: center; gap: 16px; list-style: none; margin: 0; padding: 0; }
            .custom-uni-tabs--left   { justify-content: flex-start; }
            .custom-uni-tabs--right  { justify-content: flex-end; }
            .custom-uni-tabs--center { justify-content: center; }
            .custom-uni-tabs__item { margin: 0; padding: 0; }
            .custom-uni-tabs__link { display: inline-block; margin: 0; padding: 0; text-decoration: none; line-height: 1; }
            .custom-uni-tabs__link--current { font-weight: 600; }
            .custom-uni-tabs__flag { width: 20px; height: auto; vertical-align: middle; }
            .custom-uni-tabs__flag--with-label { margin-right: 6px; }
            .custom-uni-tabs__label { vertical-align: middle; }
        </style>';
    }

    $out = $styles . '<ul class="custom-uni-tabs custom-uni-tabs--' . esc_attr($align) . '">';
    foreach ($langs as $lang) {
        if (empty($lang['url'])) continue;
        if (!empty($lang['isDisabled'])) continue;

        $label = esc_html($lang['originalName'] ?? '');
        $url   = esc_url($lang['url']);
        $isCur = !empty($lang['isCurrent']);

        $flag = '';
        if ($showFlags && !empty($lang['flagUrl'])) {
            $flagCls = 'custom-uni-tabs__flag' . ($showNames ? ' custom-uni-tabs__flag--with-label' : '');
            $alt     = $showNames ? '' : $label;
            $flag    = '<img class="' . $flagCls . '" src="' . esc_url($lang['flagUrl']) . '" alt="' . esc_attr($alt) . '">';
        }

        $linkCls = 'custom-uni-tabs__link' . ($isCur ? ' custom-uni-tabs__link--current' : '');
        $aria    = $isCur ? ' aria-current="page"' : '';

        $out .= '<li class="custom-uni-tabs__item">';
        $out .= '<a class="' . $linkCls . '" href="' . $url . '"' . $aria . '>';
        $out .= $flag;
        if ($showNames) {
            $out .= '<span class="custom-uni-tabs__label">' . $label . '</span>';
        }
        $out .= '</a></li>';
    }
    $out .= '</ul>';

    return $out;
}
add_shortcode('custom_uni_tabs', 'custom_uni_tabs');

Usarlo

Código corto

[custom_uni_tabs]

Atributos:

Atributo Valores Predeterminado Lo que hace
alinear izquierda, derecha, centro centro Alineación horizontal de la fila.
mostrar_banderas verdadero, falso verdadero Mostrar/ocultar iconos de banderas.
mostrar_nombres verdadero, falso verdadero Mostrar/ocultar nombres de idiomas nativos.

Ejemplos:

[custom_uni_tabs]
[custom_uni_tabs align="right"]
[custom_uni_tabs show_names="false"]               (flags only) 
[custom_uni_tabs show_flags="false" align="left"]  (names only, left)

PHP

Para cuando quieras insertarlo directamente en un archivo de plantilla de tema (ej. header.php), en un hook, o en un widget de PHP/código de un page-builder. Llama a la función directamente y pasa cualquier atributo como un array:

<?php
if (function_exists('custom_uni_tabs')) {
    echo custom_uni_tabs([
        'align'      => 'center',
        'show_flags' => 'true',
        'show_names' => 'false',
    ]);
}
?>

⚠️ Edita header.php (o cualquier archivo principal del tema) solo dentro de un tema hijo. Editar el tema padre directamente significa que tus cambios se perderán la próxima vez que el tema se actualice.

Anulaciones CSS opcionales

El ejemplo viene con valores predeterminados sensatos. Anúlalos desde la hoja de estilos de tu tema siempre que lo necesites:

/* Bigger flags, more spacing, and a colour for the active language */
.custom-uni-tabs            { gap: 24px; }
.custom-uni-tabs__flag      { width: 24px; }
.custom-uni-tabs__link      { font-size: 14px; color: #333; }
.custom-uni-tabs__link:hover            { color: #000; }
.custom-uni-tabs__link--current         { color: #c00; font-weight: 700; }

Referencia de clases:

Clase Aplicado a
.custom-uni-tabs La <ul> exterior.
.custom-uni-tabs--left/right/center Modificador de alineación en la <ul>.
.custom-uni-tabs__item Cada <li>.
.custom-uni-tabs__link Cada <a> (inline-block, sin padding).
.custom-uni-tabs__link--current El enlace del idioma activo.
.custom-uni-tabs__flag La <img> de la bandera.
.custom-uni-tabs__flag--with-label Bandera cuando se muestra junto a un nombre.
.custom-uni-tabs__label El <span> native-name.

Dónde colocar el selector

Una vez que tu shortcode esté registrado, puedes colocarlo en cualquier lugar de tu sitio. Los pasos exactos dependen de tu tema/constructor:

  • Temas de bloque (Twenty Twenty-Four, etc.): abre Apariencia → Editor, busca la plantilla o parte de plantilla que quieras editar (p. ej., Encabezado, Pie de página, una página individual), inserta un bloque de Shortcode y pega [custom_uni_tabs].
  • Constructores de páginas (Elementor / Divi / Beaver Builder / Bricks): añade un widget de Shortcode o Código donde quieras el selector y pega [custom_uni_tabs].
  • Temas clásicos: pega [custom_uni_tabs] directamente en una entrada, página o área de widgets. Para colocarlo dentro de un archivo de plantilla de tema (p. ej., header.php, footer.php, sidebar.php), edita el archivo en un tema hijo y añade:
<?php if (function_exists('custom_uni_tabs')) echo custom_uni_tabs(); ?>

Solución de problemas

No aparece nada. Asegúrate de que el plugin Universally esté activo y de que al menos un idioma de destino esté habilitado en tu proyecto. Los fragmentos no devuelven nada cuando universally_get_switcher_urls() no está disponible o está vacío.

La lista está vacía o desactualizada. La lista de idiomas se almacena en caché en tu sitio durante 15 minutos. Añadir o quitar un idioma normalmente la actualiza de inmediato, pero ese envío no puede llegar a un sitio al que Universally no pueda acceder: una instalación local o de staging, un host con cortafuegos, un proyecto sin dominio establecido o una solicitud que expiró. Un sitio que acaba de conectarse también puede seguir teniendo una lista vacía. Abrir Universally » General » Idiomas fuerza una actualización. Guardar la configuración del plugin no lo hace.

No se muestran banderas. La configuración de Banderas de país no afecta a un selector personalizado: solo se aplica al integrado. Comprueba si flagUrl está vacío para ese idioma en su lugar, lo que ocurre cuando no hay ninguna bandera mapeada para su región. La configuración está en Universally » Language Switcher » Country Flags si también quieres cambiar el selector integrado.

Los visitantes no pueden volver al idioma de origen. Ver una URL traducida almacena el idioma del visitante durante 30 días, y las URLs sin prefijo se redirigen a él. El selector integrado evita eso añadiendo ?universally_switch=source al enlace del idioma de origen, lo que borra la elección almacenada. Reproduce eso en tu propio selector:

$url = $lang['url'];
if (!empty($lang['isSource'])) {
    $url .= (strpos($url, '?') === false ? '?' : '&') . 'universally_switch=source';
}

Las pestañas no se alinean como esperaba. En el ejemplo de trabajo, el atributo align posiciona los elementos dentro de <ul>, pero el <ul> en sí solo ocupa el ancho de su contenedor padre. Si el padre no tiene ancho completo, las pestañas pueden parecer descentradas o desplazadas hacia un lado. Envuelve el shortcode en un contenedor de ancho completo, o añade:

.custom-uni-tabs { width: 100%; }

El selector flotante predeterminado todavía se muestra. Ve a Universally → Language Switcher y cambia la implementación de Auto a Custom, de lo contrario aparecerán ambos.

Error “Cannot redeclare function”. Has pegado dos fragmentos que definen el mismo nombre de función. Conserva solo uno, o renombra uno de ellos.

¿Te ha resultado útil?