A Vite plugin that provides a visual editor for .po (Gettext) translation files. Designed to work seamlessly with wuchale and other i18n solutions.
- π¨ Visual Translation Editor - Browse and edit
.pofiles 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
.pofiles change - π οΈ Framework Agnostic - Works with React, Vue, Svelte, SolidJS, or any Vite-powered project
- π― wuchale Integration - Auto-detect config and
.polocations
# 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// 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:lingo({ route: '/_translations', localesDir: './src/locales', // Common in SvelteKit projects })
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
bun run dev
# or
npm run devNavigate to http://localhost:5173/_translations to access the visual editor.
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'
// },
})| 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 .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. |
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. |
The following premium features are currently in development and will be available in future releases:
- Status: Under development
- Purpose: Enable premium features with license validation
- Expected: Q1 2026
- Status: Under development
- Purpose: Assist with translations using your preferred AI provider
- Supported Providers: OpenAI, Anthropic, Google
- Expected: Q1 2026
This is an advanced fallback option that should only be used in specific situations:
- β
Use when: Another plugin (like wuchale) doesn't respond to
.pofile changes - β Don't use: As your primary reload strategy (use
reloadOnPoChangeinstead) β οΈ Impact: Full dev server restart is slower than page reload
Example scenario where this might be needed:
lingo({
localesDir: './locales',
restartOnPoChange: true, // Only if wuchale or other plugins conflict
reloadOnPoChange: false, // Disable the default fast reload
})Main configuration interface for the Vite plugin. See Plugin Options table above.
Represents a single translation entry in a .po file:
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
}Represents a language with all its translations:
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
};
}Statistics for language translation progress:
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)
}All types are exported from the main package:
import type {
PluginOptions,
Translation,
Language,
LanguageStats
} from 'vite-plugin-lingo';SvelteKit projects commonly place locales in the src/ directory:
// 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
Standard Vite projects typically use the root-level locales/ directory:
// 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
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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 β
βββββββββββββββββββ
The plugin works with standard Gettext .po files:
# 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}!"- Bun (recommended) or Node.js 18+
- Git
# 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 testvite-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
π For detailed publishing instructions, see PUBLISHING.md
# 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| 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 |
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
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.
Made with β€οΈ for the i18n community