@dialpad/i18n 1.16.0 → 1.20.4
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/.rush/temp/chunked-rush-logs/i18n.build.chunks.jsonl +22 -0
- package/.rush/temp/package-deps_build.json +25 -0
- package/.rush/temp/shrinkwrap-deps.json +10 -0
- package/CHANGELOG.json +34 -0
- package/CHANGELOG.md +9 -27
- package/dialpad-i18n-1.20.1.tgz +0 -0
- package/dialpad-i18n-1.20.2.tgz +0 -0
- package/dialpad-i18n-1.20.3.tgz +0 -0
- package/dist/i18n.cjs +6905 -49
- package/dist/i18n.cjs.map +1 -1
- package/dist/i18n.js +6896 -34
- package/dist/i18n.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +2 -0
- package/dist/types/src/locale-manager.d.ts +57 -0
- package/dist/types/src/locale-manager.js +138 -0
- package/package/package.json +51 -0
- package/package.json +30 -28
- package/rush-logs/i18n.build.error.log +0 -0
- package/rush-logs/i18n.build.log +22 -0
- package/src/__test__/locale-manager.find.test.ts +78 -0
- package/src/__test__/locale-manager.formatters.test.ts +1 -1
- package/src/__test__/locale-manager.multiple.test.ts +349 -0
- package/src/__test__/locale-manager.test.ts +2 -2
- package/src/locale-manager.ts +102 -43
- package/vite.config.ts +1 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { RawBundleSource } from '@dialpad/i18n-services/bundle-source';
|
|
2
|
+
import type { LocaleManagerParams } from '@dialpad/i18n-services/locale-manager';
|
|
3
|
+
import { MemoryStorageWrapper } from '@dialpad/i18n-services/storage';
|
|
4
|
+
|
|
5
|
+
import { expect, describe, it, beforeEach, vi } from 'vitest';
|
|
6
|
+
import { INJECTION_KEY_PREFIX, LocaleManager } from '../locale-manager';
|
|
7
|
+
import type { App } from 'vue';
|
|
8
|
+
|
|
9
|
+
const EN_US = 'en-US';
|
|
10
|
+
const ES_LA = 'es-LA';
|
|
11
|
+
|
|
12
|
+
function createTestBundleSources() {
|
|
13
|
+
const PowerdialerBundle = new RawBundleSource({
|
|
14
|
+
resources: [
|
|
15
|
+
[
|
|
16
|
+
EN_US,
|
|
17
|
+
'app-core',
|
|
18
|
+
`PAGE_TITLE_CAMPAIGNS = Campaigns
|
|
19
|
+
LEAD_PHONE_NUM_LABEL = Phone Number
|
|
20
|
+
CREATE_LEAD_BUTTON = Create Lead`,
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
ES_LA,
|
|
24
|
+
'app-core',
|
|
25
|
+
`PAGE_TITLE_CAMPAIGNS = Campañas
|
|
26
|
+
LEAD_PHONE_NUM_LABEL = Número de Teléfono
|
|
27
|
+
CREATE_LEAD_BUTTON = Crear Cliente Potencial`,
|
|
28
|
+
],
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Admin bundle source
|
|
33
|
+
const adminBundle = new RawBundleSource({
|
|
34
|
+
resources: [
|
|
35
|
+
[
|
|
36
|
+
EN_US,
|
|
37
|
+
'admin-test',
|
|
38
|
+
`ADMIN_DASHBOARD = Admin Dashboard
|
|
39
|
+
ADMIN_USERS = Manage Users
|
|
40
|
+
ADMIN_REPORTS = Admin Reports
|
|
41
|
+
BUTTON_SAVE = Admin Save
|
|
42
|
+
test-admin-only = This key ONLY exists in admin bundle
|
|
43
|
+
|
|
44
|
+
# Same key as Powerdialer but different value
|
|
45
|
+
PAGE_TITLE_CAMPAIGNS = Admin Campaigns View`,
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
ES_LA,
|
|
49
|
+
'admin-test',
|
|
50
|
+
`ADMIN_DASHBOARD = Panel de Administración
|
|
51
|
+
ADMIN_USERS = Gestionar Usuarios
|
|
52
|
+
ADMIN_REPORTS = Reportes de Admin
|
|
53
|
+
BUTTON_SAVE = Guardar Admin
|
|
54
|
+
test-admin-only = Esta clave SOLO existe en el bundle de admin
|
|
55
|
+
|
|
56
|
+
# Same key as Powerdialer but different value
|
|
57
|
+
PAGE_TITLE_CAMPAIGNS = Vista de Campañas Admin`,
|
|
58
|
+
],
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return { PowerdialerBundle, adminBundle };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createTestManager(
|
|
66
|
+
bundleSource: RawBundleSource,
|
|
67
|
+
namespaces: string[],
|
|
68
|
+
props?: Partial<LocaleManagerParams>,
|
|
69
|
+
): LocaleManager {
|
|
70
|
+
return new LocaleManager({
|
|
71
|
+
bundleSource,
|
|
72
|
+
namespaces,
|
|
73
|
+
warmUp: false,
|
|
74
|
+
fallbackLocale: EN_US,
|
|
75
|
+
preferredLocale: EN_US,
|
|
76
|
+
storageWrapper: new MemoryStorageWrapper(),
|
|
77
|
+
...props,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function mockVueApp(): App {
|
|
82
|
+
return {
|
|
83
|
+
_context: { provides: {} },
|
|
84
|
+
use: vi.fn(),
|
|
85
|
+
provide: vi.fn(),
|
|
86
|
+
component: vi.fn(),
|
|
87
|
+
} as unknown as App;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
describe('LocaleManager - Multiple Instances', () => {
|
|
91
|
+
let powerdialerManager: LocaleManager;
|
|
92
|
+
let adminManager: LocaleManager;
|
|
93
|
+
let mockApp: App;
|
|
94
|
+
|
|
95
|
+
beforeEach(async () => {
|
|
96
|
+
const { PowerdialerBundle, adminBundle } = createTestBundleSources();
|
|
97
|
+
|
|
98
|
+
powerdialerManager = createTestManager(PowerdialerBundle, ['app-core']);
|
|
99
|
+
adminManager = createTestManager(adminBundle, ['admin-test']);
|
|
100
|
+
|
|
101
|
+
mockApp = mockVueApp();
|
|
102
|
+
|
|
103
|
+
// Warm up both managers with proper initialization
|
|
104
|
+
powerdialerManager.updateLocaleSettings({
|
|
105
|
+
namespaces: ['app-core'],
|
|
106
|
+
preferredLocale: EN_US,
|
|
107
|
+
});
|
|
108
|
+
adminManager.updateLocaleSettings({
|
|
109
|
+
namespaces: ['admin-test'],
|
|
110
|
+
preferredLocale: EN_US,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Install managers to Vue app (required for fluentFormat to work)
|
|
114
|
+
powerdialerManager.install(mockApp);
|
|
115
|
+
adminManager.install(mockApp);
|
|
116
|
+
|
|
117
|
+
// Wait for both managers to be ready
|
|
118
|
+
await powerdialerManager.ready;
|
|
119
|
+
await adminManager.ready;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('Bundle Key Isolation', () => {
|
|
123
|
+
it('should allow each manager to access only its own namespace keys', async () => {
|
|
124
|
+
// Powerdialer manager should access app-core keys
|
|
125
|
+
expect(powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
126
|
+
'Campaigns',
|
|
127
|
+
);
|
|
128
|
+
expect(powerdialerManager.fluentFormat('LEAD_PHONE_NUM_LABEL')).toBe(
|
|
129
|
+
'Phone Number',
|
|
130
|
+
);
|
|
131
|
+
expect(powerdialerManager.fluentFormat('CREATE_LEAD_BUTTON')).toBe(
|
|
132
|
+
'Create Lead',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Admin manager should access admin-test keys
|
|
136
|
+
expect(adminManager.fluentFormat('ADMIN_DASHBOARD')).toBe(
|
|
137
|
+
'Admin Dashboard',
|
|
138
|
+
);
|
|
139
|
+
expect(adminManager.fluentFormat('test-admin-only')).toBe(
|
|
140
|
+
'This key ONLY exists in admin bundle',
|
|
141
|
+
);
|
|
142
|
+
expect(adminManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
143
|
+
'Admin Campaigns View',
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should return raw keys for non-existent keys in each namespace', () => {
|
|
148
|
+
// Powerdialer manager should not access admin-only keys
|
|
149
|
+
expect(powerdialerManager.fluentFormat('ADMIN_DASHBOARD')).toBe(
|
|
150
|
+
'ADMIN_DASHBOARD',
|
|
151
|
+
);
|
|
152
|
+
expect(powerdialerManager.fluentFormat('test-admin-only')).toBe(
|
|
153
|
+
'test-admin-only',
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Admin manager should not access Powerdialer-only keys
|
|
157
|
+
expect(adminManager.fluentFormat('LEAD_PHONE_NUM_LABEL')).toBe(
|
|
158
|
+
'LEAD_PHONE_NUM_LABEL',
|
|
159
|
+
);
|
|
160
|
+
expect(adminManager.fluentFormat('CREATE_LEAD_BUTTON')).toBe(
|
|
161
|
+
'CREATE_LEAD_BUTTON',
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('Cross-contamination Prevention', () => {
|
|
167
|
+
it("should prevent managers from accessing each other's namespaces", () => {
|
|
168
|
+
// Same key name, different values based on namespace
|
|
169
|
+
const PowerdialerCampaigns = powerdialerManager.fluentFormat(
|
|
170
|
+
'PAGE_TITLE_CAMPAIGNS',
|
|
171
|
+
);
|
|
172
|
+
const adminCampaigns = adminManager.fluentFormat('PAGE_TITLE_CAMPAIGNS');
|
|
173
|
+
|
|
174
|
+
expect(PowerdialerCampaigns).toBe('Campaigns');
|
|
175
|
+
expect(adminCampaigns).toBe('Admin Campaigns View');
|
|
176
|
+
expect(PowerdialerCampaigns).not.toBe(adminCampaigns);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should maintain namespace isolation even with similar key names', () => {
|
|
180
|
+
// Admin has BUTTON_SAVE, Powerdialer doesn't
|
|
181
|
+
expect(adminManager.fluentFormat('BUTTON_SAVE')).toBe('Admin Save');
|
|
182
|
+
expect(powerdialerManager.fluentFormat('BUTTON_SAVE')).toBe(
|
|
183
|
+
'BUTTON_SAVE',
|
|
184
|
+
); // Raw key returned
|
|
185
|
+
|
|
186
|
+
// Powerdialer has CREATE_LEAD_BUTTON, Admin doesn't
|
|
187
|
+
expect(powerdialerManager.fluentFormat('CREATE_LEAD_BUTTON')).toBe(
|
|
188
|
+
'Create Lead',
|
|
189
|
+
);
|
|
190
|
+
expect(adminManager.fluentFormat('CREATE_LEAD_BUTTON')).toBe(
|
|
191
|
+
'CREATE_LEAD_BUTTON',
|
|
192
|
+
); // Raw key returned
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('Language Switching', () => {
|
|
197
|
+
it('should switch language for individual managers independently', async () => {
|
|
198
|
+
// Set up injection keys for both managers (like the working test)
|
|
199
|
+
const provides = mockApp._context.provides as Record<
|
|
200
|
+
string,
|
|
201
|
+
LocaleManager
|
|
202
|
+
>;
|
|
203
|
+
provides[`${INJECTION_KEY_PREFIX}.app-core`] = powerdialerManager;
|
|
204
|
+
provides[`${INJECTION_KEY_PREFIX}.admin-test`] = adminManager;
|
|
205
|
+
|
|
206
|
+
// Switch Powerdialer to Spanish using change() method with namespace
|
|
207
|
+
powerdialerManager.changeLocale({ preferredLocale: ES_LA }, 'app-core');
|
|
208
|
+
await powerdialerManager.ready;
|
|
209
|
+
|
|
210
|
+
// Powerdialer should show Spanish
|
|
211
|
+
expect(powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
212
|
+
'Campañas',
|
|
213
|
+
);
|
|
214
|
+
expect(powerdialerManager.fluentFormat('LEAD_PHONE_NUM_LABEL')).toBe(
|
|
215
|
+
'Número de Teléfono',
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Admin should still show English
|
|
219
|
+
expect(adminManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
220
|
+
'Admin Campaigns View',
|
|
221
|
+
);
|
|
222
|
+
expect(adminManager.fluentFormat('ADMIN_DASHBOARD')).toBe(
|
|
223
|
+
'Admin Dashboard',
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should switch language for all managers when using changeLocale globally', async () => {
|
|
228
|
+
// Set up injection keys for both managers
|
|
229
|
+
const provides = mockApp._context.provides as Record<
|
|
230
|
+
string,
|
|
231
|
+
LocaleManager
|
|
232
|
+
>;
|
|
233
|
+
provides[`${INJECTION_KEY_PREFIX}.app-core`] = powerdialerManager;
|
|
234
|
+
provides[`${INJECTION_KEY_PREFIX}.admin-test`] = adminManager;
|
|
235
|
+
|
|
236
|
+
// Switch all managers to Spanish
|
|
237
|
+
powerdialerManager.changeLocale({ preferredLocale: ES_LA });
|
|
238
|
+
|
|
239
|
+
// Wait for both managers to be ready
|
|
240
|
+
await powerdialerManager.ready;
|
|
241
|
+
await adminManager.ready;
|
|
242
|
+
|
|
243
|
+
// Both managers should show Spanish
|
|
244
|
+
expect(powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
245
|
+
'Campañas',
|
|
246
|
+
);
|
|
247
|
+
expect(adminManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
248
|
+
'Vista de Campañas Admin',
|
|
249
|
+
);
|
|
250
|
+
expect(adminManager.fluentFormat('ADMIN_DASHBOARD')).toBe(
|
|
251
|
+
'Panel de Administración',
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should maintain namespace isolation during language switching', async () => {
|
|
256
|
+
// Switch both to Spanish
|
|
257
|
+
const provides = mockApp._context.provides as Record<
|
|
258
|
+
string,
|
|
259
|
+
LocaleManager
|
|
260
|
+
>;
|
|
261
|
+
provides[`${INJECTION_KEY_PREFIX}.app-core`] = powerdialerManager;
|
|
262
|
+
provides[`${INJECTION_KEY_PREFIX}.admin-test`] = adminManager;
|
|
263
|
+
|
|
264
|
+
powerdialerManager.changeLocale({ preferredLocale: ES_LA });
|
|
265
|
+
await powerdialerManager.ready;
|
|
266
|
+
await adminManager.ready;
|
|
267
|
+
|
|
268
|
+
// Each manager should access only its own Spanish translations
|
|
269
|
+
expect(powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
270
|
+
'Campañas',
|
|
271
|
+
);
|
|
272
|
+
expect(adminManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
273
|
+
'Vista de Campañas Admin',
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// Admin-only keys should still be isolated
|
|
277
|
+
expect(adminManager.fluentFormat('test-admin-only')).toBe(
|
|
278
|
+
'Esta clave SOLO existe en el bundle de admin',
|
|
279
|
+
);
|
|
280
|
+
expect(powerdialerManager.fluentFormat('test-admin-only')).toBe(
|
|
281
|
+
'test-admin-only',
|
|
282
|
+
); // Raw key
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('Manager Independence', () => {
|
|
287
|
+
it('should allow managers to have different preferred locales simultaneously', async () => {
|
|
288
|
+
// Set up injection keys for both managers
|
|
289
|
+
const provides = mockApp._context.provides as Record<
|
|
290
|
+
string,
|
|
291
|
+
LocaleManager
|
|
292
|
+
>;
|
|
293
|
+
provides[`${INJECTION_KEY_PREFIX}.app-core`] = powerdialerManager;
|
|
294
|
+
provides[`${INJECTION_KEY_PREFIX}.admin-test`] = adminManager;
|
|
295
|
+
|
|
296
|
+
// Set Powerdialer to Spanish using change() method with namespace
|
|
297
|
+
powerdialerManager.changeLocale({ preferredLocale: ES_LA }, 'app-core');
|
|
298
|
+
await powerdialerManager.ready;
|
|
299
|
+
|
|
300
|
+
// Keep Admin in English (no change)
|
|
301
|
+
|
|
302
|
+
// Verify different locales
|
|
303
|
+
expect(powerdialerManager.currentLocaleProp.value).toBe(ES_LA);
|
|
304
|
+
expect(adminManager.currentLocaleProp.value).toBe(EN_US);
|
|
305
|
+
|
|
306
|
+
// Verify translations reflect different locales
|
|
307
|
+
expect(powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
308
|
+
'Campañas',
|
|
309
|
+
);
|
|
310
|
+
expect(adminManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
311
|
+
'Admin Campaigns View',
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should not interfere with each other's bundle loading", async () => {
|
|
316
|
+
// Force reload bundles for one manager
|
|
317
|
+
powerdialerManager.changeLocale({ useCache: false });
|
|
318
|
+
await powerdialerManager.ready;
|
|
319
|
+
|
|
320
|
+
// Other manager should still work normally
|
|
321
|
+
expect(adminManager.fluentFormat('ADMIN_DASHBOARD')).toBe(
|
|
322
|
+
'Admin Dashboard',
|
|
323
|
+
);
|
|
324
|
+
expect(powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS')).toBe(
|
|
325
|
+
'Campaigns',
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe('Error Handling', () => {
|
|
331
|
+
it('should handle missing translations gracefully in each manager', () => {
|
|
332
|
+
// Non-existent keys should return raw key strings
|
|
333
|
+
expect(powerdialerManager.fluentFormat('NON_EXISTENT_KEY')).toBe(
|
|
334
|
+
'NON_EXISTENT_KEY',
|
|
335
|
+
);
|
|
336
|
+
expect(adminManager.fluentFormat('ANOTHER_MISSING_KEY')).toBe(
|
|
337
|
+
'ANOTHER_MISSING_KEY',
|
|
338
|
+
);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should not crash when one manager has bundle issues', () => {
|
|
342
|
+
// This test ensures that if one manager has problems, others continue working
|
|
343
|
+
expect(() => {
|
|
344
|
+
powerdialerManager.fluentFormat('PAGE_TITLE_CAMPAIGNS');
|
|
345
|
+
adminManager.fluentFormat('ADMIN_DASHBOARD');
|
|
346
|
+
}).not.toThrow();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
});
|
|
@@ -23,11 +23,11 @@ function mockBundleSource(): BundleSource {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function mockVueApp(): any {
|
|
26
|
-
return { use: () => {}, provide: () => {} };
|
|
26
|
+
return { use: () => {}, provide: () => {}, component: () => {} };
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// TODO - would go well in a testing util lib. not quite sync, but immediate
|
|
30
|
-
// and reliable for stuff that you don't expect to
|
|
30
|
+
// and reliable for stuff that you don't expect to resolve for "a hot second"
|
|
31
31
|
async function isResolved(check: Promise<unknown>): Promise<boolean> {
|
|
32
32
|
const dummy = {};
|
|
33
33
|
return (await Promise.race([check, Promise.resolve(dummy)])) !== dummy;
|
package/src/locale-manager.ts
CHANGED
|
@@ -6,11 +6,23 @@ import type {
|
|
|
6
6
|
SetLocaleParams,
|
|
7
7
|
} from '@dialpad/i18n-services/locale-manager';
|
|
8
8
|
|
|
9
|
-
import { computed,
|
|
9
|
+
import { computed, ref, inject } from 'vue';
|
|
10
10
|
import { BaseLocaleManager } from '@dialpad/i18n-services/locale-manager';
|
|
11
11
|
|
|
12
12
|
export const INJECTION_KEY_PREFIX = 'GLOBAL_LOCALE_MANAGER';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* This is a backup storage for LocaleManagers in order to solve
|
|
16
|
+
* the constraint of having to call useI18N() inside a <script setup>.
|
|
17
|
+
*
|
|
18
|
+
* This is kinda hackish (I tried to keep it simple as well) but otherwise we're forcing consumers
|
|
19
|
+
* (i.e. Dialtone) to potentially modify their whole database
|
|
20
|
+
* and set the <script> for each components to <script setup>
|
|
21
|
+
*
|
|
22
|
+
* (I haven't found a way to do this only relying on Vue 3 capabilities T_T)
|
|
23
|
+
*/
|
|
24
|
+
export const globalLocaleManagers = new Map<string, LocaleManager>();
|
|
25
|
+
|
|
14
26
|
export interface UseI18N {
|
|
15
27
|
currentLocale: Ref<string | null>;
|
|
16
28
|
setI18N: (args?: Partial<SetLocaleParams>, namespace?: string) => void;
|
|
@@ -59,9 +71,17 @@ export class LocaleManager extends BaseLocaleManager {
|
|
|
59
71
|
);
|
|
60
72
|
}
|
|
61
73
|
this.app = app;
|
|
62
|
-
|
|
63
|
-
//
|
|
74
|
+
|
|
75
|
+
// Only install fluent plugin if not already installed
|
|
76
|
+
// This prevents "component i18n has already been registered" warnings
|
|
77
|
+
const isFluentInstalled = app.component('i18n') !== undefined;
|
|
78
|
+
if (!isFluentInstalled) {
|
|
79
|
+
app.use(this.fluent);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Always register this LocaleManager for injection, regardless of plugin installation
|
|
64
83
|
app.provide(parseInjectionName(namespace), this);
|
|
84
|
+
globalLocaleManagers.set(parseInjectionName(namespace), this);
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
/**
|
|
@@ -74,56 +94,95 @@ export class LocaleManager extends BaseLocaleManager {
|
|
|
74
94
|
* provided, all LocaleManagers will be changed.
|
|
75
95
|
*/
|
|
76
96
|
changeLocale(args?: Partial<SetLocaleParams>, namespace?: string): void {
|
|
77
|
-
const localeManagers
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (namespace) {
|
|
81
|
-
// We cannot use inject here because is likely to not either inside setup() or functional components. Also see Line #93
|
|
82
|
-
// btw, Object.entries() returns [key, value]
|
|
83
|
-
const keyValue =
|
|
84
|
-
Object.entries(providedValues).find(
|
|
85
|
-
([key, value]) =>
|
|
86
|
-
typeof key === 'string' &&
|
|
87
|
-
key === parseInjectionName(namespace) &&
|
|
88
|
-
value instanceof LocaleManager,
|
|
89
|
-
) ?? [];
|
|
90
|
-
const localeManager = keyValue[1];
|
|
91
|
-
if (!localeManager) {
|
|
92
|
-
throw new Error('LocaleManager not found!');
|
|
93
|
-
}
|
|
94
|
-
localeManagers.push(localeManager);
|
|
95
|
-
} else {
|
|
96
|
-
// This is very ugly but it's the only way I found to get all the localeManagers without
|
|
97
|
-
// having to create a singleton, if you have a better idea, let me know!! :-]
|
|
98
|
-
// Get string-keyed properties
|
|
99
|
-
Object.entries(providedValues).forEach(([key, value]) => {
|
|
100
|
-
if (
|
|
101
|
-
typeof key === 'string' &&
|
|
102
|
-
key.startsWith(INJECTION_KEY_PREFIX.toString()) &&
|
|
103
|
-
value instanceof LocaleManager
|
|
104
|
-
) {
|
|
105
|
-
localeManagers.push(value);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
throw new Error('No locale managers are set up yet!');
|
|
111
|
-
}
|
|
97
|
+
const localeManagers = namespace
|
|
98
|
+
? this.getLocaleManagerByNamespace(namespace)
|
|
99
|
+
: this.getAllLocaleManagers();
|
|
112
100
|
|
|
113
101
|
for (const localeManager of localeManagers) {
|
|
114
102
|
localeManager.updateLocaleSettings(args);
|
|
115
103
|
}
|
|
116
104
|
}
|
|
105
|
+
|
|
106
|
+
private getLocaleManagerByNamespace(namespace: string): LocaleManager[] {
|
|
107
|
+
const localeManager =
|
|
108
|
+
this.findLocaleManagerInProvides(namespace) ??
|
|
109
|
+
findLocaleManager(namespace);
|
|
110
|
+
if (!localeManager) {
|
|
111
|
+
throw new Error('LocaleManager not found!');
|
|
112
|
+
}
|
|
113
|
+
return [localeManager];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// This method mostly focuses on remain backward compatible and reduce risks when implementing new instance providing mechanisms
|
|
117
|
+
private findLocaleManagerInProvides(
|
|
118
|
+
namespace: string,
|
|
119
|
+
): LocaleManager | undefined {
|
|
120
|
+
const providedValues = this.app?._context.provides;
|
|
121
|
+
if (!providedValues) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const targetKey = parseInjectionName(namespace);
|
|
126
|
+
return this.findLocaleManagersByKeyFilter(
|
|
127
|
+
providedValues,
|
|
128
|
+
(key) => key === targetKey,
|
|
129
|
+
)[0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private getAllLocaleManagers(): LocaleManager[] {
|
|
133
|
+
const providedValues = this.app?._context.provides;
|
|
134
|
+
if (!providedValues) {
|
|
135
|
+
throw new Error('No locale managers are set up yet!');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return this.findLocaleManagersByKeyFilter(providedValues, (key) =>
|
|
139
|
+
key.startsWith(INJECTION_KEY_PREFIX.toString()),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private findLocaleManagersByKeyFilter(
|
|
144
|
+
providedValues: Record<string | symbol, unknown>,
|
|
145
|
+
keyFilter: (key: string) => boolean,
|
|
146
|
+
): LocaleManager[] {
|
|
147
|
+
const localeManagers: LocaleManager[] = [];
|
|
148
|
+
Object.entries(providedValues).forEach(([key, value]) => {
|
|
149
|
+
if (
|
|
150
|
+
typeof key === 'string' &&
|
|
151
|
+
keyFilter(key) &&
|
|
152
|
+
value instanceof LocaleManager
|
|
153
|
+
) {
|
|
154
|
+
localeManagers.push(value);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return localeManagers;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Use i18n functionality without requiring to depend only on Vue's inject
|
|
164
|
+
* @param namespace - The namespace of the locale manager to use, defaults to 'default'
|
|
165
|
+
*/
|
|
166
|
+
export function findLocaleManager(
|
|
167
|
+
namespace: string,
|
|
168
|
+
): LocaleManager | undefined {
|
|
169
|
+
const injectionKey = parseInjectionName(namespace);
|
|
170
|
+
// Use null as default to suppress Vue injection warning when key is not found
|
|
171
|
+
const injected = inject<LocaleManager | null>(injectionKey, null);
|
|
172
|
+
return injected ?? globalLocaleManagers.get(injectionKey);
|
|
117
173
|
}
|
|
118
174
|
|
|
119
175
|
// TODO - allow custom injection key or whatever???
|
|
120
176
|
// TODO - also maybe just use getCurrentApp + the app's global config? idk.
|
|
121
177
|
export function useI18N(namespace = 'default'): UseI18N {
|
|
122
|
-
const localeManager
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
178
|
+
const localeManager = findLocaleManager(namespace);
|
|
179
|
+
|
|
180
|
+
if (!localeManager) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`locale manager doesn't exist using ${namespace}. Make sure your locale manager was set up correctly`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
127
186
|
return {
|
|
128
187
|
currentLocale: computed(() => localeManager.currentLocaleProp.value),
|
|
129
188
|
setI18N: (args?: Partial<SetLocaleParams>, namespace?: string) => {
|