A lightweight, professional localization package for React and React Native applications built on top of i18next and react-i18next. This package provides a clean, type-safe API for managing translations with built-in language switching, variable injection, and component interpolation capabilities.
- π Simple React Hooks - Clean, intuitive hooks for translations and language management
- π Language Switching - Built-in language switching with persistence
- π Variable Injection - Dynamic content insertion in translations
- π§© Component Interpolation - Embed React components within translations
- π Translation Validation - CLI tools to ensure translation consistency
- π Translation Sync - Automated synchronization of translation files
- π± React Native Support - Full compatibility with React Native applications
- π‘οΈ Type Safety - Full TypeScript support with comprehensive type definitions
- β‘ Performance Optimized - Lightweight wrapper with minimal overhead
npm install @weprodev/ui-localizationCreate a translations directory in your project with language files:
// translations/en.ts
const en = {
common: {
hello: "Hello",
welcome: "Welcome to our app",
goodbye: "Goodbye"
},
auth: {
login: "Login",
signup: "Sign Up",
forgotPassword: "Forgot Password"
},
dashboard: {
title: "Dashboard",
summary: "Summary",
recentActivity: "Recent Activity"
}
};
export default en;// translations/es.ts
const es = {
common: {
hello: "Hola",
welcome: "Bienvenido a nuestra aplicaciΓ³n",
goodbye: "AdiΓ³s"
},
auth: {
login: "Iniciar sesiΓ³n",
signup: "Registrarse",
forgotPassword: "ContraseΓ±a olvidada"
},
dashboard: {
title: "Panel de control",
summary: "Resumen",
recentActivity: "Actividad reciente"
}
};
export default es;// src/localizationConfig.ts
import { LocalizationConfig, LanguageStore } from '@weprodev/ui-localization';
import en from '../translations/en';
import es from '../translations/es';
// Optional: Create a custom language store for persistence
class CustomLanguageStore implements LanguageStore {
getLanguage(): string | null {
return localStorage.getItem("app-language") || null;
}
setLanguage(language: string): void {
localStorage.setItem("app-language", language);
}
}
export const localizationConfig: LocalizationConfig = {
resources: {
en: { translation: en },
es: { translation: es }
},
fallbackLng: 'en',
languageStore: new CustomLanguageStore()
};// src/index.tsx
import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import { initLocalization } from '@weprodev/ui-localization';
import { localizationConfig } from './localizationConfig';
import App from './App';
const rootElement = document.getElementById('root');
// Initialize localization before rendering the app
initLocalization(localizationConfig).then(() => {
ReactDOM.createRoot(rootElement).render(
<StrictMode>
<App />
</StrictMode>
);
});// src/components/Welcome.tsx
import React from 'react';
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';
const Welcome: React.FC = () => {
// Type-safe translation hook with intellisense
const t = useTranslation<typeof en>(en);
return (
<div>
<h1>{t.common.welcome}</h1>
<p>{t.common.hello}</p>
</div>
);
};
export default Welcome;Alternative: Create a custom hook for better reusability
// src/hooks/useAppTranslation.ts
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';
export const useAppTranslation = () => {
return useTranslation<typeof en>(en);
};
// Usage in components
import { useAppTranslation } from '../hooks/useAppTranslation';
const Welcome: React.FC = () => {
const t = useAppTranslation();
return (
<div>
<h1>{t.common.welcome}</h1> {/* Full intellisense support */}
<p>{t.common.hello}</p>
</div>
);
};Troubleshooting: If you encounter TypeScript errors with the type-safe hook, you can use useTranslationFallback() as an escape hatch. See the API Reference for details.
// src/components/LanguageSwitcher.tsx
import React from 'react';
import { useLanguage } from '@weprodev/ui-localization';
const LanguageSwitcher: React.FC = () => {
const { currentLanguage, changeLanguage, availableLanguages } = useLanguage();
return (
<select
value={currentLanguage}
onChange={(e) => changeLanguage(e.target.value)}
>
{availableLanguages.map(lang => (
<option key={lang} value={lang}>
{lang.toUpperCase()}
</option>
))}
</select>
);
};
export default LanguageSwitcher;Same structure as React applications (see above).
// src/localizationConfig.ts
import { LocalizationConfig, LanguageStore } from '@weprodev/ui-localization';
import AsyncStorage from '@react-native-async-storage/async-storage';
import en from '../translations/en';
import es from '../translations/es';
// React Native language store using AsyncStorage
class ReactNativeLanguageStore implements LanguageStore {
async getLanguage(): Promise<string | null> {
try {
return await AsyncStorage.getItem("app-language");
} catch {
return null;
}
}
async setLanguage(language: string): Promise<void> {
try {
await AsyncStorage.setItem("app-language", language);
} catch {
// Handle storage error silently
}
}
}
export const localizationConfig: LocalizationConfig = {
resources: {
en: { translation: en },
es: { translation: es }
},
fallbackLng: 'en',
languageStore: new ReactNativeLanguageStore()
};// src/App.tsx
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import { initLocalization } from '@weprodev/ui-localization';
import { localizationConfig } from './localizationConfig';
const App: React.FC = () => {
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
initLocalization(localizationConfig).then(() => {
setIsInitialized(true);
});
}, []);
if (!isInitialized) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Loading...</Text>
</View>
);
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* Your app content */}
</View>
);
};
export default App;// src/components/Welcome.tsx
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';
const Welcome: React.FC = () => {
// Type-safe translation hook with intellisense
const t = useTranslation<typeof en>(en);
return (
<View style={styles.container}>
<Text style={styles.title}>{t.common.welcome}</Text>
<Text style={styles.subtitle}>{t.common.hello}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
},
subtitle: {
fontSize: 16,
color: '#666',
},
});
export default Welcome;// src/components/LanguageSwitcher.tsx
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { useLanguage } from '@weprodev/ui-localization';
const LanguageSwitcher: React.FC = () => {
const { currentLanguage, changeLanguage, availableLanguages } = useLanguage();
return (
<View style={styles.container}>
<Text style={styles.label}>Language:</Text>
<View style={styles.buttonContainer}>
{availableLanguages.map(lang => (
<TouchableOpacity
key={lang}
style={[
styles.button,
currentLanguage === lang && styles.activeButton
]}
onPress={() => changeLanguage(lang)}
>
<Text style={[
styles.buttonText,
currentLanguage === lang && styles.activeButtonText
]}>
{lang.toUpperCase()}
</Text>
</TouchableOpacity>
))}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 20,
},
label: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
},
buttonContainer: {
flexDirection: 'row',
gap: 10,
},
button: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 4,
borderWidth: 1,
borderColor: '#ddd',
},
activeButton: {
backgroundColor: '#007AFF',
borderColor: '#007AFF',
},
buttonText: {
fontSize: 14,
color: '#333',
},
activeButtonText: {
color: '#fff',
},
});
export default LanguageSwitcher;import { useTranslation, useTranslationInjection } from '@weprodev/ui-localization';
import en from '../translations/en';
const Greeting: React.FC<{ name: string }> = ({ name }) => {
const t = useTranslation<typeof en>(en);
// Translation key: "greeting": "Hello, {{name}}!"
const greeting = useTranslationInjection(t.common.greeting, { name });
return <p>{greeting}</p>;
};import { useTranslation, useTranslationWithInterpolation } from '@weprodev/ui-localization';
import en from '../translations/en';
const TermsAgreement: React.FC<{ name: string }> = ({ name }) => {
const t = useTranslation<typeof en>(en);
// Translation key: "welcome": "Welcome <strong>{{name}}</strong>"
const welcomeElement = useTranslationWithInterpolation(
t.common.welcome,
{ name },
{
strong: <strong style={{ color: "red" }} />
}
);
return <div>{welcomeElement}</div>;
};The package includes powerful CLI tools to help manage your translations. You can use these tools in multiple ways:
Add these commands to your project's package.json scripts:
{
"scripts": {
"translation:validate": "wpd-translation-validate --dir ./translations --source en",
"translation:sync": "wpd-translation-sync --dir ./translations --source en"
}
}Then run:
npm run translation:validate
npm run translation:syncCustomizing paths:
Modify the scripts in your package.json to use different directories or source language:
{
"scripts": {
"translation:validate": "wpd-translation-validate --dir ./src/translations --source es",
"translation:sync": "wpd-translation-sync --dir ./src/translations --source es"
}
}Run the CLI tools directly via npx:
# Validate translations
npx wpd-translation-validate --dir ./translations --source en
# Sync translations
npx wpd-translation-sync --dir ./translations --source enValidate Translations (validate-translations)
- Checks if all language files have the same keys as the source language
- Reports missing keys for each language file
- Exits with error code if inconsistencies are found
- Perfect for CI/CD integration
Sync Translations (sync-translations)
- Adds missing keys from the source language to all other language files
- Preserves existing translations
- Sets empty string values for new keys (ready for translation)
- Updates translation files automatically
We recommend running translation:validate as part of your CI pipeline to ensure translation consistency:
# .github/workflows/ci.yml
- name: Validate Translations
run: npm run translation:validateReturns a type-safe translation proxy object with intellisense support.
Parameters:
translationLanguage: The translation object to provide type safety for
Returns: Type-safe translation proxy object
import en from '../translations/en';
const t = useTranslation<typeof en>(en);
const translatedText = t.common.hello; // Type-safe with intellisenseNote: For better reusability, consider creating a custom hook:
// src/hooks/useAppTranslation.ts
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';
export const useAppTranslation = () => {
return useTranslation<typeof en>(en);
};Provides language management functionality.
const { currentLanguage, changeLanguage, availableLanguages } = useLanguage();useTranslation hook has TypeScript issues.
Returns the raw i18next translation function without type safety. This should only be used in rare edge cases where the type-safe hook encounters problems.
// Only use when useTranslation has TypeScript errors
const t = useTranslationFallback();
const text = t('common.hello'); // No type safety - standard i18next usage
const withVars = t('greeting', { name: 'John' });Note: The main useTranslation hook should be preferred in 99% of cases.
Injects variables into translation strings.
const result = useTranslationInjection('greeting', { name: 'John' });Interpolates React components into translations.
const element = useTranslationWithInterpolation(
'welcome',
{ name: 'John' },
{ strong: <strong /> }
);Initializes the localization system.
await initLocalization({
resources: { en: { translation: enTranslations } },
fallbackLng: 'en',
languageStore: new CustomLanguageStore()
});Interface for custom language storage implementations.
interface LanguageStore {
getLanguage(): string | null;
setLanguage(language: string): void;
}Configuration object for localization initialization.
interface LocalizationConfig {
resources: Resource;
fallbackLng?: string;
compatibilityJSON?: "v4";
interpolation?: {
escapeValue?: boolean;
};
languageStore?: LanguageStore;
}For support, bug reports, or feature requests, please contact the WeProDev team or create an issue in our internal repository.
This project is licensed under the MIT License.
@weprodev/ui-localization - Professional localization solution by WeProDev