intor 1.0.28 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
3
+ var React7 = require('react');
4
+ var logry = require('logry');
5
+ var intorTranslator = require('intor-translator');
4
6
  var NextLink = require('next/link');
5
7
  var navigation = require('next/navigation');
6
8
 
@@ -24,10 +26,12 @@ function _interopNamespace(e) {
24
26
  return Object.freeze(n);
25
27
  }
26
28
 
27
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
+ var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
28
30
  var NextLink__default = /*#__PURE__*/_interopDefault(NextLink);
29
31
 
30
32
  var __defProp = Object.defineProperty;
33
+ var __defProps = Object.defineProperties;
34
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
31
35
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
32
36
  var __hasOwnProp = Object.prototype.hasOwnProperty;
33
37
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -43,6 +47,7 @@ var __spreadValues = (a, b) => {
43
47
  }
44
48
  return a;
45
49
  };
50
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
46
51
  var __objRest = (source, exclude) => {
47
52
  var target = {};
48
53
  for (var prop in source)
@@ -55,16 +60,98 @@ var __objRest = (source, exclude) => {
55
60
  }
56
61
  return target;
57
62
  };
58
- var IntorLocaleContext = React__namespace.createContext(void 0);
59
- var IntorConfigContext = React__namespace.createContext(void 0);
63
+ var IntorConfigContext = React7__namespace.createContext(void 0);
64
+ var IntorConfigProvider = ({
65
+ value: { config, pathname },
66
+ children
67
+ }) => {
68
+ const value = React7__namespace.useMemo(
69
+ () => ({
70
+ config,
71
+ pathname
72
+ }),
73
+ [config, pathname]
74
+ );
75
+ return /* @__PURE__ */ React7__namespace.createElement(IntorConfigContext.Provider, { value }, children);
76
+ };
60
77
  var useIntorConfig = () => {
61
- const context = React__namespace.useContext(IntorConfigContext);
78
+ const context = React7__namespace.useContext(IntorConfigContext);
62
79
  if (!context) {
63
80
  throw new Error("useIntorConfig must be used within IntorConfigProvider");
64
81
  }
65
82
  return context;
66
83
  };
67
- var IntorMessagesContext = React__namespace.createContext(void 0);
84
+ var IntorLocaleContext = React7__namespace.createContext(void 0);
85
+
86
+ // src/adapters/next-client/utils/build-cookie-string.ts
87
+ var buildCookieString = (cookie, locale) => {
88
+ var _a;
89
+ const parts = [];
90
+ parts.push(`${cookie.name}=${encodeURIComponent(locale)}`);
91
+ if (cookie.maxAge) {
92
+ const expires = new Date(Date.now() + cookie.maxAge * 1e3).toUTCString();
93
+ parts.push(`expires=${expires}`);
94
+ parts.push(`max-age=${cookie.maxAge}`);
95
+ }
96
+ parts.push(`path=${(_a = cookie.path) != null ? _a : "/"}`);
97
+ if (cookie.domain) {
98
+ parts.push(`domain=${cookie.domain}`);
99
+ }
100
+ if (cookie.sameSite) {
101
+ parts.push(
102
+ `SameSite=${cookie.sameSite[0].toUpperCase()}${cookie.sameSite.slice(1).toLowerCase()}`
103
+ );
104
+ }
105
+ if (cookie.secure !== false) {
106
+ parts.push(`Secure`);
107
+ }
108
+ return parts.join("; ");
109
+ };
110
+
111
+ // src/adapters/next-client/utils/set-locale-cookie-client.ts
112
+ var setLocaleCookieClient = ({
113
+ cookie,
114
+ locale
115
+ }) => {
116
+ if (typeof window === "undefined") {
117
+ return;
118
+ }
119
+ if (cookie.disabled || !cookie.autoSetCookie) {
120
+ return;
121
+ }
122
+ const cookieString = buildCookieString(cookie, locale);
123
+ document.cookie = cookieString;
124
+ };
125
+
126
+ // src/adapters/next-client/contexts/intor-locale/utils/change-locale.ts
127
+ var changeLocale = ({
128
+ currentLocale,
129
+ newLocale,
130
+ loaderOptions,
131
+ cookie,
132
+ setLocale,
133
+ refetchMessages
134
+ }) => {
135
+ if (typeof document === "undefined") {
136
+ return;
137
+ }
138
+ const loaderType = loaderOptions == null ? void 0 : loaderOptions.type;
139
+ if (newLocale === currentLocale) {
140
+ return;
141
+ }
142
+ if (loaderType === "import") {
143
+ console.warn(
144
+ `[Intor] You are using dynamic import to switch languages. Please make sure to use the wrapped <Link> component to trigger a page reload, ensuring that the translation data is dynamically updated.`
145
+ );
146
+ }
147
+ setLocale(newLocale);
148
+ setLocaleCookieClient({ cookie, locale: newLocale });
149
+ document.documentElement.lang = newLocale;
150
+ if (loaderType === "api" && refetchMessages) {
151
+ void refetchMessages(newLocale);
152
+ }
153
+ };
154
+ var IntorMessagesContext = React7__namespace.createContext(void 0);
68
155
 
69
156
  // src/shared/utils/pathname/normalize-pathname.ts
70
157
  var normalizePathname = (rawPathname, options = {}) => {
@@ -100,6 +187,127 @@ var normalizePathname = (rawPathname, options = {}) => {
100
187
  return result || "/";
101
188
  };
102
189
 
190
+ // src/modules/intor-messages-loader/fetch-api-messages/build-search-params.ts
191
+ var buildSearchParams = (params) => {
192
+ const searchParams = new URLSearchParams();
193
+ const appendParam = (key, value) => {
194
+ if (value === void 0 || value === null) {
195
+ return;
196
+ }
197
+ if (Array.isArray(value) && value.length > 0) {
198
+ value.forEach((v) => v && searchParams.append(key, v));
199
+ } else {
200
+ searchParams.append(key, value);
201
+ }
202
+ };
203
+ Object.entries(params).forEach(([key, value]) => {
204
+ appendParam(key, value);
205
+ });
206
+ return searchParams;
207
+ };
208
+ var fetcher = async ({
209
+ apiUrl,
210
+ locale,
211
+ searchParams,
212
+ loggerId
213
+ }) => {
214
+ const logger = logry.logry({ id: loggerId, scope: "fetcher" });
215
+ try {
216
+ const params = new URLSearchParams(searchParams);
217
+ params.append("locale", locale);
218
+ const url = `${apiUrl}?${params.toString()}`;
219
+ const response = await fetch(url, {
220
+ method: "GET",
221
+ headers: { "Content-Type": "application/json" },
222
+ cache: "no-store"
223
+ });
224
+ if (!response.ok) {
225
+ throw new Error(
226
+ `Fetch failed for locale "${locale}" at URL: ${url} - ${response.status} ${response.statusText}`
227
+ );
228
+ }
229
+ const data = await response.json();
230
+ if (data == null || typeof data === "object" && Object.keys(data).length === 0) {
231
+ throw new Error(
232
+ `Missing or invalid messages for locale "${locale}" at URL: ${url}`
233
+ );
234
+ }
235
+ return data;
236
+ } catch (e) {
237
+ logger.warn(`Failed to fetch messages for locale "${locale}".`, {
238
+ locale,
239
+ apiUrl,
240
+ searchParams: decodeURIComponent(searchParams.toString())
241
+ });
242
+ return void 0;
243
+ }
244
+ };
245
+
246
+ // src/modules/intor-messages-loader/fetch-api-messages/fetch-fallback-messages.ts
247
+ var fetchFallbackMessages = async (apiUrl, searchParams, fallbackLocales, loggerId) => {
248
+ for (const fallbackLocale of fallbackLocales) {
249
+ const result = await fetcher({
250
+ apiUrl,
251
+ searchParams,
252
+ locale: fallbackLocale,
253
+ loggerId
254
+ });
255
+ if (result) {
256
+ return { locale: fallbackLocale, messages: result };
257
+ }
258
+ }
259
+ return void 0;
260
+ };
261
+
262
+ // src/modules/intor-messages-loader/fetch-api-messages/fetch-api-messages.ts
263
+ var fetchApiMessages = async ({
264
+ apiUrl,
265
+ basePath,
266
+ locale,
267
+ fallbackLocales = [],
268
+ namespaces = [],
269
+ loggerId
270
+ }) => {
271
+ const logger = logry.logry({ id: loggerId, scope: "fetchApiMessages" });
272
+ if (!apiUrl) {
273
+ logger.warn("No apiUrl provided for fetchApiMessages. Skipping fetch.");
274
+ return void 0;
275
+ }
276
+ const searchParams = buildSearchParams({
277
+ basePath,
278
+ loggerId,
279
+ namespaces
280
+ });
281
+ const messages = await fetcher({
282
+ apiUrl,
283
+ searchParams,
284
+ locale,
285
+ loggerId
286
+ });
287
+ if (messages) {
288
+ return messages;
289
+ }
290
+ const fallbackResult = await fetchFallbackMessages(
291
+ apiUrl,
292
+ searchParams,
293
+ fallbackLocales,
294
+ loggerId
295
+ );
296
+ if (fallbackResult) {
297
+ logger.info("Fallback locale succeeded.", {
298
+ usedLocale: fallbackResult.locale,
299
+ apiUrl,
300
+ searchParams: decodeURIComponent(searchParams.toString())
301
+ });
302
+ return fallbackResult.messages;
303
+ }
304
+ logger.warn("Failed to fetch messages for all locales.", {
305
+ locale,
306
+ fallbackLocales
307
+ });
308
+ return void 0;
309
+ };
310
+
103
311
  // src/shared/utils/pathname/extract-pathname.ts
104
312
  var extractPathname = ({
105
313
  config,
@@ -152,8 +360,162 @@ var standardizePathname = ({
152
360
  const standardizedPathname = parts.join("/").replace(/\/{2,}/g, "/");
153
361
  return normalizePathname(standardizedPathname);
154
362
  };
363
+
364
+ // src/shared/utils/resolve-namespaces.ts
365
+ var resolveNamespaces = ({
366
+ config,
367
+ pathname
368
+ }) => {
369
+ var _a, _b, _c, _d;
370
+ const { loaderOptions, prefixPlaceHolder } = config;
371
+ const {
372
+ routeNamespaces = {},
373
+ namespaces: fallbackNamespaces
374
+ } = loaderOptions;
375
+ const { unprefixedPathname } = extractPathname({ config, pathname });
376
+ const standardizedPathname = standardizePathname({
377
+ config,
378
+ pathname: unprefixedPathname
379
+ });
380
+ const placeholderRemovedPathname = standardizedPathname.replace(
381
+ `/${prefixPlaceHolder}`,
382
+ ""
383
+ );
384
+ const defaultNamespaces = (_a = routeNamespaces.default) != null ? _a : [];
385
+ const exactMatchNamespaces = (_b = routeNamespaces[standardizedPathname]) != null ? _b : routeNamespaces[placeholderRemovedPathname];
386
+ if (exactMatchNamespaces) {
387
+ return [...defaultNamespaces, ...exactMatchNamespaces];
388
+ }
389
+ let bestMatch = "";
390
+ let bestNamespaces;
391
+ const prefixPatterns = Object.keys(routeNamespaces).filter(
392
+ (pattern) => pattern.endsWith("/*")
393
+ );
394
+ for (const pattern of prefixPatterns) {
395
+ const basePath = pattern.replace(/\/\*$/, "");
396
+ if (standardizedPathname.startsWith(basePath)) {
397
+ if (basePath.length > bestMatch.length) {
398
+ bestMatch = basePath;
399
+ bestNamespaces = routeNamespaces[pattern];
400
+ }
401
+ }
402
+ }
403
+ const matchedNamespaces = (_d = (_c = bestNamespaces != null ? bestNamespaces : routeNamespaces["/*"]) != null ? _c : fallbackNamespaces) != null ? _d : [];
404
+ if (matchedNamespaces.length > 0) {
405
+ return [...defaultNamespaces, ...matchedNamespaces];
406
+ } else {
407
+ return [...defaultNamespaces];
408
+ }
409
+ };
410
+
411
+ // src/adapters/next-client/hooks/api/use-fetch-messages.ts
412
+ var useFetchMessages = ({
413
+ onError
414
+ }) => {
415
+ const fetcher2 = async (params) => {
416
+ try {
417
+ const data = await fetchApiMessages(params);
418
+ return data;
419
+ } catch (error) {
420
+ onError == null ? void 0 : onError(error, params.apiUrl);
421
+ return null;
422
+ }
423
+ };
424
+ return { fetcher: fetcher2 };
425
+ };
426
+
427
+ // src/shared/utils/merge-static-and-dynamic-messages.ts
428
+ var mergeStaticAndDynamicMessages = (staticMessages = {}, dynamicMessages = {}) => {
429
+ const result = Object.keys(staticMessages).length ? __spreadValues({}, staticMessages) : {};
430
+ for (const locale in dynamicMessages) {
431
+ const dynamic = dynamicMessages[locale];
432
+ if (!result[locale]) {
433
+ result[locale] = dynamic;
434
+ continue;
435
+ }
436
+ result[locale] = __spreadValues(__spreadValues({}, result[locale]), dynamic);
437
+ }
438
+ return result;
439
+ };
440
+
441
+ // src/adapters/next-client/hooks/api/use-refetch-messages.ts
442
+ var useRefetchMessages = ({
443
+ config,
444
+ pathname,
445
+ setLoadedMessages,
446
+ setIsLoadingMessages,
447
+ onError
448
+ // Error handler
449
+ }) => {
450
+ const { messages: staticMessages } = config;
451
+ const namespaces = React7__namespace.useMemo(() => {
452
+ if (!config.loaderOptions) {
453
+ return [];
454
+ }
455
+ return resolveNamespaces({ config, pathname });
456
+ }, [config, pathname]);
457
+ const { fetcher: fetcher2 } = useFetchMessages({ onError });
458
+ const refetchMessages = React7__namespace.useCallback(
459
+ async (newLocale) => {
460
+ var _a;
461
+ if (((_a = config.loaderOptions) == null ? void 0 : _a.type) === "api") {
462
+ setIsLoadingMessages(true);
463
+ const dynamicMessages = await fetcher2(__spreadProps(__spreadValues({}, config.loaderOptions), {
464
+ locale: newLocale,
465
+ fallbackLocales: config.fallbackLocales[newLocale] || [],
466
+ namespaces,
467
+ loggerId: config.id
468
+ }));
469
+ const messages = mergeStaticAndDynamicMessages(
470
+ staticMessages,
471
+ dynamicMessages
472
+ );
473
+ setLoadedMessages(messages);
474
+ setIsLoadingMessages(false);
475
+ }
476
+ },
477
+ [
478
+ config.loaderOptions,
479
+ config.fallbackLocales,
480
+ config.id,
481
+ setIsLoadingMessages,
482
+ fetcher2,
483
+ namespaces,
484
+ staticMessages,
485
+ setLoadedMessages
486
+ ]
487
+ );
488
+ return { refetchMessages };
489
+ };
490
+
491
+ // src/adapters/next-client/contexts/intor-messages/intor-messages-provider.tsx
492
+ var IntorMessagesProvider = ({
493
+ value: { messages },
494
+ children
495
+ }) => {
496
+ const { config, pathname } = useIntorConfig();
497
+ const [loadedMessages, setLoadedMessages] = React7__namespace.useState(null);
498
+ const [isLoadingMessages, setIsLoadingMessages] = React7__namespace.useState(false);
499
+ const { refetchMessages } = useRefetchMessages({
500
+ config,
501
+ pathname,
502
+ setLoadedMessages,
503
+ setIsLoadingMessages
504
+ });
505
+ const value = React7__namespace.useMemo(
506
+ () => ({
507
+ messages: loadedMessages || messages,
508
+ isLoading: isLoadingMessages,
509
+ setLoadedMessages,
510
+ setIsLoadingMessages,
511
+ refetchMessages
512
+ }),
513
+ [loadedMessages, messages, isLoadingMessages, refetchMessages]
514
+ );
515
+ return /* @__PURE__ */ React7__namespace.createElement(IntorMessagesContext.Provider, { value }, children);
516
+ };
155
517
  var useIntorMessages = () => {
156
- const context = React__namespace.useContext(IntorMessagesContext);
518
+ const context = React7__namespace.useContext(IntorMessagesContext);
157
519
  if (!context) {
158
520
  throw new Error(
159
521
  "useIntorMessages must be used within a IntorMessagesProvider"
@@ -161,21 +523,138 @@ var useIntorMessages = () => {
161
523
  }
162
524
  return context;
163
525
  };
526
+ var useInitLazyLoad = ({
527
+ loaderOptions,
528
+ currentLocale
529
+ }) => {
530
+ const { refetchMessages } = useIntorMessages();
531
+ const lazyLoad = !!(loaderOptions == null ? void 0 : loaderOptions.lazyLoad);
532
+ const isFirstLoadedRef = React7__namespace.useRef(false);
533
+ React7__namespace.useEffect(() => {
534
+ if (lazyLoad && !isFirstLoadedRef.current) {
535
+ void refetchMessages(currentLocale);
536
+ isFirstLoadedRef.current = true;
537
+ }
538
+ }, [lazyLoad, currentLocale, refetchMessages, isFirstLoadedRef]);
539
+ };
540
+ var useInitLocaleCookie = ({
541
+ config,
542
+ locale
543
+ }) => {
544
+ React7__namespace.useEffect(() => {
545
+ if (typeof document === "undefined") {
546
+ return;
547
+ }
548
+ const { cookie, routing } = config;
549
+ const { firstVisit } = routing;
550
+ const cookies = document.cookie.split(";").map((c) => c.trim());
551
+ const isCookieExists = cookies.some((c) => c.startsWith(`${cookie.name}=`));
552
+ if (isCookieExists) {
553
+ return;
554
+ }
555
+ if (!firstVisit.redirect) {
556
+ return;
557
+ }
558
+ if (cookie.disabled || !cookie.autoSetCookie) {
559
+ return;
560
+ }
561
+ setLocaleCookieClient({ cookie, locale });
562
+ }, []);
563
+ };
564
+
565
+ // src/adapters/next-client/contexts/intor-locale/intor-locale-provider.tsx
566
+ var IntorLocaleProvider = ({
567
+ value: { initialLocale },
568
+ children
569
+ }) => {
570
+ const { config } = useIntorConfig();
571
+ const { refetchMessages } = useIntorMessages();
572
+ const { loaderOptions, cookie } = config;
573
+ const [currentLocale, setCurrentLocale] = React7__namespace.useState(initialLocale);
574
+ useInitLazyLoad({ loaderOptions, currentLocale });
575
+ useInitLocaleCookie({ config, locale: initialLocale });
576
+ const setLocale = React7__namespace.useCallback(
577
+ (newLocale) => {
578
+ changeLocale({
579
+ currentLocale,
580
+ newLocale,
581
+ loaderOptions,
582
+ cookie,
583
+ setLocale: setCurrentLocale,
584
+ refetchMessages
585
+ });
586
+ },
587
+ [currentLocale, loaderOptions, cookie, refetchMessages]
588
+ );
589
+ const value = React7__namespace.useMemo(
590
+ () => ({
591
+ locale: currentLocale,
592
+ setLocale
593
+ }),
594
+ [currentLocale, setLocale]
595
+ );
596
+ return /* @__PURE__ */ React7__namespace.createElement(IntorLocaleContext.Provider, { value }, children);
597
+ };
164
598
  var useIntorLocale = () => {
165
- const context = React__namespace.useContext(IntorLocaleContext);
599
+ const context = React7__namespace.useContext(IntorLocaleContext);
166
600
  if (!context) {
167
601
  throw new Error("useIntorLocale must be used within a IntorLocaleProvider");
168
602
  }
169
603
  return context;
170
604
  };
171
- var IntorTranslatorContext = React__namespace.createContext(void 0);
172
- var TranslateHandlersContext = React__namespace.createContext(void 0);
605
+ var IntorTranslatorContext = React7__namespace.createContext(void 0);
606
+ var TranslateHandlersContext = React7__namespace.createContext(void 0);
173
607
  var useTranslateHandlers = () => {
174
- const context = React__namespace.useContext(TranslateHandlersContext);
608
+ const context = React7__namespace.useContext(TranslateHandlersContext);
175
609
  return context;
176
610
  };
611
+ var useInitLoadingState = (config) => {
612
+ var _a;
613
+ const lazyLoad = !!((_a = config.loaderOptions) == null ? void 0 : _a.lazyLoad);
614
+ const [isCsr, setIsCsr] = React7__namespace.useState(false);
615
+ React7__namespace.useEffect(() => {
616
+ setIsCsr(true);
617
+ }, []);
618
+ const isBeforeCSRLoading = lazyLoad && !isCsr;
619
+ return isBeforeCSRLoading;
620
+ };
621
+
622
+ // src/adapters/next-client/contexts/intor-translator/intor-translator-provider.tsx
623
+ var EMPTY_OBJECT = Object.freeze({});
624
+ var IntorTranslatorProvider = ({
625
+ children
626
+ }) => {
627
+ const { config } = useIntorConfig();
628
+ const { messages, isLoading } = useIntorMessages();
629
+ const { locale } = useIntorLocale();
630
+ const translatorHandlers = useTranslateHandlers();
631
+ const { fallbackLocales, translator: translatorOptions } = config;
632
+ const isBeforeCSRLoading = useInitLoadingState(config);
633
+ const value = React7__namespace.useMemo(() => {
634
+ const translator = new intorTranslator.Translator({
635
+ messages: messages || EMPTY_OBJECT,
636
+ locale,
637
+ fallbackLocales,
638
+ loadingMessage: translatorOptions == null ? void 0 : translatorOptions.loadingMessage,
639
+ placeholder: translatorOptions == null ? void 0 : translatorOptions.placeholder,
640
+ handlers: translatorHandlers
641
+ });
642
+ translator.setLoading(isBeforeCSRLoading || isLoading);
643
+ return { translator };
644
+ }, [
645
+ fallbackLocales,
646
+ isBeforeCSRLoading,
647
+ isLoading,
648
+ locale,
649
+ messages,
650
+ translatorHandlers,
651
+ translatorOptions == null ? void 0 : translatorOptions.loadingMessage,
652
+ translatorOptions == null ? void 0 : translatorOptions.placeholder
653
+ ]);
654
+ return /* @__PURE__ */ React7__namespace.createElement(IntorTranslatorContext.Provider, { value }, children);
655
+ };
177
656
  function useIntorTranslator() {
178
- const context = React__namespace.useContext(IntorTranslatorContext);
657
+ const context = React7__namespace.useContext(IntorTranslatorContext);
179
658
  if (!context) {
180
659
  throw new Error(
181
660
  "useIntorTranslator must be used within IntorTranslatorProvider"
@@ -305,7 +784,7 @@ var Link = (_a) => {
305
784
  setLocale(targetLocale);
306
785
  }
307
786
  };
308
- return /* @__PURE__ */ React__namespace.createElement(NextLink__default.default, __spreadValues({ href, onClick: handleClick }, props), children);
787
+ return /* @__PURE__ */ React7__namespace.createElement(NextLink__default.default, __spreadValues({ href, onClick: handleClick }, props), children);
309
788
  };
310
789
  var usePathname = () => {
311
790
  const { config } = useIntorConfig();
@@ -364,7 +843,12 @@ var useRouter = () => {
364
843
  }, rest);
365
844
  };
366
845
 
846
+ exports.IntorConfigProvider = IntorConfigProvider;
847
+ exports.IntorLocaleProvider = IntorLocaleProvider;
848
+ exports.IntorMessagesProvider = IntorMessagesProvider;
849
+ exports.IntorTranslatorProvider = IntorTranslatorProvider;
367
850
  exports.Link = Link;
851
+ exports.TranslateHandlersContext = TranslateHandlersContext;
368
852
  exports.useIntor = useIntor;
369
853
  exports.useIntorConfig = useIntorConfig;
370
854
  exports.useIntorLocale = useIntorLocale;
@@ -372,4 +856,3 @@ exports.useIntorMessages = useIntorMessages;
372
856
  exports.useIntorTranslator = useIntorTranslator;
373
857
  exports.usePathname = usePathname;
374
858
  exports.useRouter = useRouter;
375
- exports.useTranslateHandlers = useTranslateHandlers;