Localización
Cada texto integrado en el kit (468 etiquetas en 77 conjuntos: aria-labels, anuncios para lectores de pantalla, el texto de botones que no escribiste, placeholders) se traduce con un solo provider. Móntalo una vez con un pack de idioma y todos los componentes de goodui hablan ese idioma; los formatos de número, fecha y moneda lo siguen vía Intl, y el árabe y el hebreo reciben su dirección. Las apps que nunca lo definen conservan el inglés y los formatos en-US, sin cargar nada extra.
Instalación
npx shadcn@latest add https://goodui.dev/r/locale.json https://goodui.dev/r/locale-tr.jsonlocale aterriza en lib/goodui-locale.ts (todo componente con etiquetas ya depende de él, así que normalmente lo tienes). Un pack aterriza en lib/goodui-locales/<lang>.ts y no depende de nada más; instala solo los idiomas que publiques.
Define el idioma
import { GooduiLocaleProvider } from "@/lib/goodui-locale"
import tr from "@/lib/goodui-locales/tr"
export default function RootLayout({ children }) {
return (
<html lang="tr">
<body>
<GooduiLocaleProvider lang="tr" labels={tr}>{children}</GooduiLocaleProvider>
</body>
</html>
)
}lang es una etiqueta BCP 47 y controla todos los formatos Intl del kit ("de-AT", "pt-BR", "en-GB" valen). labels es el pack. Para un selector de idioma, guarda la etiqueta en el estado y elige el pack desde un mapa de imports perezosos; los providers anidados funcionan, así que una pantalla puede ir en otro idioma.
Packs
18 idiomas, un archivo cada uno, todas las etiquetas de todos los componentes. Nombres nativos tal como los mostraría un selector:
locale-tr: Türkçelocale-de: Deutschlocale-fr: Françaislocale-es: Españollocale-it: Italianolocale-pt-BR: Português (Brasil)locale-nl: Nederlandslocale-pl: Polskilocale-ru: Русскийlocale-hi: हिन्दीlocale-id: Bahasa Indonesialocale-vi: Tiếng Việtlocale-th: ไทยlocale-ja: 日本語locale-ko: 한국어locale-zh-CN: 中文(简体)locale-ar: العربية (RTL)locale-he: עברית (RTL)
locale-en también existe: se genera a partir de los componentes y es contra lo que se comprueba el resto de packs. Impórtalo cuando quieras cambiar las etiquetas en inglés en toda la app.
RTL
Los componentes leen la dirección de escritura del DOM (propiedades lógicas, :dir(rtl), iconos invertidos), así que defínela tú en el documento: <html lang="ar" dir="rtl">. El provider nunca toca <html>; solo deriva dir de lang (ar, he, fa, ur son RTL) para los componentes que lo piden, y puedes pasar dir para anularlo. dirOf("ar") se exporta para tu propio código de layout.
Formatos Intl
Los componentes que formatean valores (number-flow, counter-up, stat-card, meter, sparkline-draw, data-grid, date-picker, number-input) aceptan una prop locale. Sin definir, ahora es el idioma del provider en lugar de "en-US"; una prop explícita sigue ganando. Los mismos formateadores son tuyos a través del hook:
import { useGooduiLocale } from "@/lib/goodui-locale"
const { lang, dir, formatNumber, formatDate, formatRelative, formatCurrency } = useGooduiLocale()
formatNumber(12345.6) // "12.345,6" in de
formatDate(booking.start) // "3 Kas 2026" in tr; pass Intl.DateTimeFormat options for other styles
formatRelative(-2, "hour") // "vor 2 Stunden"
formatCurrency(49, "EUR") // "49,00 €"Las instancias se cachean por idioma y opciones, así que llamarlas en el render está bien. Fuera de React, createFormatters(lang) devuelve las mismas cuatro funciones.
Anula una etiqueta
La prop labels de cualquier componente sigue ganando al pack, así que una instancia concreta puede decir otra cosa. Para un cambio en toda la app, expande el pack:
<Dialog labels={{ confirm: "Rezervasyonu iptal et" }} … /> {/* this dialog only */}
const labels = { ...tr, alert: { ...tr.alert, dismiss: "Kapat" } } // every alert
<GooduiLocaleProvider lang="tr" labels={labels}>Las etiquetas con valores son funciones, así que los plurales son de verdad: results: (n) => plural("pl", n, { one: "1 wynik", few: `${n} wyniki`, many: `${n} wyników`, other: `${n} wyniku` }). plural() viene en lib/goodui-locale y envuelve Intl.PluralRules.
Añade un idioma
Copia lib/goodui-locales/en.ts a un archivo nuevo y traduce los valores; conserva las claves y las firmas de las funciones. El manifiesto, lib/locales/labels-manifest.json en el repositorio, lista cada clave con su tipo, sus parámetros y dónde se usa (aria-label, lector de pantalla, texto visible). Nuestro propio build ejecuta node scripts/extract-labels.ts, que regenera lib/goodui-locales/en.ts a partir de los componentes y falla cuando a cualquier pack le falta un componente, una clave o un parámetro, así que los 18 packs nunca pueden quedarse atrás de los componentes. Los pull requests con idiomas nuevos son bienvenidos.
Pruébalo
El Playground de cada componente Core tiene un selector de idioma: la vista previa se vuelve a renderizar en ese idioma (RTL para árabe y hebreo) y el snippet impreso incluye el provider. Cualquier página de vista previa acepta ?lang=tr (y &dir=rtl para forzar una dirección), p. ej. /preview/c/pagination?lang=ar.
goodui Native
Los mismos packs y el mismo manifiesto están previstos para el kit de React Native (un LocaleProvider en foundation, los mismos ids de componente); hasta entonces, los componentes Native reciben sus textos por props y objetos labels por pantalla.