Cadenas compartidas
Cómo internacionalizar cadenas usadas en múltiples componentes y files
Las cadenas compartidas son valores de texto que se usan en varios lugares de tu aplicación, como etiquetas de navegación, mensajes de formularios o datos de configuración. En lugar de duplicar la lógica de traducción en todas partes, usa msg para marcar cadenas para traducción y useMessages para decodificarlas.
El problema del contenido compartido
Considera esta configuración de navegación utilizada en toda tu aplicación:
// navData.ts
export const navData = [
{
label: 'Inicio',
description: 'La página principal',
href: '/'
},
{
label: 'Acerca de',
description: 'Información sobre la empresa',
href: '/about'
}
];Para internacionalizar esto, normalmente tendrías que:
- Convertirlo en una función que reciba una función de traducción
- Actualizar cada uso para llamar a la función con
t - Gestionar la complejidad en todo tu código
Esto genera una sobrecarga de mantenimiento y hace que tu código sea más difícil de leer. La función msg soluciona esto al permitirte marcar cadenas para traducción en el mismo lugar y luego decodificarlas cuando sea necesario.
Inicio rápido
Usa msg para marcar cadenas y useMessages para decodificarlas:
// navData.ts - Marcar cadenas para traducción
import { msg } from 'gt-next';
export const navData = [
{
label: msg('Inicio'),
description: msg('Página de inicio'),
href: '/'
},
{
label: msg('Acerca de'),
description: msg('Información de la empresa'),
href: '/about'
}
];// Uso del componente - Decodificar strings marcados
import { useMessages } from 'gt-next';
import { navData } from './navData';
function Navigation() {
const m = useMessages();
return (
<nav>
{navData.map((item) => (
<a key={item.href} href={item.href} title={m(item.description)}>
{m(item.label)}
</a>
))}
</nav>
);
}Cómo funcionan las cadenas compartidas
El sistema de cadenas compartidas funciona en dos fases:
- Fase de marcado:
msgcodifica cadenas con metadatos de traducción - Fase de decodificación:
useMessagesogetMessagesdecodifican y traducen las cadenas
// msg() codifica la cadena con metadatos
const encoded = msg('¡Hola, mundo!');
console.log(encoded); // "¡Hola, mundo!:eyIkX2hhc2giOiJkMjA3MDliZGExNjNlZmM2In0="
// useMessages() decodifica y traduce
const m = useMessages();
const translated = m(encoded); // "¡Hola, mundo!" en el idioma del usuarioLas cadenas codificadas de msg no pueden usarse directamente; deben decodificarse con useMessages o getMessages.
Uso en el cliente vs. en el servidor
Componentes del cliente
Usa el hook useMessages:
import { useMessages } from 'gt-next';
const encodedString = msg('¡Hola, mundo!');
function MyComponent() {
const m = useMessages();
return <div>{m(encodedString)}</div>;
}Componentes de servidor
Utiliza la función getMessages:
import { getMessages } from 'gt-next/server';
const encodedString = msg('¡Hola, mundo!');
async function MyServerComponent() {
const m = await getMessages();
return <div>{m(encodedString)}</div>;
}Obtener cadenas originales con decodeMsg
A veces necesitas acceder a la cadena original sin traducción, por ejemplo, para registro de logs, depuración o comparaciones. Usa decodeMsg para extraer el texto original:
import { decodeMsg } from 'gt-next';
const encoded = msg('Hello, world!');
const original = decodeMsg(encoded); // "Hello, world!" (original)
const translated = m(encoded); // "Hello, world!" (en el idioma del usuario)
// Útil para logs o depuración
console.log('String original:', decodeMsg(encoded));
console.log('String traducido:', m(encoded));Casos de uso de decodeMsg
- Desarrollo y depuración: Registrar las cadenas originales para diagnosticar problemas
- Manejo de contenido de respaldo predeterminado: Usar el texto original cuando fallen las traducciones
- Comparación de cadenas: Comparar con valores originales conocidos
- Analítica: Registrar el uso de las cadenas originales
// Ejemplo: Manejo de contenido de respaldo predeterminado
function getDisplayText(encodedStr) {
const m = useMessages();
try {
return m(encodedStr);
} catch (error) {
console.warn('Traducción fallida, usando el original:', decodeMsg(encodedStr));
return decodeMsg(encodedStr);
}
}Uso de variables
Para cadenas con contenido dinámico, usa marcadores de posición y pasa variables:
// Marca la cadena con variables
const items = 100;
export const pricing = [
{
name: 'Básico',
price: 100,
description: msg('El plan Básico incluye {items} elementos', { items })
}
];// Use in component
function PricingCard() {
const m = useMessages();
return (
<div>
<h3>{pricing[0].name}</h3>
<p>{m(pricing[0].description)}</p>
</div>
);
}Formato de mensajes ICU
Para un formateo avanzado, usa la sintaxis de ICU:
const count = 10;
const message = msg('Hay {count, plural, =0 {ningún artículo} =1 {un artículo} other {{count} artículos}} en el carrito', { count });Obtén más información sobre ICU message format en la documentación de Unicode.
Ejemplos
Configuración de la navegación
// config/navigation.ts
import { msg } from 'gt-next';
export const mainNav = [
{
label: msg('Inicio'),
href: '/',
icon: 'home'
},
{
label: msg('Productos'),
href: '/products',
icon: 'package'
},
{
label: msg('Sobre nosotros'),
href: '/about',
icon: 'info'
}
];
export const footerLinks = [
{
title: msg('Empresa'),
links: [
{ label: msg('Acerca de'), href: '/about' },
{ label: msg('Carreras'), href: '/careers' },
{ label: msg('Contacto'), href: '/contact' }
]
},
{
title: msg('Soporte'),
links: [
{ label: msg('Centro de ayuda'), href: '/help' },
{ label: msg('Documentación'), href: '/docs' },
{ label: msg('Referencia de API'), href: '/api' }
]
}
];// components/Navigation.tsx
import { useMessages } from 'gt-next';
import { mainNav } from '../config/navigation';
function Navigation() {
const m = useMessages();
return (
<nav>
{mainNav.map((item) => (
<a key={item.href} href={item.href}>
<Icon name={item.icon} />
{m(item.label)}
</a>
))}
</nav>
);
}Configuración del formulario
// config/forms.ts
import { msg } from 'gt-next';
export const formMessages = {
placeholders: {
email: msg('Ingresa tu dirección de correo electrónico'),
password: msg('Ingresa tu contraseña'),
message: msg('Escribe tu mensaje aquí...')
},
actions: {
send: msg('Enviar mensaje'),
save: msg('Guardar cambios'),
cancel: msg('Cancelar')
},
validation: {
required: msg('Este campo es obligatorio'),
email: msg('Ingresa una dirección de correo electrónico válida'),
minLength: msg('Debe tener al menos {min} caracteres', { min: 8 }),
maxLength: msg('No puede superar los {max} caracteres', { max: 100 })
},
success: {
saved: msg('Cambios guardados correctamente'),
sent: msg('Mensaje enviado correctamente'),
updated: msg('Perfil actualizado')
},
errors: {
network: msg('Error de red: inténtalo de nuevo'),
server: msg('Error del servidor: contacta con soporte'),
timeout: msg('La solicitud superó el tiempo de espera: inténtalo de nuevo')
}
};/*
components/ContactForm.tsx
import { useMessages } from 'gt-next';
import { formMessages } from '../config/forms';
function ContactForm() {
const m = useMessages();
const [errors, setErrors] = useState({});
return (
<form>
<input
type="email"
placeholder={m(formMessages.placeholders.email)}
required
/>
{errors.email && <span>{m(formMessages.validation.email)}</span>}
<button type="submit">
{m(formMessages.actions.send)}
</button>
</form>
);
}
*/Generación dinámica de contenido
// utils/productData.ts
import { msg } from 'gt-next';
function mockProducts() {
return [
{ name: 'iPhone 15', company: 'Apple', category: 'Electronics' },
{ name: 'Galaxy S24', company: 'Samsung', category: 'Electronics' }
];
}
export function getProductData() {
const products = mockProducts();
return products.map(product => ({
...product,
description: msg('{name} es un producto {category} de {company}', {
name: product.name,
category: product.category,
company: product.company
})
}));
}// components/ProductList.tsx
import { useMessages } from 'gt-next';
import { getProductData } from '../utils/productData';
function ProductList() {
const m = useMessages();
const products = getProductData();
return (
<div>
{products.map(product => (
<div key={product.name}>
<h3>{product.name}</h3>
<p>{m(product.description)}</p>
</div>
))}
</div>
);
}Problemas frecuentes
Uso de cadenas codificadas directamente
Nunca uses la salida de msg directamente:
// ❌ Incorrecto: se usa la cadena codificada directamente
const encoded = msg('Hello, world!');
return <div>{encoded}</div>; // Muestra la cadena codificada, no la traducción
// ✅ Correcto: primero decodifica la cadena
const encoded = msg('Hello, world!');
const m = useMessages();
return <div>{m(encoded)}</div>; // Muestra la traducción adecuadaContenido dinámico en msg()
Las cadenas deben ser conocidas en tiempo de compilación:
// ❌ Incorrecto: template literal dinámico
const name = 'John';
const message = msg(`Hello, ${name}`); // Error en tiempo de compilación
// ✅ Correcto: usa variables
const name = 'John';
const message = msg('Hello, {name}', { name });Olvidarse de decodificar
Cada cadena msg debe decodificarse:
// ❌ Falta decodificar
const config = {
title: msg('Dashboard'),
subtitle: msg('Bienvenido de nuevo')
};
// Más adelante en el componente: se olvidó de decodificar
return <h1>{config.title}</h1>; // Muestra una cadena codificada
// ✅ Correcto: decodifica al usar
const m = useMessages();
return <h1>{m(config.title)}</h1>; // Muestra el título traducidoPróximos pasos
- Guía de dictionaries - Organiza las traducciones con datos estructurados
- Guía de idiomas - Configura los idiomas compatibles
- Referencia de API:
¿Qué te parece esta guía?