Comprehensive internationalization support for React applications with locale-aware hooks and utilities
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Components and utilities for server-side localization with client hydration support, enabling seamless internationalization in SSR applications.
Server-only component that injects localized strings into the initial HTML for client-side hydration.
/**
* A PackageLocalizationProvider can be rendered on the server to inject the localized strings
* needed by the client into the initial HTML.
* @param props - Configuration for package localization
* @returns JSX.Element for server-side rendering or null on client
*/
function PackageLocalizationProvider(props: PackageLocalizationProviderProps): JSX.Element | null;
interface PackageLocalizationProviderProps {
/** The target locale for the injected strings */
locale: string;
/** Package localization strings organized by package name */
strings: PackageLocalizedStrings;
/** Optional CSP nonce for inline script security */
nonce?: string;
}
type PackageLocalizedStrings = {
[packageName: string]: Record<string, LocalizedString>;
};Usage Examples:
// Server-side usage (Next.js, Express, etc.)
import { PackageLocalizationProvider } from "@react-aria/i18n/server";
const localizationStrings = {
"@myapp/components": {
"welcome": "Welcome to our application",
"login": "Log in",
"logout": "Log out"
},
"@myapp/forms": {
"required": "This field is required",
"invalid_email": "Please enter a valid email"
}
};
function ServerApp({ locale, nonce }: { locale: string; nonce?: string }) {
return (
<html>
<head>
<PackageLocalizationProvider
locale={locale}
strings={localizationStrings}
nonce={nonce}
/>
</head>
<body>
<div id="root">
{/* Your app content */}
</div>
</body>
</html>
);
}
// Next.js integration example
import { GetServerSideProps } from "next";
export const getServerSideProps: GetServerSideProps = async (context) => {
const locale = context.locale || "en-US";
// Load localization strings for the specific locale
const strings = await loadLocalizationStrings(locale);
return {
props: {
locale,
strings
}
};
};
function MyPage({ locale, strings }: { locale: string; strings: PackageLocalizedStrings }) {
return (
<>
<Head>
<PackageLocalizationProvider
locale={locale}
strings={strings}
/>
</Head>
<main>
{/* Your page content */}
</main>
</>
);
}
// Express.js integration example
import express from "express";
import { renderToString } from "react-dom/server";
app.get("*", (req, res) => {
const locale = req.headers["accept-language"]?.split(",")[0] || "en-US";
const strings = getLocalizationStrings(locale);
const html = renderToString(
<PackageLocalizationProvider
locale={locale}
strings={strings}
/>
);
res.send(`
<!DOCTYPE html>
<html>
<head>${html}</head>
<body>
<div id="root">${/* app content */}</div>
</body>
</html>
`);
});Utility function that generates the script content for injecting localized strings into HTML.
/**
* Returns the content for an inline <script> tag to inject localized strings into initial HTML.
* @param locale - The target locale string
* @param strings - Package localization strings to inject
* @returns String content for inline script tag
*/
function getPackageLocalizationScript(locale: string, strings: PackageLocalizedStrings): string;Usage Examples:
import { getPackageLocalizationScript } from "@react-aria/i18n/server";
// Manual script injection
function manualScriptInjection() {
const locale = "es-ES";
const strings = {
"@myapp/ui": {
"save": "Guardar",
"cancel": "Cancelar",
"delete": "Eliminar"
}
};
const scriptContent = getPackageLocalizationScript(locale, strings);
// Use with template engines
const html = `
<!DOCTYPE html>
<html>
<head>
<script>${scriptContent}</script>
</head>
<body>
<!-- App content -->
</body>
</html>
`;
return html;
}
// Custom server rendering with security headers
function secureScriptInjection(nonce: string) {
const locale = "fr-FR";
const strings = {
"@myapp/dashboard": {
"overview": "Aperçu",
"settings": "Paramètres",
"profile": "Profil"
}
};
const scriptContent = getPackageLocalizationScript(locale, strings);
return `<script nonce="${nonce}">${scriptContent}</script>`;
}
// Integration with build-time optimization
function buildTimeOptimization() {
const supportedLocales = ["en-US", "es-ES", "fr-FR", "de-DE"];
const allStrings = loadAllLocalizationStrings();
// Pre-generate scripts for each locale
const precompiledScripts = supportedLocales.reduce((acc, locale) => {
const localeStrings = filterStringsByLocale(allStrings, locale);
acc[locale] = getPackageLocalizationScript(locale, localeStrings);
return acc;
}, {} as Record<string, string>);
// Save to build artifacts for runtime use
return precompiledScripts;
}Utilities for detecting and handling locales in server environments.
Common Patterns:
// Express.js locale detection
function detectLocaleFromRequest(req: express.Request): string {
// Priority: URL param > Accept-Language header > default
const urlLocale = req.query.locale as string;
const headerLocale = req.headers["accept-language"]?.split(",")[0];
return urlLocale || headerLocale || "en-US";
}
// Next.js automatic locale detection
export const getServerSideProps: GetServerSideProps = async ({ locale, req }) => {
// Next.js automatically detects locale from routing or headers
const detectedLocale = locale || "en-US";
const strings = await loadStringsForLocale(detectedLocale);
return {
props: {
locale: detectedLocale,
strings
}
};
};
// Custom locale validation and fallback
function validateAndFallbackLocale(requestedLocale: string): string {
const supportedLocales = ["en-US", "es-ES", "fr-FR", "de-DE"];
const normalizedLocale = requestedLocale.toLowerCase();
// Exact match
if (supportedLocales.includes(requestedLocale)) {
return requestedLocale;
}
// Language-only fallback (e.g., "es" -> "es-ES")
const languageOnly = requestedLocale.split("-")[0];
const languageMatch = supportedLocales.find(locale =>
locale.startsWith(languageOnly)
);
return languageMatch || "en-US";
}Important considerations for client-side hydration with server-injected localization.
Best Practices:
// Client-side hydration handling
import { I18nProvider, useLocalizedStringFormatter } from "@react-aria/i18n";
function ClientApp({ serverLocale }: { serverLocale: string }) {
// Use server locale initially to prevent hydration mismatches
const [locale, setLocale] = useState(serverLocale);
useEffect(() => {
// After hydration, can update to browser preference if desired
const browserLocale = navigator.language;
if (browserLocale !== serverLocale) {
// Optionally update locale based on user preference
setLocale(browserLocale);
}
}, [serverLocale]);
return (
<I18nProvider locale={locale}>
<App />
</I18nProvider>
);
}
// Preventing hydration warnings with SSR-safe components
function SSRSafeLocaleDisplay() {
const [isClient, setIsClient] = useState(false);
const { locale } = useLocale();
useEffect(() => {
setIsClient(true);
}, []);
if (!isClient) {
// Return server-safe content
return <span>Loading locale...</span>;
}
return <span>Current locale: {locale}</span>;
}
// Error boundary for localization failures
class LocalizationErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error("Localization error:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Localization failed. Falling back to default content.</div>;
}
return this.props.children;
}
}Techniques for optimizing server-side localization performance.
// String compression and caching
const localizationCache = new Map<string, PackageLocalizedStrings>();
function getCachedStrings(locale: string): PackageLocalizedStrings {
if (!localizationCache.has(locale)) {
const strings = loadLocalizationStrings(locale);
localizationCache.set(locale, strings);
}
return localizationCache.get(locale)!;
}
// Lazy loading of locale strings
async function lazyLoadStrings(locale: string, packages: string[]) {
const promises = packages.map(pkg =>
import(`./locales/${locale}/${pkg}.json`)
);
const results = await Promise.all(promises);
return packages.reduce((acc, pkg, index) => {
acc[pkg] = results[index].default;
return acc;
}, {} as PackageLocalizedStrings);
}
// Build-time string optimization
function optimizeStringsForProduction(strings: PackageLocalizedStrings) {
// Remove developer comments and descriptions
const optimized = {};
for (const [pkg, pkgStrings] of Object.entries(strings)) {
optimized[pkg] = {};
for (const [key, value] of Object.entries(pkgStrings)) {
if (typeof value === "string") {
optimized[pkg][key] = value;
} else {
// Remove description, keep only message
optimized[pkg][key] = value.message;
}
}
}
return optimized as PackageLocalizedStrings;
}Install with Tessl CLI
npx tessl i tessl/npm-react-aria--i18n