An open API service indexing awesome lists of open source software.

https://github.com/weprodev/ui-localization

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.
https://github.com/weprodev/ui-localization

localization react react-native reactjs translation translation-package ui-localization

Last synced: 11 days ago
JSON representation

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.

Awesome Lists containing this project

README

          

# @weprodev/ui-localization

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.

## โœจ Features

- ๐ŸŒ **Simple React Hooks** - Clean, intuitive hooks for translations and language management
- ๐Ÿ”„ **Language Switching** - Built-in language switching with persistence
- ๐Ÿ“ **Type-Safe Variable Injection** - Dynamic content insertion with compile-time parameter validation
- ๐Ÿงฉ **Unified Component Interpolation** - Embed React components within translations using the same `t` function
- ๐Ÿ” **Translation Validation** - CLI tools to ensure translation consistency
- ๐Ÿ”„ **Translation Sync** - Automated synchronization of translation files
- ๐Ÿ“ฑ **React Native Support** - Full compatibility with React Native applications
- ๐Ÿ›ก๏ธ **Full Type Safety** - TypeScript support with type-safe keys, parameters, and component interpolation
- โšก **Performance Optimized** - Lightweight wrapper with minimal overhead

## ๐Ÿ“ฆ Installation

```bash
npm install @weprodev/ui-localization
```

## ๐Ÿš€ Quick Start

### React Applications

#### 1. Create Translation Files

Create a `translations` directory in your project with language files. **Important:** Use `as const` to enable type-safe parameter validation:

```typescript
// translations/en.ts
const en = {
common: {
hello: "Hello {{name}}!",
welcome: "Welcome {{name}}!",
goodbye: "Goodbye {{name}}!",
greeting: "Hello, {{name}}!"
},
auth: {
login: "Login",
signup: "Sign Up",
forgotPassword: "Forgot Password",
welcomeMessage: "Welcome {{name}}! Please sign in to continue."
},
dashboard: {
title: "Dashboard",
summary: "Summary",
recentActivity: "Recent Activity"
}
} as const;

export default en;
```

```typescript
// translations/es.ts
const es = {
common: {
hello: "Hola {{name}}!",
welcome: "Bienvenido {{name}}!",
goodbye: "Adiรณs {{name}}!",
greeting: "ยกHola, {{name}}!"
},
auth: {
login: "Iniciar sesiรณn",
signup: "Registrarse",
forgotPassword: "Contraseรฑa olvidada",
welcomeMessage: "Bienvenido {{name}}! Por favor inicia sesiรณn para continuar."
},
dashboard: {
title: "Panel de control",
summary: "Resumen",
recentActivity: "Actividad reciente"
}
} as const;

export default es;
```

#### 2. Create Localization Configuration

```typescript
// 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()
};
```

#### 3. Initialize Localization

```typescript
// 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(



);
});
```

#### 4. Use Translations in Components

```typescript
// src/components/Welcome.tsx
import React from 'react';
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';

const Welcome: React.FC<{ name: string }> = ({ name }) => {
// Type-safe translation hook with path-based keys
const { t } = useTranslation();

return (


{/* TypeScript requires 'name' parameter because translation has {{name}} placeholder */}

{t('common.welcome', { name })}


{t('common.hello', { name })}


{/* TypeScript will error if you use invalid keys like t('common.invalid') */}
{/* TypeScript will error if you forget required parameters */}

);
};

export default Welcome;
```

**Alternative: Create a custom hook for better reusability**

```typescript
// src/hooks/useAppTranslation.ts
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';

export const useAppTranslation = () => {
return useTranslation();
};

// Usage in components
import { useAppTranslation } from '../hooks/useAppTranslation';

const Welcome: React.FC = () => {
const { t } = useAppTranslation();

return (


{t('common.welcome')}

{/* Full intellisense and type safety */}

{t('common.hello')}


{/* t('common.invalid') will show TypeScript error */}

);
};
```

**Important:** For type-safe parameter validation to work, your translation files must use `as const`. This ensures TypeScript preserves literal string types, allowing the system to extract parameter names from placeholders like `{{name}}`.

**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.

#### 5. Language Switching

```typescript
// src/components/LanguageSwitcher.tsx
import React from 'react';
import { useLanguage } from '@weprodev/ui-localization';

const LanguageSwitcher: React.FC = () => {
const { currentLanguage, changeLanguage, availableLanguages } = useLanguage();

return (
changeLanguage(e.target.value)}
>
{availableLanguages.map(lang => (

{lang.toUpperCase()}

))}

);
};

export default LanguageSwitcher;
```

### React Native Applications

#### 1. Create Translation Files

Same structure as React applications (see above).

#### 2. Create Localization Configuration

```typescript
// 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 {
try {
return await AsyncStorage.getItem("app-language");
} catch {
return null;
}
}

async setLanguage(language: string): Promise {
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()
};
```

#### 3. Initialize Localization

```typescript
// 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 (

Loading...

);
}

return (

{/* Your app content */}

);
};

export default App;
```

#### 4. Use Translations in React Native Components

```typescript
// 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<{ name: string }> = ({ name }) => {
// Type-safe translation hook with path-based keys
const { t } = useTranslation();

return (

{t('common.welcome', { name })}
{t('common.hello', { name })}

);
};

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;
```

#### 5. Language Switching in React Native

```typescript
// 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 (

Language:

{availableLanguages.map(lang => (
changeLanguage(lang)}
>

{lang.toUpperCase()}


))}


);
};

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;
```

## ๐Ÿ”ง Advanced Usage

### Translation with Variables (Type-Safe Parameters)

The `t` function enforces type-safe parameters based on placeholders in your translation strings. Parameters are **required** when placeholders exist, and **optional** when they don't.

```typescript
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';

const Greeting: React.FC<{ name: string }> = ({ name }) => {
const { t } = useTranslation();

// Translation: "common.greeting": "Hello, {{name}}!"
// โœ… TypeScript requires the 'name' parameter
const greeting = t('common.greeting', { name });

// โŒ TypeScript error: missing required parameter 'name'
// const greeting = t('common.greeting');

// โŒ TypeScript error: wrong parameter name
// const greeting = t('common.greeting', { wrongName: name });

// โœ… No parameters needed for translations without placeholders
const title = t('dashboard.title');

return

{greeting}

;
};
```

### Translation with Component Interpolation

The unified `t` function supports both string and component interpolation. When you pass a `components` object as the third argument, it returns a React element instead of a string.

```typescript
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';

const WelcomeMessage: React.FC<{ name: string }> = ({ name }) => {
const { t } = useTranslation();

// Translation: "auth.welcomeMessage": "Welcome {{name}}! Please sign in to continue."
// The translation string must contain matching HTML-like tags (, , etc.)
const welcomeElement = t(
'auth.welcomeMessage',
{ name },
{
strong: ,
link: (props: { children?: React.ReactNode }) => (

{props.children}

)
}
);

// Returns JSX.Element when components are provided
return

{welcomeElement}
;
};
```

**Important Notes:**
- Component interpolation only works when the translation string contains matching HTML-like tags (e.g., ``, ``)
- The component keys in your `components` object must match the tag names in the translation
- For self-closing tags like ``, use self-closing components
- For tags with content like `text`, use function components that accept `props.children`

### Usage Outside React Components

For utility functions or other non-component files where hooks are not available, you can use `createTranslation` to get a type-safe `t` function.

```typescript
import { createTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';

// Create a standalone translation function
const t = createTranslation();

// Basic usage - Returns string
const greeting = t('common.hello');

// With parameters - Returns string
const message = t('common.welcome', { name: 'World' });

// With components - Returns JSX.Element
// Useful for creating localized constants with React elements
const content = t('common.info', undefined, {
link: More
});
```

## ๐Ÿ› ๏ธ Translation Management Tools

The package includes powerful CLI tools to help manage your translations. You can use these tools in multiple ways:

### Option 1: Using NPM Scripts (Recommended)

Add these commands to your project's `package.json` scripts:

```json
{
"scripts": {
"translation:validate": "wpd-translation-validate --dir ./translations --source en",
"translation:sync": "wpd-translation-sync --dir ./translations --source en"
}
}
```

Then run:

```bash
npm run translation:validate
npm run translation:sync
```

**Customizing paths:**

Modify the scripts in your `package.json` to use different directories or source language:

```json
{
"scripts": {
"translation:validate": "wpd-translation-validate --dir ./src/translations --source es",
"translation:sync": "wpd-translation-sync --dir ./src/translations --source es"
}
}
```

### Option 2: Using npx (Direct CLI)

Run the CLI tools directly via npx:

```bash
# Validate translations
npx wpd-translation-validate --dir ./translations --source en

# Sync translations
npx wpd-translation-sync --dir ./translations --source en
```

### What These Tools Do

**Validate 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

### CI/CD Integration

We recommend running `translation:validate` as part of your CI pipeline to ensure translation consistency:

```yaml
# .github/workflows/ci.yml
- name: Validate Translations
run: npm run translation:validate
```

## ๐Ÿ“š API Reference

### Hooks

#### `useTranslation()`
Returns a type-safe translation function with intellisense support and type-safe parameter validation.

**Parameters:**
- None (the translation object type `T` is provided as a generic parameter for type safety)

**Returns:** Object with `t` function that accepts type-safe translation keys

**The `t` function supports two modes:**

1. **String interpolation** (returns `string`):
```typescript
import en from '../translations/en';

const { t } = useTranslation();

// No parameters needed for translations without placeholders
const title = t('dashboard.title'); // โœ… Returns string

// Parameters required when placeholders exist
const greeting = t('common.greeting', { name: 'John' }); // โœ… Returns string
const greeting = t('common.greeting'); // โŒ TypeScript error: missing required parameter
```

2. **Component interpolation** (returns `JSX.Element`):
```typescript
// Pass components as third argument
const welcomeElement = t(
'auth.welcomeMessage',
{ name: 'Alice' },
{
strong: ,
link: (props) => {props.children}
}
); // โœ… Returns JSX.Element
```

**Type-Safe Parameters:**
- Parameters are **required** when the translation string contains placeholders like `{{name}}`
- Parameters are **optional** when the translation has no placeholders
- TypeScript validates parameter names match the placeholders in the translation

**Note:** For better reusability, consider creating a custom hook:

```typescript
// src/hooks/useAppTranslation.ts
import { useTranslation } from '@weprodev/ui-localization';
import en from '../translations/en';

export const useAppTranslation = () => {
return useTranslation();
};

// Usage
const { t } = useAppTranslation();
const text = t('common.welcome', { name: 'User' }); // Full type safety and intellisense
```

#### `useLanguage()`
Provides language management functionality.

```typescript
const { currentLanguage, changeLanguage, availableLanguages } = useLanguage();
```

#### `useTranslationFallback()`
**โš ๏ธ Escape hatch hook - use only when the main `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.

```typescript
// 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.

### Core Functions

#### `initLocalization(config)`
Initializes the localization system.

```typescript
await initLocalization({
resources: { en: { translation: enTranslations } },
fallbackLng: 'en',
languageStore: new CustomLanguageStore()
});
```

### Types

#### `LanguageStore`
Interface for custom language storage implementations.

```typescript
interface LanguageStore {
getLanguage(): string | null;
setLanguage(language: string): void;
}
```

#### `LocalizationConfig`
Configuration object for localization initialization.

```typescript
interface LocalizationConfig {
resources: Resource;
fallbackLng?: string;
compatibilityJSON?: "v4";
interpolation?: {
escapeValue?: boolean;
};
languageStore?: LanguageStore;
}
```

#### `ComponentMap`
Type for component interpolation map. Supports both React elements and function components.

```typescript
type ComponentMap = {
[key: string]:
| React.ReactElement
| ((props: { children?: React.ReactNode; [key: string]: any }) => React.ReactElement)
}
```

#### `TranslateFunction`
Type definition for the type-safe translation function.

```typescript
type TranslateFunction
```

#### `UseTranslationReturn`
Return type of the `useTranslation` hook.

```typescript
interface UseTranslationReturn {
t: TranslateFunction;
}
```

#### `NestedRecord`
Base type for translation resources, allowing recursive nesting of primitive values.

```typescript
type NestedRecord = {
[key: string]: string | number | boolean | null | undefined | NestedRecord
};
```

## ๐Ÿ†˜ Support

For support, bug reports, or feature requests, please contact the WeProDev team or create an issue in our internal repository.

## ๐Ÿ“„ License

This project is licensed under the MIT License.

## ๐Ÿ”— Links

- [i18next Documentation](https://www.i18next.com/)
- [react-i18next Documentation](https://react.i18next.com/)

---

**@weprodev/ui-localization** - Professional localization solution by WeProDev