npm package: @ovcovc/translations
Description: React translations provider using Tolgee for multi-project translation management
Author: Muzaic
Latest version: 1.0.3

# @muzaic/translations

A flexible, project-agnostic React translations provider library that fetches translations from multiple Tolgee projects with configurable priority merging.

## Features

- **Multi-project support**: Fetch from multiple Tolgee projects simultaneously
- **Priority-based merging**: Lower index in projects array = higher priority for conflicting keys
- **Project-agnostic**: Works with any Tolgee projects, not tied to specific app types
- **Dynamic locale switching**: Change languages on the fly
- **TypeScript support**: Full type definitions included
- **Parameter interpolation**: Dynamic values in translations with `{{param}}` syntax
- **Automatic fallback**: Falls back to default locale on fetch errors
- **Loading and error states**: Built-in state management
- **Simple API**: Minimal configuration required

## Installation

```bash
npm install @muzaic/translations
```

## Quick Start

### 1. Define your Tolgee projects

```tsx
import { TolgeeProject } from '@muzaic/translations';

const myAppProject: TolgeeProject = {
  apiUrl: 'https://app.tolgee.io',
  apiKey: 'tgpak_your_app_api_key',
  projectId: 'your_app_project_id',
  name: 'My App', // Optional: helpful for debugging
};

const commonProject: TolgeeProject = {
  apiUrl: 'https://app.tolgee.io',
  apiKey: 'tgpak_common_api_key',
  projectId: 'common_project_id',
  name: 'Common',
};
```

### 2. Wrap your app with TranslationsProvider

```tsx
import { TranslationsProvider } from '@muzaic/translations';

function App() {
  return (
    <TranslationsProvider
      projects={[myAppProject, commonProject]} // myAppProject has priority
      defaultLocale="en"
      availableLocales={['en', 'es', 'fr', 'de']}
      fallbackLocale="en"
      onError={(error) => console.error('Translation error:', error)}
    >
      <YourApp />
    </TranslationsProvider>
  );
}
```

### 3. Use translations in your components

```tsx
import { useTranslations } from '@muzaic/translations';

function MyComponent() {
  const { t, locale, changeLocale, isLoading, error } = useTranslations();

  if (isLoading) {
    return <div>Loading translations...</div>;
  }

  if (error) {
    return <div>Error loading translations: {error.message}</div>;
  }

  return (
    <div>
      <h1>{t('welcome.title')}</h1>
      <p>{t('welcome.message', { name: 'John' })}</p>

      <select
        value={locale}
        onChange={(e) => changeLocale(e.target.value)}
      >
        <option value="en">English</option>
        <option value="es">Español</option>
        <option value="fr">Français</option>
      </select>
    </div>
  );
}
```

## API Reference

### TranslationsProvider

The main provider component that wraps your application.

#### Props

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `children` | `React.ReactNode` | Yes | Your application components |
| `projects` | `TolgeeProject[]` | Yes | Array of Tolgee projects to fetch from. Lower index = higher priority for conflicting keys. |
| `defaultLocale` | `string` | No | Default locale to use (default: `'en'`) |
| `availableLocales` | `string[]` | No | Array of available locale codes. If not provided, fetches from first project in array |
| `onError` | `(error: Error) => void` | No | Error callback function |
| `fallbackLocale` | `string` | No | Fallback locale if translation fetch fails (default: `'en'`) |

#### TolgeeProject

```typescript
interface TolgeeProject {
  apiUrl: string;     // Tolgee API URL (e.g., 'https://app.tolgee.io')
  apiKey: string;     // Your Tolgee API key
  projectId: string;  // Your Tolgee project ID
  name?: string;      // Optional name for debugging/logging
}
```

### useTranslations

Hook to access translations and locale management in your components.

#### Returns

```typescript
{
  t: (key: string, params?: Record<string, string | number>) => string;
  locale: string;
  changeLocale: (newLocale: string) => Promise<void>;
  availableLocales: string[];
  isLoading: boolean;
  error: Error | null;
}
```

#### Properties

- `t`: Translation function that accepts a key and optional parameters
- `locale`: Current active locale
- `changeLocale`: Function to change the current locale
- `availableLocales`: Array of available locale codes
- `isLoading`: Boolean indicating if translations are being loaded
- `error`: Error object if translation loading failed, null otherwise

## Usage Examples

### Basic Translation

```tsx
const { t } = useTranslations();

<h1>{t('app.title')}</h1>
```

### Translation with Parameters

Use `{{paramName}}` in your Tolgee translations:

```tsx
// In Tolgee: "Hello, {{name}}! You have {{count}} messages."
const { t } = useTranslations();

<p>{t('greeting', { name: 'Alice', count: 5 })}</p>
// Output: "Hello, Alice! You have 5 messages."
```

### Changing Locale

```tsx
const { changeLocale, locale } = useTranslations();

<button onClick={() => changeLocale('es')}>
  Switch to Spanish
</button>
```

### Handling Loading State

```tsx
const { isLoading } = useTranslations();

if (isLoading) {
  return <LoadingSpinner />;
}
```

### Priority-Based Merging

When you provide multiple projects, translations are merged with **priority based on array index**:

```tsx
const projects = [
  specificProject,  // Index 0 - HIGHEST priority
  commonProject,    // Index 1 - Lower priority
  fallbackProject,  // Index 2 - Lowest priority
];

<TranslationsProvider projects={projects}>
  <App />
</TranslationsProvider>
```

**How it works:**
- If a translation key exists in multiple projects, the one from the **lower index wins**
- Projects with index 0 override all others
- This allows app-specific translations to override common ones

**Example:**
```typescript
// Project 0 (specific): { "greeting": "Hello from App!", "title": "My App" }
// Project 1 (common):   { "greeting": "Hello", "footer": "© 2024" }

// Merged result: { "greeting": "Hello from App!", "title": "My App", "footer": "© 2024" }
// Note: "greeting" from project 0 overrode the one from project 1
```

### Real-World Example: App-Specific + Common Translations

```tsx
import { TranslationsProvider, TolgeeProject } from '@muzaic/translations';

const canvaProject: TolgeeProject = {
  apiUrl: 'https://app.tolgee.io',
  apiKey: 'tgpak_canva_key',
  projectId: 'canva_id',
  name: 'Canva App',
};

const commonProject: TolgeeProject = {
  apiUrl: 'https://app.tolgee.io',
  apiKey: 'tgpak_common_key',
  projectId: 'common_id',
  name: 'Common Strings',
};

function CanvaApp() {
  return (
    <TranslationsProvider
      projects={[canvaProject, commonProject]} // Canva overrides Common
      defaultLocale="en"
    >
      <App />
    </TranslationsProvider>
  );
}
```

### Single Project Example

You can also use just one project:

```tsx
const singleProject: TolgeeProject = {
  apiUrl: 'https://app.tolgee.io',
  apiKey: 'tgpak_key',
  projectId: 'project_id',
};

<TranslationsProvider projects={[singleProject]}>
  <App />
</TranslationsProvider>
```

## Configuration

Define your Tolgee project configurations in your consuming application:

```typescript
// config/translations.ts (in your app)
import { TolgeeProject } from '@muzaic/translations';

export const myAppProjects: TolgeeProject[] = [
  {
    apiUrl: process.env.REACT_APP_TOLGEE_URL || 'https://app.tolgee.io',
    apiKey: process.env.REACT_APP_TOLGEE_APP_KEY || '',
    projectId: process.env.REACT_APP_TOLGEE_APP_ID || '',
    name: 'My App',
  },
  {
    apiUrl: process.env.REACT_APP_TOLGEE_URL || 'https://app.tolgee.io',
    apiKey: process.env.REACT_APP_TOLGEE_COMMON_KEY || '',
    projectId: process.env.REACT_APP_TOLGEE_COMMON_ID || '',
    name: 'Common',
  },
];
```

Then use environment variables to manage your API keys securely.

## Development

### Build

```bash
npm run build
```

### Watch Mode

```bash
npm run dev
```

### Type Check

```bash
npm run typecheck
```

## How It Works

1. On mount, the provider fetches translations from **all projects** in the `projects` array in parallel
2. Translations are merged with **priority based on array index** (lower index = higher priority)
3. The merged translations are stored and used by the `t` function
4. When locale changes, new translations are fetched from all projects
5. If fetching from a project fails, the provider attempts to fetch the fallback locale
6. If that also fails, an empty translation object is used for that project
7. The `t` function looks up keys in the merged translations and performs parameter interpolation

## TypeScript Support

This library is written in TypeScript and includes full type definitions.

```typescript
import type {
  TolgeeProject,
  TranslationsProviderProps,
  TranslationsContextValue,
  Translations,
} from '@muzaic/translations';
```

All exported types:
- `TolgeeProject` - Configuration for a single Tolgee project
- `TranslationsProviderProps` - Props for the TranslationsProvider component
- `TranslationsContextValue` - Return type of useTranslations hook
- `Translations` - Type for translation objects (Record<string, string>)

## License

MIT