@userfrosting/sprinkle-core 6.0.0-alpha.2 → 6.0.0-alpha.3

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.
@@ -1,5 +1,6 @@
1
1
  import { computed, ref, watch } from 'vue'
2
2
  import { useRoute } from 'vue-router'
3
+ import { useTranslator } from '@userfrosting/sprinkle-core/stores'
3
4
  import { useConfigStore } from '../stores'
4
5
  import { defineStore } from 'pinia'
5
6
 
@@ -17,6 +18,7 @@ export const usePageMeta = defineStore('pageMeta', () => {
17
18
  * Globally provided properties
18
19
  */
19
20
  const route = useRoute()
21
+ const { translate } = useTranslator()
20
22
 
21
23
  /**
22
24
  * States
@@ -68,7 +70,7 @@ export const usePageMeta = defineStore('pageMeta', () => {
68
70
 
69
71
  // Update Page Title & Description with current route
70
72
  title.value = route.meta.title || ''
71
- description.value = route.meta.description || ''
73
+ description.value = translate(route.meta.description || '')
72
74
  }
73
75
 
74
76
  // Update the document title
@@ -90,7 +92,7 @@ export const usePageMeta = defineStore('pageMeta', () => {
90
92
  */
91
93
  const siteTitle = computed<string>(() => useConfigStore().get('site.title') || '')
92
94
  const pageFullTitle = computed<string>(() => {
93
- return title.value ? title.value + ' | ' + siteTitle.value : siteTitle.value
95
+ return title.value ? translate(title.value) + ' | ' + siteTitle.value : siteTitle.value
94
96
  })
95
97
 
96
98
  /**
@@ -0,0 +1,9 @@
1
+ export {}
2
+
3
+ declare module 'vue' {
4
+ interface ComponentCustomProperties {
5
+ // TODO : Use interface from sprinkle-core
6
+ $t: (key: string, placeholders?: string | number | object) => string
7
+ $tdate: (date: string, format?: string | object) => string
8
+ }
9
+ }
@@ -1,8 +1,19 @@
1
- import { useConfigStore } from './stores'
1
+ import type { App } from 'vue'
2
+ import { useConfigStore, useTranslator } from './stores'
2
3
 
4
+ /**
5
+ * Core Sprinkle initialization recipe.
6
+ *
7
+ * This recipe is responsible for loading the configuration from the api.
8
+ */
3
9
  export default {
4
- install: () => {
5
- const config = useConfigStore()
6
- config.load()
10
+ install: (app: App) => {
11
+ useConfigStore().load()
12
+
13
+ // Load translations & add $t to global properties
14
+ const { translate, translateDate, load } = useTranslator()
15
+ load()
16
+ app.config.globalProperties.$t = translate
17
+ app.config.globalProperties.$tdate = translateDate
7
18
  }
8
19
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Api Interface - What the API expects and what it returns
3
+ *
4
+ * This interface is tied to the `DictionaryController` API, accessed at the
5
+ * GET `/api/dictionary` endpoint.
6
+ *
7
+ * This api doesn't have a corresponding Request data interface.
8
+ */
9
+ export interface DictionaryResponse {
10
+ identifier: string
11
+ config: DictionaryConfig
12
+ dictionary: DictionaryEntries
13
+ }
14
+
15
+ export interface DictionaryEntries {
16
+ [key: string]: string
17
+ }
18
+
19
+ export interface DictionaryConfig {
20
+ name: string
21
+ regional: string
22
+ authors: string[]
23
+ plural_rule: number
24
+ dates: string
25
+ }
@@ -24,6 +24,7 @@ export type { AssociativeArray } from './common'
24
24
  export { Severity } from './severity'
25
25
  export type { Sprunjer, SprunjerData, SprunjerListable, SprunjerListableOption } from './sprunjer'
26
26
  export type { SprunjerRequest, SprunjerResponse } from './sprunjerApi'
27
+ export type { DictionaryResponse, DictionaryEntries, DictionaryConfig } from './DictionaryApi'
27
28
 
28
29
  // Misc
29
30
  export type { ApiResponse } from './ApiResponse'
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Families: Asian (Chinese, Japanese, Korean, Vietnamese), Persian, Turkic/Altaic (Turkish), Thai, Lao
3
+ * 1 - everything: 0, 1, 2, ...
4
+ */
5
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
+ export const rule0 = (number: number): number => {
7
+ return 1
8
+ }
9
+
10
+ /**
11
+ * Families: Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish),
12
+ * Finno-Ugric (Estonian, Finnish, Hungarian), Language isolate (Basque),
13
+ * Latin/Greek (Greek), Semitic (Hebrew), Romanic (Italian, Portuguese, Spanish, Catalan)
14
+ * 1 - 1
15
+ * 2 - everything else: 0, 2, 3, ...
16
+ */
17
+ export const rule1 = (number: number): number => {
18
+ return number === 1 ? 1 : 2
19
+ }
20
+
21
+ /**
22
+ * Families: Romanic (French, Brazilian Portuguese)
23
+ * 1 - 0, 1
24
+ * 2 - everything else: 2, 3, ...
25
+ */
26
+ export const rule2 = (number: number): number => {
27
+ return number === 0 || number === 1 ? 1 : 2
28
+ }
29
+
30
+ /**
31
+ * Families: Baltic (Latvian)
32
+ * 1 - 0
33
+ * 2 - ends in 1, not 11: 1, 21, ... 101, 121, ...
34
+ * 3 - everything else: 2, 3, ... 10, 11, 12, ... 20, 22, ...
35
+ */
36
+ export const rule3 = (number: number): number => {
37
+ return number === 0 ? 1 : number % 10 == 1 && number % 100 != 11 ? 2 : 3
38
+ }
39
+
40
+ /**
41
+ * Families: Celtic (Scottish Gaelic)
42
+ * 1 - is 1 or 11: 1, 11
43
+ * 2 - is 2 or 12: 2, 12
44
+ * 3 - others between 3 and 19: 3, 4, ... 10, 13, ... 18, 19
45
+ * 4 - everything else: 0, 20, 21, ...
46
+ */
47
+ export const rule4 = (number: number): number => {
48
+ return number === 1 || number == 11
49
+ ? 1
50
+ : number === 2 || number === 12
51
+ ? 2
52
+ : number >= 3 && number <= 19
53
+ ? 3
54
+ : 4
55
+ }
56
+
57
+ /**
58
+ * Families: Romanic (Romanian)
59
+ * 1 - 1
60
+ * 2 - is 0 or ends in 01-19: 0, 2, 3, ... 19, 101, 102, ... 119, 201, ...
61
+ * 3 - everything else: 20, 21, ...
62
+ */
63
+ export const rule5 = (number: number): number => {
64
+ return number === 1 ? 1 : number === 0 || (number % 100 > 0 && number % 100 < 20) ? 2 : 3
65
+ }
66
+
67
+ /**
68
+ * Families: Baltic (Lithuanian)
69
+ * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ...
70
+ * 2 - ends in 0 or ends in 10-20: 0, 10, 11, 12, ... 19, 20, 30, 40, ...
71
+ * 3 - everything else: 2, 3, ... 8, 9, 22, 23, ... 29, 32, 33, ...
72
+ */
73
+ export const rule6 = (number: number): number => {
74
+ return number % 10 === 1 && number % 100 !== 11
75
+ ? 1
76
+ : number % 10 < 2 || (number % 100 >= 10 && number % 100 < 20)
77
+ ? 2
78
+ : 3
79
+ }
80
+
81
+ /**
82
+ * Families: Slavic (Croatian, Serbian, Russian, Ukrainian)
83
+ * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ...
84
+ * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ...
85
+ * 3 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, ...
86
+ */
87
+ export const rule7 = (number: number): number => {
88
+ return number % 10 === 1 && number % 100 !== 11
89
+ ? 1
90
+ : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)
91
+ ? 2
92
+ : 3
93
+ }
94
+
95
+ /**
96
+ * Families: Slavic (Slovak, Czech)
97
+ * 1 - 1
98
+ * 2 - 2, 3, 4
99
+ * 3 - everything else: 0, 5, 6, 7, ...
100
+ */
101
+ export const rule8 = (number: number): number => {
102
+ return number === 1 ? 1 : number >= 2 && number <= 4 ? 2 : 3
103
+ }
104
+
105
+ /**
106
+ * Families: Slavic (Polish)
107
+ * 1 - 1
108
+ * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ... 104, 122, ...
109
+ * 3 - everything else: 0, 5, 6, ... 11, 12, 13, 14, 15, ... 20, 21, 25, ...
110
+ */
111
+ export const rule9 = (number: number): number => {
112
+ return number === 1
113
+ ? 1
114
+ : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14)
115
+ ? 2
116
+ : 3
117
+ }
118
+
119
+ /**
120
+ * Families: Slavic (Slovenian, Sorbian)
121
+ * 1 - ends in 01: 1, 101, 201, ...
122
+ * 2 - ends in 02: 2, 102, 202, ...
123
+ * 3 - ends in 03-04: 3, 4, 103, 104, 203, 204, ...
124
+ * 4 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, ...
125
+ */
126
+ export const rule10 = (number: number): number => {
127
+ return number % 100 === 1
128
+ ? 1
129
+ : number % 100 === 2
130
+ ? 2
131
+ : number % 100 === 3 || number % 100 === 4
132
+ ? 3
133
+ : 4
134
+ }
135
+
136
+ /**
137
+ * Families: Celtic (Irish Gaeilge)
138
+ * 1 - 1
139
+ * 2 - 2
140
+ * 3 - is 3-6: 3, 4, 5, 6
141
+ * 4 - is 7-10: 7, 8, 9, 10
142
+ * 5 - everything else: 0, 11, 12, ...
143
+ */
144
+ export const rule11 = (number: number): number => {
145
+ return number === 1
146
+ ? 1
147
+ : number === 2
148
+ ? 2
149
+ : number >= 3 && number <= 6
150
+ ? 3
151
+ : number >= 7 && number <= 10
152
+ ? 4
153
+ : 5
154
+ }
155
+
156
+ /**
157
+ * Families: Semitic (Arabic).
158
+ *
159
+ * 1 - 1
160
+ * 2 - 2
161
+ * 3 - ends in 03-10: 3, 4, ... 10, 103, 104, ... 110, 203, 204, ...
162
+ * 4 - ends in 11-99: 11, ... 99, 111, 112, ...
163
+ * 5 - everything else: 100, 101, 102, 200, 201, 202, ...
164
+ * 6 - 0
165
+ */
166
+ export const rule12 = (number: number): number => {
167
+ return number === 1
168
+ ? 1
169
+ : number === 2
170
+ ? 2
171
+ : number % 100 >= 3 && number % 100 <= 10
172
+ ? 3
173
+ : number % 100 >= 11
174
+ ? 4
175
+ : number != 0
176
+ ? 5
177
+ : 6
178
+ }
179
+
180
+ /**
181
+ * Families: Semitic (Maltese)
182
+ * 1 - 1
183
+ * 2 - is 0 or ends in 01-10: 0, 2, 3, ... 9, 10, 101, 102, ...
184
+ * 3 - ends in 11-19: 11, 12, ... 18, 19, 111, 112, ...
185
+ * 4 - everything else: 20, 21, ...
186
+ */
187
+ export const rule13 = (number: number): number => {
188
+ return number === 1
189
+ ? 1
190
+ : number === 0 || (number % 100 >= 1 && number % 100 < 11)
191
+ ? 2
192
+ : number % 100 > 10 && number % 100 < 20
193
+ ? 3
194
+ : 4
195
+ }
196
+
197
+ /**
198
+ * Families: Slavic (Macedonian)
199
+ * 1 - ends in 1: 1, 11, 21, ...
200
+ * 2 - ends in 2: 2, 12, 22, ...
201
+ * 3 - everything else: 0, 3, 4, ... 10, 13, 14, ... 20, 23, ...
202
+ */
203
+ export const rule14 = (number: number): number => {
204
+ return number % 10 === 1 ? 1 : number % 10 === 2 ? 2 : 3
205
+ }
206
+
207
+ /**
208
+ * Families: Icelandic
209
+ * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, 131, ...
210
+ * 2 - everything else: 0, 2, 3, ... 10, 11, 12, ... 20, 22, ...
211
+ */
212
+ export const rule15 = (number: number): number => {
213
+ return number % 10 === 1 && number % 100 != 11 ? 1 : 2
214
+ }
215
+
216
+ export interface PluralRules {
217
+ [key: number]: (pluralValue: number) => number
218
+ }
@@ -1 +1,2 @@
1
1
  export { useConfigStore } from './config'
2
+ export { useTranslator } from './useTranslator'
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Translator composable store.
3
+ *
4
+ * This pinia store is used to access the translator and to use the translator.
5
+ */
6
+ import { ref } from 'vue'
7
+ import { defineStore } from 'pinia'
8
+ import axios from 'axios'
9
+ import type { DictionaryEntries, DictionaryResponse, DictionaryConfig } from '../interfaces'
10
+ import { DateTime } from 'luxon'
11
+ import type { PluralRules } from './Helpers/PluralRules'
12
+ import {
13
+ rule0,
14
+ rule1,
15
+ rule2,
16
+ rule3,
17
+ rule4,
18
+ rule5,
19
+ rule6,
20
+ rule7,
21
+ rule8,
22
+ rule9,
23
+ rule10,
24
+ rule11,
25
+ rule12,
26
+ rule13,
27
+ rule14,
28
+ rule15
29
+ } from './Helpers/PluralRules'
30
+
31
+ // List all available plural rules
32
+ const rules: PluralRules = {
33
+ 0: rule0,
34
+ 1: rule1,
35
+ 2: rule2,
36
+ 3: rule3,
37
+ 4: rule4,
38
+ 5: rule5,
39
+ 6: rule6,
40
+ 7: rule7,
41
+ 8: rule8,
42
+ 9: rule9,
43
+ 10: rule10,
44
+ 11: rule11,
45
+ 12: rule12,
46
+ 13: rule13,
47
+ 14: rule14,
48
+ 15: rule15
49
+ }
50
+
51
+ export const useTranslator = defineStore(
52
+ 'translator',
53
+ () => {
54
+ /**
55
+ * Variables
56
+ */
57
+ const defaultPluralKey = 'plural'
58
+ const identifier = ref<string>('')
59
+ const dictionary = ref<DictionaryEntries>({})
60
+ const config = ref<DictionaryConfig>({
61
+ name: '',
62
+ regional: '',
63
+ authors: [],
64
+ plural_rule: 0,
65
+ dates: ''
66
+ })
67
+
68
+ /**
69
+ * Functions
70
+ */
71
+ // Load the dictionary from the API
72
+ async function load() {
73
+ axios.get<DictionaryResponse>('/api/dictionary').then((response) => {
74
+ identifier.value = response.data.identifier
75
+ config.value = response.data.config
76
+ dictionary.value = response.data.dictionary
77
+ })
78
+ }
79
+
80
+ // The translate function
81
+ function translate(key: string, placeholders: string | number | object = {}): string {
82
+ const { message, placeholders: mutatedPlaceholders } = getMessageFromKey(
83
+ key,
84
+ placeholders
85
+ )
86
+ placeholders = mutatedPlaceholders
87
+
88
+ return replacePlaceholders(message, placeholders)
89
+ }
90
+
91
+ /**
92
+ * Format a date to the user locale
93
+ *
94
+ * @param date The date to format, in ISO format
95
+ * @param format The format to use. Default to `DATETIME_MED_WITH_WEEKDAY`.
96
+ * See the Luxon documentation for more information on formatting
97
+ *
98
+ * @see https://moment.github.io/luxon/#/formatting?id=presets
99
+ * @see https://moment.github.io/luxon/#/formatting?id=table-of-tokens
100
+ */
101
+ function translateDate(
102
+ date: string,
103
+ format: string | object = DateTime.DATETIME_MED_WITH_WEEKDAY
104
+ ): string {
105
+ const dt = getDateTime(date)
106
+ if (typeof format === 'object') {
107
+ return dt.toLocaleString(format)
108
+ } else {
109
+ return dt.toFormat(format)
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Returns the Luxon DateTime object for the given date, with the user
115
+ * locale, so Luxon methods can be used without having to set the locale
116
+ *
117
+ * @param date The date to format, in ISO format
118
+ */
119
+ function getDateTime(date: string): DateTime {
120
+ return DateTime.fromISO(date).setLocale(config.value.dates)
121
+ }
122
+
123
+ // TODO : Add doc + make Placeholders a type
124
+ function getMessageFromKey(
125
+ key: string,
126
+ placeholders: string | number | Record<string, any>
127
+ ): { message: string; placeholders: string | number | Record<string, any> } {
128
+ // Return direct match
129
+ if (dictionary.value[key] !== undefined) {
130
+ return { message: dictionary.value[key], placeholders }
131
+ }
132
+
133
+ // First, let's see if we can get the plural rules.
134
+ // A plural form will always have priority over the `@TRANSLATION` instruction
135
+ // We start by picking up the plural key, aka which placeholder contains the numeric value defining how many {x} we have
136
+ const pluralKey = dictionary.value[key + '.@PLURAL'] || defaultPluralKey
137
+
138
+ // Let's get the plural value, aka how many {x} we have
139
+ // If no plural value was found, we fallback to `@TRANSLATION` instruction or default to 1 as a last resort
140
+ let pluralValue: number = 1
141
+ if (typeof placeholders === 'object' && placeholders[pluralKey] !== undefined) {
142
+ pluralValue = Number(placeholders[pluralKey])
143
+ } else if (typeof placeholders === 'number' || typeof placeholders === 'string') {
144
+ pluralValue = Number(placeholders)
145
+ } else if (dictionary.value[key + '.@TRANSLATION'] !== undefined) {
146
+ // We have a `@TRANSLATION` instruction, return this
147
+ return { message: dictionary.value[key + '.@TRANSLATION'], placeholders }
148
+ }
149
+
150
+ // If placeholders is a numeric value, we transform back to an array for replacement in the main message
151
+ if (typeof placeholders === 'number' || typeof placeholders === 'string') {
152
+ placeholders = { [pluralKey]: pluralValue }
153
+ } else if (typeof placeholders === 'object' && placeholders[pluralKey] === undefined) {
154
+ placeholders = { ...placeholders, [pluralKey]: pluralValue }
155
+ }
156
+
157
+ // At this point, we need to go deeper and find the correct plural form to use
158
+ const pluralRuleKey = getPluralMessageKey(key, pluralValue)
159
+
160
+ // Only return if the plural is not null. Will happen if the message array don't follow the rules
161
+ if (dictionary.value[key + '.' + pluralRuleKey] !== undefined) {
162
+ return { message: dictionary.value[key + '.' + pluralRuleKey], placeholders }
163
+ }
164
+
165
+ // One last check... If we don't have a rule, but the $pluralValue
166
+ // as a key does exist, we might still be able to return it
167
+ if (dictionary.value[key + '.' + pluralValue] !== undefined) {
168
+ return { message: dictionary.value[key + '.' + pluralValue], placeholders }
169
+ }
170
+
171
+ // Return @TRANSLATION match
172
+ if (dictionary.value[key + '.@TRANSLATION'] !== undefined) {
173
+ return { message: dictionary.value[key + '.@TRANSLATION'], placeholders }
174
+ }
175
+
176
+ // If the message is an array, but we can't find a plural form or a "@TRANSLATION" instruction, we can't go further.
177
+ // We can't return the array, so we'll return the key
178
+ return { message: key, placeholders }
179
+ }
180
+
181
+ function replacePlaceholders(
182
+ message: string,
183
+ placeholders: string | number | Record<string, any>
184
+ ): string {
185
+ // If placeholders is not an object at this point, we make it an object, using `plural` as the key
186
+ if (typeof placeholders !== 'object') {
187
+ placeholders = { [defaultPluralKey]: placeholders }
188
+ }
189
+
190
+ // Interpolate translatable placeholders values. This allows to
191
+ // pre-translate placeholder which value starts with the `&` character
192
+ // console.debug('Looping Placeholders', placeholders)
193
+ for (const [name, value] of Object.entries(placeholders)) {
194
+ // console.debug(`> ${name}: ${value}`)
195
+
196
+ //We don't allow nested placeholders. They will return errors on the next lines
197
+ if (typeof value !== 'string') {
198
+ continue
199
+ }
200
+
201
+ // We test if the placeholder value starts the "&" character.
202
+ // That means we need to translate that placeholder value
203
+ if (value.startsWith('&')) {
204
+ // Remove the current placeholder from the master $placeholder
205
+ // array, otherwise we end up in an infinite loop
206
+ const data = Object.fromEntries(
207
+ Object.entries(placeholders).filter(([k]) => k !== name)
208
+ )
209
+
210
+ // Translate placeholders value and place it in the main $placeholder array
211
+ placeholders[name] = translate(value.substring(1), data)
212
+ }
213
+ }
214
+
215
+ // We check for {{&...}} strings in the resulting message.
216
+ // While the previous loop pre-translated placeholder value, this one
217
+ // pre-translate the message string vars
218
+ // We use some regex magic to detect them !
219
+ message = message.replace(/{{&(([^}]+[^a-z]))}}/g, (match, p1) => {
220
+ return translate(p1, placeholders)
221
+ })
222
+
223
+ // Now it's time to replace the remaining placeholder.
224
+ for (const [name, value] of Object.entries(placeholders)) {
225
+ const regex = new RegExp(`{{${name}}}`, 'g')
226
+ message = message.replace(regex, String(value))
227
+ }
228
+
229
+ return message
230
+ }
231
+
232
+ /**
233
+ * Return the correct plural message form to use.
234
+ * When multiple plural form are available for a message, this method will return the correct oen to use based on the numeric value.
235
+ *
236
+ * @param int $pluralValue The numeric value used to select the correct message
237
+ *
238
+ * @return int|null Returns which key from $messageArray to use
239
+ */
240
+ function getPluralMessageKey(key: string, pluralValue: number): number | null {
241
+ // Bypass the rules for a value of "0". Instead of returning the
242
+ // correct plural form (>= 1), we force return the "0" form, which
243
+ // can used to display "0 users" as "No users".
244
+ if (pluralValue === 0 && dictionary.value[key + '.0'] !== undefined) {
245
+ return 0
246
+ }
247
+
248
+ // Get the correct plural form to use depending on the language
249
+ const pluralForm = getPluralForm(pluralValue)
250
+
251
+ // If the dictionary contains a string for this form, return the form
252
+ if (dictionary.value[key + '.' + pluralForm] !== undefined) {
253
+ return pluralForm
254
+ }
255
+
256
+ // If the key we need doesn't exist, use the previous available one, including the special "0" form
257
+ // This is a fallback to avoid errors when the dictionary is not complete
258
+ for (let i = pluralForm; i >= 0; i--) {
259
+ if (dictionary.value[key + '.' + i] !== undefined) {
260
+ return i
261
+ }
262
+ }
263
+
264
+ // If no key was found, null will be returned
265
+ return null
266
+ }
267
+
268
+ function getPluralForm(pluralValue: number, forceRule?: number): number {
269
+ const rule = forceRule ?? config.value.plural_rule
270
+
271
+ if (rule < 0 || rule >= Object.keys(rules).length) {
272
+ throw new Error(`The rule number ${rule} must be between 0 and 15`)
273
+ }
274
+
275
+ return rules[rule](pluralValue)
276
+ }
277
+
278
+ /**
279
+ * Return store
280
+ */
281
+ return {
282
+ dictionary,
283
+ load,
284
+ config,
285
+ identifier,
286
+ translate,
287
+ translateDate,
288
+ getPluralForm,
289
+ getDateTime
290
+ }
291
+ },
292
+ { persist: true }
293
+ )
@@ -1,17 +1,26 @@
1
1
  import { describe, expect, test, vi } from 'vitest'
2
+ import { createApp } from 'vue'
2
3
  import { useConfigStore } from '../stores/config'
3
4
  import plugin from '..'
4
5
  import * as Config from '../stores/config'
6
+ import * as Translator from '../stores/useTranslator'
5
7
 
6
8
  const mockConfigStore = {
7
9
  load: vi.fn()
8
10
  }
9
11
 
12
+ const mockTranslatorStore = {
13
+ load: vi.fn()
14
+ }
15
+
10
16
  describe('Plugin', () => {
11
17
  test('should install the plugin and initiate load', () => {
18
+ const app = createApp({})
19
+
12
20
  vi.spyOn(Config, 'useConfigStore').mockReturnValue(mockConfigStore as any)
21
+ vi.spyOn(Translator, 'useTranslator').mockReturnValue(mockTranslatorStore as any)
13
22
 
14
- plugin.install()
23
+ plugin.install(app)
15
24
 
16
25
  expect(useConfigStore).toHaveBeenCalled()
17
26
  expect(mockConfigStore.load).toHaveBeenCalled()