# 🌍 vite-plugin-lingo

A **Vite plugin** that provides a visual editor for `.po` (Gettext) translation files. Designed to work seamlessly with [wuchale](https://wuchale.dev/) and other i18n solutions.

[![npm version](https://img.shields.io/npm/v/vite-plugin-lingo.svg)](https://www.npmjs.com/package/vite-plugin-lingo)
[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)

## ✨ Features

- 🎨 **Visual Translation Editor** - Browse and edit `.po` files in a beautiful web UI
- 📊 **Language Overview** - See all locales with translation progress at a glance
- 🔍 **Search & Filter** - Find translations by text, filter by status
- ⌨️ **Keyboard Shortcuts** - Ctrl+S save, arrow keys navigate
- 🔄 **HMR Support** - Live reload when `.po` files change
- 🛠️ **Framework Agnostic** - Works with React, Vue, Svelte, SolidJS, or any Vite-powered project
- 🎯 **wuchale Integration** - Auto-detect config and `.po` locations

## 📦 Installation

```bash
# npm
npm install vite-plugin-lingo --save-dev

# pnpm
pnpm add -D vite-plugin-lingo

# bun (recommended)
bun add -d vite-plugin-lingo

# yarn
yarn add -D vite-plugin-lingo
```

## 🚀 Quick Start

### 1. Add to your Vite config

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import lingo from 'vite-plugin-lingo';

export default defineConfig({
  plugins: [
    lingo({
      route: '/_translations',  // Route where editor UI is served
      localesDir: './locales',  // Path to .po files
    })
  ]
});
```

> **Note for SvelteKit users**: If your locales are in `src/locales/` (common SvelteKit convention), use:
> ```ts
> lingo({
>   route: '/_translations',
>   localesDir: './src/locales',  // Common in SvelteKit projects
> })
> ```

### 2. Create your locales directory

```
your-project/
├── locales/           # Default location
│   ├── en.po
│   ├── es.po
│   └── fr.po
├── src/
│   └── locales/       # Alternative: SvelteKit convention
│       ├── en.po
│       ├── es.po
│       └── fr.po
└── vite.config.ts
```

### 3. Start your dev server

```bash
bun run dev
# or
npm run dev
```

### 4. Open the translation editor

Navigate to `http://localhost:5173/_translations` to access the visual editor.

## ⚙️ Configuration Options

```ts
lingo({
  // Route where editor UI is served (default: '/_translations')
  route: '/_translations',

  // Path to .po files relative to project root (default: './locales')
  // For SvelteKit projects, commonly './src/locales'
  // For other frameworks, './locales' at project root is typical
  localesDir: './locales',

  // ⚠️ NUCLEAR OPTION - Only use if another plugin conflicts with .po file changes
  // Restart the dev server when a .po file is updated (default: false)
  // Advanced: Use this only when reloadOnPoChange is insufficient and another
  // plugin (like wuchale) stops reacting to changes. Most users won't need this.
  restartOnPoChange: false,

  // Trigger a full page reload when a .po file is updated (default: true)
  // Ensures UI stays in sync with backend translation files
  reloadOnPoChange: true,

  // Enable in production (default: false)
  // ⚠️ Only enable with proper authentication!
  production: false,

  // 🔒 PREMIUM FEATURE (Coming Soon)
  // License key for premium features
  // licenseKey: 'your-license-key',

  // 🔒 PREMIUM FEATURE (Coming Soon)
  // AI configuration for translation assistance
  // ai: {
  //   provider: 'openai' | 'anthropic' | 'google',
  //   apiKey: 'your-api-key'
  // },
})
```

## 📖 API Reference

### Plugin Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `route` | `string` | `'/_translations'` | URL path where the editor is served |
| `localesDir` | `string` | `'./locales'` | Directory containing `.po` files. For SvelteKit projects, commonly `'./src/locales'`. Relative to project root. |
| `restartOnPoChange` | `boolean` | `false` | **Nuclear Option** ⚠️ - Only use when another plugin conflicts with `.po` file changes (e.g., wuchale). Restarts the dev server when a `.po` file is updated. Most users won't need this. |
| `reloadOnPoChange` | `boolean` | `true` | Trigger a full page reload when a `.po` file is updated. Ensures UI stays in sync with backend translation files. |
| `production` | `boolean` | `false` | Enable editor in production builds. ⚠️ Only enable with proper authentication! |
| `licenseKey` | `string` | `undefined` | **Coming Soon** 🔒 - License key for premium features (not yet available). |
| `ai` | `object` | `undefined` | **Coming Soon** 🔒 - AI configuration for translation assistance (not yet available). Will support `'openai'`, `'anthropic'`, or `'google'` as provider with optional `apiKey`. |

### Premium Features (Coming Soon)

The following premium features are currently in development and will be available in future releases:

#### License Key System (`licenseKey`)
- **Status**: Under development
- **Purpose**: Enable premium features with license validation
- **Expected**: Q1 2026

#### AI-Powered Translation (`ai`)
- **Status**: Under development
- **Purpose**: Assist with translations using your preferred AI provider
- **Supported Providers**: OpenAI, Anthropic, Google
- **Expected**: Q1 2026

### Advanced Configuration Notes

#### `restartOnPoChange` - Nuclear Option
This is an **advanced fallback option** that should only be used in specific situations:

- ✅ **Use when**: Another plugin (like [wuchale](https://wuchale.dev/)) doesn't respond to `.po` file changes
- ❌ **Don't use**: As your primary reload strategy (use `reloadOnPoChange` instead)
- ⚠️ **Impact**: Full dev server restart is slower than page reload

**Example scenario** where this might be needed:
```ts
lingo({
  localesDir: './locales',
  restartOnPoChange: true,  // Only if wuchale or other plugins conflict
  reloadOnPoChange: false,   // Disable the default fast reload
})
```

### Type Definitions

#### PluginOptions
Main configuration interface for the Vite plugin. See **Plugin Options** table above.

#### Translation
Represents a single translation entry in a `.po` file:

```ts
interface Translation {
  msgid: string;                    // Message ID (original text)
  msgstr: string;                   // Message string (translated text)
  context?: string;                 // Optional context for disambiguation
  comments?: {                      // Optional metadata
    reference?: string;             // File reference where string is used
    translator?: string;            // Translator notes
    extracted?: string;             // Extracted comments
    flag?: string;                  // Fuzzy or other flags
  };
  fuzzy?: boolean;                  // Whether translation is marked as fuzzy
}
```

#### Language
Represents a language with all its translations:

```ts
interface Language {
  code: string;                     // Language code (e.g., 'en', 'es', 'fr')
  name: string;                     // Language name (e.g., 'English', 'Spanish')
  path: string;                     // Path to the .po file
  translations: Translation[];       // Array of translation entries
  progress: {
    total: number;                  // Total number of strings
    translated: number;             // Number of translated strings
    fuzzy: number;                  // Number of fuzzy translations
  };
}
```

#### LanguageStats
Statistics for language translation progress:

```ts
interface LanguageStats {
  code: string;                     // Language code
  name: string;                     // Language name
  total: number;                    // Total number of strings
  translated: number;               // Number of translated strings
  fuzzy: number;                    // Number of fuzzy translations
  untranslated: number;             // Number of untranslated strings
  progress: number;                 // Progress percentage (0-100)
}
```

### Exported Types

All types are exported from the main package:

```ts
import type { 
  PluginOptions,
  Translation,
  Language,
  LanguageStats 
} from 'vite-plugin-lingo';
```

## 🎯 Framework Examples

### SvelteKit

SvelteKit projects commonly place locales in the `src/` directory:

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import lingo from 'vite-plugin-lingo';

export default defineConfig({
  plugins: [
    lingo({
      route: '/_translations',
      localesDir: './src/locales',  // SvelteKit convention
    })
  ]
});
```

Project structure:
```
sveltekit-project/
├── src/
│   ├── locales/           # ← Locales directory
│   │   ├── en.po
│   │   ├── es.po
│   │   └── fr.po
│   ├── routes/
│   └── app.html
├── vite.config.ts
└── svelte.config.js
```

### React/Vite

Standard Vite projects typically use the root-level `locales/` directory:

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import lingo from 'vite-plugin-lingo';

export default defineConfig({
  plugins: [
    lingo({
      route: '/_translations',
      localesDir: './locales',  // Default location
    })
  ]
});
```

Project structure:
```
vite-react-project/
├── locales/               # ← Locales directory
│   ├── en.po
│   ├── es.po
│   └── fr.po
├── src/
├── index.html
└── vite.config.ts
```

## 🔧 How It Works

```
┌─────────────────────────────────────────────────────────┐
│                    Vite Dev Server                       │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────────┐    ┌─────────────────────────────┐ │
│  │ Your App        │    │ vite-plugin-lingo           │ │
│  │ (React/Svelte/  │    │ ├─ Middleware (/_translations)│
│  │  Vue/Solid)     │    │ ├─ API (GET/PUT /api/*)     │ │
│  │                 │    │ ├─ Editor UI (Svelte SPA)   │ │
│  │                 │    │ └─ File Watcher (.po files) │ │
│  └─────────────────┘    └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │  .po Files      │
                    │  └─ locales/    │
                    │     ├─ en.po    │
                    │     ├─ es.po    │
                    │     └─ fr.po    │
                    └─────────────────┘
```

## 📁 .po File Format

The plugin works with standard Gettext `.po` files:

```po
# English translations
msgid ""
msgstr ""
"Language: en\n"
"Content-Type: text/plain; charset=UTF-8\n"

#: src/components/Header.svelte:5
msgid "Welcome to our website"
msgstr "Welcome to our website"

#: src/components/Header.svelte:10
msgid "Hello, {name}!"
msgstr "Hello, {name}!"
```

## 🛠️ Development

### Prerequisites

- [Bun](https://bun.sh/) (recommended) or Node.js 18+
- Git

### Setup

```bash
# Clone the repository
git clone https://github.com/Michael-Obele/vite-plugin-lingo.git
cd vite-plugin-lingo

# Install dependencies
bun install

# Start development server
bun run dev

# Build the plugin
bun run build

# Run type checking
bun run check

# Run tests
bun run test
```

### Project Structure

```
vite-plugin-lingo/
├── src/
│   ├── lib/
│   │   ├── plugin/          # Vite plugin source
│   │   │   ├── index.ts     # Main plugin entry
│   │   │   ├── middleware.ts # API endpoints
│   │   │   ├── po-parser.ts # .po file parser
│   │   │   └── types.ts     # TypeScript types
│   │   └── ui/              # Editor UI (Svelte)
│   │       ├── App.svelte   # Main editor component
│   │       └── components/  # UI components
│   └── routes/              # Demo/showcase app
├── locales/                 # Sample .po files
├── dist/                    # Built output
└── package.json
```

## 📤 Publishing to npm

> 📚 **For detailed publishing instructions, see [PUBLISHING.md](./PUBLISHING.md)**

### Quick Publishing Guide

```bash
# 1. Login to npm (first time only)
npm login

# 2. Build and verify
bun run build

# 3. Bump version
npm version patch  # or minor/major

# 4. Publish
npm publish

# 5. Push tags
git push && git push --tags
```

### Quick Reference

| Command | Description |
|---------|-------------|
| `npm version patch` | Bug fixes (0.0.1 → 0.0.2) |
| `npm version minor` | New features (0.0.2 → 0.1.0) |
| `npm version major` | Breaking changes (0.1.0 → 1.0.0) |
| `npm publish` | Publish to npm registry |
| `npm pack --dry-run` | Preview what will be published |

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## 📄 License

[AGPL-3.0](LICENSE) © [Michael-Obele](https://github.com/Michael-Obele)

This is a copyleft license that requires anyone who distributes your code or a derivative work to make the source available under the same terms.

## 🔗 Links

- [GitHub Repository](https://github.com/Michael-Obele/vite-plugin-lingo)
- [npm Package](https://www.npmjs.com/package/vite-plugin-lingo)
- [Issue Tracker](https://github.com/Michael-Obele/vite-plugin-lingo/issues)

---

**Made with ❤️ for the i18n community**
