Babelize Elements
Selectores de idioma

Selector de idioma

Selector de idioma para React listo para copiar y pegar, con desplegable, búsqueda, compatibilidad con RTL y accesibilidad completa. Funciona con next-intl, react-i18next y cualquier configuración de i18n.

Instalación

npx shadcn@latest add @babelize/language-switcher

Uso

"use client";

import { useState } from "react";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

const locales = [{ code: "en" }, { code: "fr" }, { code: "es" }, { code: "ja" }, { code: "ar" }];

export function LocaleSwitcher() {
  const [locale, setLocale] = useState("en");

  return (
    <LanguageSwitcher
      locales={locales} // only `code` is required, labels and flags are auto-resolved
      value={locale} // omit for uncontrolled, use defaultValue instead
      onValueChange={setLocale}
      label="native" // or "english" (default)
      showFlags
    />
  );
}

Props

PropTipoObligatorioDescripción
localesLocale[]SíArray de configuraciones regionales disponibles; solo code es obligatorio
valuestringNoCódigo de configuración regional seleccionado: pásalo para controlar el componente
defaultValuestringNoCódigo de idioma seleccionado inicialmente (predeterminado: código del primer idioma)
onValueChange(code: string) => voidNoCallback que se ejecuta cuando cambia la configuración regional
label"native" | "english"NoEtiquetas visibles en el idioma nativo o en inglés (predeterminado: "english")
showFlagsbooleanNoMuestra los emojis de bandera (predeterminado: false)
maxHeightnumber | stringNoAltura máxima de la lista de opciones con desplazamiento (predeterminado: "15rem")
showCodebooleanNoMuestra el código de configuración regional junto a cada opción (predeterminado: false)
variant"outline" | "ghost" | "secondary"NoEstilo del disparador (predeterminado: "outline")
size"sm" | "default" | "lg"NoTamaño del disparador (predeterminado: "default")
align"start" | "end"NoBorde del disparador con el que se alinea el menú (predeterminado: "start")
side"top" | "bottom"NoLado preferido del menú; cambia de lado si no hay espacio (predeterminado: "bottom")
searchablebooleanNoMuestra el campo de búsqueda (predeterminado: true)
searchPlaceholderstringNoMarcador de posición de la búsqueda (predeterminado: "Search languages...")
emptyMessageReactNodeNoSe muestra cuando la búsqueda no coincide con nada (predeterminado: "No languages found.")
labelsPartial<LanguageSwitcherLabels>NoSustituye el texto de interfaz en inglés integrado (consulta más abajo)
disabledbooleanNoDesactiva el disparador
renderOption(locale, state) => ReactNodeNoSustituye el contenido de cada fila de opción; state tiene selected, active, label, flag
classNamesLanguageSwitcherClassNamesNoClases por parte: trigger, content, search, list, item, empty
classNamestringNoClases CSS adicionales en la raíz

Objeto de configuración regional

PropiedadTipoDescripción
codestringCódigo ISO 639-1 / BCP 47 (p. ej. "en", "fr-FR", "ar-SA") — obligatorio
labelstringNombre visible personalizado (se resuelve automáticamente desde la asignación integrada si se omite)
flagstringEmoji de bandera personalizado (se detecta automáticamente desde el código si se omite)
rtlbooleanSustitución de RTL (se detecta automáticamente desde el código si se omite)

Ejemplos

Con el SDK de Babelize

Combina el selector de idioma con el SDK de Babelize para traducciones automáticas:

"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { LanguageSwitcher } from "@/components/ui/language-switcher";
import { useBabelize } from "@babelize/sdk/react";

const locales = [
  { code: "en", label: "English", flag: "🇺🇸" },
  { code: "fr", label: "Français", flag: "🇫🇷" },
  { code: "es", label: "Español", flag: "🇪🇸" },
  { code: "de", label: "Deutsch", flag: "🇩🇪" },
  { code: "ja", label: "日本語", flag: "🇯🇵" },
  { code: "ar", label: "العربية", flag: "🇸🇦" },
];

export function LocaleSwitcher() {
  const router = useRouter();
  const { locale, setLocale } = useBabelize();

  return (
    <LanguageSwitcher
      locales={locales}
      value={locale}
      onValueChange={(code) => {
        setLocale(code);
        router.push(`/${code}`);
      }}
    />
  );
}

Con next-intl

"use client";

import { useRouter } from "next/navigation";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

export function LocaleSwitcher() {
  const router = useRouter();

  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }]}
      defaultValue="en"
      onValueChange={(code) => router.push(`/${code}`)}
    />
  );
}

Con react-i18next

"use client";

import { useTranslation } from "react-i18next";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

export function LocaleSwitcher() {
  const { i18n } = useTranslation();

  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }]}
      value={i18n.language}
      onValueChange={(code) => i18n.changeLanguage(code)}
    />
  );
}

Etiquetas nativas + banderas

"use client";

import { LanguageSwitcher } from "@/components/ui/language-switcher";

export default function App() {
  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }, { code: "es" }]}
      defaultValue="en"
      label="native"
      showFlags
    />
  );
}

Personalizar partes

El selector usa tokens de tema de shadcn, así que sigue tu tema. Usa classNames para cambiar el estilo de cualquier parte, o renderOption para cambiar lo que muestra cada fila:

<LanguageSwitcher
  locales={locales}
  variant="ghost"
  size="sm"
  align="end"
  classNames={{ content: "w-64", item: "py-2" }}
  renderOption={(locale, { label, flag, selected }) => (
    <>
      <span className="text-base leading-none">{flag}</span>
      <span className="flex-1">{label}</span>
      <span className="text-xs text-muted-foreground">{selected ? "Current" : locale.code}</span>
    </>
  )}
/>

Texto de interfaz traducido

El marcador de posición de búsqueda, el estado vacío y las etiquetas accesibles usan inglés de forma predeterminada. Pasa labels para traducirlos: cualquier clave que omitas conserva su valor en inglés. Las props searchPlaceholder y emptyMessage siguen funcionando y tienen prioridad sobre labels cuando ambas están definidas.

"use client";

import { LanguageSwitcher } from "@/components/ui/language-switcher";

export default function App() {
  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }, { code: "es" }]}
      defaultValue="fr"
      label="native"
      labels={{
        searchPlaceholder: "Rechercher une langue…",
        searchLabel: "Rechercher une langue",
        clearSearch: "Effacer la recherche",
        emptyMessage: "Aucune langue trouvée.",
        listbox: "Choisir la langue",
        current: (name) => `Langue actuelle : ${name}`,
      }}
    />
  );
}
ClavePredeterminadoSe usa para
searchPlaceholder"Search languages..."Marcador de posición del campo de búsqueda
searchLabel"Search languages"Etiqueta accesible del campo de búsqueda
clearSearch"Clear search"Etiqueta accesible del botón de borrar
emptyMessage"No languages found."Se muestra cuando nada coincide
listbox"Select language"Etiqueta accesible de la lista de opciones
current(name) => "Current language: " + nameEtiqueta accesible del disparador

Soporte de teclado

El popup sigue el patrón combobox de WAI-ARIA: el foco permanece en el campo de búsqueda, que se expone como role="combobox", y la opción resaltada se anuncia mediante aria-activedescendant.

ClaveAcción
Enter, Space o clicAbre el popup desde el disparador
ArrowDown / ArrowUp (cerrado)Abre con la primera / última configuración regional resaltada
ArrowDown / ArrowUpMueve el resaltado, dando la vuelta en ambos extremos
Home / EndResalta la primera / última configuración regional
EnterSelecciona la configuración regional resaltada y devuelve el foco al disparador
EscapeCierra el popup y devuelve el foco al disparador
EscrituraFiltra por nombre o código; el resaltado vuelve al principio

El resaltado se mantiene en la fila en la que apareció al abrir el menú: la lista se desplaza por debajo, como un menú nativo, y la página nunca se desplaza. Pasa maxHeight para cambiar la altura de la lista antes de que empiece a desplazarse.

Características

  • Sin configuración — basta con pasar los códigos; las etiquetas y banderas se resuelven automáticamente
  • Desplegable con búsqueda — filtra configuraciones regionales por nombre o código
  • Compatible con RTL — detecta automáticamente las configuraciones regionales RTL (ar, he, fa, ur) y ajusta el diseño
  • Accesible con teclado — navegación con flechas, Inicio / Fin, Intro y Escape
  • Etiquetas ARIA — campo de búsqueda role="combobox" con aria-activedescendant, además de aria-expanded, aria-haspopup y aria-selected
  • Modo oscuro — creado con Tailwind CSS, compatible con el modo oscuro
  • Independiente de la biblioteca — funciona con cualquier configuración de i18n mediante callback

Idiomas compatibles

Las etiquetas y banderas se detectan automáticamente a partir del código de idioma. Pasa solo el código:

CódigoIdiomaBanderaRTL
enEnglish🇺🇸
frFrançais🇫🇷
esEspañol🇪🇸
deDeutsch🇩🇪
ja日本語🇯🇵
ko한국어🇰🇷
zh中文🇨🇳
arالعربية🇸🇦✓
ptPortuguês🇧🇷
itItaliano🇮🇹
nlNederlands🇳🇱
ruРусский🇷🇺
plPolski🇵🇱
trTürkçe🇹🇷
viTiếng Việt🇻🇳
thไทย🇹🇭
idBahasa Indonesia🇮🇩
hiहिन्दी🇮🇳
bnবাংলা🇧🇩
ukУкраїнська🇺🇦
csČeština🇨🇿
svSvenska🇸🇪
daDansk🇩🇰
fiSuomi🇫🇮
noNorsk🇳🇴
elΕλληνικά🇬🇷
heעברית🇮🇱✓
faفارسی🇮🇷✓
roRomână🇷🇴
huMagyar🇭🇺
skSlovenčina🇸🇰
bgБългарски🇧🇬
hrHrvatski🇭🇷
srСрпски🇷🇸
slSlovenščina🇸🇮
ltLietuvių🇱🇹
lvLatviešu🇱🇻
etEesti🇪🇪
gaGaeilge🇮🇪
caCatalà🇪🇸
isÍslenska🇮🇸
swKiswahili🇹🇿
amአማርኛ🇪🇹
neनेपाली🇳🇵
siසිංහල🇱🇰
myမြန်မာ🇲🇲
kmខ្មែរ🇰🇭
loລາວ🇱🇦
kaქართული🇬🇪
hyՀայերեն🇦🇲
kkҚазақ🇰🇿
uzOʻzbek🇺🇿
mnМонгол🇲🇳
urاردو🇵🇰✓
mlമലയാളം🇮🇳
taதமிழ்🇮🇳
teతెలుగు🇮🇳
knಕನ್ನಡ🇮🇳
mrमराठी🇮🇳
guગુજરાતી🇮🇳
paਪੰਜਾਬੀ🇮🇳
yoYorùbá🇳🇬
igIgbo🇳🇬
zuisiZulu🇿🇦
afAfrikaans🇿🇦
haHausa🇳🇬
tlFilipino🇵🇭
miTe Reo Māori🇳🇿
smGagana Samoa🇼🇸
toLea Faka-Tonga🇹🇴
eoEsperanto🌐

¿Necesitas un idioma que no aparece? Pasa un label y un flag personalizados para sustituirlo:

<LanguageSwitcher
  locales={[{ code: "xyz", label: "My Language", flag: "🏴" }]}
  defaultValue="xyz"
/>

Dependencias

Este componente usa:

  • clsx y tailwind-merge (incluidos como dependencias)

Dependencias de pares:

  • react >= 18
  • react-dom >= 18
  • tailwindcss >= 3

Créditos

Creado por sapatmohit.

En esta página