@tolgee/core 4.7.0 → 4.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tolgee.cjs.js +2 -4
- 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.js → tolgee.esm.min.mjs} +2 -2
- package/dist/tolgee.esm.min.mjs.map +1 -0
- package/dist/tolgee.esm.mjs +5690 -0
- package/dist/tolgee.esm.mjs.map +1 -0
- package/dist/tolgee.umd.js +2 -4
- 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/package.json +10 -9
- package/src/Constants/Global.ts +9 -0
- package/src/Constants/ModifierKey.ts +6 -0
- package/src/Errors/ApiHttpError.ts +8 -0
- package/src/Observer.test.ts +119 -0
- package/src/Observer.ts +68 -0
- package/src/Properties.test.ts +150 -0
- package/src/Properties.ts +112 -0
- package/src/Tolgee.test.ts +473 -0
- package/src/Tolgee.ts +335 -0
- package/src/TolgeeConfig.test.ts +21 -0
- package/src/TolgeeConfig.ts +134 -0
- package/src/__integration/FormatterIcu.test.ts +80 -0
- package/src/__integration/FormatterMissing.ts +54 -0
- package/src/__integration/Tolgee.test.ts +90 -0
- package/src/__integration/TolgeeInvisible.test.ts +145 -0
- package/src/__integration/mockTranslations.ts +6 -0
- package/src/__integration/testConfig.ts +16 -0
- package/src/__testFixtures/classMock.ts +11 -0
- package/src/__testFixtures/createElement.ts +43 -0
- package/src/__testFixtures/createTestDom.ts +25 -0
- package/src/__testFixtures/mocked.ts +25 -0
- package/src/__testFixtures/setupAfterEnv.ts +34 -0
- package/src/helpers/NodeHelper.ts +90 -0
- package/src/helpers/TextHelper.test.ts +62 -0
- package/src/helpers/TextHelper.ts +58 -0
- package/src/helpers/commonTypes.ts +8 -0
- package/src/helpers/encoderPolyfill.ts +96 -0
- package/src/helpers/secret.test.ts +61 -0
- package/src/helpers/secret.ts +68 -0
- package/src/helpers/sleep.ts +2 -0
- package/src/highlighter/HighlightFunctionsInitializer.test.ts +40 -0
- package/src/highlighter/HighlightFunctionsInitializer.ts +61 -0
- package/src/highlighter/MouseEventHandler.test.ts +151 -0
- package/src/highlighter/MouseEventHandler.ts +191 -0
- package/src/highlighter/TranslationHighlighter.test.ts +177 -0
- package/src/highlighter/TranslationHighlighter.ts +113 -0
- package/src/index.ts +10 -0
- package/src/internal.ts +2 -0
- package/src/modules/IcuFormatter.ts +17 -0
- package/src/modules/index.ts +1 -0
- package/src/services/ApiHttpService.ts +85 -0
- package/src/services/CoreService.test.ts +142 -0
- package/src/services/CoreService.ts +76 -0
- package/src/services/DependencyService.test.ts +51 -0
- package/src/services/DependencyService.ts +116 -0
- package/src/services/ElementRegistrar.test.ts +131 -0
- package/src/services/ElementRegistrar.ts +108 -0
- package/src/services/EventEmitter.ts +52 -0
- package/src/services/EventService.ts +14 -0
- package/src/services/ModuleService.ts +14 -0
- package/src/services/ScreenshotService.ts +31 -0
- package/src/services/Subscription.ts +7 -0
- package/src/services/TextService.test.ts +88 -0
- package/src/services/TextService.ts +82 -0
- package/src/services/TranslationService.test.ts +358 -0
- package/src/services/TranslationService.ts +417 -0
- package/src/services/__mocks__/CoreService.ts +17 -0
- package/src/toolsManager/Messages.test.ts +79 -0
- package/src/toolsManager/Messages.ts +60 -0
- package/src/toolsManager/PluginManager.test.ts +108 -0
- package/src/toolsManager/PluginManager.ts +129 -0
- package/src/types/DTOs.ts +25 -0
- package/src/types/apiSchema.generated.ts +6208 -0
- package/src/types.ts +146 -0
- package/src/wrappers/AbstractWrapper.ts +14 -0
- package/src/wrappers/NodeHandler.ts +143 -0
- package/src/wrappers/WrappedHandler.ts +28 -0
- package/src/wrappers/invisible/AttributeHandler.ts +23 -0
- package/src/wrappers/invisible/Coder.ts +65 -0
- package/src/wrappers/invisible/ContentHandler.ts +15 -0
- package/src/wrappers/invisible/CoreHandler.ts +17 -0
- package/src/wrappers/invisible/InvisibleWrapper.ts +59 -0
- package/src/wrappers/invisible/ValueMemory.test.ts +25 -0
- package/src/wrappers/invisible/ValueMemory.ts +16 -0
- package/src/wrappers/text/AttributeHandler.test.ts +117 -0
- package/src/wrappers/text/AttributeHandler.ts +25 -0
- package/src/wrappers/text/Coder.test.ts +298 -0
- package/src/wrappers/text/Coder.ts +202 -0
- package/src/wrappers/text/ContentHandler.test.ts +185 -0
- package/src/wrappers/text/ContentHandler.ts +21 -0
- package/src/wrappers/text/CoreHandler.test.ts +106 -0
- package/src/wrappers/text/CoreHandler.ts +45 -0
- package/src/wrappers/text/TextWrapper.ts +69 -0
- package/dist/tolgee.esm.js.map +0 -1
package/src/Tolgee.ts
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { TolgeeConfig } from './TolgeeConfig';
|
|
2
|
+
import {
|
|
3
|
+
InstantProps,
|
|
4
|
+
InstantPropsTags,
|
|
5
|
+
TolgeeModule,
|
|
6
|
+
TranslateProps,
|
|
7
|
+
TranslatePropsTags,
|
|
8
|
+
TranslationTags,
|
|
9
|
+
TranslationParams,
|
|
10
|
+
TranslationParamsTags,
|
|
11
|
+
} from './types';
|
|
12
|
+
|
|
13
|
+
import { EventEmitterImpl } from './services/EventEmitter';
|
|
14
|
+
import { DependencyService } from './services/DependencyService';
|
|
15
|
+
|
|
16
|
+
export class Tolgee {
|
|
17
|
+
private dependencyService: DependencyService;
|
|
18
|
+
|
|
19
|
+
private constructor() {
|
|
20
|
+
this.dependencyService = new DependencyService();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get properties() {
|
|
24
|
+
return this.dependencyService.properties;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public get lang() {
|
|
28
|
+
return this.properties.currentLanguage;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This sets a new language.
|
|
33
|
+
*
|
|
34
|
+
* Using this setter can behave buggy when you change languages
|
|
35
|
+
* too fast, since it changes the language property before
|
|
36
|
+
* translations are actually loaded.
|
|
37
|
+
* @deprecated use asynchronous changeLanguage method.
|
|
38
|
+
*/
|
|
39
|
+
public set lang(newLanguage) {
|
|
40
|
+
this.properties.currentLanguage = newLanguage;
|
|
41
|
+
|
|
42
|
+
this.dependencyService.translationService
|
|
43
|
+
.loadTranslations(newLanguage)
|
|
44
|
+
.then(() => {
|
|
45
|
+
this.emitLangChangeEvent(newLanguage);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public get defaultLanguage() {
|
|
50
|
+
return this.properties.config.defaultLanguage;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public get onLangChange() {
|
|
54
|
+
return this.dependencyService.eventService.LANGUAGE_CHANGED;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public get onTranslationChange() {
|
|
58
|
+
return this.dependencyService.eventService.TRANSLATION_CHANGED;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Is emitted when language is loaded for the first time
|
|
63
|
+
*/
|
|
64
|
+
public get onLangLoaded() {
|
|
65
|
+
return this.dependencyService.eventService.LANGUAGE_LOADED;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* True if loading is needed to wait for Tolgee init.
|
|
70
|
+
* When translation data are provided statically (using import
|
|
71
|
+
* as staticData config property) then there is no need for translation
|
|
72
|
+
* fetching so initial loading is not needed at all.
|
|
73
|
+
*/
|
|
74
|
+
get initialLoading(): boolean {
|
|
75
|
+
const currentLang = this.properties.currentLanguage;
|
|
76
|
+
const fallbackLang = this.properties.config.fallbackLanguage;
|
|
77
|
+
const fallbackPreloading = this.properties.config.preloadFallback;
|
|
78
|
+
const isStaticDataProvided = (data?: any) => {
|
|
79
|
+
return data !== undefined && typeof data !== 'function';
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
!isStaticDataProvided(this.properties.config.staticData?.[currentLang]) ||
|
|
84
|
+
(!!fallbackPreloading &&
|
|
85
|
+
!isStaticDataProvided(
|
|
86
|
+
this.properties.config.staticData?.[fallbackLang]
|
|
87
|
+
))
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private get coreService() {
|
|
92
|
+
return this.dependencyService.coreService;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static use(module: TolgeeModule) {
|
|
96
|
+
return new Tolgee().use(module);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static init(config: TolgeeConfig) {
|
|
100
|
+
return new Tolgee().init(config);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Sets the new language.
|
|
105
|
+
*
|
|
106
|
+
* Emits the onLangChange and onLangChangeAndLoad events after
|
|
107
|
+
* the translations are loaded.
|
|
108
|
+
*
|
|
109
|
+
* @return Promise<void> Resolves when translations are loaded
|
|
110
|
+
*/
|
|
111
|
+
public async changeLanguage(newLanguage: string): Promise<void> {
|
|
112
|
+
await this.dependencyService.translationService.loadTranslations(
|
|
113
|
+
newLanguage
|
|
114
|
+
);
|
|
115
|
+
this.properties.currentLanguage = newLanguage;
|
|
116
|
+
this.emitLangChangeEvent(newLanguage);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
use(module: TolgeeModule) {
|
|
120
|
+
this.dependencyService.moduleService.addModule(module);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
init(config: TolgeeConfig) {
|
|
125
|
+
this.dependencyService.init(config);
|
|
126
|
+
const { apiKey, apiUrl } = this.dependencyService.properties.config;
|
|
127
|
+
this.dependencyService.properties.mode =
|
|
128
|
+
apiKey && apiUrl ? 'development' : 'production';
|
|
129
|
+
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public async run(): Promise<void> {
|
|
134
|
+
this.dependencyService.run();
|
|
135
|
+
if (this.properties.mode === 'development') {
|
|
136
|
+
try {
|
|
137
|
+
await this.coreService.loadApiKeyDetails();
|
|
138
|
+
} catch (e) {
|
|
139
|
+
// eslint-disable-next-line no-console
|
|
140
|
+
console.error("Couldn't connect to Tolgee");
|
|
141
|
+
// eslint-disable-next-line no-console
|
|
142
|
+
console.error(e);
|
|
143
|
+
this.properties.mode = 'production';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await this.dependencyService.translationService.loadTranslations();
|
|
148
|
+
await this.dependencyService.pluginManager.run();
|
|
149
|
+
|
|
150
|
+
if (this.properties.config.preloadFallback) {
|
|
151
|
+
await this.dependencyService.translationService.loadTranslations(
|
|
152
|
+
this.properties.config.fallbackLanguage
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await this.refresh();
|
|
157
|
+
|
|
158
|
+
if (this.properties.config.watch) {
|
|
159
|
+
this.dependencyService.observer.observe();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public async refresh() {
|
|
164
|
+
return this.dependencyService.wrapper.handleSubtree(
|
|
165
|
+
this.properties.config.targetElement
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async translate(props: TranslateProps): Promise<string>;
|
|
170
|
+
async translate<T>(props: TranslatePropsTags<T>): Promise<TranslationTags<T>>;
|
|
171
|
+
|
|
172
|
+
async translate(
|
|
173
|
+
key: string,
|
|
174
|
+
params?: TranslationParams,
|
|
175
|
+
noWrap?: boolean,
|
|
176
|
+
defaultValue?: string
|
|
177
|
+
): Promise<string>;
|
|
178
|
+
async translate<T>(
|
|
179
|
+
key: string,
|
|
180
|
+
params?: TranslationParamsTags<T>,
|
|
181
|
+
noWrap?: boolean,
|
|
182
|
+
defaultValue?: string
|
|
183
|
+
): Promise<TranslationTags<T>>;
|
|
184
|
+
|
|
185
|
+
async translate(
|
|
186
|
+
keyOrProps: string | TranslatePropsTags<any>,
|
|
187
|
+
params: TranslationParamsTags<any> = {},
|
|
188
|
+
noWrap = false,
|
|
189
|
+
defaultValue: string | undefined = undefined
|
|
190
|
+
): Promise<TranslationTags<any>> {
|
|
191
|
+
const key = typeof keyOrProps === 'string' ? keyOrProps : keyOrProps.key;
|
|
192
|
+
let orEmpty = undefined;
|
|
193
|
+
if (typeof keyOrProps === 'object') {
|
|
194
|
+
const props = keyOrProps as TranslateProps;
|
|
195
|
+
// if values are not provided in props object, get them from function
|
|
196
|
+
// params defaults
|
|
197
|
+
params = props.params !== undefined ? props.params : params;
|
|
198
|
+
noWrap = props.noWrap !== undefined ? props.noWrap : noWrap;
|
|
199
|
+
defaultValue =
|
|
200
|
+
props.defaultValue !== undefined ? props.defaultValue : defaultValue;
|
|
201
|
+
orEmpty = props.orEmpty;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const translation = await this.dependencyService.textService.translate(
|
|
205
|
+
key,
|
|
206
|
+
params,
|
|
207
|
+
undefined,
|
|
208
|
+
orEmpty,
|
|
209
|
+
defaultValue
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
if (this.properties.mode === 'development' && !noWrap) {
|
|
213
|
+
await this.coreService.loadApiKeyDetails();
|
|
214
|
+
return this.dependencyService.wrapper.wrap(
|
|
215
|
+
key,
|
|
216
|
+
params,
|
|
217
|
+
defaultValue,
|
|
218
|
+
translation
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return translation;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
wrap(
|
|
226
|
+
key: string,
|
|
227
|
+
params?: TranslationParams,
|
|
228
|
+
defaultValue?: string | undefined,
|
|
229
|
+
translation?: string
|
|
230
|
+
): string;
|
|
231
|
+
wrap<T>(
|
|
232
|
+
key: string,
|
|
233
|
+
params?: TranslationTags<T>,
|
|
234
|
+
defaultValue?: string | undefined,
|
|
235
|
+
translation?: TranslationTags<T>
|
|
236
|
+
): TranslationTags<T>;
|
|
237
|
+
|
|
238
|
+
wrap(
|
|
239
|
+
key: string,
|
|
240
|
+
params?: any,
|
|
241
|
+
defaultValue?: string | undefined,
|
|
242
|
+
translation?: TranslationTags<any>
|
|
243
|
+
): TranslationTags<any> {
|
|
244
|
+
if (this.properties.mode === 'development') {
|
|
245
|
+
return this.dependencyService.wrapper.wrap(
|
|
246
|
+
key,
|
|
247
|
+
params,
|
|
248
|
+
defaultValue,
|
|
249
|
+
translation
|
|
250
|
+
);
|
|
251
|
+
} else {
|
|
252
|
+
return translation || defaultValue;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
instant(
|
|
257
|
+
key: string,
|
|
258
|
+
params?: TranslationParams,
|
|
259
|
+
noWrap?: boolean,
|
|
260
|
+
orEmpty?: boolean,
|
|
261
|
+
defaultValue?: string
|
|
262
|
+
): string;
|
|
263
|
+
instant<T>(
|
|
264
|
+
key: string,
|
|
265
|
+
params?: TranslationParamsTags<T>,
|
|
266
|
+
noWrap?: boolean,
|
|
267
|
+
orEmpty?: boolean,
|
|
268
|
+
defaultValue?: string
|
|
269
|
+
): TranslationTags<T>;
|
|
270
|
+
|
|
271
|
+
instant(props: InstantProps): string;
|
|
272
|
+
instant<T>(props: InstantPropsTags<T>): TranslationTags<T>;
|
|
273
|
+
|
|
274
|
+
instant(
|
|
275
|
+
keyOrProps: string | InstantPropsTags<any>,
|
|
276
|
+
params: TranslationParams = {},
|
|
277
|
+
noWrap = false,
|
|
278
|
+
orEmpty?: boolean,
|
|
279
|
+
defaultValue?: string
|
|
280
|
+
) {
|
|
281
|
+
const key = typeof keyOrProps === 'string' ? keyOrProps : keyOrProps.key;
|
|
282
|
+
if (typeof keyOrProps === 'object') {
|
|
283
|
+
const props = keyOrProps as InstantProps;
|
|
284
|
+
// if values are not provided in props object, get them from function
|
|
285
|
+
// params defaults
|
|
286
|
+
params = props.params !== undefined ? props.params : params;
|
|
287
|
+
noWrap = props.noWrap !== undefined ? props.noWrap : noWrap;
|
|
288
|
+
defaultValue =
|
|
289
|
+
props.defaultValue !== undefined ? props.defaultValue : defaultValue;
|
|
290
|
+
orEmpty = props.orEmpty !== undefined ? props.orEmpty : orEmpty;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const translation = this.dependencyService.textService.instant(
|
|
294
|
+
key,
|
|
295
|
+
params,
|
|
296
|
+
undefined,
|
|
297
|
+
orEmpty,
|
|
298
|
+
defaultValue
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (this.properties.mode === 'development' && !noWrap) {
|
|
302
|
+
return this.dependencyService.wrapper.wrap(
|
|
303
|
+
key,
|
|
304
|
+
params,
|
|
305
|
+
defaultValue,
|
|
306
|
+
translation
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
return translation;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get currently cached translations for all languages
|
|
314
|
+
*/
|
|
315
|
+
public getCachedTranslations() {
|
|
316
|
+
return this.dependencyService.translationService.getCachedTranslations();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Loads translations for given language or returns them from cache
|
|
321
|
+
* @returns Loaded translations
|
|
322
|
+
*/
|
|
323
|
+
public loadTranslations(lang: string) {
|
|
324
|
+
return this.dependencyService.translationService.loadTranslations(lang);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public stop = () => {
|
|
328
|
+
this.dependencyService.stop();
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
private emitLangChangeEvent(value: string) {
|
|
332
|
+
const langChangedEmitter = this.onLangChange as EventEmitterImpl<any>;
|
|
333
|
+
langChangedEmitter.emit(value);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
jest.dontMock('./TolgeeConfig');
|
|
2
|
+
|
|
3
|
+
import { TolgeeConfig } from './TolgeeConfig';
|
|
4
|
+
|
|
5
|
+
describe('TolgeeConfig', () => {
|
|
6
|
+
test('will be created with default targetElement', () => {
|
|
7
|
+
const tolgeeConfig = new TolgeeConfig();
|
|
8
|
+
expect(tolgeeConfig.targetElement).toEqual(document.body);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('will be created with provided targetElement', () => {
|
|
12
|
+
const div = document.createElement('div');
|
|
13
|
+
const tolgeeConfig = new TolgeeConfig({ targetElement: div });
|
|
14
|
+
expect(tolgeeConfig.targetElement).toEqual(div);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('will be created with default when empty object', () => {
|
|
18
|
+
const tolgeeConfig = new TolgeeConfig({});
|
|
19
|
+
expect(tolgeeConfig.targetElement).toEqual(document.body);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { TreeTranslationsData } from './types';
|
|
2
|
+
import { NodeHelper } from './helpers/NodeHelper';
|
|
3
|
+
import { ModifierKey } from './Constants/ModifierKey';
|
|
4
|
+
import { Mode } from 'fs';
|
|
5
|
+
|
|
6
|
+
const API_KEY_LOCAL_STORAGE = '__tolgee_apiKey';
|
|
7
|
+
const API_URL_LOCAL_STORAGE = '__tolgee_apiUrl';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_TARGET_ELEMENT_SUPPLIER = () => {
|
|
10
|
+
if (typeof document !== 'undefined') {
|
|
11
|
+
return document.body;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type UiConstructor = new (...args) => any;
|
|
16
|
+
|
|
17
|
+
interface UiLibInterface {
|
|
18
|
+
UI: UiConstructor;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type UiType =
|
|
22
|
+
| UiConstructor
|
|
23
|
+
| UiLibInterface
|
|
24
|
+
| Promise<UiConstructor>
|
|
25
|
+
| Promise<UiLibInterface>;
|
|
26
|
+
|
|
27
|
+
export class TolgeeConfig {
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated This option won't have any effect,
|
|
30
|
+
* because mode is now automatically detected when apiKey + apiUrl are set
|
|
31
|
+
*/
|
|
32
|
+
mode?: Mode;
|
|
33
|
+
apiUrl?: string;
|
|
34
|
+
apiKey?: string;
|
|
35
|
+
inputPrefix?: string = '%-%tolgee:';
|
|
36
|
+
inputSuffix?: string = '%-%';
|
|
37
|
+
/**
|
|
38
|
+
* Overrides all language settings
|
|
39
|
+
*/
|
|
40
|
+
forceLanguage?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Used when auto detection is not available or is turned off
|
|
43
|
+
*/
|
|
44
|
+
defaultLanguage?: string = 'en';
|
|
45
|
+
/**
|
|
46
|
+
* Languages which can be used for language detection
|
|
47
|
+
* and also limits which values can be stored
|
|
48
|
+
*/
|
|
49
|
+
availableLanguages?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Language which is used when no translation is available for current one
|
|
52
|
+
*/
|
|
53
|
+
fallbackLanguage?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Store user language in localStorage (default: true)
|
|
56
|
+
*/
|
|
57
|
+
enableLanguageStore?: boolean = true;
|
|
58
|
+
/**
|
|
59
|
+
* Use auto language detection by browser locale (default: true)
|
|
60
|
+
*/
|
|
61
|
+
enableLanguageDetection?: boolean = true;
|
|
62
|
+
filesUrlPrefix?: string = 'i18n/';
|
|
63
|
+
watch?: boolean;
|
|
64
|
+
ui?: UiType;
|
|
65
|
+
targetElement?: Element;
|
|
66
|
+
tagAttributes?: { [key: string]: string[] } = {
|
|
67
|
+
textarea: ['placeholder'],
|
|
68
|
+
input: ['value', 'placeholder'],
|
|
69
|
+
img: ['alt'],
|
|
70
|
+
'*': ['aria-label', 'title'],
|
|
71
|
+
};
|
|
72
|
+
highlightKeys?: ModifierKey[] = [ModifierKey.Alt];
|
|
73
|
+
passToParent?:
|
|
74
|
+
| (keyof HTMLElementTagNameMap)[]
|
|
75
|
+
| ((node: Element) => boolean) = ['option', 'optgroup'];
|
|
76
|
+
restrictedElements?: string[] = ['script', 'style'];
|
|
77
|
+
highlightColor?: string = 'rgb(255, 0, 0)';
|
|
78
|
+
highlightWidth?: number = 5;
|
|
79
|
+
/** localization data to use in production mode */
|
|
80
|
+
staticData?: {
|
|
81
|
+
[key: string]: TreeTranslationsData | (() => Promise<TreeTranslationsData>);
|
|
82
|
+
};
|
|
83
|
+
wrapperMode?: 'text' | 'invisible' = 'text';
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* When true, fallback language will be preloaded on Tolgee.run
|
|
87
|
+
*/
|
|
88
|
+
preloadFallback?: boolean = false;
|
|
89
|
+
private _targetElement?: Element;
|
|
90
|
+
|
|
91
|
+
constructor(config?: TolgeeConfig) {
|
|
92
|
+
//workaround for: https://stackoverflow.com/questions/48725916/typescript-optional-property-with-a-getter
|
|
93
|
+
Object.defineProperty(this, 'targetElement', {
|
|
94
|
+
set(targetElement: Element) {
|
|
95
|
+
if (this.targetElement !== undefined) {
|
|
96
|
+
throw new Error('Target element is already defined!');
|
|
97
|
+
}
|
|
98
|
+
if (targetElement === undefined) {
|
|
99
|
+
this._targetElement = DEFAULT_TARGET_ELEMENT_SUPPLIER();
|
|
100
|
+
}
|
|
101
|
+
if (NodeHelper.isElementTargetElement(targetElement)) {
|
|
102
|
+
// eslint-disable-next-line no-console
|
|
103
|
+
console.error('Target element: ', this._targetElement);
|
|
104
|
+
throw new Error(
|
|
105
|
+
'An tolgee instance is inited with provided target element'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
this._targetElement = targetElement;
|
|
109
|
+
NodeHelper.markElementAsTargetElement(this._targetElement);
|
|
110
|
+
},
|
|
111
|
+
get() {
|
|
112
|
+
return this._targetElement;
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
Object.assign(this, config || {});
|
|
117
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
118
|
+
this.apiUrl =
|
|
119
|
+
sessionStorage.getItem(API_URL_LOCAL_STORAGE) || this.apiUrl;
|
|
120
|
+
this.apiKey =
|
|
121
|
+
sessionStorage.getItem(API_KEY_LOCAL_STORAGE) || this.apiKey;
|
|
122
|
+
}
|
|
123
|
+
if (this._targetElement === undefined) {
|
|
124
|
+
this._targetElement = DEFAULT_TARGET_ELEMENT_SUPPLIER();
|
|
125
|
+
}
|
|
126
|
+
this.fallbackLanguage = this.fallbackLanguage || this.defaultLanguage;
|
|
127
|
+
if (this.watch === undefined) {
|
|
128
|
+
this.watch = Boolean(this.apiKey && this.apiUrl);
|
|
129
|
+
}
|
|
130
|
+
if (this.availableLanguages === undefined && this.staticData) {
|
|
131
|
+
this.availableLanguages = Object.keys(this.staticData);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
jest.autoMockOff();
|
|
2
|
+
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { IcuFormatter, Tolgee } from '../index';
|
|
5
|
+
import mockTranslations from './mockTranslations';
|
|
6
|
+
import fetchMock from 'jest-fetch-mock';
|
|
7
|
+
import { testConfig } from './testConfig';
|
|
8
|
+
|
|
9
|
+
const API_URL = 'http://localhost';
|
|
10
|
+
const API_KEY = 'dummyApiKey';
|
|
11
|
+
|
|
12
|
+
const fetch = fetchMock.mockResponse(async (req) => {
|
|
13
|
+
if (req.url.includes('/v2/api-keys/current')) {
|
|
14
|
+
return JSON.stringify(testConfig);
|
|
15
|
+
} else if (req.url.includes('/v2/projects/translations/en')) {
|
|
16
|
+
return JSON.stringify(mockTranslations);
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Invalid request');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('Tolgee with icu formatter', () => {
|
|
22
|
+
let tolgee: Tolgee;
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
fetch.enableMocks();
|
|
25
|
+
tolgee = Tolgee.use(IcuFormatter).init({
|
|
26
|
+
targetElement: document.body,
|
|
27
|
+
apiKey: API_KEY,
|
|
28
|
+
apiUrl: API_URL,
|
|
29
|
+
inputPrefix: '{{',
|
|
30
|
+
inputSuffix: '}}',
|
|
31
|
+
watch: false,
|
|
32
|
+
});
|
|
33
|
+
document.body.innerHTML = '';
|
|
34
|
+
await tolgee.run();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
tolgee.stop();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('Formats variable', async () => {
|
|
42
|
+
const translation = await tolgee.translate(
|
|
43
|
+
'peter_dogs',
|
|
44
|
+
{ dogsCount: '10' },
|
|
45
|
+
true
|
|
46
|
+
);
|
|
47
|
+
expect(translation).toEqual('Peter has 10 dogs.');
|
|
48
|
+
|
|
49
|
+
const instantTranslation = tolgee.instant(
|
|
50
|
+
'peter_dogs',
|
|
51
|
+
{ dogsCount: '10' },
|
|
52
|
+
true
|
|
53
|
+
);
|
|
54
|
+
expect(instantTranslation).toEqual('Peter has 10 dogs.');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('Wont format key', async () => {
|
|
58
|
+
const translation = await tolgee.translate(
|
|
59
|
+
'weird {key}',
|
|
60
|
+
{ key: '10' },
|
|
61
|
+
true
|
|
62
|
+
);
|
|
63
|
+
expect(translation).toEqual('weird {key}');
|
|
64
|
+
|
|
65
|
+
const instantTranslation = tolgee.instant(
|
|
66
|
+
'weird {key}',
|
|
67
|
+
{ key: '10' },
|
|
68
|
+
true
|
|
69
|
+
);
|
|
70
|
+
expect(instantTranslation).toEqual('weird {key}');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('Returns key if missing', async () => {
|
|
74
|
+
const translation = await tolgee.translate('nonexistant', undefined, true);
|
|
75
|
+
expect(translation).toEqual('nonexistant');
|
|
76
|
+
|
|
77
|
+
const instantTranslation = tolgee.instant('nonexistant', undefined, true);
|
|
78
|
+
expect(instantTranslation).toEqual('nonexistant');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
jest.autoMockOff();
|
|
2
|
+
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { Tolgee } from '..';
|
|
5
|
+
import mockTranslations from './mockTranslations';
|
|
6
|
+
import fetchMock from 'jest-fetch-mock';
|
|
7
|
+
import { testConfig } from './testConfig';
|
|
8
|
+
|
|
9
|
+
const API_URL = 'http://localhost';
|
|
10
|
+
const API_KEY = 'dummyApiKey';
|
|
11
|
+
|
|
12
|
+
const fetch = fetchMock.mockResponse(async (req) => {
|
|
13
|
+
if (req.url.includes('/v2/api-keys/current')) {
|
|
14
|
+
return JSON.stringify(testConfig);
|
|
15
|
+
} else if (req.url.includes('/v2/projects/translations/en')) {
|
|
16
|
+
return JSON.stringify(mockTranslations);
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Invalid request');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('Tolgee without formatter', () => {
|
|
22
|
+
let tolgee: Tolgee;
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
fetch.enableMocks();
|
|
25
|
+
tolgee = Tolgee.init({
|
|
26
|
+
targetElement: document.body,
|
|
27
|
+
apiKey: API_KEY,
|
|
28
|
+
apiUrl: API_URL,
|
|
29
|
+
watch: false,
|
|
30
|
+
});
|
|
31
|
+
document.body.innerHTML = '';
|
|
32
|
+
await tolgee.run();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
tolgee.stop();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('Returns plain translation', async () => {
|
|
40
|
+
const translation = await tolgee.translate('peter_dogs', undefined, true);
|
|
41
|
+
expect(translation).toEqual('Peter has {dogsCount} dogs.');
|
|
42
|
+
|
|
43
|
+
const instantTranslation = tolgee.instant('peter_dogs', undefined, true);
|
|
44
|
+
expect(instantTranslation).toEqual('Peter has {dogsCount} dogs.');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('Returns key if missing', async () => {
|
|
48
|
+
const translation = await tolgee.translate('nonexistant', undefined, true);
|
|
49
|
+
expect(translation).toEqual('nonexistant');
|
|
50
|
+
|
|
51
|
+
const instantTranslation = tolgee.instant('nonexistant', undefined, true);
|
|
52
|
+
expect(instantTranslation).toEqual('nonexistant');
|
|
53
|
+
});
|
|
54
|
+
});
|