@tolgee/core 5.0.0-rc.9be0f0e.0 → 5.0.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/README.md +174 -0
- package/README.njk.md +61 -0
- package/dist/tolgee.cjs.js +723 -351
- package/dist/tolgee.cjs.js.map +1 -1
- package/dist/tolgee.cjs.min.js +1 -1
- package/dist/tolgee.cjs.min.js.map +1 -1
- package/dist/{tolgee.esm.mjs → tolgee.esm.js} +722 -346
- package/dist/tolgee.esm.js.map +1 -0
- package/dist/tolgee.esm.min.mjs +1 -1
- package/dist/tolgee.esm.min.mjs.map +1 -1
- package/dist/tolgee.umd.js +723 -351
- package/dist/tolgee.umd.js.map +1 -1
- package/dist/tolgee.umd.min.js +1 -1
- package/dist/tolgee.umd.min.js.map +1 -1
- package/lib/Controller/Cache/Cache.d.ts +10 -9
- package/lib/Controller/Controller.d.ts +104 -45
- package/lib/Controller/Events/EventEmitter.d.ts +6 -0
- package/lib/Controller/Events/EventEmitterSelective.d.ts +7 -0
- package/lib/Controller/Events/Events.d.ts +14 -0
- package/lib/Controller/Plugins/Plugins.d.ts +12 -25
- package/lib/Controller/State/State.d.ts +27 -9
- package/lib/Controller/State/initState.d.ts +46 -15
- package/lib/Controller/State/observerOptions.d.ts +41 -0
- package/lib/Controller/ValueObserver.d.ts +5 -5
- package/lib/FormatSimple/FormatError.d.ts +7 -0
- package/lib/FormatSimple/FormatSimple.d.ts +2 -0
- package/lib/FormatSimple/formatParser.d.ts +1 -0
- package/lib/FormatSimple/formatter.d.ts +2 -0
- package/lib/TolgeeCore.d.ts +204 -0
- package/lib/TranslateParams.d.ts +1 -1
- package/lib/helpers.d.ts +8 -0
- package/lib/index.d.ts +4 -4
- package/lib/types/cache.d.ts +25 -0
- package/lib/types/events.d.ts +66 -0
- package/lib/types/general.d.ts +34 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/plugin.d.ts +127 -0
- package/package.json +5 -4
- package/src/Controller/Cache/Cache.ts +31 -31
- package/src/Controller/Cache/helpers.ts +6 -6
- package/src/Controller/Controller.ts +78 -50
- package/src/Controller/Events/EventEmitter.ts +34 -0
- package/src/Controller/Events/EventEmitterSelective.test.ts +110 -0
- package/src/Controller/Events/EventEmitterSelective.ts +132 -0
- package/src/Controller/Events/Events.ts +69 -0
- package/src/Controller/Plugins/Plugins.ts +182 -133
- package/src/Controller/State/State.ts +43 -26
- package/src/Controller/State/initState.ts +97 -25
- package/src/Controller/State/observerOptions.ts +66 -0
- package/src/Controller/ValueObserver.ts +5 -2
- package/src/FormatSimple/FormatError.ts +26 -0
- package/src/FormatSimple/FormatSimple.ts +13 -0
- package/src/FormatSimple/formatParser.ts +133 -0
- package/src/FormatSimple/formatter.test.ts +190 -0
- package/src/FormatSimple/formatter.ts +19 -0
- package/src/TolgeeCore.ts +267 -0
- package/src/TranslateParams.test.ts +9 -12
- package/src/TranslateParams.ts +6 -5
- package/src/__test/backend.test.ts +6 -6
- package/src/__test/cache.test.ts +190 -0
- package/src/__test/client.test.ts +2 -2
- package/src/__test/events.test.ts +32 -7
- package/src/__test/format.simple.test.ts +14 -0
- package/src/__test/formatError.test.ts +61 -0
- package/src/__test/initialization.test.ts +15 -3
- package/src/__test/languageDetection.test.ts +14 -8
- package/src/__test/languageStorage.test.ts +10 -11
- package/src/__test/languages.test.ts +30 -6
- package/src/__test/loading.test.ts +2 -2
- package/src/__test/{namespacesFallback.test.ts → namespaces.fallback.test.ts} +10 -8
- package/src/__test/namespaces.test.ts +30 -7
- package/src/__test/options.test.ts +64 -0
- package/src/__test/plugins.test.ts +29 -18
- package/src/helpers.ts +53 -0
- package/src/index.ts +4 -10
- package/src/types/cache.ts +37 -0
- package/src/types/events.ts +85 -0
- package/src/types/general.ts +50 -0
- package/src/types/index.ts +19 -0
- package/src/types/plugin.ts +181 -0
- package/dist/tolgee.esm.mjs.map +0 -1
- package/lib/Controller/State/helpers.d.ts +0 -6
- package/lib/Events/EventEmitter.d.ts +0 -6
- package/lib/Events/EventEmitterSelective.d.ts +0 -15
- package/lib/Events/Events.d.ts +0 -50
- package/lib/Tolgee.d.ts +0 -2
- package/lib/constants.d.ts +0 -5
- package/lib/types.d.ts +0 -274
- package/src/Controller/State/helpers.ts +0 -41
- package/src/Events/EventEmitter.ts +0 -27
- package/src/Events/EventEmitterSelective.test.ts +0 -108
- package/src/Events/EventEmitterSelective.ts +0 -160
- package/src/Events/Events.ts +0 -66
- package/src/Tolgee.ts +0 -77
- package/src/constants.ts +0 -7
- package/src/types.ts +0 -380
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CacheAsyncRequests,
|
|
3
2
|
CacheDescriptor,
|
|
4
3
|
CacheDescriptorInternal,
|
|
5
4
|
CacheDescriptorWithKey,
|
|
6
|
-
|
|
7
|
-
FallbackNSTranslation,
|
|
8
|
-
Options,
|
|
5
|
+
NsFallback,
|
|
9
6
|
TranslationsFlat,
|
|
10
7
|
TranslationValue,
|
|
11
8
|
TreeTranslationsData,
|
|
12
9
|
BackendGetRecord,
|
|
13
10
|
BackendGetDevRecord,
|
|
14
11
|
} from '../../types';
|
|
15
|
-
import { getFallbackArray } from '
|
|
12
|
+
import { getFallbackArray, unique } from '../../helpers';
|
|
13
|
+
import { EventEmitterInstance } from '../Events/EventEmitter';
|
|
14
|
+
import { TolgeeStaticData } from '../State/initState';
|
|
16
15
|
import { ValueObserverInstance } from '../ValueObserver';
|
|
17
16
|
|
|
18
17
|
import { decodeCacheKey, encodeCacheKey, flattenTranslations } from './helpers';
|
|
19
18
|
|
|
19
|
+
type CacheAsyncRequests = Map<
|
|
20
|
+
string,
|
|
21
|
+
Promise<TreeTranslationsData | undefined> | undefined
|
|
22
|
+
>;
|
|
23
|
+
|
|
20
24
|
type CacheRecord = {
|
|
21
25
|
version: number;
|
|
22
26
|
data: TranslationsFlat;
|
|
@@ -25,7 +29,7 @@ type CacheRecord = {
|
|
|
25
29
|
type StateCache = Map<string, CacheRecord>;
|
|
26
30
|
|
|
27
31
|
export const Cache = (
|
|
28
|
-
onCacheChange:
|
|
32
|
+
onCacheChange: EventEmitterInstance<CacheDescriptorWithKey>,
|
|
29
33
|
backendGetRecord: BackendGetRecord,
|
|
30
34
|
backendGetDevRecord: BackendGetDevRecord,
|
|
31
35
|
withDefaultNs: (descriptor: CacheDescriptor) => CacheDescriptorInternal,
|
|
@@ -35,10 +39,10 @@ export const Cache = (
|
|
|
35
39
|
) => {
|
|
36
40
|
const asyncRequests: CacheAsyncRequests = new Map();
|
|
37
41
|
const cache: StateCache = new Map();
|
|
38
|
-
let staticData: NonNullable<
|
|
42
|
+
let staticData: NonNullable<TolgeeStaticData> = {};
|
|
39
43
|
let version = 0;
|
|
40
44
|
|
|
41
|
-
function addStaticData(data:
|
|
45
|
+
function addStaticData(data: TolgeeStaticData | undefined) {
|
|
42
46
|
if (data) {
|
|
43
47
|
staticData = { ...staticData, ...data };
|
|
44
48
|
Object.entries(data).forEach(([key, value]) => {
|
|
@@ -86,8 +90,8 @@ export const Cache = (
|
|
|
86
90
|
return Boolean(record);
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
function getRecord(descriptor:
|
|
90
|
-
return cache.get(encodeCacheKey(descriptor))?.data;
|
|
93
|
+
function getRecord(descriptor: CacheDescriptor) {
|
|
94
|
+
return cache.get(encodeCacheKey(withDefaultNs(descriptor)))?.data;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
function getTranslation(descriptor: CacheDescriptorInternal, key: string) {
|
|
@@ -105,11 +109,11 @@ export const Cache = (
|
|
|
105
109
|
.get(encodeCacheKey({ language, namespace }))
|
|
106
110
|
?.data.get(key);
|
|
107
111
|
if (value !== undefined && value !== null) {
|
|
108
|
-
return namespace;
|
|
112
|
+
return [namespace];
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
|
-
return
|
|
116
|
+
return unique(namespaces);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
function getTranslationFallback(
|
|
@@ -140,11 +144,7 @@ export const Cache = (
|
|
|
140
144
|
onCacheChange.emit({ ...descriptor, key });
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
function
|
|
144
|
-
cache.clear();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function isFetching(ns?: FallbackNSTranslation) {
|
|
147
|
+
function isFetching(ns?: NsFallback) {
|
|
148
148
|
if (isInitialLoading()) {
|
|
149
149
|
return true;
|
|
150
150
|
}
|
|
@@ -160,7 +160,7 @@ export const Cache = (
|
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
function isLoading(language: string | undefined, ns?:
|
|
163
|
+
function isLoading(language: string | undefined, ns?: NsFallback) {
|
|
164
164
|
const namespaces = getFallbackArray(ns);
|
|
165
165
|
|
|
166
166
|
return Boolean(
|
|
@@ -178,7 +178,10 @@ export const Cache = (
|
|
|
178
178
|
);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Fetches production data
|
|
183
|
+
*/
|
|
184
|
+
function fetchProd(keyObject: CacheDescriptorInternal) {
|
|
182
185
|
let dataPromise = undefined as
|
|
183
186
|
| Promise<TreeTranslationsData | undefined>
|
|
184
187
|
| undefined;
|
|
@@ -186,8 +189,6 @@ export const Cache = (
|
|
|
186
189
|
const staticDataValue = staticData[encodeCacheKey(keyObject)];
|
|
187
190
|
if (typeof staticDataValue === 'function') {
|
|
188
191
|
dataPromise = staticDataValue();
|
|
189
|
-
} else if (staticDataValue) {
|
|
190
|
-
dataPromise = Promise.resolve(staticDataValue);
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
|
|
@@ -195,10 +196,6 @@ export const Cache = (
|
|
|
195
196
|
dataPromise = backendGetRecord(keyObject);
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
if (!dataPromise) {
|
|
199
|
-
// return empty data, so we know it has already been attempted to fetch
|
|
200
|
-
dataPromise = Promise.resolve({});
|
|
201
|
-
}
|
|
202
199
|
return dataPromise;
|
|
203
200
|
}
|
|
204
201
|
|
|
@@ -210,13 +207,13 @@ export const Cache = (
|
|
|
210
207
|
dataPromise = backendGetDevRecord(keyObject)?.catch(() => {
|
|
211
208
|
// eslint-disable-next-line no-console
|
|
212
209
|
console.warn(`Tolgee: Failed to fetch data from dev backend`);
|
|
213
|
-
// fallback to
|
|
214
|
-
return
|
|
210
|
+
// fallback to prod fetch if dev fails
|
|
211
|
+
return fetchProd(keyObject);
|
|
215
212
|
});
|
|
216
213
|
}
|
|
217
214
|
|
|
218
215
|
if (!dataPromise) {
|
|
219
|
-
dataPromise =
|
|
216
|
+
dataPromise = fetchProd(keyObject);
|
|
220
217
|
}
|
|
221
218
|
|
|
222
219
|
return dataPromise;
|
|
@@ -236,7 +233,8 @@ export const Cache = (
|
|
|
236
233
|
cacheKey,
|
|
237
234
|
};
|
|
238
235
|
}
|
|
239
|
-
const dataPromise =
|
|
236
|
+
const dataPromise =
|
|
237
|
+
fetchData(keyObject, isDev) || Promise.resolve(undefined);
|
|
240
238
|
asyncRequests.set(cacheKey, dataPromise);
|
|
241
239
|
return {
|
|
242
240
|
new: true,
|
|
@@ -260,6 +258,9 @@ export const Cache = (
|
|
|
260
258
|
const data = results[i];
|
|
261
259
|
if (data) {
|
|
262
260
|
addRecord(value.keyObject, data);
|
|
261
|
+
} else if (!getRecord(value.keyObject)) {
|
|
262
|
+
// if no data exist, put empty object
|
|
263
|
+
addRecord(value.keyObject, {});
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
});
|
|
@@ -292,9 +293,8 @@ export const Cache = (
|
|
|
292
293
|
isFetching,
|
|
293
294
|
isLoading,
|
|
294
295
|
loadRecords,
|
|
295
|
-
clear,
|
|
296
296
|
getAllRecords,
|
|
297
297
|
});
|
|
298
298
|
};
|
|
299
299
|
|
|
300
|
-
export type
|
|
300
|
+
export type CacheInstance = ReturnType<typeof Cache>;
|
|
@@ -10,11 +10,9 @@ export const flattenTranslations = (
|
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
if (typeof value === 'object') {
|
|
13
|
-
|
|
14
|
-
(
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
);
|
|
13
|
+
flattenTranslations(value).forEach((flatValue, flatKey) => {
|
|
14
|
+
result.set(key + '.' + flatKey, flatValue);
|
|
15
|
+
});
|
|
18
16
|
return;
|
|
19
17
|
}
|
|
20
18
|
result.set(key, value as string);
|
|
@@ -23,7 +21,9 @@ export const flattenTranslations = (
|
|
|
23
21
|
};
|
|
24
22
|
|
|
25
23
|
export const decodeCacheKey = (key: string): CacheDescriptorInternal => {
|
|
26
|
-
const [firstPart,
|
|
24
|
+
const [firstPart, ...rest] = key.split(':');
|
|
25
|
+
// if namespaces contains ":" it won't get lost
|
|
26
|
+
const secondPart = rest.join(':');
|
|
27
27
|
return { language: firstPart, namespace: secondPart || '' };
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Events } from './Events/Events';
|
|
2
2
|
import {
|
|
3
3
|
CacheDescriptor,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
NsFallback,
|
|
5
|
+
TolgeeOptions,
|
|
6
6
|
TFnType,
|
|
7
|
-
|
|
7
|
+
NsType,
|
|
8
|
+
KeyAndNamespacesInternal,
|
|
8
9
|
} from '../types';
|
|
9
10
|
import { Cache } from './Cache/Cache';
|
|
10
|
-
import { getFallbackArray } from '
|
|
11
|
-
import {
|
|
11
|
+
import { getFallbackArray } from '../helpers';
|
|
12
|
+
import { Plugins } from './Plugins/Plugins';
|
|
12
13
|
import { ValueObserver } from './ValueObserver';
|
|
13
14
|
import { State } from './State/State';
|
|
14
15
|
import { isPromise, missingOptionError, valueOrPromise } from '../helpers';
|
|
15
|
-
import {
|
|
16
|
+
import { getTranslateProps } from '../TranslateParams';
|
|
16
17
|
|
|
17
18
|
type StateServiceProps = {
|
|
18
|
-
|
|
19
|
-
options?: Partial<Options>;
|
|
19
|
+
options?: Partial<TolgeeOptions>;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export const Controller = ({
|
|
22
|
+
export const Controller = ({ options }: StateServiceProps) => {
|
|
23
|
+
const events = Events(getFallbackNs, getDefaultNs);
|
|
23
24
|
const fetchingObserver = ValueObserver<boolean>(
|
|
24
25
|
false,
|
|
25
26
|
() => cache.isFetching(),
|
|
@@ -37,7 +38,7 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
37
38
|
events.onRunningChange
|
|
38
39
|
);
|
|
39
40
|
|
|
40
|
-
const pluginService =
|
|
41
|
+
const pluginService = Plugins(
|
|
41
42
|
state.getLanguage,
|
|
42
43
|
state.getInitialOptions,
|
|
43
44
|
state.getAvailableLanguages,
|
|
@@ -56,24 +57,38 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
56
57
|
loadingObserver
|
|
57
58
|
);
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (isDev()) {
|
|
62
|
-
cache.invalidate();
|
|
60
|
+
if (options) {
|
|
61
|
+
init(options);
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
events.
|
|
64
|
+
events.onUpdate.listen(() => {
|
|
66
65
|
if (state.isRunning()) {
|
|
67
66
|
pluginService.retranslate();
|
|
68
67
|
}
|
|
69
68
|
});
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
function getFallbackNs() {
|
|
71
|
+
return state.getFallbackNs();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getDefaultNs(ns?: NsType) {
|
|
75
|
+
return state.getDefaultNs(ns);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// gets all namespaces where translation could be located
|
|
79
|
+
// takes (ns|default, fallback ns)
|
|
80
|
+
function getDefaultAndFallbackNs(ns?: NsType) {
|
|
81
|
+
return [...getFallbackArray(getDefaultNs(ns)), ...getFallbackNs()];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// gets all namespaces which need to be loaded
|
|
85
|
+
// takes (ns|default, initial ns, fallback ns, active ns)
|
|
86
|
+
function getRequiredNamespaces(ns: NsFallback) {
|
|
87
|
+
return [
|
|
88
|
+
...getFallbackArray(ns || getDefaultNs()),
|
|
89
|
+
...state.getRequiredNamespaces(),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
77
92
|
|
|
78
93
|
function changeTranslation(
|
|
79
94
|
descriptor: CacheDescriptor,
|
|
@@ -90,22 +105,22 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
90
105
|
};
|
|
91
106
|
}
|
|
92
107
|
|
|
93
|
-
function init(options: Partial<
|
|
108
|
+
function init(options: Partial<TolgeeOptions>) {
|
|
94
109
|
state.init(options);
|
|
95
110
|
cache.addStaticData(state.getInitialOptions().staticData);
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
function isLoading(ns?:
|
|
113
|
+
function isLoading(ns?: NsFallback) {
|
|
99
114
|
return cache.isLoading(state.getLanguage()!, ns);
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
function isDev() {
|
|
103
118
|
return Boolean(
|
|
104
|
-
state.getInitialOptions().apiKey &&
|
|
119
|
+
state.getInitialOptions().apiKey && state.getInitialOptions().apiUrl
|
|
105
120
|
);
|
|
106
121
|
}
|
|
107
122
|
|
|
108
|
-
async function addActiveNs(ns:
|
|
123
|
+
async function addActiveNs(ns: NsFallback, forget?: boolean) {
|
|
109
124
|
if (!forget) {
|
|
110
125
|
state.addActiveNs(ns);
|
|
111
126
|
}
|
|
@@ -114,10 +129,9 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
131
|
|
|
117
|
-
function getRequiredRecords(lang?: string, ns?:
|
|
132
|
+
function getRequiredRecords(lang?: string, ns?: NsFallback) {
|
|
118
133
|
const languages = state.getFallbackLangs(lang);
|
|
119
|
-
const namespaces =
|
|
120
|
-
ns !== undefined ? getFallbackArray(ns) : state.getRequiredNamespaces();
|
|
134
|
+
const namespaces = getRequiredNamespaces(ns);
|
|
121
135
|
const result: CacheDescriptor[] = [];
|
|
122
136
|
languages.forEach((language) => {
|
|
123
137
|
namespaces.forEach((namespace) => {
|
|
@@ -129,14 +143,13 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
129
143
|
return result;
|
|
130
144
|
}
|
|
131
145
|
|
|
132
|
-
function isLoaded(ns?:
|
|
146
|
+
function isLoaded(ns?: NsFallback) {
|
|
133
147
|
const language = state.getLanguage();
|
|
134
148
|
if (!language) {
|
|
135
149
|
return false;
|
|
136
150
|
}
|
|
137
151
|
const languages = state.getFallbackLangs(language);
|
|
138
|
-
const namespaces =
|
|
139
|
-
ns !== undefined ? getFallbackArray(ns) : state.getRequiredNamespaces();
|
|
152
|
+
const namespaces = getRequiredNamespaces(ns);
|
|
140
153
|
const result: CacheDescriptor[] = [];
|
|
141
154
|
languages.forEach((language) => {
|
|
142
155
|
namespaces.forEach((namespace) => {
|
|
@@ -148,7 +161,7 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
148
161
|
return result.length === 0;
|
|
149
162
|
}
|
|
150
163
|
|
|
151
|
-
function loadRequiredRecords(lang?: string, ns?:
|
|
164
|
+
function loadRequiredRecords(lang?: string, ns?: NsFallback) {
|
|
152
165
|
const descriptors = getRequiredRecords(lang, ns);
|
|
153
166
|
if (descriptors.length) {
|
|
154
167
|
return valueOrPromise(loadRecords(descriptors), () => {});
|
|
@@ -176,24 +189,14 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
176
189
|
}
|
|
177
190
|
}
|
|
178
191
|
|
|
179
|
-
function getTranslationNs({
|
|
180
|
-
key,
|
|
181
|
-
ns,
|
|
182
|
-
}: Pick<TranslatePropsInternal, 'key' | 'ns'>) {
|
|
183
|
-
const namespaces = ns
|
|
184
|
-
? getFallbackArray(ns)
|
|
185
|
-
: state.getFallbackNamespaces();
|
|
192
|
+
function getTranslationNs({ key, ns }: KeyAndNamespacesInternal) {
|
|
186
193
|
const languages = state.getFallbackLangs();
|
|
194
|
+
const namespaces = getDefaultAndFallbackNs(ns);
|
|
187
195
|
return cache.getTranslationNs(namespaces, languages, key);
|
|
188
196
|
}
|
|
189
197
|
|
|
190
|
-
function getTranslation({
|
|
191
|
-
|
|
192
|
-
ns,
|
|
193
|
-
}: Pick<TranslatePropsInternal, 'key' | 'ns'>) {
|
|
194
|
-
const namespaces = ns
|
|
195
|
-
? getFallbackArray(ns)
|
|
196
|
-
: state.getFallbackNamespaces();
|
|
198
|
+
function getTranslation({ key, ns }: KeyAndNamespacesInternal) {
|
|
199
|
+
const namespaces = getDefaultAndFallbackNs(ns);
|
|
197
200
|
const languages = state.getFallbackLangs();
|
|
198
201
|
return cache.getTranslationFallback(namespaces, languages, key);
|
|
199
202
|
}
|
|
@@ -201,7 +204,6 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
201
204
|
function loadInitial() {
|
|
202
205
|
const data = valueOrPromise(initializeLanguage(), () => {
|
|
203
206
|
// fail if there is no language
|
|
204
|
-
state.getLanguageOrFail();
|
|
205
207
|
return loadRequiredRecords();
|
|
206
208
|
});
|
|
207
209
|
|
|
@@ -245,8 +247,27 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
245
247
|
return cache.loadRecords(descriptors, isDev());
|
|
246
248
|
}
|
|
247
249
|
|
|
250
|
+
const checkCorrectConfiguration = () => {
|
|
251
|
+
const languageComputable =
|
|
252
|
+
pluginService.getLanguageDetector() || pluginService.getLanguageStorage();
|
|
253
|
+
if (languageComputable) {
|
|
254
|
+
const availableLanguages = state.getAvailableLanguages();
|
|
255
|
+
if (!availableLanguages) {
|
|
256
|
+
throw new Error(missingOptionError('availableLanguages'));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!state.getLanguage() && !state.getInitialOptions().defaultLanguage) {
|
|
260
|
+
if (languageComputable) {
|
|
261
|
+
throw new Error(missingOptionError('defaultLanguage'));
|
|
262
|
+
} else {
|
|
263
|
+
throw new Error(missingOptionError('language'));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
248
268
|
function run() {
|
|
249
269
|
let result: Promise<void> | undefined = undefined;
|
|
270
|
+
checkCorrectConfiguration();
|
|
250
271
|
if (!state.isRunning()) {
|
|
251
272
|
if (isDev()) {
|
|
252
273
|
cache.invalidate();
|
|
@@ -265,7 +286,15 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
265
286
|
}
|
|
266
287
|
}
|
|
267
288
|
|
|
289
|
+
const t: TFnType = (...args) => {
|
|
290
|
+
// @ts-ignore
|
|
291
|
+
const params = getTranslateProps(...args);
|
|
292
|
+
const translation = getTranslation(params);
|
|
293
|
+
return pluginService.formatTranslation({ ...params, translation });
|
|
294
|
+
};
|
|
295
|
+
|
|
268
296
|
return Object.freeze({
|
|
297
|
+
...events,
|
|
269
298
|
...state,
|
|
270
299
|
...pluginService,
|
|
271
300
|
...cache,
|
|
@@ -274,7 +303,6 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
274
303
|
getTranslation,
|
|
275
304
|
changeTranslation,
|
|
276
305
|
addActiveNs,
|
|
277
|
-
loadRequiredRecords,
|
|
278
306
|
loadRecords,
|
|
279
307
|
loadRecord,
|
|
280
308
|
isLoading,
|
|
@@ -286,4 +314,4 @@ export const Controller = ({ events, options }: StateServiceProps) => {
|
|
|
286
314
|
});
|
|
287
315
|
};
|
|
288
316
|
|
|
289
|
-
export type
|
|
317
|
+
export type ControllerInstance = ReturnType<typeof Controller>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Subscription, Listener } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const EventEmitter = <T>(
|
|
4
|
+
isActive: () => boolean
|
|
5
|
+
): EventEmitterInstance<T> => {
|
|
6
|
+
let handlers: Listener<T>[] = [];
|
|
7
|
+
|
|
8
|
+
const listen = (handler: Listener<T>): Subscription => {
|
|
9
|
+
const handlerWrapper: Listener<T> = (e) => {
|
|
10
|
+
handler(e);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
handlers.push(handlerWrapper);
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
unsubscribe: () => {
|
|
17
|
+
handlers = handlers.filter((i) => handlerWrapper !== i);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const emit = (data: T) => {
|
|
23
|
+
if (isActive()) {
|
|
24
|
+
handlers.forEach((handler) => handler({ value: data }));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return Object.freeze({ listen, emit });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type EventEmitterInstance<T> = {
|
|
32
|
+
readonly listen: (handler: Listener<T>) => Subscription;
|
|
33
|
+
readonly emit: (data: T) => void;
|
|
34
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { EventEmitterSelective } from './EventEmitterSelective';
|
|
2
|
+
|
|
3
|
+
describe('event emitter selective', () => {
|
|
4
|
+
it('handles correctly default namespace', () => {
|
|
5
|
+
const emitter = EventEmitterSelective(
|
|
6
|
+
() => true,
|
|
7
|
+
() => [],
|
|
8
|
+
() => 'default'
|
|
9
|
+
);
|
|
10
|
+
const handler = jest.fn();
|
|
11
|
+
const listener = emitter.listenSome(handler);
|
|
12
|
+
|
|
13
|
+
// subscribe to default ns
|
|
14
|
+
listener.subscribeNs();
|
|
15
|
+
|
|
16
|
+
// emmit
|
|
17
|
+
emitter.emit(['default']);
|
|
18
|
+
// should be ignored
|
|
19
|
+
emitter.emit(['c']);
|
|
20
|
+
|
|
21
|
+
expect(handler).toBeCalledTimes(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('unsubscribes', () => {
|
|
25
|
+
const emitter = EventEmitterSelective(
|
|
26
|
+
() => true,
|
|
27
|
+
() => [],
|
|
28
|
+
() => ''
|
|
29
|
+
);
|
|
30
|
+
const handler = jest.fn();
|
|
31
|
+
const listener = emitter.listen(handler);
|
|
32
|
+
|
|
33
|
+
emitter.emit();
|
|
34
|
+
|
|
35
|
+
listener.unsubscribe();
|
|
36
|
+
emitter.emit();
|
|
37
|
+
expect(handler).toBeCalledTimes(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('groups events correctly', async () => {
|
|
41
|
+
const emitter = EventEmitterSelective(
|
|
42
|
+
() => true,
|
|
43
|
+
() => ['test', 'opqrst'],
|
|
44
|
+
() => ''
|
|
45
|
+
);
|
|
46
|
+
const handler = jest.fn();
|
|
47
|
+
const hanlderAll = jest.fn();
|
|
48
|
+
const listener = emitter.listenSome(handler);
|
|
49
|
+
const listenerAll = emitter.listen(hanlderAll);
|
|
50
|
+
|
|
51
|
+
listener.subscribeNs('test');
|
|
52
|
+
|
|
53
|
+
// is fallback should always call handler
|
|
54
|
+
emitter.emit(['opqrst'], true);
|
|
55
|
+
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
57
|
+
|
|
58
|
+
expect(hanlderAll).toBeCalledTimes(1);
|
|
59
|
+
expect(handler).toBeCalledTimes(1);
|
|
60
|
+
|
|
61
|
+
// these should be merged together
|
|
62
|
+
emitter.emit(['abcd'], true);
|
|
63
|
+
emitter.emit(['abcd']);
|
|
64
|
+
|
|
65
|
+
expect(hanlderAll).toBeCalledTimes(2);
|
|
66
|
+
expect(handler).toBeCalledTimes(1);
|
|
67
|
+
|
|
68
|
+
listener.unsubscribe();
|
|
69
|
+
listenerAll.unsubscribe();
|
|
70
|
+
emitter.emit();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('always subscribes to fallback ns', async () => {
|
|
74
|
+
const emitter = EventEmitterSelective(
|
|
75
|
+
() => true,
|
|
76
|
+
() => ['fallback1', 'fallback2'],
|
|
77
|
+
() => ''
|
|
78
|
+
);
|
|
79
|
+
const handler = jest.fn();
|
|
80
|
+
emitter.listenSome(handler);
|
|
81
|
+
|
|
82
|
+
emitter.emit(['fallback1']);
|
|
83
|
+
expect(handler).toBeCalledTimes(1);
|
|
84
|
+
|
|
85
|
+
emitter.emit(['fallback2']);
|
|
86
|
+
expect(handler).toBeCalledTimes(2);
|
|
87
|
+
|
|
88
|
+
emitter.emit(['test']);
|
|
89
|
+
expect(handler).toBeCalledTimes(2);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('switches off emitting', () => {
|
|
93
|
+
const emitter = EventEmitterSelective(
|
|
94
|
+
() => false,
|
|
95
|
+
() => ['fallback1', 'fallback2'],
|
|
96
|
+
() => ''
|
|
97
|
+
);
|
|
98
|
+
const handler = jest.fn();
|
|
99
|
+
emitter.listenSome(handler);
|
|
100
|
+
|
|
101
|
+
emitter.emit(['fallback1']);
|
|
102
|
+
expect(handler).toBeCalledTimes(0);
|
|
103
|
+
|
|
104
|
+
emitter.emit(['fallback2']);
|
|
105
|
+
expect(handler).toBeCalledTimes(0);
|
|
106
|
+
|
|
107
|
+
emitter.emit(['']);
|
|
108
|
+
expect(handler).toBeCalledTimes(0);
|
|
109
|
+
});
|
|
110
|
+
});
|