Skip to content
SoyGarfield
Research #07

Cómo detecté el idioma por país sin selector manual: el caso iFace Simulator

¿Conviene detectar el idioma del visitante por geolocalización IP, o un selector manual de idioma es siempre más confiable?

Por Pietro Fiorillo ·

Índice del artículo
Diagrama de un flujo lineal. El visitante entra sin idioma en la URL, se geolocaliza su IP con ipapi.co, se compara contra una lista de países hispanohablantes y lusófonos escrita a mano, se redirige a es, en o pt, y finalmente se carga de forma diferida la ruta del simulador.
De una visita sin idioma a una ruta cargada de forma diferida en el idioma correcto, sin que el visitante elija nada. Diagrama propio.

El problema: explicar modelos de entrenamiento médico-estético a mercados distintos

iFace fabrica modelos de entrenamiento físicos y digitales para que médicos practiquen procedimientos estéticos antes de operar. Necesitaba un sitio que explicara cada simulador con claridad y funcionara en varios idiomas según el país del visitante, sin obligarlo a elegir idioma a mano.

Cómo se arma cada simulador: rutas cargadas de forma diferida, sin un registro central

No hay un array de configuración que liste los simuladores disponibles. Cada uno es, literalmente, una línea de import() diferido más una línea de <Route> en el router.

const R1   = lazy(() => import('./components/R1/R1'));
const Lips = lazy(() => import('./components/Lips/Lips'));
const INose = lazy(() => import('./components/INose/INose'));

<Route path="simulators/r1" element={<R1 />} />
<Route path="simulators/lips" element={<Lips />} />
<Route path="simulators/inose" element={<INose />} />
{/* Models */}
<Route path="models/discovery-model" element={<DiscoveryModel />} />
Código real (trimmed) de src/App.jsx.

Agregar un simulador nuevo es agregar estas dos líneas, no modificar un sistema de módulos. Es una arquitectura simple hasta que el número de simuladores crezca mucho, momento en el que un registro de datos probablemente valga la pena.

Cómo decide el sitio en qué idioma mostrarte el contenido

El sitio usa i18next con tres paquetes de traducción completos: inglés, español y portugués. No hay italiano, a pesar de que yo lo hable: el producto apunta a mercados angloparlante, hispanohablante y lusófono específicamente.

import en from './locales/en/translation.json';
import es from './locales/es/translation.json';
import pt from './locales/pt/translation.json';

const resources = { en: { translation: en }, es: { translation: es }, pt: { translation: pt } };

i18n.use(initReactI18next).init({
  resources,
  lng: 'en',
  fallbackLng: 'en',
});
Código real (trimmed) de src/i18n.js.

La decisión de qué idioma mostrar no depende de un selector que el visitante elige: un componente geolocaliza la IP con ipapi.co y la compara contra listas de países hispanohablantes y lusófonos escritas directamente en el código. Si el país coincide, redirige a /es o /pt; si no, se queda en /en.

No es una plataforma de simuladores espaciales: es una tienda con carrito simple

Cada producto tiene un objeto de carrito simple, con id, nombre y precio.

{
  cartId: 'r1-simulator',
  cartName: 'R1 Simulator',
  price: 344,
}
Código real (trimmed) de R1Simulator.jsx.

Cómo validé esta arquitectura

Corrí npm run build contra el repositorio real (Vite 7, React 19) y medí el resultado en vez de describirlo de memoria.

Métricas reales medidas el 18 de octubre de 2026.
MétricaResultadoEntorno / evidencia
Lighthouse Performance (móvil, lab)69 / 100Lighthouse 13.5.0, throttling simulado
LCP (lab)5,0 sMisma medición Lighthouse, no es un dato de campo (CrUX)
Tiempo de build local3,39 svite build, el más rápido de toda esta serie
Tamaño del export3,7 MB en 129 archivosCarpeta /dist generada por ese build
Idiomas soportados3 (en, es, pt)Carpetas reales en src/locales, sin italiano
Commits de desarrollo23, de julio a agosto de 2026git log del repositorio

El propio build advirtió sobre un chunk de JavaScript de más de 500 KB (index-BEfQztCT.js, 560 KB sin comprimir), algo que vale la pena dividir más si el sitio sigue creciendo.

El idioma de un producto lo decide su mercado, no la biografía de quien lo construye.

Pietro Fiorillo

Mi participación en este proyecto

Mi participación en iFace Simulator incluyó el desarrollo del frontend, la integración de Sanity como CMS y la estrategia multilingüe, como parte de mi trabajo en MANYA Digital.

Lo que haría distinto: una descripción pública que no correspondía al proyecto

Antes de escribir este artículo, la página de este proyecto en mi sitio describía una «plataforma de simuladores espaciales» con un «dashboard de control en tiempo real». Al revisar el repositorio real, encontré que se trata de un sitio de marketing y e-commerce para modelos de entrenamiento médico-estético, sin dashboard ni datos en tiempo real de ningún tipo. Ya corregí esa descripción en la página del proyecto; la documento acá porque es exactamente el tipo de error que vale la pena admitir en vez de dejar pasar.

Qué probaría después

Verificaría que exista un selector de idioma manual visible para cuando la geolocalización se equivoca (una VPN, por ejemplo), y dividiría el chunk de JavaScript de 560 KB que señaló el build antes de que el sitio agregue más simuladores.

Fuentes

Preguntas frecuentes

¿Por qué el sitio no tiene versión en italiano si vos sos italiano?

Porque el producto está pensado para mercados angloparlante, hispanohablante y lusófono específicamente, no para cubrir todos los idiomas que yo hablo. Las decisiones de idioma de un proyecto responden a su mercado, no a quién lo construye.

¿Cómo decide el sitio qué idioma mostrar?

Geolocaliza la IP del visitante con ipapi.co y la compara contra dos listas de países (hispanohablantes y lusófonos) escritas a mano en el código. Si el país está en alguna lista, redirige a /es o /pt; si no, se queda en /en por defecto.

¿Qué pasa si alguien usa una VPN?

Vería el idioma correspondiente al país de salida de la VPN, no a su país real. Es una limitación inherente a cualquier detección por IP, no específica de este proyecto, y el selector de idioma manual (si existe en la interfaz) sigue siendo la forma de corregirlo.

¿Hay algún dashboard o datos en tiempo real?

No. Revisé el código buscando websockets, librerías de gráficos o hooks de polling, y no encontré ninguno. Es un sitio de marketing y e-commerce con rutas cargadas de forma diferida, no una plataforma de monitoreo.

¿Por qué Vite y React Router en vez de Next.js?

Porque el sitio no necesita renderizado en servidor ni rutas híbridas: es una SPA de marketing con carga diferida por producto, y Vite da un build más simple y rápido para ese caso de uso.

¿Cuánto cuesta mantener un sitio así?

El hosting de una SPA estática es mínimo. El contenido se edita desde Sanity sin tocar código, así que el costo real es el tiempo de mantener las traducciones en los tres idiomas sincronizadas entre sí.