Ir al contenido

FOS Localization

Versión 1.0.1 · requiere FOS Essentials Core 1.0.0 o posterior

Traduce los textos y las imágenes de un mundo de VRChat a tantos idiomas como quieras, editados en una tabla de verdad en lugar de en dos arrays sin relación, con ida y vuelta en CSV para quienes traducen y con el idioma elegido recordado entre visitas. Requiere FOS Essentials Core 1.0.0.


  1. Crea un GameObject vacío y añade Add Component > FOS Essentials > Localization > Locale Manager.
  2. Rellena Language codes (en, fr, es, de) y Language names (English, Français, Español, Deutsch). El primero es el idioma pivote al que recurren todos los demás, así que pon primero aquel en el que escribes el mundo.
  3. Abre FOS Essentials > FOS Localization > Translation Table y añade tus claves.
  4. En cada texto de tu interfaz, añade Add Component > FOS Essentials > Localization > Localized Text, apúntalo al gestor y dale su clave.
  5. Para el selector de idioma, añade Language Button a cada botón, uno por idioma. Su inspector conecta el OnClick por ti.

Componente Función
FOSLocaleManager Uno por mundo. Guarda los idiomas, la tabla y el idioma actual.
FOSLocalizedText Enlaza una clave con un TMP_Text o un Text heredado.
FOSLocalizedImage Cambia un sprite según el idioma.
FOSLanguageButton Cambia el mundo a un idioma, con resalte opcional de seleccionado.

Todo aquí es local. Ninguna variable sincronizada, ningún tráfico de red, ninguna propiedad de objeto. Dos jugadores uno al lado del otro no tienen por qué leer el mismo idioma, y sincronizar la elección significaría que uno la pierde cada vez que el otro hace clic.


FOS Essentials > FOS Localization > Translation Table, o el botón del inspector del gestor. Una fila por clave, una columna por idioma, y las celdas vacías van coloreadas para que un hueco se vea en lugar de tener que buscarlo.

Color Significado
Rojo El primer idioma está vacío. Nada a lo que recurrir: el mundo mostrará #clave.
Ámbar Otro idioma está vacío. El mundo recurre al primer idioma.

El primer idioma declarado es el pivote: es al que recurren todos los demás. Pon primero aquel en el que realmente escribes el mundo.

La barra de herramientas filtra por texto — la búsqueda también mira dentro de las traducciones, así que una frase vista en el juego basta para encontrar su clave — y Untranslated only reduce la tabla a las filas que aún necesitan trabajo.

Las claves y las traducciones se guardan en dos arrays, ocultos del inspector a propósito. La rejilla es plana: la traducción j de la clave i está en i × idiomas + j. Una clave añadida a mano en el array, sin su fila de celdas, desplaza un lugar todos los textos siguientes: sin error, sin nada visible en pantalla y sin forma de reconstruir el original. La ventana es por tanto el único camino de escritura, y mantiene ambos arrays acompasados.

Añadir o quitar un idioma en el inspector sí está bien: el gestor detecta el desajuste y repara la rejilla, moviendo cada traducción a su nueva posición.


Export CSV escribe toda la tabla, una fila por clave, una columna por idioma, con los códigos de idioma como cabeceras. Import CSV la vuelve a leer.

La importación es una fusión, nunca un reemplazo:

  • Una clave presente en el archivo pero no en la tabla se añade.
  • Una clave de la tabla ausente del archivo se deja intacta.
  • Una columna cuya cabecera no corresponde a ningún idioma declarado se ignora: una columna de comentarios no cuesta nada.
  • Una celda vacía nunca sobrescribe una traducción existente.

Así, un solo idioma puede salir a revisión y volver sin ningún riesgo para los demás, y quien devuelva una columna a medias no destruye nada.

El archivo se escribe en UTF-8 con BOM. Sin él, Excel abre el archivo en la página de códigos local y destroza todos los acentos, que es con diferencia la forma más habitual de estropear un viaje de ida y vuelta de traducción.


Una clave es un identificador, no una frase: menu.title, rules.adult.warning, teleport.button.spawn. Ponles prefijo por área para que una familia quede junta al ordenar la tabla — el botón Sort ordena las claves alfabéticamente llevándose sus traducciones, y eso es lo que hace visible al miembro que falta de una familia.

Una clave ausente aparece como #la.clave en el juego. Es deliberado: un hueco de traducción tiene que notarse, y una etiqueta en blanco nunca se nota.

Los saltos de línea se escriben \n en la tabla. Un campo de inspector no admite un salto de línea real, y un viaje por CSV no lo sobreviviría.


VRChat no le dice a un mundo en qué idioma está el cliente de un jugador. No existe API para ello, así que ninguna herramienta puede detectarlo, esta incluida. El jugador llega en el idioma por defecto y cambia a mano.

Lo que sí se puede hacer es recordar su elección. Con Remember the choice activado, el idioma se escribe en los PlayerData de VRChat y se restaura en la siguiente visita a este mundo.

  • Nada sale del mundo y nada se comparte con otros jugadores.
  • La elección se guarda por el código del idioma, nunca por su índice, así que reordenar o insertar un idioma no mueve a todo el mundo en silencio.
  • Un idioma retirado del mundo desde la última visita deja al jugador en el idioma por defecto en lugar de en ninguno.

Los PlayerData no se pueden leer en Start. El idioma por defecto se aplica de inmediato y el guardado lo sustituye en OnPlayerRestored, y por eso quien vuelve puede ver un fotograma en el idioma por defecto.


La búsqueda se resuelve una sola vez, en Start: cada FOSLocalizedText recuerda el índice de su clave y no vuelve a buscar jamás. Un cambio de idioma pasa a ser una lectura indexada por texto.

Buscar por nombre en cada cambio — la forma obvia de escribir esto — cuesta textos × claves comparaciones de cadenas cada vez que alguien pulsa una bandera. En una interfaz de tableta con cien etiquetas y doscientas claves, son veinte mil comparaciones de cadenas por clic, en Udon.

Los componentes se registran solos en el gestor, así que no hay ninguna lista que rellenar a mano ni nada que actualizar cuando una página gana una etiqueta.


Llamada En Efecto
FOSSetLanguage(int) FOSLocaleManager Cambia de idioma y guarda la elección
FOSCurrentLanguage() FOSLocaleManager Índice del idioma mostrado
FOSIndexOfLanguage(string) FOSLocaleManager Índice de un código de idioma, -1 si es desconocido
FOSIndexOfKey(string) FOSLocaleManager Índice de una clave, -1 si es desconocida. Llámalo una vez
FOSTextAt(int) FOSLocaleManager Traducción de una clave ya resuelta
FOSText(string) FOSLocaleManager Traducción de una clave, búsqueda incluida
FOSRegister(UdonSharpBehaviour) FOSLocaleManager Seguir los cambios de idioma
SelectLanguage() FOSLanguageButton La acción del control

Cualquier UdonSharpBehaviour puede llamar a FOSRegister(this) y exponer un FOSApplyLanguage() público: se le avisará en cada cambio, exactamente igual que a los componentes que vienen aquí. Ese es el punto de extensión para todo lo que este pack no cubre: una fuente de audio por idioma, un reproductor de vídeo, la textura de un cartel.


  • Un gestor por mundo es el montaje previsto. Varios funcionan, pero cada uno tiene su tabla y su idioma actual, y quien cambie uno no cambia el otro.
  • Los textos creados en ejecución no se recogen. Llama tú a FOSRegister sobre ellos.
  • El idioma no se sincroniza, por diseño. Ver arriba.
  • Quest y PC se comportan igual: no hay código específico de plataforma, ni podría haberlo, porque Udon se compila una sola vez para ambas.