@dialpad/i18n 1.16.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/.eslintignore +1 -0
- package/.eslintrc.cjs +12 -0
- package/.prettierignore +3 -0
- package/CHANGELOG.md +34 -0
- package/README.md +533 -0
- package/base-tsconfig.json +19 -0
- package/dist/i18n.cjs +119 -0
- package/dist/i18n.cjs.map +1 -0
- package/dist/i18n.js +113 -0
- package/dist/i18n.js.map +1 -0
- package/eslint-tsconfig.json +5 -0
- package/index.html +11 -0
- package/index.ts +18 -0
- package/package.json +49 -0
- package/src/__test__/locale-manager.formatters.test.ts +139 -0
- package/src/__test__/locale-manager.test.ts +511 -0
- package/src/locale-manager.ts +139 -0
- package/tsconfig.json +10 -0
- package/vite.config.ts +39 -0
package/dist/i18n.cjs
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => {
|
|
5
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
|
+
return value;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
9
|
+
const vue = require("vue");
|
|
10
|
+
const localeManager = require("@dialpad/i18n-services/locale-manager");
|
|
11
|
+
const bundleSource = require("@dialpad/i18n-services/bundle-source");
|
|
12
|
+
const INJECTION_KEY_PREFIX = "GLOBAL_LOCALE_MANAGER";
|
|
13
|
+
class LocaleManager extends localeManager.BaseLocaleManager {
|
|
14
|
+
constructor(params) {
|
|
15
|
+
super(params);
|
|
16
|
+
__publicField(this, "currentLocaleProp", vue.ref(null));
|
|
17
|
+
__publicField(this, "app", null);
|
|
18
|
+
this.setCurrentLocaleProp(
|
|
19
|
+
this.determineLocale({
|
|
20
|
+
preferredLocale: params.preferredLocale,
|
|
21
|
+
allowedLocales: params.allowedLocales
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
setCurrentLocaleProp(locale) {
|
|
26
|
+
this.currentLocaleProp = vue.ref(locale);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Registers this LocaleManager with the Vue instance so that it can
|
|
30
|
+
* be used by the {@link useI18N} hook.
|
|
31
|
+
*
|
|
32
|
+
* @param app - The Vue app to register with.
|
|
33
|
+
* @param namespace - The namespace to install the LocaleManager under.
|
|
34
|
+
* Defaults to 'default'
|
|
35
|
+
*/
|
|
36
|
+
install(app, namespace = "default") {
|
|
37
|
+
if (this.fluent === null) {
|
|
38
|
+
this.fluent = this.initFluent();
|
|
39
|
+
}
|
|
40
|
+
this.addToVue(app, namespace);
|
|
41
|
+
}
|
|
42
|
+
addToVue(app, namespace) {
|
|
43
|
+
if (!this.fluent) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"fluent not ready, you probably want to call install(...) first"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
this.app = app;
|
|
49
|
+
app.use(this.fluent);
|
|
50
|
+
app.provide(parseInjectionName(namespace), this);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Changes the locale of the {@link LocaleManager} that is currently active in
|
|
54
|
+
* the app, or the one specified by the namespace.
|
|
55
|
+
*
|
|
56
|
+
* @param args - Optional parameters to pass to the underlying
|
|
57
|
+
* {@link BaseLocaleManager#updateLocaleSettings} method.
|
|
58
|
+
* @param namespace - Optional namespace to change the locale of. If not
|
|
59
|
+
* provided, all LocaleManagers will be changed.
|
|
60
|
+
*/
|
|
61
|
+
changeLocale(args, namespace) {
|
|
62
|
+
var _a;
|
|
63
|
+
const localeManagers = [];
|
|
64
|
+
const providedValues = (_a = this.app) == null ? void 0 : _a._context.provides;
|
|
65
|
+
if (providedValues) {
|
|
66
|
+
if (namespace) {
|
|
67
|
+
const keyValue = Object.entries(providedValues).find(
|
|
68
|
+
([key, value]) => typeof key === "string" && key === parseInjectionName(namespace) && value instanceof LocaleManager
|
|
69
|
+
) ?? [];
|
|
70
|
+
const localeManager2 = keyValue[1];
|
|
71
|
+
if (!localeManager2) {
|
|
72
|
+
throw new Error("LocaleManager not found!");
|
|
73
|
+
}
|
|
74
|
+
localeManagers.push(localeManager2);
|
|
75
|
+
} else {
|
|
76
|
+
Object.entries(providedValues).forEach(([key, value]) => {
|
|
77
|
+
if (typeof key === "string" && key.startsWith(INJECTION_KEY_PREFIX.toString()) && value instanceof LocaleManager) {
|
|
78
|
+
localeManagers.push(value);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error("No locale managers are set up yet!");
|
|
84
|
+
}
|
|
85
|
+
for (const localeManager2 of localeManagers) {
|
|
86
|
+
localeManager2.updateLocaleSettings(args);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function useI18N(namespace = "default") {
|
|
91
|
+
const localeManager2 = vue.inject(
|
|
92
|
+
parseInjectionName(namespace)
|
|
93
|
+
);
|
|
94
|
+
if (!localeManager2)
|
|
95
|
+
throw new Error(`locale manager doesn't exist using ${namespace}`);
|
|
96
|
+
return {
|
|
97
|
+
currentLocale: vue.computed(() => localeManager2.currentLocaleProp.value),
|
|
98
|
+
setI18N: (args, namespace2) => {
|
|
99
|
+
localeManager2.changeLocale(args, namespace2);
|
|
100
|
+
},
|
|
101
|
+
$t: localeManager2.fluentFormat,
|
|
102
|
+
$ta: localeManager2.fluentFormatAttrs
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function parseInjectionName(namespace) {
|
|
106
|
+
return `${INJECTION_KEY_PREFIX}.${namespace}`;
|
|
107
|
+
}
|
|
108
|
+
Object.defineProperty(exports, "HTTPBundleSource", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get: () => bundleSource.HTTPBundleSource
|
|
111
|
+
});
|
|
112
|
+
Object.defineProperty(exports, "RawBundleSource", {
|
|
113
|
+
enumerable: true,
|
|
114
|
+
get: () => bundleSource.RawBundleSource
|
|
115
|
+
});
|
|
116
|
+
exports.INJECTION_KEY_PREFIX = INJECTION_KEY_PREFIX;
|
|
117
|
+
exports.LocaleManager = LocaleManager;
|
|
118
|
+
exports.useI18N = useI18N;
|
|
119
|
+
//# sourceMappingURL=i18n.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.cjs","sources":["../src/locale-manager.ts"],"sourcesContent":["import type { App, Ref } from 'vue';\nimport type {\n FluentFormat,\n FluentFormatAttrs,\n LocaleManagerParams,\n SetLocaleParams,\n} from '@dialpad/i18n-services/locale-manager';\n\nimport { computed, inject, ref } from 'vue';\nimport { BaseLocaleManager } from '@dialpad/i18n-services/locale-manager';\n\nexport const INJECTION_KEY_PREFIX = 'GLOBAL_LOCALE_MANAGER';\n\nexport interface UseI18N {\n currentLocale: Ref<string | null>;\n setI18N: (args?: Partial<SetLocaleParams>, namespace?: string) => void;\n $t: FluentFormat;\n $ta: FluentFormatAttrs;\n}\n\nexport class LocaleManager extends BaseLocaleManager {\n currentLocaleProp: Ref<string | null> = ref<string | null>(null);\n app: App | null = null;\n\n constructor(params: LocaleManagerParams) {\n super(params);\n this.setCurrentLocaleProp(\n this.determineLocale({\n preferredLocale: params.preferredLocale,\n allowedLocales: params.allowedLocales,\n }),\n );\n }\n\n protected setCurrentLocaleProp(locale: string): void {\n this.currentLocaleProp = ref(locale);\n }\n\n /**\n * Registers this LocaleManager with the Vue instance so that it can\n * be used by the {@link useI18N} hook.\n *\n * @param app - The Vue app to register with.\n * @param namespace - The namespace to install the LocaleManager under.\n * Defaults to 'default'\n */\n install(app: App, namespace = 'default'): void {\n if (this.fluent === null) {\n this.fluent = this.initFluent();\n }\n\n this.addToVue(app, namespace);\n }\n\n private addToVue(app: App, namespace: string): void {\n if (!this.fluent) {\n throw new Error(\n 'fluent not ready, you probably want to call install(...) first',\n );\n }\n this.app = app;\n app.use(this.fluent);\n // TODO - allow custom injection key i 'spose\n app.provide(parseInjectionName(namespace), this);\n }\n\n /**\n * Changes the locale of the {@link LocaleManager} that is currently active in\n * the app, or the one specified by the namespace.\n *\n * @param args - Optional parameters to pass to the underlying\n * {@link BaseLocaleManager#updateLocaleSettings} method.\n * @param namespace - Optional namespace to change the locale of. If not\n * provided, all LocaleManagers will be changed.\n */\n changeLocale(args?: Partial<SetLocaleParams>, namespace?: string): void {\n const localeManagers: LocaleManager[] = [];\n const providedValues = this.app?._context.provides;\n if (providedValues) {\n if (namespace) {\n // We cannot use inject here because is likely to not either inside setup() or functional components. Also see Line #93\n // btw, Object.entries() returns [key, value]\n const keyValue =\n Object.entries(providedValues).find(\n ([key, value]) =>\n typeof key === 'string' &&\n key === parseInjectionName(namespace) &&\n value instanceof LocaleManager,\n ) ?? [];\n const localeManager = keyValue[1];\n if (!localeManager) {\n throw new Error('LocaleManager not found!');\n }\n localeManagers.push(localeManager);\n } else {\n // This is very ugly but it's the only way I found to get all the localeManagers without\n // having to create a singleton, if you have a better idea, let me know!! :-]\n // Get string-keyed properties\n Object.entries(providedValues).forEach(([key, value]) => {\n if (\n typeof key === 'string' &&\n key.startsWith(INJECTION_KEY_PREFIX.toString()) &&\n value instanceof LocaleManager\n ) {\n localeManagers.push(value);\n }\n });\n }\n } else {\n throw new Error('No locale managers are set up yet!');\n }\n\n for (const localeManager of localeManagers) {\n localeManager.updateLocaleSettings(args);\n }\n }\n}\n\n// TODO - allow custom injection key or whatever???\n// TODO - also maybe just use getCurrentApp + the app's global config? idk.\nexport function useI18N(namespace = 'default'): UseI18N {\n const localeManager: LocaleManager | undefined = inject(\n parseInjectionName(namespace),\n );\n if (!localeManager)\n throw new Error(`locale manager doesn't exist using ${namespace}`);\n return {\n currentLocale: computed(() => localeManager.currentLocaleProp.value),\n setI18N: (args?: Partial<SetLocaleParams>, namespace?: string) => {\n localeManager.changeLocale(args, namespace);\n },\n $t: localeManager.fluentFormat,\n $ta: localeManager.fluentFormatAttrs,\n };\n}\n\nfunction parseInjectionName(namespace: string): string {\n return `${INJECTION_KEY_PREFIX}.${namespace}`;\n}\n"],"names":["BaseLocaleManager","ref","localeManager","inject","computed","namespace"],"mappings":";;;;;;;;;;;AAWO,MAAM,uBAAuB;AAS7B,MAAM,sBAAsBA,cAAAA,kBAAkB;AAAA,EAInD,YAAY,QAA6B;AACvC,UAAM,MAAM;AAJd,6CAAwCC,QAAmB,IAAI;AAC/D,+BAAkB;AAIX,SAAA;AAAA,MACH,KAAK,gBAAgB;AAAA,QACnB,iBAAiB,OAAO;AAAA,QACxB,gBAAgB,OAAO;AAAA,MACxB,CAAA;AAAA,IACH;AAAA,EAAA;AAAA,EAGQ,qBAAqB,QAAsB;AAC9C,SAAA,oBAAoBA,QAAI,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWrC,QAAQ,KAAU,YAAY,WAAiB;AACzC,QAAA,KAAK,WAAW,MAAM;AACnB,WAAA,SAAS,KAAK,WAAW;AAAA,IAAA;AAG3B,SAAA,SAAS,KAAK,SAAS;AAAA,EAAA;AAAA,EAGtB,SAAS,KAAU,WAAyB;AAC9C,QAAA,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IAAA;AAEF,SAAK,MAAM;AACP,QAAA,IAAI,KAAK,MAAM;AAEnB,QAAI,QAAQ,mBAAmB,SAAS,GAAG,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYjD,aAAa,MAAiC,WAA0B;;AACtE,UAAM,iBAAkC,CAAC;AACnC,UAAA,kBAAiB,UAAK,QAAL,mBAAU,SAAS;AAC1C,QAAI,gBAAgB;AAClB,UAAI,WAAW;AAGb,cAAM,WACJ,OAAO,QAAQ,cAAc,EAAE;AAAA,UAC7B,CAAC,CAAC,KAAK,KAAK,MACV,OAAO,QAAQ,YACf,QAAQ,mBAAmB,SAAS,KACpC,iBAAiB;AAAA,QAAA,KAChB,CAAC;AACF,cAAAC,iBAAgB,SAAS,CAAC;AAChC,YAAI,CAACA,gBAAe;AACZ,gBAAA,IAAI,MAAM,0BAA0B;AAAA,QAAA;AAE5C,uBAAe,KAAKA,cAAa;AAAA,MAAA,OAC5B;AAIE,eAAA,QAAQ,cAAc,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAErD,cAAA,OAAO,QAAQ,YACf,IAAI,WAAW,qBAAqB,SAAU,CAAA,KAC9C,iBAAiB,eACjB;AACA,2BAAe,KAAK,KAAK;AAAA,UAAA;AAAA,QAC3B,CACD;AAAA,MAAA;AAAA,IACH,OACK;AACC,YAAA,IAAI,MAAM,oCAAoC;AAAA,IAAA;AAGtD,eAAWA,kBAAiB,gBAAgB;AAC1C,MAAAA,eAAc,qBAAqB,IAAI;AAAA,IAAA;AAAA,EACzC;AAEJ;AAIgB,SAAA,QAAQ,YAAY,WAAoB;AACtD,QAAMA,iBAA2CC,IAAA;AAAA,IAC/C,mBAAmB,SAAS;AAAA,EAC9B;AACA,MAAI,CAACD;AACH,UAAM,IAAI,MAAM,sCAAsC,SAAS,EAAE;AAC5D,SAAA;AAAA,IACL,eAAeE,IAAAA,SAAS,MAAMF,eAAc,kBAAkB,KAAK;AAAA,IACnE,SAAS,CAAC,MAAiCG,eAAuB;AAClD,MAAAH,eAAA,aAAa,MAAMG,UAAS;AAAA,IAC5C;AAAA,IACA,IAAIH,eAAc;AAAA,IAClB,KAAKA,eAAc;AAAA,EACrB;AACF;AAEA,SAAS,mBAAmB,WAA2B;AAC9C,SAAA,GAAG,oBAAoB,IAAI,SAAS;AAC7C;;;;;;;;;;;;"}
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => {
|
|
4
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
return value;
|
|
6
|
+
};
|
|
7
|
+
import { ref, inject, computed } from "vue";
|
|
8
|
+
import { BaseLocaleManager } from "@dialpad/i18n-services/locale-manager";
|
|
9
|
+
import { HTTPBundleSource, RawBundleSource } from "@dialpad/i18n-services/bundle-source";
|
|
10
|
+
const INJECTION_KEY_PREFIX = "GLOBAL_LOCALE_MANAGER";
|
|
11
|
+
class LocaleManager extends BaseLocaleManager {
|
|
12
|
+
constructor(params) {
|
|
13
|
+
super(params);
|
|
14
|
+
__publicField(this, "currentLocaleProp", ref(null));
|
|
15
|
+
__publicField(this, "app", null);
|
|
16
|
+
this.setCurrentLocaleProp(
|
|
17
|
+
this.determineLocale({
|
|
18
|
+
preferredLocale: params.preferredLocale,
|
|
19
|
+
allowedLocales: params.allowedLocales
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
setCurrentLocaleProp(locale) {
|
|
24
|
+
this.currentLocaleProp = ref(locale);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Registers this LocaleManager with the Vue instance so that it can
|
|
28
|
+
* be used by the {@link useI18N} hook.
|
|
29
|
+
*
|
|
30
|
+
* @param app - The Vue app to register with.
|
|
31
|
+
* @param namespace - The namespace to install the LocaleManager under.
|
|
32
|
+
* Defaults to 'default'
|
|
33
|
+
*/
|
|
34
|
+
install(app, namespace = "default") {
|
|
35
|
+
if (this.fluent === null) {
|
|
36
|
+
this.fluent = this.initFluent();
|
|
37
|
+
}
|
|
38
|
+
this.addToVue(app, namespace);
|
|
39
|
+
}
|
|
40
|
+
addToVue(app, namespace) {
|
|
41
|
+
if (!this.fluent) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"fluent not ready, you probably want to call install(...) first"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
this.app = app;
|
|
47
|
+
app.use(this.fluent);
|
|
48
|
+
app.provide(parseInjectionName(namespace), this);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Changes the locale of the {@link LocaleManager} that is currently active in
|
|
52
|
+
* the app, or the one specified by the namespace.
|
|
53
|
+
*
|
|
54
|
+
* @param args - Optional parameters to pass to the underlying
|
|
55
|
+
* {@link BaseLocaleManager#updateLocaleSettings} method.
|
|
56
|
+
* @param namespace - Optional namespace to change the locale of. If not
|
|
57
|
+
* provided, all LocaleManagers will be changed.
|
|
58
|
+
*/
|
|
59
|
+
changeLocale(args, namespace) {
|
|
60
|
+
var _a;
|
|
61
|
+
const localeManagers = [];
|
|
62
|
+
const providedValues = (_a = this.app) == null ? void 0 : _a._context.provides;
|
|
63
|
+
if (providedValues) {
|
|
64
|
+
if (namespace) {
|
|
65
|
+
const keyValue = Object.entries(providedValues).find(
|
|
66
|
+
([key, value]) => typeof key === "string" && key === parseInjectionName(namespace) && value instanceof LocaleManager
|
|
67
|
+
) ?? [];
|
|
68
|
+
const localeManager = keyValue[1];
|
|
69
|
+
if (!localeManager) {
|
|
70
|
+
throw new Error("LocaleManager not found!");
|
|
71
|
+
}
|
|
72
|
+
localeManagers.push(localeManager);
|
|
73
|
+
} else {
|
|
74
|
+
Object.entries(providedValues).forEach(([key, value]) => {
|
|
75
|
+
if (typeof key === "string" && key.startsWith(INJECTION_KEY_PREFIX.toString()) && value instanceof LocaleManager) {
|
|
76
|
+
localeManagers.push(value);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
throw new Error("No locale managers are set up yet!");
|
|
82
|
+
}
|
|
83
|
+
for (const localeManager of localeManagers) {
|
|
84
|
+
localeManager.updateLocaleSettings(args);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function useI18N(namespace = "default") {
|
|
89
|
+
const localeManager = inject(
|
|
90
|
+
parseInjectionName(namespace)
|
|
91
|
+
);
|
|
92
|
+
if (!localeManager)
|
|
93
|
+
throw new Error(`locale manager doesn't exist using ${namespace}`);
|
|
94
|
+
return {
|
|
95
|
+
currentLocale: computed(() => localeManager.currentLocaleProp.value),
|
|
96
|
+
setI18N: (args, namespace2) => {
|
|
97
|
+
localeManager.changeLocale(args, namespace2);
|
|
98
|
+
},
|
|
99
|
+
$t: localeManager.fluentFormat,
|
|
100
|
+
$ta: localeManager.fluentFormatAttrs
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function parseInjectionName(namespace) {
|
|
104
|
+
return `${INJECTION_KEY_PREFIX}.${namespace}`;
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
HTTPBundleSource,
|
|
108
|
+
INJECTION_KEY_PREFIX,
|
|
109
|
+
LocaleManager,
|
|
110
|
+
RawBundleSource,
|
|
111
|
+
useI18N
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=i18n.js.map
|
package/dist/i18n.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sources":["../src/locale-manager.ts"],"sourcesContent":["import type { App, Ref } from 'vue';\nimport type {\n FluentFormat,\n FluentFormatAttrs,\n LocaleManagerParams,\n SetLocaleParams,\n} from '@dialpad/i18n-services/locale-manager';\n\nimport { computed, inject, ref } from 'vue';\nimport { BaseLocaleManager } from '@dialpad/i18n-services/locale-manager';\n\nexport const INJECTION_KEY_PREFIX = 'GLOBAL_LOCALE_MANAGER';\n\nexport interface UseI18N {\n currentLocale: Ref<string | null>;\n setI18N: (args?: Partial<SetLocaleParams>, namespace?: string) => void;\n $t: FluentFormat;\n $ta: FluentFormatAttrs;\n}\n\nexport class LocaleManager extends BaseLocaleManager {\n currentLocaleProp: Ref<string | null> = ref<string | null>(null);\n app: App | null = null;\n\n constructor(params: LocaleManagerParams) {\n super(params);\n this.setCurrentLocaleProp(\n this.determineLocale({\n preferredLocale: params.preferredLocale,\n allowedLocales: params.allowedLocales,\n }),\n );\n }\n\n protected setCurrentLocaleProp(locale: string): void {\n this.currentLocaleProp = ref(locale);\n }\n\n /**\n * Registers this LocaleManager with the Vue instance so that it can\n * be used by the {@link useI18N} hook.\n *\n * @param app - The Vue app to register with.\n * @param namespace - The namespace to install the LocaleManager under.\n * Defaults to 'default'\n */\n install(app: App, namespace = 'default'): void {\n if (this.fluent === null) {\n this.fluent = this.initFluent();\n }\n\n this.addToVue(app, namespace);\n }\n\n private addToVue(app: App, namespace: string): void {\n if (!this.fluent) {\n throw new Error(\n 'fluent not ready, you probably want to call install(...) first',\n );\n }\n this.app = app;\n app.use(this.fluent);\n // TODO - allow custom injection key i 'spose\n app.provide(parseInjectionName(namespace), this);\n }\n\n /**\n * Changes the locale of the {@link LocaleManager} that is currently active in\n * the app, or the one specified by the namespace.\n *\n * @param args - Optional parameters to pass to the underlying\n * {@link BaseLocaleManager#updateLocaleSettings} method.\n * @param namespace - Optional namespace to change the locale of. If not\n * provided, all LocaleManagers will be changed.\n */\n changeLocale(args?: Partial<SetLocaleParams>, namespace?: string): void {\n const localeManagers: LocaleManager[] = [];\n const providedValues = this.app?._context.provides;\n if (providedValues) {\n if (namespace) {\n // We cannot use inject here because is likely to not either inside setup() or functional components. Also see Line #93\n // btw, Object.entries() returns [key, value]\n const keyValue =\n Object.entries(providedValues).find(\n ([key, value]) =>\n typeof key === 'string' &&\n key === parseInjectionName(namespace) &&\n value instanceof LocaleManager,\n ) ?? [];\n const localeManager = keyValue[1];\n if (!localeManager) {\n throw new Error('LocaleManager not found!');\n }\n localeManagers.push(localeManager);\n } else {\n // This is very ugly but it's the only way I found to get all the localeManagers without\n // having to create a singleton, if you have a better idea, let me know!! :-]\n // Get string-keyed properties\n Object.entries(providedValues).forEach(([key, value]) => {\n if (\n typeof key === 'string' &&\n key.startsWith(INJECTION_KEY_PREFIX.toString()) &&\n value instanceof LocaleManager\n ) {\n localeManagers.push(value);\n }\n });\n }\n } else {\n throw new Error('No locale managers are set up yet!');\n }\n\n for (const localeManager of localeManagers) {\n localeManager.updateLocaleSettings(args);\n }\n }\n}\n\n// TODO - allow custom injection key or whatever???\n// TODO - also maybe just use getCurrentApp + the app's global config? idk.\nexport function useI18N(namespace = 'default'): UseI18N {\n const localeManager: LocaleManager | undefined = inject(\n parseInjectionName(namespace),\n );\n if (!localeManager)\n throw new Error(`locale manager doesn't exist using ${namespace}`);\n return {\n currentLocale: computed(() => localeManager.currentLocaleProp.value),\n setI18N: (args?: Partial<SetLocaleParams>, namespace?: string) => {\n localeManager.changeLocale(args, namespace);\n },\n $t: localeManager.fluentFormat,\n $ta: localeManager.fluentFormatAttrs,\n };\n}\n\nfunction parseInjectionName(namespace: string): string {\n return `${INJECTION_KEY_PREFIX}.${namespace}`;\n}\n"],"names":["namespace"],"mappings":";;;;;;;;;AAWO,MAAM,uBAAuB;AAS7B,MAAM,sBAAsB,kBAAkB;AAAA,EAInD,YAAY,QAA6B;AACvC,UAAM,MAAM;AAJd,6CAAwC,IAAmB,IAAI;AAC/D,+BAAkB;AAIX,SAAA;AAAA,MACH,KAAK,gBAAgB;AAAA,QACnB,iBAAiB,OAAO;AAAA,QACxB,gBAAgB,OAAO;AAAA,MACxB,CAAA;AAAA,IACH;AAAA,EAAA;AAAA,EAGQ,qBAAqB,QAAsB;AAC9C,SAAA,oBAAoB,IAAI,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWrC,QAAQ,KAAU,YAAY,WAAiB;AACzC,QAAA,KAAK,WAAW,MAAM;AACnB,WAAA,SAAS,KAAK,WAAW;AAAA,IAAA;AAG3B,SAAA,SAAS,KAAK,SAAS;AAAA,EAAA;AAAA,EAGtB,SAAS,KAAU,WAAyB;AAC9C,QAAA,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IAAA;AAEF,SAAK,MAAM;AACP,QAAA,IAAI,KAAK,MAAM;AAEnB,QAAI,QAAQ,mBAAmB,SAAS,GAAG,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYjD,aAAa,MAAiC,WAA0B;;AACtE,UAAM,iBAAkC,CAAC;AACnC,UAAA,kBAAiB,UAAK,QAAL,mBAAU,SAAS;AAC1C,QAAI,gBAAgB;AAClB,UAAI,WAAW;AAGb,cAAM,WACJ,OAAO,QAAQ,cAAc,EAAE;AAAA,UAC7B,CAAC,CAAC,KAAK,KAAK,MACV,OAAO,QAAQ,YACf,QAAQ,mBAAmB,SAAS,KACpC,iBAAiB;AAAA,QAAA,KAChB,CAAC;AACF,cAAA,gBAAgB,SAAS,CAAC;AAChC,YAAI,CAAC,eAAe;AACZ,gBAAA,IAAI,MAAM,0BAA0B;AAAA,QAAA;AAE5C,uBAAe,KAAK,aAAa;AAAA,MAAA,OAC5B;AAIE,eAAA,QAAQ,cAAc,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAErD,cAAA,OAAO,QAAQ,YACf,IAAI,WAAW,qBAAqB,SAAU,CAAA,KAC9C,iBAAiB,eACjB;AACA,2BAAe,KAAK,KAAK;AAAA,UAAA;AAAA,QAC3B,CACD;AAAA,MAAA;AAAA,IACH,OACK;AACC,YAAA,IAAI,MAAM,oCAAoC;AAAA,IAAA;AAGtD,eAAW,iBAAiB,gBAAgB;AAC1C,oBAAc,qBAAqB,IAAI;AAAA,IAAA;AAAA,EACzC;AAEJ;AAIgB,SAAA,QAAQ,YAAY,WAAoB;AACtD,QAAM,gBAA2C;AAAA,IAC/C,mBAAmB,SAAS;AAAA,EAC9B;AACA,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,sCAAsC,SAAS,EAAE;AAC5D,SAAA;AAAA,IACL,eAAe,SAAS,MAAM,cAAc,kBAAkB,KAAK;AAAA,IACnE,SAAS,CAAC,MAAiCA,eAAuB;AAClD,oBAAA,aAAa,MAAMA,UAAS;AAAA,IAC5C;AAAA,IACA,IAAI,cAAc;AAAA,IAClB,KAAK,cAAc;AAAA,EACrB;AACF;AAEA,SAAS,mBAAmB,WAA2B;AAC9C,SAAA,GAAG,oBAAoB,IAAI,SAAS;AAC7C;"}
|
package/index.html
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
LocaleCode,
|
|
3
|
+
NamespaceCode,
|
|
4
|
+
ResourceKey,
|
|
5
|
+
LocaleManagerParams,
|
|
6
|
+
SetLocaleParams,
|
|
7
|
+
} from '@dialpad/i18n-services/locale-manager';
|
|
8
|
+
|
|
9
|
+
export type { BundleSource } from '@dialpad/i18n-services/bundle-source';
|
|
10
|
+
export {
|
|
11
|
+
LocaleManager,
|
|
12
|
+
useI18N,
|
|
13
|
+
INJECTION_KEY_PREFIX,
|
|
14
|
+
} from './src/locale-manager';
|
|
15
|
+
export {
|
|
16
|
+
HTTPBundleSource,
|
|
17
|
+
RawBundleSource,
|
|
18
|
+
} from '@dialpad/i18n-services/bundle-source';
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dialpad/i18n",
|
|
3
|
+
"version": "1.16.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Dialpad's internationalization library",
|
|
6
|
+
"author": "Dialpad",
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"lint": "eslint ./ --fix",
|
|
11
|
+
"lint:ci": "eslint ./ --output-file eslint_report.json --format json",
|
|
12
|
+
"format": "prettier ./ --write",
|
|
13
|
+
"format:ci": "prettier ./ --check",
|
|
14
|
+
"test": "vitest",
|
|
15
|
+
"coverage": "vitest run --coverage --passWithNoTests",
|
|
16
|
+
"coverage:open": "pnpm run coverage && open coverage/index.html",
|
|
17
|
+
"build": "vite build --config vite.config.ts",
|
|
18
|
+
"build:types": "pnpm run types:build",
|
|
19
|
+
"types:build": "tsc",
|
|
20
|
+
"types:check": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"vue": "3.3.4",
|
|
24
|
+
"@dialpad/i18n-services": "1.5.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@dialpad/configurator": "workspace:*",
|
|
28
|
+
"@dialpad/eslint-config": "workspace:*",
|
|
29
|
+
"@vitejs/plugin-vue": "4.5.0",
|
|
30
|
+
"typescript": "5.0.4",
|
|
31
|
+
"vite": "5.0.4",
|
|
32
|
+
"vue-tsc": "1.8.8",
|
|
33
|
+
"vitest": "1.0.0-beta.6"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"development": "./index.ts",
|
|
38
|
+
"production": "./index.ts",
|
|
39
|
+
"default": "./index.ts"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"dp": {
|
|
43
|
+
"nodpend": true
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/dialpad/web-clients.git"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { LocaleManager } from '../locale-manager';
|
|
2
|
+
import { RawBundleSource } from '@dialpad/i18n-services/bundle-source';
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
|
|
5
|
+
function mockVueApp(): any {
|
|
6
|
+
return { use: () => {}, provide: () => {} };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('Fluent Formatters', () => {
|
|
10
|
+
it('formats a message successfully', async () => {
|
|
11
|
+
const bundleSource = new RawBundleSource({
|
|
12
|
+
resources: await RawBundleSource.dynamicResources([
|
|
13
|
+
['en-US', 'x', 'existing-key = Hello, world!'],
|
|
14
|
+
]),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const manager = new LocaleManager({
|
|
18
|
+
bundleSource,
|
|
19
|
+
preferredLocale: 'en-US',
|
|
20
|
+
warmUp: true,
|
|
21
|
+
fallbackLocale: 'en-US',
|
|
22
|
+
namespaces: ['x'],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await manager.change({ namespaces: ['x'] });
|
|
26
|
+
|
|
27
|
+
const app = mockVueApp();
|
|
28
|
+
manager.install(app);
|
|
29
|
+
await manager.ready;
|
|
30
|
+
|
|
31
|
+
expect(manager.fluentFormat('existing-key')).toEqual('Hello, world!');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns the key instead of missing if not found **for both this locale and the fallback locale', async () => {
|
|
35
|
+
const bundleSource = new RawBundleSource({
|
|
36
|
+
resources: await RawBundleSource.dynamicResources([
|
|
37
|
+
['en-US', 'x', 'existing-key = Hello, world!'],
|
|
38
|
+
]),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const manager = new LocaleManager({
|
|
42
|
+
bundleSource,
|
|
43
|
+
preferredLocale: 'en-US',
|
|
44
|
+
warmUp: true,
|
|
45
|
+
fallbackLocale: 'en-US',
|
|
46
|
+
namespaces: ['x'],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await manager.change({ namespaces: ['x'] });
|
|
50
|
+
|
|
51
|
+
const app = mockVueApp();
|
|
52
|
+
manager.install(app);
|
|
53
|
+
await manager.ready;
|
|
54
|
+
|
|
55
|
+
expect(manager.fluentFormat('missing-key')).toEqual('missing-key');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('uses fallbackBundle when key is not found', async () => {
|
|
59
|
+
const bundleSource = new RawBundleSource({
|
|
60
|
+
resources: await RawBundleSource.dynamicResources([
|
|
61
|
+
['es-ES', 'x', 'existing-key = Hola, mundo!'],
|
|
62
|
+
[
|
|
63
|
+
'en-US',
|
|
64
|
+
'x',
|
|
65
|
+
'existing-key = Hello, world!\nfallback-key = Fallback message',
|
|
66
|
+
],
|
|
67
|
+
]),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const manager = new LocaleManager({
|
|
71
|
+
bundleSource,
|
|
72
|
+
preferredLocale: 'es-ES',
|
|
73
|
+
warmUp: true,
|
|
74
|
+
fallbackLocale: 'en-US',
|
|
75
|
+
namespaces: ['x'],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await manager.change({ namespaces: ['x'] });
|
|
79
|
+
|
|
80
|
+
const app = mockVueApp();
|
|
81
|
+
manager.install(app);
|
|
82
|
+
await manager.ready;
|
|
83
|
+
|
|
84
|
+
expect(manager.fluentFormat('fallback-key')).toEqual('Fallback message');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('formats attributes correctly', async () => {
|
|
88
|
+
const bundleSource = new RawBundleSource({
|
|
89
|
+
resources: await RawBundleSource.dynamicResources([
|
|
90
|
+
[
|
|
91
|
+
'en-US',
|
|
92
|
+
'x',
|
|
93
|
+
'existing-key = stuff\n.attr1 = Value 1\n.attr2 = Value 2',
|
|
94
|
+
],
|
|
95
|
+
]),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const manager = new LocaleManager({
|
|
99
|
+
bundleSource,
|
|
100
|
+
preferredLocale: 'en-US',
|
|
101
|
+
warmUp: true,
|
|
102
|
+
fallbackLocale: 'en-US',
|
|
103
|
+
namespaces: ['x'],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await manager.change({ namespaces: ['x'] });
|
|
107
|
+
|
|
108
|
+
const app = mockVueApp();
|
|
109
|
+
manager.install(app);
|
|
110
|
+
await manager.ready;
|
|
111
|
+
|
|
112
|
+
const attributes = manager.fluentFormatAttrs('existing-key');
|
|
113
|
+
expect(attributes).toEqual({ attr1: 'Value 1', attr2: 'Value 2' });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns an empty object when attributes are not found', async () => {
|
|
117
|
+
const bundleSource = new RawBundleSource({
|
|
118
|
+
resources: await RawBundleSource.dynamicResources([
|
|
119
|
+
['en-US', 'x', 'no-attributes = stuff'],
|
|
120
|
+
]),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const manager = new LocaleManager({
|
|
124
|
+
bundleSource,
|
|
125
|
+
preferredLocale: 'en-US',
|
|
126
|
+
warmUp: true,
|
|
127
|
+
fallbackLocale: 'en-US',
|
|
128
|
+
namespaces: ['x'],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await manager.change({ namespaces: ['x'] });
|
|
132
|
+
|
|
133
|
+
const app = mockVueApp();
|
|
134
|
+
manager.install(app);
|
|
135
|
+
await manager.ready;
|
|
136
|
+
|
|
137
|
+
expect(manager.fluentFormatAttrs('no-attributes')).toEqual({});
|
|
138
|
+
});
|
|
139
|
+
});
|