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:
- 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.
- 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--currentpara 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.