@mapomodule/i18n 2.0.0-beta.2
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/README.md +62 -0
- package/dist/index.d.mts +30 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1 -0
- package/dist/module.d.mts +70 -0
- package/dist/module.d.ts +68 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +70 -0
- package/dist/runtime/composables/useMapoT.d.ts +12 -0
- package/dist/runtime/composables/useMapoT.d.ts.map +1 -0
- package/dist/runtime/composables/useMapoT.js +6 -0
- package/dist/runtime/locales/mapo.en.json +261 -0
- package/dist/runtime/locales/mapo.it.json +261 -0
- package/dist/types.d.mts +9 -0
- package/package.json +61 -0
- package/src/index.ts +33 -0
- package/src/module.ts +193 -0
- package/src/runtime/composables/useMapoT.ts +21 -0
- package/src/runtime/locales/mapo.en.json +261 -0
- package/src/runtime/locales/mapo.it.json +261 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mapo": {
|
|
3
|
+
"cancel": "Annulla",
|
|
4
|
+
"save": "Salva",
|
|
5
|
+
"apply": "Applica",
|
|
6
|
+
"confirm": "Conferma",
|
|
7
|
+
"ok": "OK",
|
|
8
|
+
"close": "Chiudi",
|
|
9
|
+
"edit": "Modifica",
|
|
10
|
+
"delete": "Elimina",
|
|
11
|
+
"add": "Aggiungi",
|
|
12
|
+
"remove": "Rimuovi",
|
|
13
|
+
"clear": "Svuota",
|
|
14
|
+
"expand": "Espandi",
|
|
15
|
+
"collapse": "Comprimi",
|
|
16
|
+
"search": "Cerca...",
|
|
17
|
+
"create": "Crea",
|
|
18
|
+
"new": "Nuovo",
|
|
19
|
+
"createContinue": "Crea e continua",
|
|
20
|
+
"saveContinue": "Salva e continua",
|
|
21
|
+
"back": "Indietro",
|
|
22
|
+
"leave": "Esci",
|
|
23
|
+
"preview": "Anteprima",
|
|
24
|
+
"refresh": "Aggiorna",
|
|
25
|
+
"select": "Seleziona",
|
|
26
|
+
"change": "Cambia",
|
|
27
|
+
"duplicate": "Duplica",
|
|
28
|
+
"restore": "Ripristina",
|
|
29
|
+
"discard": "Scarta",
|
|
30
|
+
"loading": "Caricamento...",
|
|
31
|
+
"genericError": "Qualcosa è andato storto, riprova più tardi...",
|
|
32
|
+
"saveSuccess": "Elemento salvato con successo!",
|
|
33
|
+
"createSuccess": "Elemento creato con successo!",
|
|
34
|
+
"deleteSuccess": "Elemento eliminato con successo!",
|
|
35
|
+
"saveError": "Salvataggio non riuscito",
|
|
36
|
+
"deleteError": "Eliminazione non riuscita",
|
|
37
|
+
"loadError": "Caricamento dati non riuscito",
|
|
38
|
+
"loadItemError": "Caricamento elemento non riuscito",
|
|
39
|
+
"fixErrors": "Correggi gli errori prima di salvare.",
|
|
40
|
+
"confirmDelete": "Vuoi veramente eliminare questo elemento?",
|
|
41
|
+
"unsavedData": "Modifiche non salvate",
|
|
42
|
+
"confirmLeaveChanges": "Ci sono modifiche non salvate. Vuoi uscire senza salvare?",
|
|
43
|
+
"fetchingData": "Caricamento in corso...",
|
|
44
|
+
"login": {
|
|
45
|
+
"welcome": "Benvenuto in Mapo",
|
|
46
|
+
"subtitle": "Inserisci le tue credenziali per continuare",
|
|
47
|
+
"tagline": "Un framework admin moderno costruito su Nuxt 4, Pinia e Nuxt UI v4.",
|
|
48
|
+
"invalidCredentials": "Credenziali non valide. Riprova."
|
|
49
|
+
},
|
|
50
|
+
"logout": "Esci",
|
|
51
|
+
"signIn": "Accedi",
|
|
52
|
+
"username": "Nome utente",
|
|
53
|
+
"password": "Password",
|
|
54
|
+
"name": "Nome",
|
|
55
|
+
"title": "Titolo",
|
|
56
|
+
"description": "Descrizione",
|
|
57
|
+
"mime": "Tipo MIME",
|
|
58
|
+
"fileName": "Nome file",
|
|
59
|
+
"file": "File",
|
|
60
|
+
"folder": "Cartella",
|
|
61
|
+
"created": "Creato il",
|
|
62
|
+
"size": "Dimensioni",
|
|
63
|
+
"altTag": "Testo alternativo",
|
|
64
|
+
"url": "URL",
|
|
65
|
+
"linkedModels": "Modelli collegati",
|
|
66
|
+
"latitude": "Latitudine",
|
|
67
|
+
"longitude": "Longitudine",
|
|
68
|
+
"language": "Lingua",
|
|
69
|
+
"index": {
|
|
70
|
+
"welcomeMessage": "Congratulazioni!! 🥳 Mapo è stato installato correttamente!"
|
|
71
|
+
},
|
|
72
|
+
"themeToggle": {
|
|
73
|
+
"light": "Passa al tema chiaro",
|
|
74
|
+
"dark": "Passa al tema scuro"
|
|
75
|
+
},
|
|
76
|
+
"confirmDialog": {
|
|
77
|
+
"areYouSure": "Sei sicuro di ciò che stai facendo?"
|
|
78
|
+
},
|
|
79
|
+
"dropArea": {
|
|
80
|
+
"dropOrClick": "Trascina qui i tuoi file, o clicca per selezionarli.",
|
|
81
|
+
"dropHere": "Trascina qui",
|
|
82
|
+
"onlyOneFile": "È possibile caricare un solo file alla volta..."
|
|
83
|
+
},
|
|
84
|
+
"detail": {
|
|
85
|
+
"deleteQuestion": "Vuoi veramente eliminare questo elemento? Questa azione non può essere annullata.",
|
|
86
|
+
"draftFound": "È presente una bozza locale non salvata. Vuoi ripristinarla o scartarla?",
|
|
87
|
+
"draftSavedAt": "Bozza non salvata trovata — ultima modifica {date}"
|
|
88
|
+
},
|
|
89
|
+
"form": {
|
|
90
|
+
"required": "Questo campo è obbligatorio.",
|
|
91
|
+
"tabErrors": "Questa scheda contiene errori"
|
|
92
|
+
},
|
|
93
|
+
"fksField": {
|
|
94
|
+
"searchSelect": "Cerca e seleziona...",
|
|
95
|
+
"noResults": "Nessun risultato trovato",
|
|
96
|
+
"noItems": "Nessun elemento disponibile"
|
|
97
|
+
},
|
|
98
|
+
"dateField": {
|
|
99
|
+
"selectDate": "Seleziona data",
|
|
100
|
+
"selectDateTime": "Seleziona data e ora"
|
|
101
|
+
},
|
|
102
|
+
"fileField": {
|
|
103
|
+
"removeFile": "Rimuovi file"
|
|
104
|
+
},
|
|
105
|
+
"mediaField": {
|
|
106
|
+
"selectMedia": "Seleziona media"
|
|
107
|
+
},
|
|
108
|
+
"mediaM2MField": {
|
|
109
|
+
"addMedia": "Aggiungi media"
|
|
110
|
+
},
|
|
111
|
+
"enhancedMediaField": {
|
|
112
|
+
"altText": "Testo alternativo",
|
|
113
|
+
"caption": "Didascalia"
|
|
114
|
+
},
|
|
115
|
+
"repeater": {
|
|
116
|
+
"repeater": "Repeater",
|
|
117
|
+
"nItems": "{n} elemento | {n} elementi",
|
|
118
|
+
"nSelected": "{n} selezionati",
|
|
119
|
+
"all": "Tutti",
|
|
120
|
+
"collapseAll": "Comprimi tutti",
|
|
121
|
+
"expandAll": "Espandi tutti",
|
|
122
|
+
"deleteItem": "Elimina elemento",
|
|
123
|
+
"deleteItems": "Elimina elementi",
|
|
124
|
+
"confirmBulkDelete": "Eliminare {count} elementi selezionati?",
|
|
125
|
+
"actionBlocked": "Azione bloccata",
|
|
126
|
+
"minItemsBlock": "Impossibile eliminare: devono rimanere almeno {min} elementi.",
|
|
127
|
+
"undoLastAction": "Annulla ultima azione",
|
|
128
|
+
"bulkSelection": "Selezione multipla",
|
|
129
|
+
"exitSelection": "Esci dalla selezione",
|
|
130
|
+
"duplicateSelected": "Duplica selezionati",
|
|
131
|
+
"duplicateLimited": "Solo {slots} di {count} possono essere duplicati (raggiunto maxItems)",
|
|
132
|
+
"moveToTop": "Sposta in cima",
|
|
133
|
+
"moveUp": "Sposta su",
|
|
134
|
+
"moveDown": "Sposta giù",
|
|
135
|
+
"position": "Posizione",
|
|
136
|
+
"editFullScreen": "Modifica a schermo intero",
|
|
137
|
+
"empty": "Nessun elemento. Clicca \"Aggiungi\" per iniziare."
|
|
138
|
+
},
|
|
139
|
+
"seoPreview": {
|
|
140
|
+
"seoTitle": "Titolo SEO",
|
|
141
|
+
"metaDescription": "Meta descrizione",
|
|
142
|
+
"googlePreview": "Anteprima Google",
|
|
143
|
+
"titlePlaceholder": "Titolo della pagina...",
|
|
144
|
+
"descriptionPlaceholder": "Descrizione della pagina...",
|
|
145
|
+
"defaultTitle": "Titolo della pagina",
|
|
146
|
+
"defaultDescription": "Descrizione della pagina che apparirà nei risultati di ricerca di Google."
|
|
147
|
+
},
|
|
148
|
+
"wygEditor": {
|
|
149
|
+
"insertImage": "Inserisci immagine",
|
|
150
|
+
"requiresUikit": "Richiede {'@'}mapomodule/uikit"
|
|
151
|
+
},
|
|
152
|
+
"listActions": {
|
|
153
|
+
"bulkActions": "Azioni di gruppo...",
|
|
154
|
+
"permanentDelete": "Eliminazione permanente",
|
|
155
|
+
"confirmBulk": "Questa azione verrà applicata a tutti gli elementi. Vuoi continuare?",
|
|
156
|
+
"confirmBulkCount": "Questa azione verrà applicata a {count} elementi. Vuoi continuare?",
|
|
157
|
+
"actionCompleted": "Azione completata",
|
|
158
|
+
"actionFailed": "Azione non riuscita"
|
|
159
|
+
},
|
|
160
|
+
"listFilters": {
|
|
161
|
+
"filters": "Filtri",
|
|
162
|
+
"clearAll": "Rimuovi tutti"
|
|
163
|
+
},
|
|
164
|
+
"listQuickEdit": {
|
|
165
|
+
"newItem": "Nuovo elemento"
|
|
166
|
+
},
|
|
167
|
+
"listTable": {
|
|
168
|
+
"deleteItemTitle": "Elimina elemento",
|
|
169
|
+
"itemDeleted": "Elemento eliminato",
|
|
170
|
+
"deleteItemError": "Eliminazione elemento non riuscita",
|
|
171
|
+
"orderSaved": "Ordinamento salvato",
|
|
172
|
+
"orderError": "Riordinamento non riuscito",
|
|
173
|
+
"selectAllRows": "Seleziona tutte le righe",
|
|
174
|
+
"selectRow": "Seleziona riga",
|
|
175
|
+
"dragToReorder": "Trascina per riordinare",
|
|
176
|
+
"clearToReorder": "Svuota ricerca e ordinamento per riordinare",
|
|
177
|
+
"noItems": "Nessun elemento trovato",
|
|
178
|
+
"totalItems": "{total} elementi",
|
|
179
|
+
"perPage": "{n} / pagina"
|
|
180
|
+
},
|
|
181
|
+
"menuManager": {
|
|
182
|
+
"menu": "Menu",
|
|
183
|
+
"noSelectedNode": "Seleziona un nodo da modificare o creane uno nuovo."
|
|
184
|
+
},
|
|
185
|
+
"menuTreeview": {
|
|
186
|
+
"newNode": "Crea nodo",
|
|
187
|
+
"noRootNodes": "Non c'è nessun nodo qui. Creane uno nuovo!",
|
|
188
|
+
"deleteTitle": "Elimina nodo",
|
|
189
|
+
"areYouSureDelete": "Sei sicuro di voler eliminare questo nodo e tutti i suoi discendenti?",
|
|
190
|
+
"maxDepthReached": "Raggiunta la profondità massima di annidamento ({max} livelli)"
|
|
191
|
+
},
|
|
192
|
+
"menuNodeEditor": {
|
|
193
|
+
"editNode": "Nodo",
|
|
194
|
+
"title": "Titolo",
|
|
195
|
+
"linkTypeLabel": "Tipo di link",
|
|
196
|
+
"styleLabel": "Stile",
|
|
197
|
+
"openInNewTabLabel": "Apri in una nuova finestra",
|
|
198
|
+
"relContentTypeLabel": "Tipo di pagina",
|
|
199
|
+
"relPageIdLabel": "Pagina",
|
|
200
|
+
"staticLabel": "URL statico",
|
|
201
|
+
"relational": "Relazionale",
|
|
202
|
+
"static": "Statico",
|
|
203
|
+
"nodeErrors": "Questo nodo contiene errori"
|
|
204
|
+
},
|
|
205
|
+
"mediaEditor": {
|
|
206
|
+
"details": "Dettagli",
|
|
207
|
+
"defaultLang": "Predefinita",
|
|
208
|
+
"openInNewTab": "Apri in una nuova scheda",
|
|
209
|
+
"confirmDelete": "Vuoi veramente eliminare \"{name}\"?"
|
|
210
|
+
},
|
|
211
|
+
"mediaFileChanger": {
|
|
212
|
+
"replaceFile": "Sostituisci file",
|
|
213
|
+
"dragOrClick": "Trascina o clicca per scegliere un file",
|
|
214
|
+
"keepUrl": "Mantieni URL attuale"
|
|
215
|
+
},
|
|
216
|
+
"mediaFolders": {
|
|
217
|
+
"folders": "Cartelle",
|
|
218
|
+
"goBack": "Torna alla radice",
|
|
219
|
+
"noFolders": "Non sono presenti cartelle... Creane una!",
|
|
220
|
+
"newFolder": "Nuova cartella",
|
|
221
|
+
"folderName": "Nome cartella",
|
|
222
|
+
"allFolder": "Tutti",
|
|
223
|
+
"imageFolder": "Immagini",
|
|
224
|
+
"videoFolder": "Video",
|
|
225
|
+
"audioFolder": "Audio",
|
|
226
|
+
"docFolder": "Documenti",
|
|
227
|
+
"deleteTitle": "Elimina cartella",
|
|
228
|
+
"confirmDelete": "Eliminare la cartella \"{name}\"? I media al suo interno non verranno eliminati."
|
|
229
|
+
},
|
|
230
|
+
"mediaGallery": {
|
|
231
|
+
"noMediaFound": "Nessun media trovato",
|
|
232
|
+
"selectAll": "Seleziona pagina",
|
|
233
|
+
"deselectAll": "Deseleziona pagina",
|
|
234
|
+
"nSelected": "{number} selezionati",
|
|
235
|
+
"deleteTitle": "Elimina media selezionati",
|
|
236
|
+
"deleteSelectedLabel": "Elimina selezionati",
|
|
237
|
+
"deleteSelected": "Stai per eliminare {number} elementi. Vuoi continuare?",
|
|
238
|
+
"deleteSuccess": "{number} elementi eliminati!"
|
|
239
|
+
},
|
|
240
|
+
"mediaManager": {
|
|
241
|
+
"root": "Media",
|
|
242
|
+
"gallery": "Galleria",
|
|
243
|
+
"uploader": "Caricamento",
|
|
244
|
+
"fileInfo": "Media aggiornato",
|
|
245
|
+
"deleteTitle": "Elimina media",
|
|
246
|
+
"selectMedia": "Seleziona media",
|
|
247
|
+
"selectAMedia": "Seleziona un media"
|
|
248
|
+
},
|
|
249
|
+
"mediaUploader": {
|
|
250
|
+
"dragHere": "Trascina qui i file oppure",
|
|
251
|
+
"browse": "sfoglia",
|
|
252
|
+
"limits": "Immagini fino a {img} MB · Video fino a {vid} MB",
|
|
253
|
+
"nSelected": "{n} file selezionato | {n} file selezionati",
|
|
254
|
+
"uploadCta": "Carica {n} file | Carica {n} file",
|
|
255
|
+
"uploaded": "Caricato",
|
|
256
|
+
"uploadFailed": "Caricamento non riuscito",
|
|
257
|
+
"fileTooLarge": "File \"{name}\" troppo grande (max {max} MB)",
|
|
258
|
+
"success": "{numberFiles} file caricati con successo"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NuxtModule } from '@nuxt/schema'
|
|
2
|
+
|
|
3
|
+
import type { default as Module } from './module.mjs'
|
|
4
|
+
|
|
5
|
+
export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
|
6
|
+
|
|
7
|
+
export { default } from './module.mjs'
|
|
8
|
+
|
|
9
|
+
export { type MapoI18nOptions, type MapoLocale } from './module.mjs'
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mapomodule/i18n",
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
|
+
"description": "Mapo i18n wrapper: @nuxtjs/i18n v9 + base translations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Lotrek Agency (https://lotrek.it)",
|
|
8
|
+
"homepage": "https://lotrekagency.github.io/mapo-new/",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/lotrekagency/mapo-new.git",
|
|
12
|
+
"directory": "packages/@mapomodule/i18n"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/lotrekagency/mapo-new/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"tag": "next"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/module.d.mts",
|
|
24
|
+
"import": "./dist/module.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./types": {
|
|
27
|
+
"types": "./dist/index.d.mts",
|
|
28
|
+
"import": "./dist/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./runtime/*": {
|
|
31
|
+
"types": "./src/runtime/*.ts",
|
|
32
|
+
"import": "./dist/runtime/*.js",
|
|
33
|
+
"default": "./src/runtime/*"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"dev": "nuxt-module-build build --stub --watch",
|
|
42
|
+
"build": "nuxt-module-build build && tsc -p tsconfig.build-types.json",
|
|
43
|
+
"dev:prepare": "nuxt-module-build build --stub",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"lint": "eslint src"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@nuxtjs/i18n": "^10.4.1",
|
|
49
|
+
"defu": "^6.1.4"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"nuxt": ">=4.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@nuxt/kit": "4.4.2",
|
|
56
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
57
|
+
"@nuxt/schema": "4.4.2",
|
|
58
|
+
"nuxt": "^4.5.1",
|
|
59
|
+
"vue": "^3.5.40"
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Re-export types for direct TypeScript access outside Nuxt context
|
|
2
|
+
import en from "./runtime/locales/mapo.en.json";
|
|
3
|
+
|
|
4
|
+
export type { MapoI18nOptions, MapoLocale } from "./module";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shape of the Mapo built-in message catalog (English is the source of
|
|
8
|
+
* truth — every locale ships the same keys).
|
|
9
|
+
*/
|
|
10
|
+
export type MapoMessages = typeof en;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Recursively builds the union of dot-separated key paths of an object type.
|
|
14
|
+
* `{ mapo: { save: string } }` → `"mapo" | "mapo.save"`.
|
|
15
|
+
*/
|
|
16
|
+
type DotPaths<T, Prefix extends string = ""> = {
|
|
17
|
+
[K in keyof T & string]: T[K] extends Record<string, unknown>
|
|
18
|
+
? `${Prefix}${K}` | DotPaths<T[K], `${Prefix}${K}.`>
|
|
19
|
+
: `${Prefix}${K}`;
|
|
20
|
+
}[keyof T & string];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Union of every valid Mapo translation key
|
|
24
|
+
* (e.g. `"mapo.save"`, `"mapo.menuManager.noSelectedNode"`).
|
|
25
|
+
*
|
|
26
|
+
* Useful to type helper wrappers in consuming apps:
|
|
27
|
+
* ```ts
|
|
28
|
+
* import type { MapoI18nKey } from "@mapomodule/i18n/types";
|
|
29
|
+
* const key: MapoI18nKey = "mapo.save";
|
|
30
|
+
* t(key);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export type MapoI18nKey = DotPaths<MapoMessages>;
|
package/src/module.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineNuxtModule,
|
|
3
|
+
hasNuxtModule,
|
|
4
|
+
installModule,
|
|
5
|
+
createResolver,
|
|
6
|
+
addImports,
|
|
7
|
+
addTemplate,
|
|
8
|
+
} from "@nuxt/kit";
|
|
9
|
+
import type { NuxtModule } from "@nuxt/schema";
|
|
10
|
+
import { defu } from "defu";
|
|
11
|
+
// Side-effect type import: augments NuxtHooks with `i18n:registerModule`.
|
|
12
|
+
import type {} from "@nuxtjs/i18n";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Locale descriptor accepted by `mapoI18n.locales`.
|
|
16
|
+
* Mirrors the shape of `@nuxtjs/i18n` LocaleObject without importing its types
|
|
17
|
+
* (they are only available once the module is installed in the app).
|
|
18
|
+
*/
|
|
19
|
+
export interface MapoLocale {
|
|
20
|
+
/** Locale code used in URLs/cookies and by `setLocale` (e.g. `"en"`). */
|
|
21
|
+
code: string;
|
|
22
|
+
/** BCP-47 language tag used for SEO/browser matching (e.g. `"en-US"`). */
|
|
23
|
+
language?: string;
|
|
24
|
+
/** Human readable name shown by `<MapoLangSwitcher>`. */
|
|
25
|
+
name?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Translation file(s) resolved from the app's `i18n/locales/` directory.
|
|
28
|
+
* Only needed when the app provides its own messages for this locale.
|
|
29
|
+
*/
|
|
30
|
+
file?: string;
|
|
31
|
+
/** Multiple translation files merged in order. */
|
|
32
|
+
files?: string[];
|
|
33
|
+
/** Text direction. */
|
|
34
|
+
dir?: "ltr" | "rtl" | "auto";
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MapoI18nOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Default UI locale. Also used as `fallbackLocale` for missing keys.
|
|
41
|
+
* @default "en"
|
|
42
|
+
*/
|
|
43
|
+
defaultLocale?: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Locales available in the app. Defaults to English + Italian (the two
|
|
47
|
+
* languages Mapo ships translations for). Add entries with `file`/`files`
|
|
48
|
+
* to bring your own languages — Mapo strings fall back to `defaultLocale`
|
|
49
|
+
* for locales it doesn't cover.
|
|
50
|
+
*/
|
|
51
|
+
locales?: MapoLocale[];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Detect the browser language on first visit and persist the choice in a
|
|
55
|
+
* cookie. Set `false` to always start from `defaultLocale`.
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
detectBrowserLanguage?: boolean;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extra options forwarded verbatim to `@nuxtjs/i18n` (they win over the
|
|
62
|
+
* Mapo defaults). Use this as an escape hatch for advanced setups —
|
|
63
|
+
* e.g. `strategy`, `customRoutes`, `vueI18n` config path…
|
|
64
|
+
* @see https://i18n.nuxtjs.org/docs/api/options
|
|
65
|
+
*/
|
|
66
|
+
i18n?: Record<string, unknown>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const MAPO_LOCALES = [
|
|
70
|
+
{ code: "en", language: "en-US", name: "English" },
|
|
71
|
+
{ code: "it", language: "it-IT", name: "Italiano" },
|
|
72
|
+
] satisfies MapoLocale[];
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Nuxt module that wires `@nuxtjs/i18n` into a Mapo app:
|
|
76
|
+
*
|
|
77
|
+
* - registers Mapo's built-in UI translations (`en`, `it`) under the `mapo.*`
|
|
78
|
+
* key namespace via the `i18n:registerModule` hook;
|
|
79
|
+
* - installs `@nuxtjs/i18n` with admin-friendly defaults (`no_prefix`
|
|
80
|
+
* strategy, browser detection, cookie persistence) unless the app already
|
|
81
|
+
* declares it in `modules[]` — in that case the app's configuration wins
|
|
82
|
+
* and this module only contributes the Mapo message catalogs.
|
|
83
|
+
*
|
|
84
|
+
* Apps override single strings by shipping the same key in their own locale
|
|
85
|
+
* files (`i18n/locales/<code>.json`): project-level messages are merged on
|
|
86
|
+
* top of module-registered ones.
|
|
87
|
+
*/
|
|
88
|
+
export default defineNuxtModule<MapoI18nOptions>({
|
|
89
|
+
meta: {
|
|
90
|
+
name: "@mapomodule/i18n",
|
|
91
|
+
configKey: "mapoI18n",
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
defaults: {
|
|
95
|
+
defaultLocale: "en",
|
|
96
|
+
detectBrowserLanguage: true,
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async setup(options, nuxt) {
|
|
100
|
+
const resolver = createResolver(import.meta.url);
|
|
101
|
+
|
|
102
|
+
// Auto-import the translator so consuming apps never have to import it from
|
|
103
|
+
// `@mapomodule/i18n/runtime/*`: with pnpm's isolated layout that subpath is
|
|
104
|
+
// only resolvable when the app lists this package in its own dependencies,
|
|
105
|
+
// and apps install it transitively through `mapomodule`.
|
|
106
|
+
// Registered before the early return below so it also applies when the app
|
|
107
|
+
// manages @nuxtjs/i18n itself.
|
|
108
|
+
addImports([
|
|
109
|
+
{
|
|
110
|
+
name: "useMapoT",
|
|
111
|
+
from: resolver.resolve("./runtime/composables/useMapoT"),
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
// Register the hook BEFORE installing @nuxtjs/i18n so the catalogs are
|
|
116
|
+
// picked up regardless of module ordering in the consuming app.
|
|
117
|
+
// The catalog files are named `mapo.<code>.json`, not `<code>.json`:
|
|
118
|
+
// @nuxtjs/i18n keys its locale loaders by file basename, so a Mapo catalog
|
|
119
|
+
// called `en.json` would collide with the app's own `i18n/locales/en.json`
|
|
120
|
+
// and only one of the two would survive — silently dropping the consumer's
|
|
121
|
+
// overrides.
|
|
122
|
+
nuxt.hook("i18n:registerModule", (register) => {
|
|
123
|
+
register({
|
|
124
|
+
langDir: resolver.resolve("./runtime/locales"),
|
|
125
|
+
locales: [
|
|
126
|
+
{ code: "en", language: "en-US", file: "mapo.en.json" },
|
|
127
|
+
{ code: "it", language: "it-IT", file: "mapo.it.json" },
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (hasNuxtModule("@nuxtjs/i18n")) {
|
|
133
|
+
// The app manages @nuxtjs/i18n itself — don't fight its configuration.
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Generated vue-i18n config: `fallbackLocale` follows `defaultLocale` so
|
|
138
|
+
// locales without Mapo catalogs (e.g. a consumer-added "fr") still render
|
|
139
|
+
// Mapo strings instead of raw keys.
|
|
140
|
+
const vueI18nConfig = addTemplate({
|
|
141
|
+
filename: "mapo-i18n.config.mjs",
|
|
142
|
+
write: true,
|
|
143
|
+
getContents: () =>
|
|
144
|
+
`export default () => ({\n` +
|
|
145
|
+
` legacy: false,\n` +
|
|
146
|
+
` fallbackLocale: ${JSON.stringify(options.defaultLocale ?? "en")},\n` +
|
|
147
|
+
`});\n`,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const i18nConfig = defu(options.i18n ?? {}, {
|
|
151
|
+
strategy: "no_prefix",
|
|
152
|
+
defaultLocale: options.defaultLocale ?? "en",
|
|
153
|
+
locales: options.locales ?? MAPO_LOCALES,
|
|
154
|
+
detectBrowserLanguage: options.detectBrowserLanguage
|
|
155
|
+
? {
|
|
156
|
+
useCookie: true,
|
|
157
|
+
cookieKey: "i18n_redirected",
|
|
158
|
+
redirectOn: "root",
|
|
159
|
+
}
|
|
160
|
+
: false,
|
|
161
|
+
vueI18n: vueI18nConfig.dst,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// The configuration has to land on the LAYER config, not just on
|
|
165
|
+
// `nuxt.options.i18n`: @nuxtjs/i18n discovers per-locale translation files
|
|
166
|
+
// by walking `nuxt.options._layers[].config.i18n` (see `getLayerI18n`),
|
|
167
|
+
// which is the raw nuxt.config object and ignores anything a module writes
|
|
168
|
+
// to `nuxt.options` at setup time. Writing only to `nuxt.options.i18n`
|
|
169
|
+
// configures the module but silently drops every `file` declared by the
|
|
170
|
+
// consuming app — so `mapo.i18n.locales` overrides would never load.
|
|
171
|
+
// Both are set, and whatever the app declared itself always wins.
|
|
172
|
+
// `i18n` comes from @nuxtjs/i18n's own NuxtOptions augmentation, which is
|
|
173
|
+
// not resolvable here — hence the structural casts.
|
|
174
|
+
const nuxtOptions = nuxt.options as unknown as {
|
|
175
|
+
i18n?: Record<string, unknown>;
|
|
176
|
+
_layers?: Array<{ config: { i18n?: Record<string, unknown> } }>;
|
|
177
|
+
};
|
|
178
|
+
nuxtOptions.i18n = defu(nuxtOptions.i18n ?? {}, i18nConfig) as Record<
|
|
179
|
+
string,
|
|
180
|
+
unknown
|
|
181
|
+
>;
|
|
182
|
+
|
|
183
|
+
const rootLayer = nuxtOptions._layers?.[0];
|
|
184
|
+
if (rootLayer) {
|
|
185
|
+
rootLayer.config.i18n = defu(
|
|
186
|
+
rootLayer.config.i18n ?? {},
|
|
187
|
+
i18nConfig,
|
|
188
|
+
) as Record<string, unknown>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await installModule("@nuxtjs/i18n");
|
|
192
|
+
},
|
|
193
|
+
}) satisfies NuxtModule<MapoI18nOptions>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useNuxtApp } from "nuxt/app";
|
|
2
|
+
|
|
3
|
+
type TranslateFn = (key: string, named?: Record<string, unknown>) => string;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the global vue-i18n `t` function bound to the app's i18n instance.
|
|
7
|
+
*
|
|
8
|
+
* Use this in contexts where `useI18n()` is not available — Pinia store
|
|
9
|
+
* actions, composables invoked outside component `setup`, imperative
|
|
10
|
+
* snack/confirm messages. Inside components prefer `useI18n()` from
|
|
11
|
+
* `vue-i18n`, which is reactive to locale changes in render functions.
|
|
12
|
+
*/
|
|
13
|
+
export function useMapoT(): TranslateFn {
|
|
14
|
+
const { $i18n } = useNuxtApp() as unknown as {
|
|
15
|
+
$i18n?: { t: TranslateFn };
|
|
16
|
+
};
|
|
17
|
+
// Degrade to the raw key instead of throwing when no i18n instance is
|
|
18
|
+
// available (unit tests instantiating a store outside a Nuxt app).
|
|
19
|
+
if (!$i18n?.t) return (key) => key;
|
|
20
|
+
return (key, named) => (named ? $i18n.t(key, named) : $i18n.t(key));
|
|
21
|
+
}
|