i18n-dashboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +715 -0
- package/app.vue +8 -0
- package/assets/css/main.css +21 -0
- package/assets/locales/en.json +380 -0
- package/bin/cli.mjs +279 -0
- package/components/LinkedKeyPicker.vue +135 -0
- package/components/PathPicker.vue +153 -0
- package/components/PluralEditor.vue +295 -0
- package/components/ScanModal.vue +153 -0
- package/components/TranslationHistoryModal.vue +66 -0
- package/components/TranslationRow.vue +541 -0
- package/components/dashboard/WidgetConfigModal.vue +121 -0
- package/components/dashboard/WidgetGrid.vue +190 -0
- package/components/dashboard/WidgetPicker.vue +75 -0
- package/components/dashboard/widgets/ActivityWidget.vue +109 -0
- package/components/dashboard/widgets/LanguagesCoverageWidget.vue +104 -0
- package/components/dashboard/widgets/ProjectsWidget.vue +77 -0
- package/components/dashboard/widgets/ReviewWidget.vue +150 -0
- package/components/dashboard/widgets/StatWidget.vue +133 -0
- package/composables/useAuth.ts +72 -0
- package/composables/useConfig.ts +14 -0
- package/composables/useDashboard.ts +89 -0
- package/composables/useFormats.ts +100 -0
- package/composables/useKeys.ts +231 -0
- package/composables/useLanguages.ts +221 -0
- package/composables/useProfile.ts +76 -0
- package/composables/useProject.ts +180 -0
- package/composables/useReview.ts +94 -0
- package/composables/useSettings.ts +30 -0
- package/composables/useStats.ts +16 -0
- package/composables/useT.ts +38 -0
- package/composables/useUsers.ts +101 -0
- package/composables/useWidgetData.ts +50 -0
- package/consts/commons.const.ts +6 -0
- package/consts/dashboard.const.ts +94 -0
- package/consts/languages.const.ts +223 -0
- package/enums/commons.enum.ts +7 -0
- package/i18n-dashboard.config.example.js +40 -0
- package/interfaces/commons.interface.ts +23 -0
- package/interfaces/job.interface.ts +10 -0
- package/interfaces/key.interface.ts +39 -0
- package/interfaces/languages.interface.ts +23 -0
- package/interfaces/project.interface.ts +9 -0
- package/interfaces/scan.interface.ts +12 -0
- package/interfaces/settings.interface.ts +4 -0
- package/interfaces/stat.interface.ts +30 -0
- package/interfaces/translation.interface.ts +11 -0
- package/interfaces/user.interface.ts +24 -0
- package/layouts/auth.vue +5 -0
- package/layouts/default.vue +327 -0
- package/middleware/auth.global.ts +26 -0
- package/nuxt.config.ts +66 -0
- package/package.json +89 -0
- package/pages/index.vue +5 -0
- package/pages/login.vue +74 -0
- package/pages/onboarding.vue +563 -0
- package/pages/projects/[id]/formats/datetime.vue +240 -0
- package/pages/projects/[id]/formats/modifiers.vue +194 -0
- package/pages/projects/[id]/formats/number.vue +250 -0
- package/pages/projects/[id]/index.vue +182 -0
- package/pages/projects/[id]/languages.vue +537 -0
- package/pages/projects/[id]/review.vue +109 -0
- package/pages/projects/[id]/settings.vue +515 -0
- package/pages/projects/[id]/translations/[keyId].vue +642 -0
- package/pages/projects/[id]/translations/index.vue +250 -0
- package/pages/projects/[id]/users.vue +276 -0
- package/pages/projects/index.vue +334 -0
- package/pages/users/[id]/profile.vue +421 -0
- package/pages/users/index.vue +345 -0
- package/plugins/loading.client.ts +3 -0
- package/plugins/ui-i18n.ts +6 -0
- package/server/api/auth/login.post.ts +28 -0
- package/server/api/auth/logout.post.ts +7 -0
- package/server/api/auth/me.get.ts +11 -0
- package/server/api/auth/me.put.ts +31 -0
- package/server/api/auth/password.put.ts +27 -0
- package/server/api/auth/status.get.ts +16 -0
- package/server/api/config.get.ts +10 -0
- package/server/api/dashboard/layout.get.ts +18 -0
- package/server/api/dashboard/layout.post.ts +18 -0
- package/server/api/db-config.get.ts +44 -0
- package/server/api/db-config.post.ts +73 -0
- package/server/api/export.get.ts +64 -0
- package/server/api/formats/datetime/[id].delete.ts +8 -0
- package/server/api/formats/datetime/[id].put.ts +15 -0
- package/server/api/formats/datetime.get.ts +11 -0
- package/server/api/formats/datetime.post.ts +16 -0
- package/server/api/formats/modifiers/[id].delete.ts +8 -0
- package/server/api/formats/modifiers/[id].put.ts +10 -0
- package/server/api/formats/modifiers.get.ts +10 -0
- package/server/api/formats/modifiers.post.ts +14 -0
- package/server/api/formats/number/[id].delete.ts +8 -0
- package/server/api/formats/number/[id].put.ts +15 -0
- package/server/api/formats/number.get.ts +11 -0
- package/server/api/formats/number.post.ts +16 -0
- package/server/api/formats/snippet.get.ts +87 -0
- package/server/api/fs/browse.get.ts +50 -0
- package/server/api/history/[translationId].get.ts +13 -0
- package/server/api/keys/[id].delete.ts +14 -0
- package/server/api/keys/[id].get.ts +41 -0
- package/server/api/keys/[id].patch.ts +20 -0
- package/server/api/keys/index.get.ts +98 -0
- package/server/api/keys/index.post.ts +17 -0
- package/server/api/languages/[code].delete.ts +15 -0
- package/server/api/languages/[id].put.ts +24 -0
- package/server/api/languages/index.get.ts +13 -0
- package/server/api/languages/index.post.ts +42 -0
- package/server/api/onboarding.post.ts +56 -0
- package/server/api/profile.get.ts +81 -0
- package/server/api/project-snapshot.get.ts +73 -0
- package/server/api/project-snapshot.post.ts +160 -0
- package/server/api/projects/[id].delete.ts +13 -0
- package/server/api/projects/[id].put.ts +40 -0
- package/server/api/projects/index.get.ts +19 -0
- package/server/api/projects/index.post.ts +34 -0
- package/server/api/scan.post.ts +165 -0
- package/server/api/settings/index.get.ts +9 -0
- package/server/api/settings/index.post.ts +20 -0
- package/server/api/setup.post.ts +39 -0
- package/server/api/stats/global.get.ts +126 -0
- package/server/api/stats.get.ts +70 -0
- package/server/api/sync.post.ts +179 -0
- package/server/api/translate.post.ts +52 -0
- package/server/api/translations/batch-translate.post.ts +121 -0
- package/server/api/translations/bulk-status.post.ts +24 -0
- package/server/api/translations/index.post.ts +62 -0
- package/server/api/translations/job/[id].get.ts +23 -0
- package/server/api/translations/status.post.ts +30 -0
- package/server/api/translations/translate-all.post.ts +18 -0
- package/server/api/ui-locale.get.ts +39 -0
- package/server/api/users/[id]/profile.get.ts +107 -0
- package/server/api/users/[id]/roles.put.ts +67 -0
- package/server/api/users/[id].delete.ts +36 -0
- package/server/api/users/[id].put.ts +43 -0
- package/server/api/users/index.get.ts +49 -0
- package/server/api/users/index.post.ts +89 -0
- package/server/consts/auto-translate.const.ts +2 -0
- package/server/consts/commons.const.ts +10 -0
- package/server/consts/db.const.ts +3 -0
- package/server/consts/scanner.const.ts +4 -0
- package/server/consts/translation-job.const.ts +8 -0
- package/server/db/index.ts +672 -0
- package/server/enums/auth.enum.ts +5 -0
- package/server/enums/translation.enum.ts +6 -0
- package/server/interfaces/profile.interface.ts +48 -0
- package/server/interfaces/project-config.interface.ts +9 -0
- package/server/interfaces/scanner.interface.ts +18 -0
- package/server/interfaces/translation-job.interface.ts +13 -0
- package/server/middleware/auth.ts +32 -0
- package/server/plugins/db.ts +6 -0
- package/server/routes/locale/[lang].get.ts +179 -0
- package/server/types/auth.type.ts +3 -0
- package/server/utils/auth.util.ts +89 -0
- package/server/utils/auto-translate.util.ts +112 -0
- package/server/utils/lang-api.util.ts +24 -0
- package/server/utils/mailer.util.ts +80 -0
- package/server/utils/project-config.util.ts +37 -0
- package/server/utils/scanner.uti.ts +307 -0
- package/server/utils/translation-job.util.ts +142 -0
- package/services/auth.service.ts +31 -0
- package/services/base.service.ts +140 -0
- package/services/job.service.ts +10 -0
- package/services/key.service.ts +26 -0
- package/services/language.service.ts +26 -0
- package/services/profile.service.ts +14 -0
- package/services/project.service.ts +23 -0
- package/services/scan.service.ts +14 -0
- package/services/settings.service.ts +14 -0
- package/services/stats.service.ts +11 -0
- package/services/translation.service.ts +36 -0
- package/services/user.service.ts +28 -0
- package/tsconfig.json +3 -0
- package/types/commons.type.ts +3 -0
- package/types/dashboard.type.ts +26 -0
- package/utils/config.util.ts +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
# i18n-dashboard
|
|
2
|
+
|
|
3
|
+
> A full-featured web dashboard to manage [vue-i18n](https://vue-i18n.intlify.dev/) translation keys — inspired by [Storybook](https://storybook.js.org/): run it alongside your project, manage all your translations in one place, and consume them via a ready-to-use API.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/i18n-dashboard)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### Core
|
|
14
|
+
- **Multi-project** — manage multiple Vue.js projects from one dashboard, each with its own languages, keys, and settings
|
|
15
|
+
- **Translation editor** — inline editing per language, keyboard shortcuts (`Ctrl+Enter` to save, `Esc` to cancel)
|
|
16
|
+
- **Plural forms** — dedicated plural editor supporting 2-form (EN/DE), 3-form (FR/ES), 4-form (Slavic), or custom rules
|
|
17
|
+
- **Key linking** — insert `@:key` linked references with optional modifiers (`.lower`, `.upper`, `.capitalize`) via a searchable modal picker
|
|
18
|
+
- **Escape helpers** — quick-insert toolbar for vue-i18n special characters (`{'@'}`, `{'{'}`, `\\{`, etc.)
|
|
19
|
+
- **Translation history** — every change (manual, sync, auto-translate) is tracked; restore any previous version
|
|
20
|
+
- **Inline auto-translate** — Google Translate per key (free tier, no API key required)
|
|
21
|
+
- **Batch auto-translate** — translate all missing keys for an entire language at once
|
|
22
|
+
- **Review workflow** — `Draft → Reviewed → Approved` status pipeline with role-based access control
|
|
23
|
+
- **Dashboard widgets** — customizable overview: coverage, total keys, unused keys, recent activity, per-language progress
|
|
24
|
+
|
|
25
|
+
### Languages
|
|
26
|
+
- **BCP 47 support** — full regional codes: `fr-CA`, `en-GB`, `pt-BR`, `zh-CN`, `sr-Latn`, etc. (170+ locales)
|
|
27
|
+
- **Fallback chains** — configure explicit fallbacks per language (`fr-CA → fr → en`); automatic BCP 47 parent resolution
|
|
28
|
+
- **Auto-detect** — scan or sync automatically detects locale files and creates the corresponding languages
|
|
29
|
+
- **Default language** — designate one language as the source for auto-translate
|
|
30
|
+
|
|
31
|
+
### Scan & Sync
|
|
32
|
+
- **Source scan (local)** — browse your file system with the built-in folder picker, detect all `$t()`, `t()`, `<i18n-t>`, `v-t`, and `<i18n>` block usages across `.vue`, `.ts`, `.js` files
|
|
33
|
+
- **Source scan (URL)** — fetch `en.json`, `fr.json`… from any remote URL and import all keys
|
|
34
|
+
- **Sync** — import existing `.json` locale files (local path or remote URL) into the database
|
|
35
|
+
- **Unused key detection** — keys not found in source files are automatically flagged
|
|
36
|
+
|
|
37
|
+
### Advanced Formats
|
|
38
|
+
*(enable per project in Settings)*
|
|
39
|
+
- **Number formats** — configure `$n(value, 'currency')` presets using `Intl.NumberFormat` with live preview
|
|
40
|
+
- **Datetime formats** — configure `$d(date, 'short')` presets using `Intl.DateTimeFormat` with live preview
|
|
41
|
+
- **Custom modifiers** — define `@.modifier:key` transform functions with a built-in test runner
|
|
42
|
+
- **Snippet generator** — generates a ready-to-paste `createI18n()` configuration block
|
|
43
|
+
|
|
44
|
+
### Projects
|
|
45
|
+
- **Inline settings** — edit project name, root path, locales folder, key separator, color, and description directly from the project settings page
|
|
46
|
+
- **Folder browser** — navigate your file system visually to pick the project root path
|
|
47
|
+
- **Project snapshot** — export a complete backup (config + languages + all keys + translations) as a single JSON file, import it on any other instance (merge or replace mode)
|
|
48
|
+
|
|
49
|
+
### Users & Authentication
|
|
50
|
+
- **Role-based access** — `Super Admin`, `Admin`, `Moderator`, `Translator` — per-project assignments
|
|
51
|
+
- **Onboarding wizard** — guided setup on first launch
|
|
52
|
+
- **Multi-language UI** — the dashboard interface itself is translatable
|
|
53
|
+
|
|
54
|
+
### Technical
|
|
55
|
+
- **Multi-database** — SQLite (default, zero config), PostgreSQL, MySQL/MariaDB
|
|
56
|
+
- **Auto-migration** — schema is created and updated automatically on startup
|
|
57
|
+
- **REST API** — full API for all operations, consume locale JSON from your Vue app
|
|
58
|
+
- **Dark mode** — system preference + manual toggle
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Requirements
|
|
63
|
+
|
|
64
|
+
- **Node.js** >= 18
|
|
65
|
+
- **npm** >= 9
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
### As a dev dependency (recommended — per project)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install i18n-dashboard --save-dev
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Globally (use across multiple projects)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm install -g i18n-dashboard
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
### 1 — Initialize
|
|
88
|
+
|
|
89
|
+
Run the interactive setup wizard from your project root:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx i18n-dashboard init
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This creates an `i18n-dashboard.config.js` file at the root of your project.
|
|
96
|
+
|
|
97
|
+
### 2 — Start
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npx i18n-dashboard start
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Open **http://localhost:3333** in your browser.
|
|
104
|
+
|
|
105
|
+
The onboarding wizard will guide you through:
|
|
106
|
+
1. Creating an administrator account
|
|
107
|
+
2. Selecting the dashboard UI language
|
|
108
|
+
3. Configuring your first project
|
|
109
|
+
|
|
110
|
+
### 3 — Import existing locale files (optional)
|
|
111
|
+
|
|
112
|
+
If you already have `.json` locale files, import them into the database:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx i18n-dashboard sync
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> The dashboard must be running for this command to work.
|
|
119
|
+
|
|
120
|
+
### 4 — Use the API in your Vue app
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
// src/i18n.js
|
|
124
|
+
import { createI18n } from 'vue-i18n'
|
|
125
|
+
|
|
126
|
+
export const i18n = createI18n({
|
|
127
|
+
locale: 'en',
|
|
128
|
+
fallbackLocale: 'en',
|
|
129
|
+
messages: {
|
|
130
|
+
en: await fetch('http://localhost:3333/locale/en.json').then(r => r.json()),
|
|
131
|
+
fr: await fetch('http://localhost:3333/locale/fr.json').then(r => r.json()),
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 5 — Add a script to your package.json
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"scripts": {
|
|
141
|
+
"dev": "vite",
|
|
142
|
+
"i18n": "i18n-dashboard start"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Configuration
|
|
150
|
+
|
|
151
|
+
### i18n-dashboard.config.js
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
// i18n-dashboard.config.js
|
|
155
|
+
export default {
|
|
156
|
+
// Port the dashboard will run on
|
|
157
|
+
port: 3333,
|
|
158
|
+
|
|
159
|
+
// Key separator for nested keys ('home.title' uses '.')
|
|
160
|
+
keySeparator: '.',
|
|
161
|
+
|
|
162
|
+
// URL path pattern for serving locale JSON files
|
|
163
|
+
apiPath: '/locale/[lang].json',
|
|
164
|
+
|
|
165
|
+
// Root path of your Vue project (absolute or relative)
|
|
166
|
+
projectRoot: './',
|
|
167
|
+
|
|
168
|
+
// Database configuration
|
|
169
|
+
database: {
|
|
170
|
+
// Options: 'better-sqlite3' (default), 'pg', 'mysql2'
|
|
171
|
+
client: 'better-sqlite3',
|
|
172
|
+
|
|
173
|
+
// SQLite: path to the .db file
|
|
174
|
+
connection: './i18n-dashboard.db',
|
|
175
|
+
|
|
176
|
+
// PostgreSQL / MySQL: use a connection object instead:
|
|
177
|
+
// connection: {
|
|
178
|
+
// host: 'localhost',
|
|
179
|
+
// port: 5432,
|
|
180
|
+
// user: 'myuser',
|
|
181
|
+
// password: 'mypassword',
|
|
182
|
+
// database: 'i18n_dashboard',
|
|
183
|
+
// },
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// Google Translate API key (optional — free tier works without a key)
|
|
187
|
+
// googleTranslate: {
|
|
188
|
+
// apiKey: process.env.GOOGLE_TRANSLATE_API_KEY,
|
|
189
|
+
// },
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Environment variables
|
|
194
|
+
|
|
195
|
+
All options can be passed as environment variables (useful for CI/CD and Docker):
|
|
196
|
+
|
|
197
|
+
| Variable | Description | Default |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `I18N_PORT` | Server port | `3333` |
|
|
200
|
+
| `I18N_DB_CLIENT` | DB driver (`better-sqlite3` / `pg` / `mysql2`) | `better-sqlite3` |
|
|
201
|
+
| `I18N_DB_CONNECTION` | SQLite file path | `./i18n-dashboard.db` |
|
|
202
|
+
| `I18N_DB_HOST` | PostgreSQL/MySQL host | `localhost` |
|
|
203
|
+
| `I18N_DB_PORT` | PostgreSQL/MySQL port | `5432` |
|
|
204
|
+
| `I18N_DB_USER` | Database user | — |
|
|
205
|
+
| `I18N_DB_PASSWORD` | Database password | — |
|
|
206
|
+
| `I18N_DB_NAME` | Database name | `i18n_dashboard` |
|
|
207
|
+
| `I18N_KEY_SEPARATOR` | Key separator | `.` |
|
|
208
|
+
| `I18N_API_PATH` | Locale API path pattern | `/locale/[lang].json` |
|
|
209
|
+
| `I18N_PROJECT_ROOT` | Project root path | `process.cwd()` |
|
|
210
|
+
| `I18N_LOCALES_PATH` | Locales folder (relative to root) | `src/locales` |
|
|
211
|
+
| `GOOGLE_TRANSLATE_API_KEY` | Google Translate API key | — |
|
|
212
|
+
| `SESSION_SECRET` | Session encryption secret | *(default, change in prod)* |
|
|
213
|
+
| `SMTP_HOST` | SMTP server for email | — |
|
|
214
|
+
| `DASHBOARD_URL` | Public URL of the dashboard | `http://localhost:3333` |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## CLI Commands
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
i18n-dashboard <command> [options]
|
|
222
|
+
|
|
223
|
+
Commands:
|
|
224
|
+
start Start the dashboard server
|
|
225
|
+
stop Stop a running detached dashboard
|
|
226
|
+
build Build for production
|
|
227
|
+
init Run the interactive setup wizard
|
|
228
|
+
sync Import locale JSON files into the database
|
|
229
|
+
|
|
230
|
+
Options for start:
|
|
231
|
+
-p, --port <port> Server port (default: 3333)
|
|
232
|
+
--detach Run in the background (writes PID file)
|
|
233
|
+
|
|
234
|
+
Global options:
|
|
235
|
+
-V, --version Show version
|
|
236
|
+
-h, --help Show help
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Background mode
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Start in background
|
|
243
|
+
npx i18n-dashboard start --detach
|
|
244
|
+
|
|
245
|
+
# Stop the background process
|
|
246
|
+
npx i18n-dashboard stop
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Interface
|
|
252
|
+
|
|
253
|
+
### Dashboard
|
|
254
|
+
|
|
255
|
+
Customizable widget grid. Available widgets:
|
|
256
|
+
- **Total keys** — overall key count
|
|
257
|
+
- **Coverage** — global translation coverage percentage
|
|
258
|
+
- **Languages** — number of configured languages
|
|
259
|
+
- **Unused keys** — keys not found in source code
|
|
260
|
+
- **Language coverage** — per-language progress bars
|
|
261
|
+
- **Recent activity** — latest translation edits
|
|
262
|
+
- **Review queue** — translations awaiting approval
|
|
263
|
+
- **Projects** — quick project overview
|
|
264
|
+
|
|
265
|
+
Click **Edit** to add, remove, or rearrange widgets.
|
|
266
|
+
|
|
267
|
+
### Translations
|
|
268
|
+
|
|
269
|
+
The main translation table with:
|
|
270
|
+
- Search by key name (real-time)
|
|
271
|
+
- Filter by language and status (`All`, `Draft`, `Reviewed`, `Approved`, `Missing`, `Unused`)
|
|
272
|
+
- Status badge per language
|
|
273
|
+
- Auto-translate per key (Google Translate)
|
|
274
|
+
- Batch auto-translate for an entire language
|
|
275
|
+
|
|
276
|
+
Click any key to open its **detail page**:
|
|
277
|
+
- Edit each language with the full toolbar (params, escape helpers, plural editor, linked key picker)
|
|
278
|
+
- View translation history with one-click restore
|
|
279
|
+
- See which source files reference this key (after scan)
|
|
280
|
+
|
|
281
|
+
#### Plural editor
|
|
282
|
+
|
|
283
|
+
Switch any translation to plural mode to get a template-based form:
|
|
284
|
+
|
|
285
|
+
| Template | Languages | Example |
|
|
286
|
+
|---|---|---|
|
|
287
|
+
| 2 forms — standard | English, German, Dutch… | `car \| cars` |
|
|
288
|
+
| 3 forms — zero/one/many | French, Spanish, Italian… | `no cars \| {count} car \| {count} cars` |
|
|
289
|
+
| 4 forms — Slavic | Russian, Polish, Ukrainian… | `0 машин \| {n} машина \| {n} машины \| {n} машин` |
|
|
290
|
+
| Custom | Any | Define your own forms |
|
|
291
|
+
|
|
292
|
+
The `{count}` and `{n}` parameters are always available implicitly.
|
|
293
|
+
|
|
294
|
+
### Languages
|
|
295
|
+
|
|
296
|
+
- Add languages from 170+ BCP 47 locale codes (`fr`, `fr-CA`, `en-GB`, `zh-CN`, `sr-Latn`…)
|
|
297
|
+
- Type any custom BCP 47 code if it's not in the list
|
|
298
|
+
- Set a default language (source for auto-translate)
|
|
299
|
+
- Configure fallback chains:
|
|
300
|
+
- **Automatic** — `fr-CA → fr` (BCP 47 parent)
|
|
301
|
+
- **Manual** — pick any other configured language
|
|
302
|
+
- **None** — no fallback
|
|
303
|
+
- The API resolves the fallback chain transparently and merges translations
|
|
304
|
+
|
|
305
|
+
### Scan
|
|
306
|
+
|
|
307
|
+
Click **Scan project** in the sidebar to open the scan modal:
|
|
308
|
+
|
|
309
|
+
**Local mode** — browse your file system with the folder picker and scan `.vue`, `.ts`, `.js` files for:
|
|
310
|
+
- `$t('key')`, `$tc()`, `$te()`, `$tm()`
|
|
311
|
+
- `t('key')` via `useI18n()`
|
|
312
|
+
- `<i18n-t keypath="key">`
|
|
313
|
+
- `v-t="'key'"`
|
|
314
|
+
- `<i18n>` SFC blocks
|
|
315
|
+
|
|
316
|
+
**URL mode** — enter the base URL of your app; the scanner fetches each configured locale file (`/locale/en.json`, `/locale/fr.json`…) and imports all keys it finds.
|
|
317
|
+
|
|
318
|
+
Results (keys found, new keys, unused keys, files scanned) are displayed inline.
|
|
319
|
+
|
|
320
|
+
### Settings
|
|
321
|
+
|
|
322
|
+
Per-project settings (editable inline):
|
|
323
|
+
- **Project name, root path, source URL, locales folder, key separator, color, description**
|
|
324
|
+
- **Advanced features** — enable/disable Number formats, Datetime formats, Custom modifiers pages
|
|
325
|
+
- **Scanner** — configure excluded directories
|
|
326
|
+
- **Google Translate** — optional API key
|
|
327
|
+
- **Export** — download locale JSON files per language or all at once
|
|
328
|
+
- **Snapshot** — export/import a full project backup
|
|
329
|
+
|
|
330
|
+
### Number Formats *(requires enable in Settings)*
|
|
331
|
+
|
|
332
|
+
Configure `$n(value, 'formatName')` presets:
|
|
333
|
+
- Group by locale
|
|
334
|
+
- Style: `decimal`, `currency`, `percent`, `unit`
|
|
335
|
+
- Currency / unit selection
|
|
336
|
+
- Live `Intl.NumberFormat` preview
|
|
337
|
+
|
|
338
|
+
### Datetime Formats *(requires enable in Settings)*
|
|
339
|
+
|
|
340
|
+
Configure `$d(date, 'formatName')` presets:
|
|
341
|
+
- Shortcut styles: `dateStyle` / `timeStyle`
|
|
342
|
+
- Individual fields: `year`, `month`, `day`, `hour`, `minute`, `second`, `weekday`, `era`, `timeZone`
|
|
343
|
+
- Live `Intl.DateTimeFormat` preview
|
|
344
|
+
|
|
345
|
+
### Modifiers *(requires enable in Settings)*
|
|
346
|
+
|
|
347
|
+
Define custom `@.modifier:key` transform functions:
|
|
348
|
+
- Write JS function body
|
|
349
|
+
- Live test runner
|
|
350
|
+
- Quick templates: `snakeCase`, `camelCase`, `kebabCase`, `titleCase`
|
|
351
|
+
|
|
352
|
+
### Snippet generator
|
|
353
|
+
|
|
354
|
+
Available on all format pages — generates a ready-to-paste `createI18n()` configuration including all your number formats, datetime formats, and custom modifiers.
|
|
355
|
+
|
|
356
|
+
### Project Snapshot
|
|
357
|
+
|
|
358
|
+
Export a complete project backup:
|
|
359
|
+
|
|
360
|
+
```json
|
|
361
|
+
{
|
|
362
|
+
"version": 1,
|
|
363
|
+
"exportedAt": "2026-01-01T00:00:00.000Z",
|
|
364
|
+
"project": { "name": "My App", "locales_path": "src/locales", "key_separator": "." },
|
|
365
|
+
"languages": [{ "code": "en", "name": "English", "is_default": true }],
|
|
366
|
+
"keys": [
|
|
367
|
+
{
|
|
368
|
+
"key": "home.title",
|
|
369
|
+
"description": "Homepage title",
|
|
370
|
+
"translations": {
|
|
371
|
+
"en": { "value": "Welcome", "status": "approved" },
|
|
372
|
+
"fr": { "value": "Bienvenue", "status": "reviewed" }
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Import modes:
|
|
380
|
+
- **Merge** — add/update keys without touching existing ones
|
|
381
|
+
- **Replace** — delete everything and reimport clean
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## vue-i18n Integration
|
|
386
|
+
|
|
387
|
+
### Basic (load all on startup)
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
// src/i18n.js
|
|
391
|
+
import { createI18n } from 'vue-i18n'
|
|
392
|
+
|
|
393
|
+
const DASHBOARD = 'http://localhost:3333'
|
|
394
|
+
|
|
395
|
+
export const i18n = createI18n({
|
|
396
|
+
locale: 'en',
|
|
397
|
+
fallbackLocale: 'en',
|
|
398
|
+
messages: {
|
|
399
|
+
en: await fetch(`${DASHBOARD}/locale/en.json`).then(r => r.json()),
|
|
400
|
+
fr: await fetch(`${DASHBOARD}/locale/fr.json`).then(r => r.json()),
|
|
401
|
+
},
|
|
402
|
+
})
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### With lazy loading
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
import { createI18n } from 'vue-i18n'
|
|
409
|
+
|
|
410
|
+
const DASHBOARD = 'http://localhost:3333'
|
|
411
|
+
|
|
412
|
+
export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en', messages: {} })
|
|
413
|
+
|
|
414
|
+
export async function setLocale(locale) {
|
|
415
|
+
if (!i18n.global.availableLocales.includes(locale)) {
|
|
416
|
+
const messages = await fetch(`${DASHBOARD}/locale/${locale}.json`).then(r => r.json())
|
|
417
|
+
i18n.global.setLocaleMessage(locale, messages)
|
|
418
|
+
}
|
|
419
|
+
i18n.global.locale.value = locale
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### With BCP 47 fallbacks
|
|
424
|
+
|
|
425
|
+
The API automatically resolves fallback chains. If `fr-CA` isn't fully translated, missing keys fall back to `fr`, then to the next configured fallback:
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
GET /locale/fr-CA.json → merges fr + fr-CA (fr-CA takes precedence)
|
|
429
|
+
X-I18n-Fallback-Chain: fr-CA → fr
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
No configuration needed on the client side.
|
|
433
|
+
|
|
434
|
+
### For production
|
|
435
|
+
|
|
436
|
+
Export locale files into your project and include them in your build:
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
curl http://localhost:3333/locale/en.json -o src/locales/en.json
|
|
440
|
+
curl http://localhost:3333/locale/fr.json -o src/locales/fr.json
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Or deploy the dashboard on an internal server and keep using the API.
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Database
|
|
448
|
+
|
|
449
|
+
### SQLite (default — zero config)
|
|
450
|
+
|
|
451
|
+
```js
|
|
452
|
+
database: {
|
|
453
|
+
client: 'better-sqlite3',
|
|
454
|
+
connection: './i18n-dashboard.db',
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The `.db` file is created automatically. No setup needed.
|
|
459
|
+
|
|
460
|
+
### PostgreSQL
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
npm install pg
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
```js
|
|
467
|
+
database: {
|
|
468
|
+
client: 'pg',
|
|
469
|
+
connection: {
|
|
470
|
+
host: 'localhost',
|
|
471
|
+
port: 5432,
|
|
472
|
+
user: 'myuser',
|
|
473
|
+
password: 'mypassword',
|
|
474
|
+
database: 'i18n_dashboard',
|
|
475
|
+
},
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### MySQL / MariaDB
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
npm install mysql2
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
```js
|
|
486
|
+
database: {
|
|
487
|
+
client: 'mysql2',
|
|
488
|
+
connection: {
|
|
489
|
+
host: 'localhost',
|
|
490
|
+
port: 3306,
|
|
491
|
+
user: 'myuser',
|
|
492
|
+
password: 'mypassword',
|
|
493
|
+
database: 'i18n_dashboard',
|
|
494
|
+
},
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Schema
|
|
499
|
+
|
|
500
|
+
Tables are created and migrated automatically on startup:
|
|
501
|
+
|
|
502
|
+
```
|
|
503
|
+
projects — multi-project support
|
|
504
|
+
languages — per-project language list (BCP 47 codes, fallback_code)
|
|
505
|
+
translation_keys — key registry (key, description, is_unused, usages)
|
|
506
|
+
translations — values per key per language (value, status)
|
|
507
|
+
translation_history — full edit history (old_value, new_value, changed_by)
|
|
508
|
+
key_usages — source file references (file_path, line_number, function)
|
|
509
|
+
settings — global settings (scan_exclude, google_translate_api_key)
|
|
510
|
+
users — dashboard users (name, email, role, bcrypt password)
|
|
511
|
+
project_number_formats — Intl.NumberFormat presets
|
|
512
|
+
project_datetime_formats — Intl.DateTimeFormat presets
|
|
513
|
+
project_modifiers — custom @.modifier functions
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## REST API
|
|
519
|
+
|
|
520
|
+
All endpoints require a `project_id` parameter.
|
|
521
|
+
|
|
522
|
+
### Locale export (main endpoint for vue-i18n)
|
|
523
|
+
|
|
524
|
+
```http
|
|
525
|
+
GET /locale/:lang.json?project_id=1
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Returns nested JSON with all translations for the given language. Resolves fallback chains automatically.
|
|
529
|
+
|
|
530
|
+
**Response headers:**
|
|
531
|
+
- `X-I18n-Fallback-Chain: fr-CA → fr` — debug the resolved chain
|
|
532
|
+
|
|
533
|
+
### Projects
|
|
534
|
+
|
|
535
|
+
```http
|
|
536
|
+
GET /api/projects
|
|
537
|
+
POST /api/projects
|
|
538
|
+
PUT /api/projects/:id
|
|
539
|
+
DELETE /api/projects/:id
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Languages
|
|
543
|
+
|
|
544
|
+
```http
|
|
545
|
+
GET /api/languages?project_id=1
|
|
546
|
+
POST /api/languages
|
|
547
|
+
PUT /api/languages/:id
|
|
548
|
+
DELETE /api/languages/:code?project_id=1
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Translation keys
|
|
552
|
+
|
|
553
|
+
```http
|
|
554
|
+
GET /api/keys?project_id=1&search=&lang=&status=&page=1&limit=50
|
|
555
|
+
GET /api/keys/:id
|
|
556
|
+
POST /api/keys
|
|
557
|
+
PATCH /api/keys/:id
|
|
558
|
+
DELETE /api/keys/:id
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Translations
|
|
562
|
+
|
|
563
|
+
```http
|
|
564
|
+
POST /api/translations # Save a translation value
|
|
565
|
+
POST /api/translations/status # Update status only
|
|
566
|
+
POST /api/translations/batch-translate # Auto-translate all missing for a language
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### Scan & Sync
|
|
570
|
+
|
|
571
|
+
```http
|
|
572
|
+
POST /api/scan # body: { project_id, mode: 'local'|'url', root_path?, url? }
|
|
573
|
+
POST /api/sync # body: { project_id }
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### Project Snapshot
|
|
577
|
+
|
|
578
|
+
```http
|
|
579
|
+
GET /api/project-snapshot?project_id=1 # Export
|
|
580
|
+
POST /api/project-snapshot # Import
|
|
581
|
+
body: { snapshot, project_id?, mode: 'merge'|'replace' }
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Advanced formats
|
|
585
|
+
|
|
586
|
+
```http
|
|
587
|
+
GET /api/formats/number?project_id=1
|
|
588
|
+
POST /api/formats/number
|
|
589
|
+
PUT /api/formats/number/:id
|
|
590
|
+
DELETE /api/formats/number/:id
|
|
591
|
+
|
|
592
|
+
GET /api/formats/datetime?project_id=1
|
|
593
|
+
POST /api/formats/datetime
|
|
594
|
+
PUT /api/formats/datetime/:id
|
|
595
|
+
DELETE /api/formats/datetime/:id
|
|
596
|
+
|
|
597
|
+
GET /api/formats/modifiers?project_id=1
|
|
598
|
+
POST /api/formats/modifiers
|
|
599
|
+
PUT /api/formats/modifiers/:id
|
|
600
|
+
DELETE /api/formats/modifiers/:id
|
|
601
|
+
|
|
602
|
+
GET /api/formats/snippet?project_id=1 # Generate createI18n() config snippet
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### Settings
|
|
606
|
+
|
|
607
|
+
```http
|
|
608
|
+
GET /api/settings
|
|
609
|
+
POST /api/settings
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### File system browser
|
|
613
|
+
|
|
614
|
+
```http
|
|
615
|
+
GET /api/fs/browse?path=/some/path # List subdirectories; defaults to home dir
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
## User Roles
|
|
621
|
+
|
|
622
|
+
| Role | Permissions |
|
|
623
|
+
|---|---|
|
|
624
|
+
| **Super Admin** | Full access to all projects, users, and global settings |
|
|
625
|
+
| **Admin** | Full access to assigned projects |
|
|
626
|
+
| **Moderator** | Edit translations, approve/reject in review queue |
|
|
627
|
+
| **Translator** | Edit translations, mark as reviewed (cannot approve) |
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## Recommended Workflows
|
|
632
|
+
|
|
633
|
+
### New project from scratch
|
|
634
|
+
|
|
635
|
+
```bash
|
|
636
|
+
# 1. Install
|
|
637
|
+
npm install i18n-dashboard --save-dev
|
|
638
|
+
|
|
639
|
+
# 2. Initialize
|
|
640
|
+
npx i18n-dashboard init
|
|
641
|
+
|
|
642
|
+
# 3. Start
|
|
643
|
+
npx i18n-dashboard start
|
|
644
|
+
|
|
645
|
+
# 4. In the UI:
|
|
646
|
+
# - Add languages (Languages tab)
|
|
647
|
+
# - Add translation keys (Translations tab)
|
|
648
|
+
# - Use the API in your Vue app
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### Migrate from existing JSON files
|
|
652
|
+
|
|
653
|
+
```bash
|
|
654
|
+
# 1. Install and initialize
|
|
655
|
+
npm install i18n-dashboard --save-dev
|
|
656
|
+
npx i18n-dashboard init
|
|
657
|
+
|
|
658
|
+
# 2. Start
|
|
659
|
+
npx i18n-dashboard start
|
|
660
|
+
|
|
661
|
+
# 3. Sync your existing locale files
|
|
662
|
+
npx i18n-dashboard sync
|
|
663
|
+
|
|
664
|
+
# 4. All keys and translations are now in the database
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### Team workflow with review
|
|
668
|
+
|
|
669
|
+
1. **Translators** edit keys → status: `Draft`
|
|
670
|
+
2. **Translators** mark ready → status: `Reviewed`
|
|
671
|
+
3. **Moderators/Admins** approve → status: `Approved`
|
|
672
|
+
4. Only `Approved` translations are exported (or all, depending on your needs)
|
|
673
|
+
|
|
674
|
+
### Move between environments (local → production)
|
|
675
|
+
|
|
676
|
+
```bash
|
|
677
|
+
# On local machine: export snapshot
|
|
678
|
+
# Dashboard UI → Settings → Snapshot → Export
|
|
679
|
+
|
|
680
|
+
# On production server: import snapshot
|
|
681
|
+
# Dashboard UI → Settings → Snapshot → Import (Merge or Replace mode)
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
686
|
+
## Stack
|
|
687
|
+
|
|
688
|
+
| Technology | Version | Role |
|
|
689
|
+
|---|---|---|
|
|
690
|
+
| [Nuxt 3](https://nuxt.com/) | 3.21+ | Full-stack framework (Nitro backend + Vue 3 frontend) |
|
|
691
|
+
| [Nuxt UI](https://ui.nuxt.com/) | 3.3+ | UI components (Tailwind CSS v4 + Reka UI) |
|
|
692
|
+
| [Knex.js](https://knexjs.org/) | 3.x | Multi-database abstraction |
|
|
693
|
+
| [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) | 11.x | SQLite driver |
|
|
694
|
+
| [@vitalets/google-translate-api](https://github.com/vitalets/google-translate-api) | 9.x | Google Translate (free tier) |
|
|
695
|
+
| [Commander.js](https://github.com/tj/commander.js) | 13.x | CLI |
|
|
696
|
+
| [bcryptjs](https://github.com/dcodeIO/bcrypt.js) | 2.x | Password hashing |
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## Contributing
|
|
701
|
+
|
|
702
|
+
Contributions are welcome. Please open an issue before submitting a pull request for significant changes.
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
git clone https://github.com/your-username/i18n-dashboard
|
|
706
|
+
cd i18n-dashboard
|
|
707
|
+
npm install
|
|
708
|
+
npm run dev
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
713
|
+
## License
|
|
714
|
+
|
|
715
|
+
[MIT](./LICENSE)
|