Shared Strings
How to internationalize strings used across multiple components and files
Shared strings are text values used in multiple places throughout your application - like navigation labels, form messages, or configuration data. Instead of duplicating translation logic everywhere, use msg to mark strings for translation and useMessages to decode them.
The Problem with Shared Content
Consider this navigation configuration used across your app:
// navData.ts
export const navData = [
{
label: 'Home',
description: 'The home page',
href: '/'
},
{
label: 'About',
description: 'Information about the company',
href: '/about'
}
];To internationalize this, you'd typically need to:
- Convert it to a function that accepts a translation function
- Update every usage to call the function with
t - Manage the complexity across your codebase
This creates maintenance overhead and makes your code harder to read. The msg function solves this by letting you mark strings for translation in-place, then decode them when needed.
Quick Start
Use msg to mark strings and useMessages to decode them:
// navData.ts - Mark strings for translation
import { msg } from 'gt-react';
export const navData = [
{
label: msg('Home'),
description: msg('The home page'),
href: '/'
},
{
label: msg('About'),
description: msg('Information about the company'),
href: '/about'
}
];// Component usage - Decode marked strings
import { useMessages } from 'gt-react';
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>
);
}How Shared Strings Work
The shared string system works in two phases:
- Mark Phase:
msgencodes strings with translation metadata - Decode Phase:
useMessagesdecodes and translates the strings
// msg() encodes the string with metadata
const encoded = msg('Hello, world!');
console.log(encoded); // "Hello, world!:eyIkX2hhc2giOiJkMjA3MDliZGExNjNlZmM2In0="
// useMessages() decodes and translates
const m = useMessages();
const translated = m(encoded); // "Hello, world!" in user's languageEncoded strings from msg cannot be used directly - they must be decoded with useMessages.
Components
Use useMessages hook:
import { useMessages } from 'gt-react';
const encodedString = msg('Hello, world!');
function MyComponent() {
const m = useMessages();
return <div>{m(encodedString)}</div>;
}Getting Original Strings with decodeMsg
Sometimes you need to access the original string without translation, such as for logging, debugging, or comparisons. Use decodeMsg to extract the original text:
import { decodeMsg } from 'gt-react';
const encoded = msg('Hello, world!');
const original = decodeMsg(encoded); // "Hello, world!" (original)
const translated = m(encoded); // "Hello, world!" (in user's language)
// Useful for logging or debugging
console.log('Original string:', decodeMsg(encoded));
console.log('Translated string:', m(encoded));Use Cases for decodeMsg
- Development & Debugging: Log original strings for troubleshooting
- Fallback Handling: Use original text when translations fail
- String Comparisons: Compare against known original values
- Analytics: Track original string usage
// Example: Fallback handling
function getDisplayText(encodedStr) {
const m = useMessages();
try {
return m(encodedStr);
} catch (error) {
console.warn('Translation failed, using original:', decodeMsg(encodedStr));
return decodeMsg(encodedStr);
}
}Using Variables
For strings with dynamic content, use placeholders and pass variables:
// Mark string with variables
const items = 100;
export const pricing = [
{
name: 'Basic',
price: 100,
description: msg('The basic plan includes {items} items', { items })
}
];// Use in component
function PricingCard() {
const m = useMessages();
return (
<div>
<h3>{pricing[0].name}</h3>
<p>{m(pricing[0].description)}</p>
</div>
);
}ICU Message Format
For advanced formatting, use ICU syntax:
const count = 10;
const message = msg('There are {count, plural, =0 {no items} =1 {one item} other {{count} items}} in the cart', { count });Learn more about ICU Message Format in the Unicode documentation.
Examples
Navigation Configuration
// config/navigation.ts
import { msg } from 'gt-react';
export const mainNav = [
{
label: msg('Home'),
href: '/',
icon: 'home'
},
{
label: msg('Products'),
href: '/products',
icon: 'package'
},
{
label: msg('About Us'),
href: '/about',
icon: 'info'
}
];
export const footerLinks = [
{
title: msg('Company'),
links: [
{ label: msg('About'), href: '/about' },
{ label: msg('Careers'), href: '/careers' },
{ label: msg('Contact'), href: '/contact' }
]
},
{
title: msg('Support'),
links: [
{ label: msg('Help Center'), href: '/help' },
{ label: msg('Documentation'), href: '/docs' },
{ label: msg('API Reference'), href: '/api' }
]
}
];// components/Navigation.tsx
import { useMessages } from 'gt-react';
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>
);
}Form Configuration
// config/forms.ts
import { msg } from 'gt-react';
export const formMessages = {
placeholders: {
email: msg('Enter your email address'),
password: msg('Enter your password'),
message: msg('Type your message here...')
},
actions: {
send: msg('Send Message'),
save: msg('Save Changes'),
cancel: msg('Cancel')
},
validation: {
required: msg('This field is required'),
email: msg('Please enter a valid email address'),
minLength: msg('Must be at least {min} characters', { min: 8 }),
maxLength: msg('Cannot exceed {max} characters', { max: 100 })
},
success: {
saved: msg('Changes saved successfully'),
sent: msg('Message sent successfully'),
updated: msg('Profile updated')
},
errors: {
network: msg('Network error - please try again'),
server: msg('Server error - please contact support'),
timeout: msg('Request timed out - please try again')
}
};// components/ContactForm.tsx
import { useMessages } from 'gt-react';
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>
);
}Dynamic Content Generation
// utils/productData.ts
import { msg } from 'gt-react';
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} is a {category} product by {company}', {
name: product.name,
category: product.category,
company: product.company
})
}));
}// components/ProductList.tsx
import { useMessages } from 'gt-react';
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>
);
}Common Issues
Using Encoded Strings Directly
Never use the output of msg directly:
// ❌ Wrong - encoded string used directly
const encoded = msg('Hello, world!');
return <div>{encoded}</div>; // Shows encoded string, not translation
// ✅ Correct - decode the string first
const encoded = msg('Hello, world!');
const m = useMessages();
return <div>{m(encoded)}</div>; // Shows proper translationDynamic Content in msg()
Strings must be known at build time:
// ❌ Wrong - dynamic template literal
const name = 'John';
const message = msg(`Hello, ${name}`); // Build time error
// ✅ Correct - use variables
const name = 'John';
const message = msg('Hello, {name}', { name });Forgetting to Decode
Every msg string needs to be decoded:
// ❌ Missing decoding
const config = {
title: msg('Dashboard'),
subtitle: msg('Welcome back')
};
// Later in component - forgot to decode
return <h1>{config.title}</h1>; // Shows encoded string
// ✅ Correct - decode when using
const m = useMessages();
return <h1>{m(config.title)}</h1>; // Shows translated titleNext Steps
- Dictionaries Guide - Organize translations with structured data
- Languages Guide - Configure supported languages
- API References:
How is this guide?