@tramvai/module-i18n 7.21.1 → 7.26.9

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/lib/browser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { declareModule, provide } from '@tramvai/core';
2
2
  import { I18N_CONFIGURATION_TOKEN } from './tokens.browser.js';
3
- export { I18N_CONFIGURATION_TOKEN, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN } from './tokens.browser.js';
3
+ export { I18N_CONFIGURATION_TOKEN, I18N_HOOKS_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN } from './tokens.browser.js';
4
4
  import { sharedProviders } from './shared/providers.browser.js';
5
5
  import { defaultConfiguration } from './shared/config.browser.js';
6
6
  export { I18nStore, setAvailableLanguages, setLanguage } from './shared/store.browser.js';
package/lib/server.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { declareModule, provide } from '@tramvai/core';
2
2
  import { I18N_CONFIGURATION_TOKEN } from './tokens.es.js';
3
- export { I18N_CONFIGURATION_TOKEN, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN } from './tokens.es.js';
3
+ export { I18N_CONFIGURATION_TOKEN, I18N_HOOKS_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN } from './tokens.es.js';
4
4
  import { sharedProviders } from './shared/providers.es.js';
5
5
  import { defaultConfiguration } from './shared/config.es.js';
6
6
  export { I18nStore, setAvailableLanguages, setLanguage } from './shared/store.es.js';
package/lib/server.js CHANGED
@@ -29,6 +29,8 @@ const I18nModule = /* @__PURE__ */ core.declareModule({
29
29
  });
30
30
 
31
31
  exports.I18N_CONFIGURATION_TOKEN = tokens.I18N_CONFIGURATION_TOKEN;
32
+ exports.I18N_HOOKS_TOKEN = tokens.I18N_HOOKS_TOKEN;
33
+ exports.I18N_LANGUAGE_COOKIE_EXPIRES_VALUE = tokens.I18N_LANGUAGE_COOKIE_EXPIRES_VALUE;
32
34
  exports.I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN = tokens.I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN;
33
35
  exports.I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN = tokens.I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN;
34
36
  exports.I18N_RESOLVE_LANGUAGE_TOKEN = tokens.I18N_RESOLVE_LANGUAGE_TOKEN;
@@ -1,10 +1,12 @@
1
1
  import { provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
2
- import { COMBINE_REDUCERS, STORE_TOKEN, REQUEST_MANAGER_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN } from '@tramvai/tokens-common';
2
+ import { COMBINE_REDUCERS, STORE_TOKEN, ENV_MANAGER_TOKEN, REQUEST_MANAGER_TOKEN, LOGGER_TOKEN } from '@tramvai/tokens-common';
3
3
  import { COOKIE_MANAGER_TOKEN } from '@tramvai/tokens-cookie';
4
- import { ROUTER_TOKEN, ROUTER_GUARD_TOKEN, ROUTER_PLUGIN } from '@tramvai/tokens-router';
4
+ import { ROUTER_TOKEN, ROUTER_GUARD_TOKEN, ROUTER_PLUGIN, PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
5
5
  import { USER_LANGUAGE_TOKEN } from '@tramvai/module-client-hints';
6
- import { HTML_ATTRS, RENDER_SLOTS, ResourceType, ResourceSlot } from '@tramvai/tokens-render';
7
- import { I18N_CONFIGURATION_TOKEN, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN } from '../tokens.browser.js';
6
+ import { HTML_ATTRS } from '@tramvai/tokens-render';
7
+ import { META_UPDATER_TOKEN, META_PRIORITY_ROUTE } from '@tramvai/module-seo';
8
+ import { TAPABLE_HOOK_FACTORY_TOKEN } from '@tramvai/tokens-core';
9
+ import { I18N_CONFIGURATION_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_HOOKS_TOKEN } from '../tokens.browser.js';
8
10
  import { I18nStore, setAvailableLanguages, setLanguage } from './store.browser.js';
9
11
  import { I18nServiceImpl } from './service.browser.js';
10
12
  import { defaultConfiguration } from './config.browser.js';
@@ -19,13 +21,24 @@ const sharedProviders = [
19
21
  provide: COMBINE_REDUCERS,
20
22
  useValue: [I18nStore],
21
23
  }),
24
+ provide({
25
+ provide: I18N_LANGUAGE_COOKIE_EXPIRES_VALUE,
26
+ useValue: 7 * 24 * 60 * 60,
27
+ }),
22
28
  provide({
23
29
  provide: I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN,
24
- useFactory: ({ config }) => {
25
- // TODO: sync with state from server if SSR?
30
+ useFactory: ({ config, store, envManager }) => {
31
+ // At client-side, for SSR mode, we can resolve available languages from server state
32
+ if (typeof window !== 'undefined' &&
33
+ envManager.get('TRAMVAI_FORCE_CLIENT_SIDE_RENDERING') !== 'true') {
34
+ const { availableLanguages } = store.getState(I18nStore);
35
+ return async () => availableLanguages;
36
+ }
26
37
  return async () => config.availableLanguages ?? [config.defaultLanguage];
27
38
  },
28
39
  deps: {
40
+ store: STORE_TOKEN,
41
+ envManager: ENV_MANAGER_TOKEN,
29
42
  config: I18N_CONFIGURATION_TOKEN,
30
43
  },
31
44
  }),
@@ -39,6 +52,7 @@ const sharedProviders = [
39
52
  router: ROUTER_TOKEN,
40
53
  requestManager: REQUEST_MANAGER_TOKEN,
41
54
  logger: LOGGER_TOKEN,
55
+ languageCookieExpiresValue: I18N_LANGUAGE_COOKIE_EXPIRES_VALUE,
42
56
  },
43
57
  }),
44
58
  // Language from cookie has higher priority
@@ -174,11 +188,7 @@ const sharedProviders = [
174
188
  store.dispatch(setLanguage(language));
175
189
  if (!cookieManager.get(config.cookieName)) {
176
190
  log.info(`${config.cookieName} cookie is not found, save resolved "${language.language}" language to cookie`);
177
- cookieManager.set({
178
- name: config.cookieName,
179
- value: language.language,
180
- // TODO valid params!
181
- });
191
+ i18n.setLanguageToCookie(language.language);
182
192
  }
183
193
  const currentUrl = router.getCurrentUrl() ?? requestManager.getParsedUrl();
184
194
  const languageFromUrl = i18n.getLanguageFromUrl(currentUrl);
@@ -295,6 +305,17 @@ const sharedProviders = [
295
305
  store: STORE_TOKEN,
296
306
  },
297
307
  }),
308
+ provide({
309
+ provide: I18N_HOOKS_TOKEN,
310
+ useFactory: ({ tapableHookFactory }) => {
311
+ return {
312
+ 'i18n:get-alternate-languages': tapableHookFactory.createSync('i18n:get-alternate-languages'),
313
+ };
314
+ },
315
+ deps: {
316
+ tapableHookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
317
+ },
318
+ }),
298
319
  provide({
299
320
  provide: HTML_ATTRS,
300
321
  useFactory: ({ i18n }) => {
@@ -310,24 +331,39 @@ const sharedProviders = [
310
331
  },
311
332
  }),
312
333
  provide({
313
- provide: RENDER_SLOTS,
314
- multi: true,
315
- useFactory: ({ i18n, requestManager }) => {
316
- const parsedUrl = requestManager.getParsedUrl();
317
- const url = `${parsedUrl.origin}${parsedUrl.pathname}`;
318
- // TODO: dynamic list of available languages for specific route to generate alternate links
319
- // TODO: `x-default` link?
320
- return [
321
- {
322
- type: ResourceType.asIs,
323
- slot: ResourceSlot.HEAD_META,
324
- payload: `<link rel="alternate" hreflang="${i18n.getLanguage()}" href="${url}" />`,
325
- },
326
- ];
334
+ provide: META_UPDATER_TOKEN,
335
+ useFactory: ({ pageService, i18n, requestManager, i18nHooks }) => {
336
+ return (meta) => {
337
+ const parsedUrl = requestManager.getParsedUrl();
338
+ const routeConfig = pageService.getConfig() || {};
339
+ const urlWithoutLanguage = i18n.removeLanguageFromUrl(parsedUrl);
340
+ const baseAvailableLanguages = i18n.getAvailableLanguages();
341
+ const alternateLanguages = i18nHooks['i18n:get-alternate-languages'].call({
342
+ availableLanguages: baseAvailableLanguages,
343
+ routeConfig,
344
+ }) ?? baseAvailableLanguages;
345
+ const tags = alternateLanguages.reduce((result, languageCode) => {
346
+ const { pathname } = i18n.addLanguageToUrl(urlWithoutLanguage, languageCode);
347
+ //@ts-expect-error
348
+ result[`i18nAlternate_${languageCode}`] = {
349
+ tag: 'link',
350
+ attributes: {
351
+ rel: 'alternate',
352
+ hreflang: languageCode,
353
+ href: `${parsedUrl.origin}${pathname}${parsedUrl.search}${parsedUrl.hash}`,
354
+ },
355
+ };
356
+ return result;
357
+ }, {});
358
+ meta.updateMeta(META_PRIORITY_ROUTE, tags);
359
+ };
327
360
  },
328
361
  deps: {
329
- i18n: I18N_TOKEN,
362
+ pageService: PAGE_SERVICE_TOKEN,
363
+ i18nHooks: I18N_HOOKS_TOKEN,
330
364
  requestManager: REQUEST_MANAGER_TOKEN,
365
+ i18nConfiguration: I18N_CONFIGURATION_TOKEN,
366
+ i18n: I18N_TOKEN,
331
367
  },
332
368
  }),
333
369
  ];
@@ -1,10 +1,22 @@
1
1
  export declare const sharedProviders: (import("@tinkoff/dippy/lib/Provider").ValueProvider<any> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<unknown, any> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithoutDeps<any> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<unknown, any> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithoutDeps<any> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
2
+ store: import("@tramvai/tokens-common").Store<Record<string, any>> & {
3
+ __type?: "base token" | undefined;
4
+ };
5
+ envManager: import("@tramvai/tokens-common").EnvironmentManager & {
6
+ __type?: "base token" | undefined;
7
+ };
2
8
  config: import("../types").I18nConfiguration & {
3
9
  __type?: "base token" | undefined;
4
10
  };
5
11
  }, (() => Promise<string[]>) & {
6
12
  __type?: "base token" | undefined;
7
13
  }> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
14
+ store: import("@tramvai/tokens-common").Store<Record<string, any>> & {
15
+ __type?: "base token" | undefined;
16
+ };
17
+ envManager: import("@tramvai/tokens-common").EnvironmentManager & {
18
+ __type?: "base token" | undefined;
19
+ };
8
20
  config: import("../types").I18nConfiguration & {
9
21
  __type?: "base token" | undefined;
10
22
  };
@@ -32,6 +44,9 @@ export declare const sharedProviders: (import("@tinkoff/dippy/lib/Provider").Val
32
44
  }) => import("@tramvai/tokens-common").Logger) & {
33
45
  __type?: "base token" | undefined;
34
46
  };
47
+ languageCookieExpiresValue: number & {
48
+ __type?: "base token" | undefined;
49
+ };
35
50
  }, import("../types").I18nService & {
36
51
  __type?: "base token" | undefined;
37
52
  }> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
@@ -56,6 +71,9 @@ export declare const sharedProviders: (import("@tinkoff/dippy/lib/Provider").Val
56
71
  }) => import("@tramvai/tokens-common").Logger) & {
57
72
  __type?: "base token" | undefined;
58
73
  };
74
+ languageCookieExpiresValue: number & {
75
+ __type?: "base token" | undefined;
76
+ };
59
77
  }, import("../types").I18nService & {
60
78
  __type?: "base token" | undefined;
61
79
  }> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
@@ -290,6 +308,18 @@ export declare const sharedProviders: (import("@tinkoff/dippy/lib/Provider").Val
290
308
  };
291
309
  }, import("@tinkoff/router").RouterPlugin & {
292
310
  __type?: "multi token" | undefined;
311
+ }> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
312
+ tapableHookFactory: import("@tramvai/core").TapableHooks & {
313
+ __type?: "base token" | undefined;
314
+ };
315
+ }, import("../types").I18nHooks & {
316
+ __type?: "base token" | undefined;
317
+ }> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
318
+ tapableHookFactory: import("@tramvai/core").TapableHooks & {
319
+ __type?: "base token" | undefined;
320
+ };
321
+ }, import("../types").I18nHooks & {
322
+ __type?: "base token" | undefined;
293
323
  }> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
294
324
  i18n: import("../types").I18nService & {
295
325
  __type?: "base token" | undefined;
@@ -303,26 +333,40 @@ export declare const sharedProviders: (import("@tinkoff/dippy/lib/Provider").Val
303
333
  }, import("@tramvai/tokens-render").HtmlAttrs & {
304
334
  __type?: "multi token" | undefined;
305
335
  }> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
306
- i18n: import("../types").I18nService & {
336
+ pageService: import("@tramvai/tokens-router").PageService & {
337
+ __type?: "base token" | undefined;
338
+ };
339
+ i18nHooks: import("../types").I18nHooks & {
307
340
  __type?: "base token" | undefined;
308
341
  };
309
342
  requestManager: import("@tramvai/tokens-common").RequestManager & {
310
343
  __type?: "base token" | undefined;
311
344
  };
312
- }, (import("@tramvai/tokens-render").PageResource & {
313
- __type?: "multi token" | undefined;
314
- }) | (import("@tramvai/tokens-render").PageResource[] & {
315
- __type?: "multi token" | undefined;
316
- })> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
345
+ i18nConfiguration: import("../types").I18nConfiguration & {
346
+ __type?: "base token" | undefined;
347
+ };
317
348
  i18n: import("../types").I18nService & {
318
349
  __type?: "base token" | undefined;
319
350
  };
351
+ }, ((walker: import("@tinkoff/meta-tags-generate").MetaWalk, ...args: any[]) => void) & {
352
+ __type?: "multi token" | undefined;
353
+ }> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
354
+ pageService: import("@tramvai/tokens-router").PageService & {
355
+ __type?: "base token" | undefined;
356
+ };
357
+ i18nHooks: import("../types").I18nHooks & {
358
+ __type?: "base token" | undefined;
359
+ };
320
360
  requestManager: import("@tramvai/tokens-common").RequestManager & {
321
361
  __type?: "base token" | undefined;
322
362
  };
323
- }, (import("@tramvai/tokens-render").PageResource & {
324
- __type?: "multi token" | undefined;
325
- }) | (import("@tramvai/tokens-render").PageResource[] & {
363
+ i18nConfiguration: import("../types").I18nConfiguration & {
364
+ __type?: "base token" | undefined;
365
+ };
366
+ i18n: import("../types").I18nService & {
367
+ __type?: "base token" | undefined;
368
+ };
369
+ }, ((walker: import("@tinkoff/meta-tags-generate").MetaWalk, ...args: any[]) => void) & {
326
370
  __type?: "multi token" | undefined;
327
- })>)[];
371
+ }>)[];
328
372
  //# sourceMappingURL=providers.d.ts.map
@@ -1,10 +1,12 @@
1
1
  import { provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
2
- import { COMBINE_REDUCERS, STORE_TOKEN, REQUEST_MANAGER_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN } from '@tramvai/tokens-common';
2
+ import { COMBINE_REDUCERS, STORE_TOKEN, ENV_MANAGER_TOKEN, REQUEST_MANAGER_TOKEN, LOGGER_TOKEN } from '@tramvai/tokens-common';
3
3
  import { COOKIE_MANAGER_TOKEN } from '@tramvai/tokens-cookie';
4
- import { ROUTER_TOKEN, ROUTER_GUARD_TOKEN, ROUTER_PLUGIN } from '@tramvai/tokens-router';
4
+ import { ROUTER_TOKEN, ROUTER_GUARD_TOKEN, ROUTER_PLUGIN, PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
5
5
  import { USER_LANGUAGE_TOKEN } from '@tramvai/module-client-hints';
6
- import { HTML_ATTRS, RENDER_SLOTS, ResourceType, ResourceSlot } from '@tramvai/tokens-render';
7
- import { I18N_CONFIGURATION_TOKEN, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN } from '../tokens.es.js';
6
+ import { HTML_ATTRS } from '@tramvai/tokens-render';
7
+ import { META_UPDATER_TOKEN, META_PRIORITY_ROUTE } from '@tramvai/module-seo';
8
+ import { TAPABLE_HOOK_FACTORY_TOKEN } from '@tramvai/tokens-core';
9
+ import { I18N_CONFIGURATION_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_HOOKS_TOKEN } from '../tokens.es.js';
8
10
  import { I18nStore, setAvailableLanguages, setLanguage } from './store.es.js';
9
11
  import { I18nServiceImpl } from './service.es.js';
10
12
  import { defaultConfiguration } from './config.es.js';
@@ -19,13 +21,24 @@ const sharedProviders = [
19
21
  provide: COMBINE_REDUCERS,
20
22
  useValue: [I18nStore],
21
23
  }),
24
+ provide({
25
+ provide: I18N_LANGUAGE_COOKIE_EXPIRES_VALUE,
26
+ useValue: 7 * 24 * 60 * 60,
27
+ }),
22
28
  provide({
23
29
  provide: I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN,
24
- useFactory: ({ config }) => {
25
- // TODO: sync with state from server if SSR?
30
+ useFactory: ({ config, store, envManager }) => {
31
+ // At client-side, for SSR mode, we can resolve available languages from server state
32
+ if (typeof window !== 'undefined' &&
33
+ envManager.get('TRAMVAI_FORCE_CLIENT_SIDE_RENDERING') !== 'true') {
34
+ const { availableLanguages } = store.getState(I18nStore);
35
+ return async () => availableLanguages;
36
+ }
26
37
  return async () => config.availableLanguages ?? [config.defaultLanguage];
27
38
  },
28
39
  deps: {
40
+ store: STORE_TOKEN,
41
+ envManager: ENV_MANAGER_TOKEN,
29
42
  config: I18N_CONFIGURATION_TOKEN,
30
43
  },
31
44
  }),
@@ -39,6 +52,7 @@ const sharedProviders = [
39
52
  router: ROUTER_TOKEN,
40
53
  requestManager: REQUEST_MANAGER_TOKEN,
41
54
  logger: LOGGER_TOKEN,
55
+ languageCookieExpiresValue: I18N_LANGUAGE_COOKIE_EXPIRES_VALUE,
42
56
  },
43
57
  }),
44
58
  // Language from cookie has higher priority
@@ -174,11 +188,7 @@ const sharedProviders = [
174
188
  store.dispatch(setLanguage(language));
175
189
  if (!cookieManager.get(config.cookieName)) {
176
190
  log.info(`${config.cookieName} cookie is not found, save resolved "${language.language}" language to cookie`);
177
- cookieManager.set({
178
- name: config.cookieName,
179
- value: language.language,
180
- // TODO valid params!
181
- });
191
+ i18n.setLanguageToCookie(language.language);
182
192
  }
183
193
  const currentUrl = router.getCurrentUrl() ?? requestManager.getParsedUrl();
184
194
  const languageFromUrl = i18n.getLanguageFromUrl(currentUrl);
@@ -295,6 +305,17 @@ const sharedProviders = [
295
305
  store: STORE_TOKEN,
296
306
  },
297
307
  }),
308
+ provide({
309
+ provide: I18N_HOOKS_TOKEN,
310
+ useFactory: ({ tapableHookFactory }) => {
311
+ return {
312
+ 'i18n:get-alternate-languages': tapableHookFactory.createSync('i18n:get-alternate-languages'),
313
+ };
314
+ },
315
+ deps: {
316
+ tapableHookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
317
+ },
318
+ }),
298
319
  provide({
299
320
  provide: HTML_ATTRS,
300
321
  useFactory: ({ i18n }) => {
@@ -310,24 +331,39 @@ const sharedProviders = [
310
331
  },
311
332
  }),
312
333
  provide({
313
- provide: RENDER_SLOTS,
314
- multi: true,
315
- useFactory: ({ i18n, requestManager }) => {
316
- const parsedUrl = requestManager.getParsedUrl();
317
- const url = `${parsedUrl.origin}${parsedUrl.pathname}`;
318
- // TODO: dynamic list of available languages for specific route to generate alternate links
319
- // TODO: `x-default` link?
320
- return [
321
- {
322
- type: ResourceType.asIs,
323
- slot: ResourceSlot.HEAD_META,
324
- payload: `<link rel="alternate" hreflang="${i18n.getLanguage()}" href="${url}" />`,
325
- },
326
- ];
334
+ provide: META_UPDATER_TOKEN,
335
+ useFactory: ({ pageService, i18n, requestManager, i18nHooks }) => {
336
+ return (meta) => {
337
+ const parsedUrl = requestManager.getParsedUrl();
338
+ const routeConfig = pageService.getConfig() || {};
339
+ const urlWithoutLanguage = i18n.removeLanguageFromUrl(parsedUrl);
340
+ const baseAvailableLanguages = i18n.getAvailableLanguages();
341
+ const alternateLanguages = i18nHooks['i18n:get-alternate-languages'].call({
342
+ availableLanguages: baseAvailableLanguages,
343
+ routeConfig,
344
+ }) ?? baseAvailableLanguages;
345
+ const tags = alternateLanguages.reduce((result, languageCode) => {
346
+ const { pathname } = i18n.addLanguageToUrl(urlWithoutLanguage, languageCode);
347
+ //@ts-expect-error
348
+ result[`i18nAlternate_${languageCode}`] = {
349
+ tag: 'link',
350
+ attributes: {
351
+ rel: 'alternate',
352
+ hreflang: languageCode,
353
+ href: `${parsedUrl.origin}${pathname}${parsedUrl.search}${parsedUrl.hash}`,
354
+ },
355
+ };
356
+ return result;
357
+ }, {});
358
+ meta.updateMeta(META_PRIORITY_ROUTE, tags);
359
+ };
327
360
  },
328
361
  deps: {
329
- i18n: I18N_TOKEN,
362
+ pageService: PAGE_SERVICE_TOKEN,
363
+ i18nHooks: I18N_HOOKS_TOKEN,
330
364
  requestManager: REQUEST_MANAGER_TOKEN,
365
+ i18nConfiguration: I18N_CONFIGURATION_TOKEN,
366
+ i18n: I18N_TOKEN,
331
367
  },
332
368
  }),
333
369
  ];
@@ -8,6 +8,8 @@ var tokensCookie = require('@tramvai/tokens-cookie');
8
8
  var tokensRouter = require('@tramvai/tokens-router');
9
9
  var moduleClientHints = require('@tramvai/module-client-hints');
10
10
  var tokensRender = require('@tramvai/tokens-render');
11
+ var moduleSeo = require('@tramvai/module-seo');
12
+ var tokensCore = require('@tramvai/tokens-core');
11
13
  var tokens = require('../tokens.js');
12
14
  var store = require('./store.js');
13
15
  var service = require('./service.js');
@@ -23,13 +25,24 @@ const sharedProviders = [
23
25
  provide: tokensCommon.COMBINE_REDUCERS,
24
26
  useValue: [store.I18nStore],
25
27
  }),
28
+ core.provide({
29
+ provide: tokens.I18N_LANGUAGE_COOKIE_EXPIRES_VALUE,
30
+ useValue: 7 * 24 * 60 * 60,
31
+ }),
26
32
  core.provide({
27
33
  provide: tokens.I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN,
28
- useFactory: ({ config }) => {
29
- // TODO: sync with state from server if SSR?
34
+ useFactory: ({ config, store: store$1, envManager }) => {
35
+ // At client-side, for SSR mode, we can resolve available languages from server state
36
+ if (typeof window !== 'undefined' &&
37
+ envManager.get('TRAMVAI_FORCE_CLIENT_SIDE_RENDERING') !== 'true') {
38
+ const { availableLanguages } = store$1.getState(store.I18nStore);
39
+ return async () => availableLanguages;
40
+ }
30
41
  return async () => config.availableLanguages ?? [config.defaultLanguage];
31
42
  },
32
43
  deps: {
44
+ store: tokensCommon.STORE_TOKEN,
45
+ envManager: tokensCommon.ENV_MANAGER_TOKEN,
33
46
  config: tokens.I18N_CONFIGURATION_TOKEN,
34
47
  },
35
48
  }),
@@ -43,6 +56,7 @@ const sharedProviders = [
43
56
  router: tokensRouter.ROUTER_TOKEN,
44
57
  requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
45
58
  logger: tokensCommon.LOGGER_TOKEN,
59
+ languageCookieExpiresValue: tokens.I18N_LANGUAGE_COOKIE_EXPIRES_VALUE,
46
60
  },
47
61
  }),
48
62
  // Language from cookie has higher priority
@@ -178,11 +192,7 @@ const sharedProviders = [
178
192
  store$1.dispatch(store.setLanguage(language));
179
193
  if (!cookieManager.get(config.cookieName)) {
180
194
  log.info(`${config.cookieName} cookie is not found, save resolved "${language.language}" language to cookie`);
181
- cookieManager.set({
182
- name: config.cookieName,
183
- value: language.language,
184
- // TODO valid params!
185
- });
195
+ i18n.setLanguageToCookie(language.language);
186
196
  }
187
197
  const currentUrl = router.getCurrentUrl() ?? requestManager.getParsedUrl();
188
198
  const languageFromUrl = i18n.getLanguageFromUrl(currentUrl);
@@ -299,6 +309,17 @@ const sharedProviders = [
299
309
  store: tokensCommon.STORE_TOKEN,
300
310
  },
301
311
  }),
312
+ core.provide({
313
+ provide: tokens.I18N_HOOKS_TOKEN,
314
+ useFactory: ({ tapableHookFactory }) => {
315
+ return {
316
+ 'i18n:get-alternate-languages': tapableHookFactory.createSync('i18n:get-alternate-languages'),
317
+ };
318
+ },
319
+ deps: {
320
+ tapableHookFactory: tokensCore.TAPABLE_HOOK_FACTORY_TOKEN,
321
+ },
322
+ }),
302
323
  core.provide({
303
324
  provide: tokensRender.HTML_ATTRS,
304
325
  useFactory: ({ i18n }) => {
@@ -314,24 +335,39 @@ const sharedProviders = [
314
335
  },
315
336
  }),
316
337
  core.provide({
317
- provide: tokensRender.RENDER_SLOTS,
318
- multi: true,
319
- useFactory: ({ i18n, requestManager }) => {
320
- const parsedUrl = requestManager.getParsedUrl();
321
- const url = `${parsedUrl.origin}${parsedUrl.pathname}`;
322
- // TODO: dynamic list of available languages for specific route to generate alternate links
323
- // TODO: `x-default` link?
324
- return [
325
- {
326
- type: tokensRender.ResourceType.asIs,
327
- slot: tokensRender.ResourceSlot.HEAD_META,
328
- payload: `<link rel="alternate" hreflang="${i18n.getLanguage()}" href="${url}" />`,
329
- },
330
- ];
338
+ provide: moduleSeo.META_UPDATER_TOKEN,
339
+ useFactory: ({ pageService, i18n, requestManager, i18nHooks }) => {
340
+ return (meta) => {
341
+ const parsedUrl = requestManager.getParsedUrl();
342
+ const routeConfig = pageService.getConfig() || {};
343
+ const urlWithoutLanguage = i18n.removeLanguageFromUrl(parsedUrl);
344
+ const baseAvailableLanguages = i18n.getAvailableLanguages();
345
+ const alternateLanguages = i18nHooks['i18n:get-alternate-languages'].call({
346
+ availableLanguages: baseAvailableLanguages,
347
+ routeConfig,
348
+ }) ?? baseAvailableLanguages;
349
+ const tags = alternateLanguages.reduce((result, languageCode) => {
350
+ const { pathname } = i18n.addLanguageToUrl(urlWithoutLanguage, languageCode);
351
+ //@ts-expect-error
352
+ result[`i18nAlternate_${languageCode}`] = {
353
+ tag: 'link',
354
+ attributes: {
355
+ rel: 'alternate',
356
+ hreflang: languageCode,
357
+ href: `${parsedUrl.origin}${pathname}${parsedUrl.search}${parsedUrl.hash}`,
358
+ },
359
+ };
360
+ return result;
361
+ }, {});
362
+ meta.updateMeta(moduleSeo.META_PRIORITY_ROUTE, tags);
363
+ };
331
364
  },
332
365
  deps: {
333
- i18n: tokens.I18N_TOKEN,
366
+ pageService: tokensRouter.PAGE_SERVICE_TOKEN,
367
+ i18nHooks: tokens.I18N_HOOKS_TOKEN,
334
368
  requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
369
+ i18nConfiguration: tokens.I18N_CONFIGURATION_TOKEN,
370
+ i18n: tokens.I18N_TOKEN,
335
371
  },
336
372
  }),
337
373
  ];
@@ -9,6 +9,7 @@ class I18nServiceImpl {
9
9
  router;
10
10
  requestManager;
11
11
  log;
12
+ languageCookieExpiresValue;
12
13
  constructor(deps) {
13
14
  this.store = deps.store;
14
15
  this.config = deps.config;
@@ -16,6 +17,7 @@ class I18nServiceImpl {
16
17
  this.router = deps.router;
17
18
  this.requestManager = deps.requestManager;
18
19
  this.log = deps.logger('i18n');
20
+ this.languageCookieExpiresValue = deps.languageCookieExpiresValue;
19
21
  }
20
22
  getLanguage() {
21
23
  const state = this.store.getState(I18nStore);
@@ -25,20 +27,26 @@ class I18nServiceImpl {
25
27
  const state = this.store.getState(I18nStore);
26
28
  return state.availableLanguages;
27
29
  }
30
+ setLanguageToCookie(language, expires) {
31
+ this.cookieManager.set({
32
+ name: this.config.cookieName,
33
+ value: language,
34
+ expires: expires ?? this.languageCookieExpiresValue,
35
+ });
36
+ }
28
37
  // eslint-disable-next-line max-statements
29
- async switchLanguage(language) {
38
+ async switchLanguage(switchToLanguage) {
30
39
  const availableLanguages = this.getAvailableLanguages();
40
+ const language = switchToLanguage;
31
41
  if (!availableLanguages.includes(language)) {
32
- // TODO: is throwing an error is a best solution?
33
- throw new Error(`Language "${language}" is not available. Available languages: ${availableLanguages.join(', ')}`);
42
+ this.log.info(`Language ${language} is not in availableLanguages list. Available languages is ${availableLanguages}`);
43
+ return;
34
44
  }
35
45
  this.log.info(`Switch language to "${language}"`);
36
46
  const { cookieName, routingStrategy, updateStrategy } = this.config;
37
- this.cookieManager.set({
38
- name: cookieName,
39
- value: language,
40
- // TODO valid params!
41
- });
47
+ if (this.cookieManager.get(cookieName) !== language) {
48
+ this.setLanguageToCookie(language);
49
+ }
42
50
  const currentUrl = this.router.getCurrentUrl() ?? this.requestManager.getUrl();
43
51
  const parsedUrl = typeof currentUrl === 'string' ? parse(currentUrl) : currentUrl;
44
52
  const currentLanguage = this.getLanguage();
@@ -4,7 +4,7 @@ import { LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, STORE_TOKEN } from '@tramvai/token
4
4
  import { ROUTER_TOKEN } from '@tramvai/tokens-router';
5
5
  import { Url } from '@tinkoff/url';
6
6
  import type { I18nService, Language } from '../types';
7
- import { I18N_CONFIGURATION_TOKEN } from '../tokens';
7
+ import { I18N_CONFIGURATION_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE } from '../tokens';
8
8
  type Deps = {
9
9
  store: ExtractDependencyType<typeof STORE_TOKEN>;
10
10
  config: ExtractDependencyType<typeof I18N_CONFIGURATION_TOKEN>;
@@ -12,6 +12,7 @@ type Deps = {
12
12
  router: ExtractDependencyType<typeof ROUTER_TOKEN>;
13
13
  requestManager: ExtractDependencyType<typeof REQUEST_MANAGER_TOKEN>;
14
14
  logger: ExtractDependencyType<typeof LOGGER_TOKEN>;
15
+ languageCookieExpiresValue: ExtractDependencyType<typeof I18N_LANGUAGE_COOKIE_EXPIRES_VALUE>;
15
16
  };
16
17
  export declare class I18nServiceImpl implements I18nService {
17
18
  private store;
@@ -20,10 +21,12 @@ export declare class I18nServiceImpl implements I18nService {
20
21
  private router;
21
22
  private requestManager;
22
23
  private log;
24
+ private languageCookieExpiresValue;
23
25
  constructor(deps: Deps);
24
26
  getLanguage(): Language;
25
27
  getAvailableLanguages(): Language[];
26
- switchLanguage(language: Language): Promise<void>;
28
+ setLanguageToCookie(language: string, expires?: number): void;
29
+ switchLanguage(switchToLanguage: Language): Promise<void>;
27
30
  getLanguageFromUrl(url: string | Url): Language | null;
28
31
  removeLanguageFromUrl(url: string | Url): Url;
29
32
  addLanguageToUrl(url: string | Url, lang: Language): Url;
@@ -9,6 +9,7 @@ class I18nServiceImpl {
9
9
  router;
10
10
  requestManager;
11
11
  log;
12
+ languageCookieExpiresValue;
12
13
  constructor(deps) {
13
14
  this.store = deps.store;
14
15
  this.config = deps.config;
@@ -16,6 +17,7 @@ class I18nServiceImpl {
16
17
  this.router = deps.router;
17
18
  this.requestManager = deps.requestManager;
18
19
  this.log = deps.logger('i18n');
20
+ this.languageCookieExpiresValue = deps.languageCookieExpiresValue;
19
21
  }
20
22
  getLanguage() {
21
23
  const state = this.store.getState(I18nStore);
@@ -25,20 +27,26 @@ class I18nServiceImpl {
25
27
  const state = this.store.getState(I18nStore);
26
28
  return state.availableLanguages;
27
29
  }
30
+ setLanguageToCookie(language, expires) {
31
+ this.cookieManager.set({
32
+ name: this.config.cookieName,
33
+ value: language,
34
+ expires: expires ?? this.languageCookieExpiresValue,
35
+ });
36
+ }
28
37
  // eslint-disable-next-line max-statements
29
- async switchLanguage(language) {
38
+ async switchLanguage(switchToLanguage) {
30
39
  const availableLanguages = this.getAvailableLanguages();
40
+ const language = switchToLanguage;
31
41
  if (!availableLanguages.includes(language)) {
32
- // TODO: is throwing an error is a best solution?
33
- throw new Error(`Language "${language}" is not available. Available languages: ${availableLanguages.join(', ')}`);
42
+ this.log.info(`Language ${language} is not in availableLanguages list. Available languages is ${availableLanguages}`);
43
+ return;
34
44
  }
35
45
  this.log.info(`Switch language to "${language}"`);
36
46
  const { cookieName, routingStrategy, updateStrategy } = this.config;
37
- this.cookieManager.set({
38
- name: cookieName,
39
- value: language,
40
- // TODO valid params!
41
- });
47
+ if (this.cookieManager.get(cookieName) !== language) {
48
+ this.setLanguageToCookie(language);
49
+ }
42
50
  const currentUrl = this.router.getCurrentUrl() ?? this.requestManager.getUrl();
43
51
  const parsedUrl = typeof currentUrl === 'string' ? parse(currentUrl) : currentUrl;
44
52
  const currentLanguage = this.getLanguage();
@@ -13,6 +13,7 @@ class I18nServiceImpl {
13
13
  router;
14
14
  requestManager;
15
15
  log;
16
+ languageCookieExpiresValue;
16
17
  constructor(deps) {
17
18
  this.store = deps.store;
18
19
  this.config = deps.config;
@@ -20,6 +21,7 @@ class I18nServiceImpl {
20
21
  this.router = deps.router;
21
22
  this.requestManager = deps.requestManager;
22
23
  this.log = deps.logger('i18n');
24
+ this.languageCookieExpiresValue = deps.languageCookieExpiresValue;
23
25
  }
24
26
  getLanguage() {
25
27
  const state = this.store.getState(store.I18nStore);
@@ -29,20 +31,26 @@ class I18nServiceImpl {
29
31
  const state = this.store.getState(store.I18nStore);
30
32
  return state.availableLanguages;
31
33
  }
34
+ setLanguageToCookie(language, expires) {
35
+ this.cookieManager.set({
36
+ name: this.config.cookieName,
37
+ value: language,
38
+ expires: expires ?? this.languageCookieExpiresValue,
39
+ });
40
+ }
32
41
  // eslint-disable-next-line max-statements
33
- async switchLanguage(language) {
42
+ async switchLanguage(switchToLanguage) {
34
43
  const availableLanguages = this.getAvailableLanguages();
44
+ const language = switchToLanguage;
35
45
  if (!availableLanguages.includes(language)) {
36
- // TODO: is throwing an error is a best solution?
37
- throw new Error(`Language "${language}" is not available. Available languages: ${availableLanguages.join(', ')}`);
46
+ this.log.info(`Language ${language} is not in availableLanguages list. Available languages is ${availableLanguages}`);
47
+ return;
38
48
  }
39
49
  this.log.info(`Switch language to "${language}"`);
40
50
  const { cookieName, routingStrategy, updateStrategy } = this.config;
41
- this.cookieManager.set({
42
- name: cookieName,
43
- value: language,
44
- // TODO valid params!
45
- });
51
+ if (this.cookieManager.get(cookieName) !== language) {
52
+ this.setLanguageToCookie(language);
53
+ }
46
54
  const currentUrl = this.router.getCurrentUrl() ?? this.requestManager.getUrl();
47
55
  const parsedUrl = typeof currentUrl === 'string' ? url.parse(currentUrl) : currentUrl;
48
56
  const currentLanguage = this.getLanguage();
@@ -2,8 +2,12 @@ import { createToken, Scope } from '@tramvai/core';
2
2
 
3
3
  const I18N_CONFIGURATION_TOKEN = createToken('tramvai i18n configuration', { scope: Scope.SINGLETON });
4
4
  const I18N_TOKEN = createToken('tramvai i18n service');
5
+ const I18N_LANGUAGE_COOKIE_EXPIRES_VALUE = createToken('tramvai i18n language cookie expires value');
6
+ const I18N_HOOKS_TOKEN = createToken('tramvai i18n tapable hooks', {
7
+ scope: Scope.REQUEST,
8
+ });
5
9
  const I18N_RESOLVE_LANGUAGE_TOKEN = createToken('tramvai i18n resolve language');
6
10
  const I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN = createToken('tramvai i18n resolve language source', { multi: true });
7
11
  const I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN = createToken('tramvai i18n resolve available languages');
8
12
 
9
- export { I18N_CONFIGURATION_TOKEN, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN };
13
+ export { I18N_CONFIGURATION_TOKEN, I18N_HOOKS_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN };
package/lib/tokens.d.ts CHANGED
@@ -1,10 +1,16 @@
1
- import type { I18nConfiguration, I18nService, Language } from './types';
1
+ import type { I18nConfiguration, I18nHooks, I18nService, Language } from './types';
2
2
  export declare const I18N_CONFIGURATION_TOKEN: I18nConfiguration & {
3
3
  __type?: "base token" | undefined;
4
4
  };
5
5
  export declare const I18N_TOKEN: I18nService & {
6
6
  __type?: "base token" | undefined;
7
7
  };
8
+ export declare const I18N_LANGUAGE_COOKIE_EXPIRES_VALUE: number & {
9
+ __type?: "base token" | undefined;
10
+ };
11
+ export declare const I18N_HOOKS_TOKEN: I18nHooks & {
12
+ __type?: "base token" | undefined;
13
+ };
8
14
  export declare const I18N_RESOLVE_LANGUAGE_TOKEN: (() => Promise<{
9
15
  language: Language;
10
16
  source: string;
package/lib/tokens.es.js CHANGED
@@ -2,8 +2,12 @@ import { createToken, Scope } from '@tramvai/core';
2
2
 
3
3
  const I18N_CONFIGURATION_TOKEN = createToken('tramvai i18n configuration', { scope: Scope.SINGLETON });
4
4
  const I18N_TOKEN = createToken('tramvai i18n service');
5
+ const I18N_LANGUAGE_COOKIE_EXPIRES_VALUE = createToken('tramvai i18n language cookie expires value');
6
+ const I18N_HOOKS_TOKEN = createToken('tramvai i18n tapable hooks', {
7
+ scope: Scope.REQUEST,
8
+ });
5
9
  const I18N_RESOLVE_LANGUAGE_TOKEN = createToken('tramvai i18n resolve language');
6
10
  const I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN = createToken('tramvai i18n resolve language source', { multi: true });
7
11
  const I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN = createToken('tramvai i18n resolve available languages');
8
12
 
9
- export { I18N_CONFIGURATION_TOKEN, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN };
13
+ export { I18N_CONFIGURATION_TOKEN, I18N_HOOKS_TOKEN, I18N_LANGUAGE_COOKIE_EXPIRES_VALUE, I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN, I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN, I18N_RESOLVE_LANGUAGE_TOKEN, I18N_TOKEN };
package/lib/tokens.js CHANGED
@@ -6,11 +6,17 @@ var core = require('@tramvai/core');
6
6
 
7
7
  const I18N_CONFIGURATION_TOKEN = core.createToken('tramvai i18n configuration', { scope: core.Scope.SINGLETON });
8
8
  const I18N_TOKEN = core.createToken('tramvai i18n service');
9
+ const I18N_LANGUAGE_COOKIE_EXPIRES_VALUE = core.createToken('tramvai i18n language cookie expires value');
10
+ const I18N_HOOKS_TOKEN = core.createToken('tramvai i18n tapable hooks', {
11
+ scope: core.Scope.REQUEST,
12
+ });
9
13
  const I18N_RESOLVE_LANGUAGE_TOKEN = core.createToken('tramvai i18n resolve language');
10
14
  const I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN = core.createToken('tramvai i18n resolve language source', { multi: true });
11
15
  const I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN = core.createToken('tramvai i18n resolve available languages');
12
16
 
13
17
  exports.I18N_CONFIGURATION_TOKEN = I18N_CONFIGURATION_TOKEN;
18
+ exports.I18N_HOOKS_TOKEN = I18N_HOOKS_TOKEN;
19
+ exports.I18N_LANGUAGE_COOKIE_EXPIRES_VALUE = I18N_LANGUAGE_COOKIE_EXPIRES_VALUE;
14
20
  exports.I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN = I18N_RESOLVE_AVAILABLE_LANGUAGES_TOKEN;
15
21
  exports.I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN = I18N_RESOLVE_LANGUAGE_SOURCE_TOKEN;
16
22
  exports.I18N_RESOLVE_LANGUAGE_TOKEN = I18N_RESOLVE_LANGUAGE_TOKEN;
package/lib/types.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { SyncTapableHookInstance } from '@tinkoff/hook-runner';
2
+ import type { RouteConfig } from '@tinkoff/router';
1
3
  import { Url } from '@tinkoff/url';
2
4
  /**
3
5
  * Language code in ISO 639-1 format (e.g., 'en', 'ru', 'zh')
@@ -68,5 +70,12 @@ export interface I18nService {
68
70
  switchLanguage(language: Language): Promise<void>;
69
71
  removeLanguageFromUrl(url: string | Url): Url;
70
72
  addLanguageToUrl(url: string | Url, language: Language): Url;
73
+ setLanguageToCookie(language: string, expires?: number): void;
71
74
  }
75
+ export type I18nHooks = {
76
+ 'i18n:get-alternate-languages': SyncTapableHookInstance<{
77
+ availableLanguages: string[];
78
+ routeConfig: RouteConfig;
79
+ }, Language[]>;
80
+ };
72
81
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-i18n",
3
- "version": "7.21.1",
3
+ "version": "7.26.9",
4
4
  "description": "Internationalization (i18n) module for Tramvai",
5
5
  "main": "lib/server.js",
6
6
  "module": "lib/server.es.js",
@@ -18,21 +18,27 @@
18
18
  "build": "tramvai-build --forPublish --preserveModules",
19
19
  "watch": "tsc -w"
20
20
  },
21
+ "devDependencies": {
22
+ "@tinkoff/hook-runner": "0.9.1"
23
+ },
21
24
  "dependencies": {
22
25
  "@tinkoff/errors": "0.8.1",
23
26
  "@tinkoff/url": "0.13.1",
24
- "@tramvai/module-client-hints": "7.21.1",
25
- "@tramvai/tokens-common": "7.21.1",
26
- "@tramvai/tokens-cookie": "7.21.1",
27
- "@tramvai/tokens-render": "7.21.1",
28
- "@tramvai/tokens-router": "7.21.1"
27
+ "@tramvai/module-client-hints": "7.26.9",
28
+ "@tramvai/module-seo": "7.26.9",
29
+ "@tramvai/tokens-common": "7.26.9",
30
+ "@tramvai/tokens-cookie": "7.26.9",
31
+ "@tramvai/tokens-core": "7.26.9",
32
+ "@tramvai/tokens-render": "7.26.9",
33
+ "@tramvai/tokens-router": "7.26.9"
29
34
  },
30
35
  "peerDependencies": {
31
36
  "@tinkoff/dippy": "^1.0.0",
32
37
  "@tinkoff/logger": "^0.10.518",
33
- "@tramvai/core": "7.21.1",
34
- "@tramvai/react": "7.21.1",
35
- "@tramvai/state": "7.21.1",
38
+ "@tinkoff/utils": "^2.1.2",
39
+ "@tramvai/core": "7.26.9",
40
+ "@tramvai/react": "7.26.9",
41
+ "@tramvai/state": "7.26.9",
36
42
  "tslib": "^2.4.0"
37
43
  },
38
44
  "license": "Apache-2.0"