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.
Files changed (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +715 -0
  3. package/app.vue +8 -0
  4. package/assets/css/main.css +21 -0
  5. package/assets/locales/en.json +380 -0
  6. package/bin/cli.mjs +279 -0
  7. package/components/LinkedKeyPicker.vue +135 -0
  8. package/components/PathPicker.vue +153 -0
  9. package/components/PluralEditor.vue +295 -0
  10. package/components/ScanModal.vue +153 -0
  11. package/components/TranslationHistoryModal.vue +66 -0
  12. package/components/TranslationRow.vue +541 -0
  13. package/components/dashboard/WidgetConfigModal.vue +121 -0
  14. package/components/dashboard/WidgetGrid.vue +190 -0
  15. package/components/dashboard/WidgetPicker.vue +75 -0
  16. package/components/dashboard/widgets/ActivityWidget.vue +109 -0
  17. package/components/dashboard/widgets/LanguagesCoverageWidget.vue +104 -0
  18. package/components/dashboard/widgets/ProjectsWidget.vue +77 -0
  19. package/components/dashboard/widgets/ReviewWidget.vue +150 -0
  20. package/components/dashboard/widgets/StatWidget.vue +133 -0
  21. package/composables/useAuth.ts +72 -0
  22. package/composables/useConfig.ts +14 -0
  23. package/composables/useDashboard.ts +89 -0
  24. package/composables/useFormats.ts +100 -0
  25. package/composables/useKeys.ts +231 -0
  26. package/composables/useLanguages.ts +221 -0
  27. package/composables/useProfile.ts +76 -0
  28. package/composables/useProject.ts +180 -0
  29. package/composables/useReview.ts +94 -0
  30. package/composables/useSettings.ts +30 -0
  31. package/composables/useStats.ts +16 -0
  32. package/composables/useT.ts +38 -0
  33. package/composables/useUsers.ts +101 -0
  34. package/composables/useWidgetData.ts +50 -0
  35. package/consts/commons.const.ts +6 -0
  36. package/consts/dashboard.const.ts +94 -0
  37. package/consts/languages.const.ts +223 -0
  38. package/enums/commons.enum.ts +7 -0
  39. package/i18n-dashboard.config.example.js +40 -0
  40. package/interfaces/commons.interface.ts +23 -0
  41. package/interfaces/job.interface.ts +10 -0
  42. package/interfaces/key.interface.ts +39 -0
  43. package/interfaces/languages.interface.ts +23 -0
  44. package/interfaces/project.interface.ts +9 -0
  45. package/interfaces/scan.interface.ts +12 -0
  46. package/interfaces/settings.interface.ts +4 -0
  47. package/interfaces/stat.interface.ts +30 -0
  48. package/interfaces/translation.interface.ts +11 -0
  49. package/interfaces/user.interface.ts +24 -0
  50. package/layouts/auth.vue +5 -0
  51. package/layouts/default.vue +327 -0
  52. package/middleware/auth.global.ts +26 -0
  53. package/nuxt.config.ts +66 -0
  54. package/package.json +89 -0
  55. package/pages/index.vue +5 -0
  56. package/pages/login.vue +74 -0
  57. package/pages/onboarding.vue +563 -0
  58. package/pages/projects/[id]/formats/datetime.vue +240 -0
  59. package/pages/projects/[id]/formats/modifiers.vue +194 -0
  60. package/pages/projects/[id]/formats/number.vue +250 -0
  61. package/pages/projects/[id]/index.vue +182 -0
  62. package/pages/projects/[id]/languages.vue +537 -0
  63. package/pages/projects/[id]/review.vue +109 -0
  64. package/pages/projects/[id]/settings.vue +515 -0
  65. package/pages/projects/[id]/translations/[keyId].vue +642 -0
  66. package/pages/projects/[id]/translations/index.vue +250 -0
  67. package/pages/projects/[id]/users.vue +276 -0
  68. package/pages/projects/index.vue +334 -0
  69. package/pages/users/[id]/profile.vue +421 -0
  70. package/pages/users/index.vue +345 -0
  71. package/plugins/loading.client.ts +3 -0
  72. package/plugins/ui-i18n.ts +6 -0
  73. package/server/api/auth/login.post.ts +28 -0
  74. package/server/api/auth/logout.post.ts +7 -0
  75. package/server/api/auth/me.get.ts +11 -0
  76. package/server/api/auth/me.put.ts +31 -0
  77. package/server/api/auth/password.put.ts +27 -0
  78. package/server/api/auth/status.get.ts +16 -0
  79. package/server/api/config.get.ts +10 -0
  80. package/server/api/dashboard/layout.get.ts +18 -0
  81. package/server/api/dashboard/layout.post.ts +18 -0
  82. package/server/api/db-config.get.ts +44 -0
  83. package/server/api/db-config.post.ts +73 -0
  84. package/server/api/export.get.ts +64 -0
  85. package/server/api/formats/datetime/[id].delete.ts +8 -0
  86. package/server/api/formats/datetime/[id].put.ts +15 -0
  87. package/server/api/formats/datetime.get.ts +11 -0
  88. package/server/api/formats/datetime.post.ts +16 -0
  89. package/server/api/formats/modifiers/[id].delete.ts +8 -0
  90. package/server/api/formats/modifiers/[id].put.ts +10 -0
  91. package/server/api/formats/modifiers.get.ts +10 -0
  92. package/server/api/formats/modifiers.post.ts +14 -0
  93. package/server/api/formats/number/[id].delete.ts +8 -0
  94. package/server/api/formats/number/[id].put.ts +15 -0
  95. package/server/api/formats/number.get.ts +11 -0
  96. package/server/api/formats/number.post.ts +16 -0
  97. package/server/api/formats/snippet.get.ts +87 -0
  98. package/server/api/fs/browse.get.ts +50 -0
  99. package/server/api/history/[translationId].get.ts +13 -0
  100. package/server/api/keys/[id].delete.ts +14 -0
  101. package/server/api/keys/[id].get.ts +41 -0
  102. package/server/api/keys/[id].patch.ts +20 -0
  103. package/server/api/keys/index.get.ts +98 -0
  104. package/server/api/keys/index.post.ts +17 -0
  105. package/server/api/languages/[code].delete.ts +15 -0
  106. package/server/api/languages/[id].put.ts +24 -0
  107. package/server/api/languages/index.get.ts +13 -0
  108. package/server/api/languages/index.post.ts +42 -0
  109. package/server/api/onboarding.post.ts +56 -0
  110. package/server/api/profile.get.ts +81 -0
  111. package/server/api/project-snapshot.get.ts +73 -0
  112. package/server/api/project-snapshot.post.ts +160 -0
  113. package/server/api/projects/[id].delete.ts +13 -0
  114. package/server/api/projects/[id].put.ts +40 -0
  115. package/server/api/projects/index.get.ts +19 -0
  116. package/server/api/projects/index.post.ts +34 -0
  117. package/server/api/scan.post.ts +165 -0
  118. package/server/api/settings/index.get.ts +9 -0
  119. package/server/api/settings/index.post.ts +20 -0
  120. package/server/api/setup.post.ts +39 -0
  121. package/server/api/stats/global.get.ts +126 -0
  122. package/server/api/stats.get.ts +70 -0
  123. package/server/api/sync.post.ts +179 -0
  124. package/server/api/translate.post.ts +52 -0
  125. package/server/api/translations/batch-translate.post.ts +121 -0
  126. package/server/api/translations/bulk-status.post.ts +24 -0
  127. package/server/api/translations/index.post.ts +62 -0
  128. package/server/api/translations/job/[id].get.ts +23 -0
  129. package/server/api/translations/status.post.ts +30 -0
  130. package/server/api/translations/translate-all.post.ts +18 -0
  131. package/server/api/ui-locale.get.ts +39 -0
  132. package/server/api/users/[id]/profile.get.ts +107 -0
  133. package/server/api/users/[id]/roles.put.ts +67 -0
  134. package/server/api/users/[id].delete.ts +36 -0
  135. package/server/api/users/[id].put.ts +43 -0
  136. package/server/api/users/index.get.ts +49 -0
  137. package/server/api/users/index.post.ts +89 -0
  138. package/server/consts/auto-translate.const.ts +2 -0
  139. package/server/consts/commons.const.ts +10 -0
  140. package/server/consts/db.const.ts +3 -0
  141. package/server/consts/scanner.const.ts +4 -0
  142. package/server/consts/translation-job.const.ts +8 -0
  143. package/server/db/index.ts +672 -0
  144. package/server/enums/auth.enum.ts +5 -0
  145. package/server/enums/translation.enum.ts +6 -0
  146. package/server/interfaces/profile.interface.ts +48 -0
  147. package/server/interfaces/project-config.interface.ts +9 -0
  148. package/server/interfaces/scanner.interface.ts +18 -0
  149. package/server/interfaces/translation-job.interface.ts +13 -0
  150. package/server/middleware/auth.ts +32 -0
  151. package/server/plugins/db.ts +6 -0
  152. package/server/routes/locale/[lang].get.ts +179 -0
  153. package/server/types/auth.type.ts +3 -0
  154. package/server/utils/auth.util.ts +89 -0
  155. package/server/utils/auto-translate.util.ts +112 -0
  156. package/server/utils/lang-api.util.ts +24 -0
  157. package/server/utils/mailer.util.ts +80 -0
  158. package/server/utils/project-config.util.ts +37 -0
  159. package/server/utils/scanner.uti.ts +307 -0
  160. package/server/utils/translation-job.util.ts +142 -0
  161. package/services/auth.service.ts +31 -0
  162. package/services/base.service.ts +140 -0
  163. package/services/job.service.ts +10 -0
  164. package/services/key.service.ts +26 -0
  165. package/services/language.service.ts +26 -0
  166. package/services/profile.service.ts +14 -0
  167. package/services/project.service.ts +23 -0
  168. package/services/scan.service.ts +14 -0
  169. package/services/settings.service.ts +14 -0
  170. package/services/stats.service.ts +11 -0
  171. package/services/translation.service.ts +36 -0
  172. package/services/user.service.ts +28 -0
  173. package/tsconfig.json +3 -0
  174. package/types/commons.type.ts +3 -0
  175. package/types/dashboard.type.ts +26 -0
  176. package/utils/config.util.ts +60 -0
@@ -0,0 +1,14 @@
1
+ import { BaseService } from './base.service'
2
+ import type { UserProfile } from '../server/interfaces/profile.interface'
3
+
4
+ class ProfileService extends BaseService {
5
+ async getProfile(): Promise<UserProfile> {
6
+ return this.get<UserProfile>('/api/profile')
7
+ }
8
+
9
+ async getUserProfile(id: number | string): Promise<UserProfile> {
10
+ return this.get<UserProfile>(`/api/users/${id}/profile`)
11
+ }
12
+ }
13
+
14
+ export const profileService = new ProfileService()
@@ -0,0 +1,23 @@
1
+ import { BaseService } from './base.service'
2
+ import type { Project } from '../composables/useProject'
3
+ import type { ProjectPayload } from '../interfaces/project.interface'
4
+
5
+ class ProjectService extends BaseService {
6
+ async getAll(): Promise<Project[]> {
7
+ return this.get<Project[]>('/api/projects')
8
+ }
9
+
10
+ async create(data: ProjectPayload): Promise<Project> {
11
+ return this.post<Project>('/api/projects', { body: data, skipDedup: true })
12
+ }
13
+
14
+ async update(id: number, data: Partial<ProjectPayload>): Promise<Project> {
15
+ return this.put<Project>(`/api/projects/${id}`, { body: data })
16
+ }
17
+
18
+ async remove(id: number): Promise<void> {
19
+ return this.delete(`/api/projects/${id}`)
20
+ }
21
+ }
22
+
23
+ export const projectService = new ProjectService()
@@ -0,0 +1,14 @@
1
+ import { BaseService } from './base.service'
2
+ import type { ScanResult, SyncResult } from '../interfaces/scan.interface'
3
+
4
+ class ScanService extends BaseService {
5
+ async scan(projectId: number): Promise<ScanResult> {
6
+ return this.post<ScanResult>('/api/scan', { body: { project_id: projectId }, skipDedup: true })
7
+ }
8
+
9
+ async sync(projectId: number): Promise<SyncResult> {
10
+ return this.post<SyncResult>('/api/sync', { body: { project_id: projectId }, skipDedup: true })
11
+ }
12
+ }
13
+
14
+ export const scanService = new ScanService()
@@ -0,0 +1,14 @@
1
+ import { BaseService } from './base.service'
2
+ import type { SettingsPayload } from '../interfaces/settings.interface'
3
+
4
+ class SettingsService extends BaseService {
5
+ async getSettings(): Promise<Record<string, string>> {
6
+ return this.get<Record<string, string>>('/api/settings')
7
+ }
8
+
9
+ async saveSettings(data: SettingsPayload): Promise<void> {
10
+ return this.post('/api/settings', { body: data, skipDedup: true })
11
+ }
12
+ }
13
+
14
+ export const settingsService = new SettingsService()
@@ -0,0 +1,11 @@
1
+ import { BaseService } from './base.service'
2
+ import type { StatsResponse } from '../interfaces/stat.interface'
3
+
4
+ class StatsService extends BaseService {
5
+ async getStats(projectId?: number): Promise<StatsResponse | null> {
6
+ if (!projectId) return null
7
+ return this.get<StatsResponse>('/api/stats', { query: { project_id: projectId } })
8
+ }
9
+ }
10
+
11
+ export const statsService = new StatsService()
@@ -0,0 +1,36 @@
1
+ import { BaseService } from './base.service'
2
+ import type { SaveTranslationPayload, SetStatusPayload } from '../interfaces/translation.interface'
3
+
4
+ class TranslationService extends BaseService {
5
+ async save(data: SaveTranslationPayload): Promise<void> {
6
+ return this.post('/api/translations', { body: data, skipDedup: true })
7
+ }
8
+
9
+ async setStatus(data: SetStatusPayload): Promise<void> {
10
+ return this.post('/api/translations/status', { body: data, skipDedup: true })
11
+ }
12
+
13
+ async bulkStatus(ids: number[], status: string): Promise<void> {
14
+ return this.post('/api/translations/bulk-status', { body: { ids, status }, skipDedup: true })
15
+ }
16
+
17
+ async batchTranslate(projectId: number, targetLanguage: string): Promise<{ translated: number; skipped: number; errors: number }> {
18
+ return this.post('/api/translations/batch-translate', {
19
+ body: { project_id: projectId, target_language: targetLanguage },
20
+ skipDedup: true,
21
+ })
22
+ }
23
+
24
+ async translateAll(projectId: number, languageCode: string, languageName: string): Promise<{ jobId: string }> {
25
+ return this.post('/api/translations/translate-all', {
26
+ body: { project_id: projectId, language_code: languageCode, language_name: languageName },
27
+ skipDedup: true,
28
+ })
29
+ }
30
+
31
+ async translateText(text: string, from: string, to: string): Promise<{ text: string }> {
32
+ return this.post<{ text: string }>('/api/translate', { body: { text, from, to }, skipDedup: true })
33
+ }
34
+ }
35
+
36
+ export const translationService = new TranslationService()
@@ -0,0 +1,28 @@
1
+ import { BaseService } from './base.service'
2
+ import type { CreateUserPayload, RoleEntry, UserItem } from '../interfaces/user.interface'
3
+
4
+ class UserService extends BaseService {
5
+ async getUsers(query?: { project_id?: number }): Promise<UserItem[]> {
6
+ return this.get<UserItem[]>('/api/users', { query })
7
+ }
8
+
9
+ async create(data: CreateUserPayload): Promise<{ id: number; tempPassword: string; email: string; name: string }> {
10
+ return this.post('/api/users', { body: data, skipDedup: true })
11
+ }
12
+
13
+ async update(id: number, data: { is_active?: boolean; project_id?: number }): Promise<void> {
14
+ return this.put(`/api/users/${id}`, { body: data })
15
+ }
16
+
17
+ async updateRoles(id: number, roles: RoleEntry[]): Promise<void> {
18
+ return this.put(`/api/users/${id}/roles`, { body: { roles } })
19
+ }
20
+
21
+ async remove(id: number, projectId?: number): Promise<void> {
22
+ return this.delete(`/api/users/${id}`, {
23
+ query: projectId ? { project_id: projectId } : undefined,
24
+ })
25
+ }
26
+ }
27
+
28
+ export const userService = new UserService()
package/tsconfig.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./.nuxt/tsconfig.json"
3
+ }
@@ -0,0 +1,3 @@
1
+ import type { METHODS } from '../enums/commons.enum'
2
+
3
+ export type Method = `${METHODS}`
@@ -0,0 +1,26 @@
1
+ export type WidgetType =
2
+ | 'stat-keys'
3
+ | 'stat-coverage'
4
+ | 'stat-languages'
5
+ | 'stat-unused'
6
+ | 'projects'
7
+ | 'languages-coverage'
8
+ | 'last-activity'
9
+ | 'review-queue'
10
+
11
+ export type WidgetSize = 'sm' | 'md' | 'lg' | 'wide' | 'xl'
12
+
13
+ export type DataSourceType = 'global' | 'project'
14
+
15
+ export interface WidgetDataSource {
16
+ type: DataSourceType
17
+ projectId?: number // only when type === 'project'
18
+ }
19
+
20
+ export interface WidgetConfig {
21
+ id: string
22
+ type: WidgetType
23
+ size: WidgetSize
24
+ dataSource?: WidgetDataSource // default = 'global'
25
+ title?: string // optional custom title override
26
+ }
@@ -0,0 +1,60 @@
1
+ import { resolve } from 'path'
2
+ import { existsSync } from 'fs'
3
+
4
+ export async function loadUserConfig() {
5
+ const configPath = resolve(process.cwd(), 'i18n-dashboard.config.js')
6
+ const configPathMjs = resolve(process.cwd(), 'i18n-dashboard.config.mjs')
7
+
8
+ if (existsSync(configPath)) {
9
+ try {
10
+ const mod = await import(configPath)
11
+
12
+ return mod.default || mod
13
+ } catch (e) {
14
+ console.warn('Warning: Could not load i18n-dashboard.config.js:', e.message)
15
+ }
16
+ } else if (existsSync(configPathMjs)) {
17
+ try {
18
+ const mod = await import(configPathMjs)
19
+
20
+ return mod.default || mod
21
+ } catch (e) {
22
+ console.warn('Warning: Could not load i18n-dashboard.config.mjs:', e.message)
23
+ }
24
+ }
25
+
26
+ return {}
27
+ }
28
+
29
+ export function buildEnv(config) {
30
+ const env = { ...process.env }
31
+
32
+ if (config.port) env.I18N_PORT = String(config.port)
33
+ if (config.keySeparator) env.I18N_KEY_SEPARATOR = config.keySeparator
34
+ if (config.localesPath) env.I18N_LOCALES_PATH = config.localesPath
35
+ if (config.apiPath) env.I18N_API_PATH = config.apiPath
36
+
37
+ // Project root: explicit config > process.cwd()
38
+ env.I18N_PROJECT_ROOT = config.projectRoot
39
+ ? resolve(config.projectRoot)
40
+ : process.cwd()
41
+
42
+ if (config.database) {
43
+ if (config.database.client) env.I18N_DB_CLIENT = config.database.client
44
+ if (config.database.connection) {
45
+ if (typeof config.database.connection === 'string') {
46
+ env.I18N_DB_CONNECTION = config.database.connection
47
+ } else {
48
+ if (config.database.connection.host) env.I18N_DB_HOST = config.database.connection.host
49
+ if (config.database.connection.port) env.I18N_DB_PORT = String(config.database.connection.port)
50
+ if (config.database.connection.user) env.I18N_DB_USER = config.database.connection.user
51
+ if (config.database.connection.password) env.I18N_DB_PASSWORD = config.database.connection.password
52
+ if (config.database.connection.database) env.I18N_DB_NAME = config.database.connection.database
53
+ }
54
+ }
55
+ }
56
+
57
+ if (config.googleTranslate?.apiKey) env.GOOGLE_TRANSLATE_API_KEY = config.googleTranslate.apiKey
58
+
59
+ return env
60
+ }