@lifi/widget 3.32.2 → 3.33.1

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/esm/AppProvider.js +2 -1
  3. package/dist/esm/AppProvider.js.map +1 -1
  4. package/dist/esm/components/Header/NavigationHeader.js +4 -1
  5. package/dist/esm/components/Header/NavigationHeader.js.map +1 -1
  6. package/dist/esm/components/Tools/Tools.js +1 -1
  7. package/dist/esm/components/Tools/Tools.js.map +1 -1
  8. package/dist/esm/config/version.d.ts +1 -1
  9. package/dist/esm/config/version.js +1 -1
  10. package/dist/esm/hooks/useHeader.js +4 -1
  11. package/dist/esm/hooks/useHeader.js.map +1 -1
  12. package/dist/esm/hooks/useLanguages.d.ts +2 -3
  13. package/dist/esm/hooks/useLanguages.js +50 -14
  14. package/dist/esm/hooks/useLanguages.js.map +1 -1
  15. package/dist/esm/hooks/useRoutes.js +1 -1
  16. package/dist/esm/hooks/useRoutes.js.map +1 -1
  17. package/dist/esm/hooks/useSettingMonitor.js +2 -1
  18. package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
  19. package/dist/esm/hooks/useTokenBalances.js +2 -1
  20. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  21. package/dist/esm/hooks/useTools.js +2 -1
  22. package/dist/esm/hooks/useTools.js.map +1 -1
  23. package/dist/esm/pages/LanguagesPage.js +6 -1
  24. package/dist/esm/pages/LanguagesPage.js.map +1 -1
  25. package/dist/esm/pages/SelectEnabledToolsPage.js +1 -1
  26. package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
  27. package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js +1 -1
  28. package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  29. package/dist/esm/pages/SettingsPage/LanguageSetting.js +7 -2
  30. package/dist/esm/pages/SettingsPage/LanguageSetting.js.map +1 -1
  31. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js +1 -1
  32. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js.map +1 -1
  33. package/dist/esm/providers/I18nProvider/I18nProvider.js +47 -41
  34. package/dist/esm/providers/I18nProvider/I18nProvider.js.map +1 -1
  35. package/dist/esm/providers/I18nProvider/constants.d.ts +3 -0
  36. package/dist/esm/providers/I18nProvider/constants.js +39 -0
  37. package/dist/esm/providers/I18nProvider/constants.js.map +1 -0
  38. package/dist/esm/providers/I18nProvider/enResource.d.ts +2 -0
  39. package/dist/esm/providers/I18nProvider/enResource.js +4 -0
  40. package/dist/esm/providers/I18nProvider/enResource.js.map +1 -0
  41. package/dist/esm/providers/I18nProvider/i18n.d.ts +3 -0
  42. package/dist/esm/providers/I18nProvider/i18n.js +19 -0
  43. package/dist/esm/providers/I18nProvider/i18n.js.map +1 -0
  44. package/dist/esm/providers/I18nProvider/types.d.ts +4 -11
  45. package/dist/esm/providers/WalletProvider/SDKProviders.js +1 -1
  46. package/dist/esm/providers/WalletProvider/SDKProviders.js.map +1 -1
  47. package/dist/esm/stores/bookmarks/createBookmarkStore.d.ts +19 -1
  48. package/dist/esm/stores/bookmarks/createBookmarkStore.js +3 -3
  49. package/dist/esm/stores/bookmarks/createBookmarkStore.js.map +1 -1
  50. package/dist/esm/stores/chains/ChainOrderStore.js +3 -1
  51. package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
  52. package/dist/esm/stores/chains/createChainOrderStore.d.ts +33 -1
  53. package/dist/esm/stores/chains/createChainOrderStore.js +3 -3
  54. package/dist/esm/stores/chains/createChainOrderStore.js.map +1 -1
  55. package/dist/esm/stores/chains/useChainOrder.js +5 -2
  56. package/dist/esm/stores/chains/useChainOrder.js.map +1 -1
  57. package/dist/esm/stores/form/createFormStore.d.ts +1 -1
  58. package/dist/esm/stores/form/createFormStore.js +3 -3
  59. package/dist/esm/stores/form/createFormStore.js.map +1 -1
  60. package/dist/esm/stores/header/useHeaderStore.js +3 -3
  61. package/dist/esm/stores/header/useHeaderStore.js.map +1 -1
  62. package/dist/esm/stores/routes/createRouteExecutionStore.d.ts +17 -1
  63. package/dist/esm/stores/routes/createRouteExecutionStore.js +3 -3
  64. package/dist/esm/stores/routes/createRouteExecutionStore.js.map +1 -1
  65. package/dist/esm/stores/settings/SettingsStore.d.ts +9 -0
  66. package/dist/esm/stores/settings/SettingsStore.js +24 -0
  67. package/dist/esm/stores/settings/SettingsStore.js.map +1 -0
  68. package/dist/esm/stores/settings/createSettingsStore.d.ts +51 -0
  69. package/dist/esm/stores/settings/{useSettingsStore.js → createSettingsStore.js} +38 -11
  70. package/dist/esm/stores/settings/createSettingsStore.js.map +1 -0
  71. package/dist/esm/stores/settings/types.d.ts +7 -1
  72. package/dist/esm/stores/settings/types.js.map +1 -1
  73. package/dist/esm/stores/settings/useSendToWalletStore.d.ts +1 -1
  74. package/dist/esm/stores/settings/useSendToWalletStore.js +3 -3
  75. package/dist/esm/stores/settings/useSendToWalletStore.js.map +1 -1
  76. package/dist/esm/stores/settings/useSettings.js +1 -1
  77. package/dist/esm/stores/settings/useSettings.js.map +1 -1
  78. package/dist/esm/stores/settings/useSettingsActions.d.ts +1 -0
  79. package/dist/esm/stores/settings/useSettingsActions.js +9 -1
  80. package/dist/esm/stores/settings/useSettingsActions.js.map +1 -1
  81. package/dist/esm/stores/settings/useSplitSubvariantStore.js +3 -3
  82. package/dist/esm/stores/settings/useSplitSubvariantStore.js.map +1 -1
  83. package/dist/esm/stores/settings/utils/getStateValues.js +1 -0
  84. package/dist/esm/stores/settings/utils/getStateValues.js.map +1 -1
  85. package/dist/esm/utils/tokenList.d.ts +1 -1
  86. package/dist/esm/utils/tokenList.js +3 -3
  87. package/dist/esm/utils/tokenList.js.map +1 -1
  88. package/package.json +13 -13
  89. package/package.json.tmp +15 -15
  90. package/src/AppProvider.tsx +14 -11
  91. package/src/components/Header/NavigationHeader.tsx +4 -1
  92. package/src/components/Tools/Tools.tsx +1 -1
  93. package/src/config/version.ts +1 -1
  94. package/src/hooks/useHeader.ts +4 -1
  95. package/src/hooks/useLanguages.ts +73 -15
  96. package/src/hooks/useRoutes.ts +1 -1
  97. package/src/hooks/useSettingMonitor.ts +2 -4
  98. package/src/hooks/useTokenBalances.ts +2 -1
  99. package/src/hooks/useTools.ts +3 -1
  100. package/src/pages/LanguagesPage.tsx +10 -1
  101. package/src/pages/SelectEnabledToolsPage.tsx +1 -1
  102. package/src/pages/SettingsPage/BridgeAndExchangeSettings.tsx +1 -1
  103. package/src/pages/SettingsPage/LanguageSetting.tsx +9 -2
  104. package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.tsx +1 -1
  105. package/src/providers/I18nProvider/I18nProvider.tsx +66 -51
  106. package/src/providers/I18nProvider/constants.ts +41 -0
  107. package/src/providers/I18nProvider/enResource.ts +4 -0
  108. package/src/providers/I18nProvider/i18n.ts +26 -0
  109. package/src/providers/I18nProvider/types.ts +21 -12
  110. package/src/providers/WalletProvider/SDKProviders.tsx +2 -1
  111. package/src/stores/bookmarks/createBookmarkStore.ts +3 -5
  112. package/src/stores/chains/ChainOrderStore.tsx +3 -1
  113. package/src/stores/chains/createChainOrderStore.ts +3 -5
  114. package/src/stores/chains/useChainOrder.ts +10 -2
  115. package/src/stores/form/createFormStore.ts +3 -3
  116. package/src/stores/header/useHeaderStore.tsx +31 -34
  117. package/src/stores/routes/createRouteExecutionStore.ts +3 -5
  118. package/src/stores/settings/SettingsStore.tsx +45 -0
  119. package/src/stores/settings/createSettingsStore.ts +223 -0
  120. package/src/stores/settings/types.ts +8 -1
  121. package/src/stores/settings/useSendToWalletStore.ts +8 -11
  122. package/src/stores/settings/useSettings.ts +1 -1
  123. package/src/stores/settings/useSettingsActions.ts +13 -4
  124. package/src/stores/settings/useSplitSubvariantStore.tsx +9 -12
  125. package/src/stores/settings/utils/getStateValues.ts +1 -0
  126. package/src/themes/types.ts +0 -3
  127. package/src/utils/tokenList.ts +3 -3
  128. package/dist/esm/i18n/index.d.ts +0 -16
  129. package/dist/esm/i18n/index.js +0 -17
  130. package/dist/esm/i18n/index.js.map +0 -1
  131. package/dist/esm/stores/settings/useSettingsStore.d.ts +0 -5
  132. package/dist/esm/stores/settings/useSettingsStore.js.map +0 -1
  133. package/src/i18n/index.ts +0 -17
  134. package/src/stores/settings/useSettingsStore.ts +0 -185
@@ -16,6 +16,7 @@ import {
16
16
  } from './providers/WidgetProvider/WidgetProvider.js'
17
17
  import { URLSearchParamsBuilder } from './stores/form/URLSearchParamsBuilder.js'
18
18
  import { StoreProvider } from './stores/StoreProvider.js'
19
+ import { SettingsStoreProvider } from './stores/settings/SettingsStore.js'
19
20
  import type { WidgetConfigProps } from './types/widget.js'
20
21
 
21
22
  export const AppProvider: React.FC<PropsWithChildren<WidgetConfigProps>> = ({
@@ -25,17 +26,19 @@ export const AppProvider: React.FC<PropsWithChildren<WidgetConfigProps>> = ({
25
26
  }) => {
26
27
  return (
27
28
  <QueryClientProvider>
28
- <WidgetProvider config={config}>
29
- <I18nProvider>
30
- <ThemeProvider>
31
- <WalletProvider>
32
- <StoreProvider config={config} formRef={formRef}>
33
- <AppRouter>{children}</AppRouter>
34
- </StoreProvider>
35
- </WalletProvider>
36
- </ThemeProvider>
37
- </I18nProvider>
38
- </WidgetProvider>
29
+ <SettingsStoreProvider config={config}>
30
+ <WidgetProvider config={config}>
31
+ <I18nProvider>
32
+ <ThemeProvider>
33
+ <WalletProvider>
34
+ <StoreProvider config={config} formRef={formRef}>
35
+ <AppRouter>{children}</AppRouter>
36
+ </StoreProvider>
37
+ </WalletProvider>
38
+ </ThemeProvider>
39
+ </I18nProvider>
40
+ </WidgetProvider>
41
+ </SettingsStoreProvider>
39
42
  </QueryClientProvider>
40
43
  )
41
44
  }
@@ -22,7 +22,10 @@ export const NavigationHeader: React.FC = () => {
22
22
  useWidgetConfig()
23
23
  const { navigateBack } = useNavigateBack()
24
24
  const { account } = useAccount()
25
- const { element, title } = useHeaderStore((state) => state)
25
+ const [element, title] = useHeaderStore((state) => [
26
+ state.element,
27
+ state.title,
28
+ ])
26
29
  const { pathname } = useLocation()
27
30
 
28
31
  const cleanedPathname = pathname.endsWith('/')
@@ -1,8 +1,8 @@
1
1
  import type { ToolsResponse } from '@lifi/sdk'
2
2
  import { useMemo, useRef } from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
+ import { useSettingsStore } from '../../stores/settings/SettingsStore.js'
4
5
  import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
5
- import { useSettingsStore } from '../../stores/settings/useSettingsStore.js'
6
6
  import { SearchList } from '../Search/SearchInput.style.js'
7
7
  import { SearchNotFound } from '../Search/SearchNotFound.js'
8
8
  import { ToolItem } from './ToolItem.js'
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.32.2'
2
+ export const version = '3.33.1'
@@ -3,7 +3,10 @@ import { useEffect } from 'react'
3
3
  import { useHeaderStore } from '../stores/header/useHeaderStore.js'
4
4
 
5
5
  export function useHeader(title: string, action?: ReactNode) {
6
- const { setTitle, setAction } = useHeaderStore((state) => state)
6
+ const [setTitle, setAction] = useHeaderStore((state) => [
7
+ state.setTitle,
8
+ state.setAction,
9
+ ])
7
10
 
8
11
  useEffect(() => {
9
12
  const removeTitle = setTitle(title)
@@ -1,31 +1,89 @@
1
+ import { useCallback, useMemo } from 'react'
1
2
  import { useTranslation } from 'react-i18next'
3
+ import { allLanguages } from '../providers/I18nProvider/constants.js'
4
+ import { loadLocale } from '../providers/I18nProvider/i18n.js'
5
+ import type {
6
+ LanguageKey,
7
+ LanguageResource,
8
+ } from '../providers/I18nProvider/types.js'
2
9
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
3
10
  import { useSettings } from '../stores/settings/useSettings.js'
4
11
  import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
12
+ import { getConfigItemSets, isItemAllowedForSets } from '../utils/item.js'
5
13
 
6
14
  export const useLanguages = () => {
7
- const { t, i18n } = useTranslation()
8
- const { languages } = useWidgetConfig()
15
+ const { i18n } = useTranslation()
16
+ const { languages: languagesConfig, languageResources } = useWidgetConfig()
9
17
  const { language } = useSettings(['language'])
10
- const { setValue } = useSettingsActions()
18
+ const { setValues } = useSettingsActions()
11
19
 
12
- const sortedLanguages = Object.keys(i18n.store.data).sort()
20
+ const sortedLanguages = useMemo(() => {
21
+ const loadedLanguageKeys = Object.keys(i18n.store.data)
22
+ // NB: custom language resources added statically to i18n might not exist in allLanguages (language files)
23
+ const allLanguagesWithCustom = [
24
+ ...new Set([...allLanguages, ...loadedLanguageKeys]),
25
+ ]
26
+ let supportedLanguages: (LanguageKey | string)[] = []
27
+ if (!languagesConfig) {
28
+ supportedLanguages = allLanguagesWithCustom
29
+ } else {
30
+ const languagesConfigSets = getConfigItemSets(
31
+ languagesConfig,
32
+ (languages) => new Set(languages)
33
+ )
34
+ supportedLanguages = allLanguagesWithCustom.filter((language) =>
35
+ isItemAllowedForSets(language, languagesConfigSets)
36
+ )
37
+ }
38
+ return supportedLanguages.sort()
39
+ }, [languagesConfig, i18n.store.data])
13
40
 
14
- const selectedLanguageCode = sortedLanguages.includes(
15
- language || i18n.resolvedLanguage || ''
41
+ const handleLanguageChange = useCallback(
42
+ async (languageKey: string) => {
43
+ // Cache the language resource to prevent blinking
44
+ // after switching languages and reloading the page.
45
+ let newLanguageCache: LanguageResource | undefined
46
+ if (
47
+ !i18n.hasResourceBundle(languageKey, 'translation') &&
48
+ languageKey !== 'en'
49
+ ) {
50
+ await loadLocale(
51
+ languageKey as LanguageKey,
52
+ languageResources?.[languageKey as LanguageKey]
53
+ ).then((languageResource) => {
54
+ i18n.addResourceBundle(
55
+ languageKey,
56
+ 'translation',
57
+ languageResource,
58
+ true,
59
+ true
60
+ )
61
+ newLanguageCache = languageResource
62
+ })
63
+ } else {
64
+ // If the language is already loaded, just cache the existing resource
65
+ newLanguageCache = i18n.getResourceBundle(languageKey, 'translation')
66
+ }
67
+
68
+ // Set the new language and language cache to the settings store.
69
+ setValues({ language: languageKey, languageCache: newLanguageCache })
70
+
71
+ // Update the i18n instance to the new language.
72
+ if (languageKey !== i18n.language) {
73
+ await i18n.changeLanguage(languageKey)
74
+ }
75
+ },
76
+ [i18n, languageResources, setValues]
16
77
  )
17
- ? language || i18n.resolvedLanguage
18
- : languages?.default || languages?.allow?.[0]
78
+
79
+ const selectedLanguage = language || i18n.resolvedLanguage || ''
80
+ const selectedLanguageCode = sortedLanguages.includes(selectedLanguage)
81
+ ? selectedLanguage
82
+ : languagesConfig?.default || 'en'
19
83
 
20
84
  return {
21
85
  availableLanguages: sortedLanguages,
22
86
  selectedLanguageCode: selectedLanguageCode,
23
- selectedLanguageDisplayName: t('language.name', {
24
- lng: selectedLanguageCode,
25
- }),
26
- setLanguageWithCode: (code: string) => {
27
- setValue('language', code)
28
- i18n.changeLanguage(code)
29
- },
87
+ setLanguageWithCode: handleLanguageChange,
30
88
  }
31
89
  }
@@ -16,8 +16,8 @@ import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
16
16
  import { useFieldValues } from '../stores/form/useFieldValues.js'
17
17
  import { useIntermediateRoutesStore } from '../stores/routes/useIntermediateRoutesStore.js'
18
18
  import { useSetExecutableRoute } from '../stores/routes/useSetExecutableRoute.js'
19
+ import { defaultSlippage } from '../stores/settings/createSettingsStore.js'
19
20
  import { useSettings } from '../stores/settings/useSettings.js'
20
- import { defaultSlippage } from '../stores/settings/useSettingsStore.js'
21
21
  import { WidgetEvent } from '../types/events.js'
22
22
  import { getChainTypeFromAddress } from '../utils/chainType.js'
23
23
  import { getQueryKey } from '../utils/queries.js'
@@ -1,9 +1,7 @@
1
1
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
2
+ import { defaultConfigurableSettings } from '../stores/settings/createSettingsStore.js'
3
+ import { useSettingsStore } from '../stores/settings/SettingsStore.js'
2
4
  import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
3
- import {
4
- defaultConfigurableSettings,
5
- useSettingsStore,
6
- } from '../stores/settings/useSettingsStore.js'
7
5
  import { useTools } from './useTools.js'
8
6
 
9
7
  export const useSettingMonitor = () => {
@@ -72,7 +72,7 @@ export const useTokenBalances = (
72
72
  const { processedTokens, withCategories } = useMemo(() => {
73
73
  return processTokenBalances(
74
74
  isBalanceLoading,
75
- isAllNetworks ?? false,
75
+ isAllNetworks || !!search,
76
76
  configTokens,
77
77
  selectedChainId,
78
78
  displayedTokensList,
@@ -85,6 +85,7 @@ export const useTokenBalances = (
85
85
  selectedChainId,
86
86
  displayedTokensList,
87
87
  displayedTokensWithBalances,
88
+ search,
88
89
  ])
89
90
 
90
91
  return {
@@ -1,12 +1,14 @@
1
1
  import { getTools, type ToolsResponse } from '@lifi/sdk'
2
2
  import { useQuery } from '@tanstack/react-query'
3
3
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
4
- import { settingsStore } from '../stores/settings/useSettingsStore.js'
4
+ import { useSettingsStoreContext } from '../stores/settings/SettingsStore.js'
5
5
  import { getConfigItemSets, isItemAllowedForSets } from '../utils/item.js'
6
6
  import { getQueryKey } from '../utils/queries.js'
7
7
 
8
8
  export const useTools = () => {
9
9
  const { bridges, exchanges, keyPrefix } = useWidgetConfig()
10
+ const settingsStore = useSettingsStoreContext()
11
+
10
12
  const { data } = useQuery({
11
13
  queryKey: [
12
14
  getQueryKey('tools', keyPrefix),
@@ -6,6 +6,7 @@ import { PageContainer } from '../components/PageContainer.js'
6
6
  import { SettingsListItemButton } from '../components/SettingsListItemButton.js'
7
7
  import { useHeader } from '../hooks/useHeader.js'
8
8
  import { useLanguages } from '../hooks/useLanguages.js'
9
+ import { languageNames } from '../providers/I18nProvider/constants.js'
9
10
 
10
11
  export const LanguagesPage: React.FC = () => {
11
12
  const { t } = useTranslation()
@@ -33,7 +34,15 @@ export const LanguagesPage: React.FC = () => {
33
34
  key={language}
34
35
  onClick={() => setLanguageWithCode(language)}
35
36
  >
36
- <ListItemText primary={t('language.name', { lng: language })} />
37
+ <ListItemText
38
+ primary={
39
+ languageNames[language as keyof typeof languageNames] ||
40
+ t('language.name', {
41
+ lng: language,
42
+ }) ||
43
+ language
44
+ }
45
+ />
37
46
  {selectedLanguageCode === language && <Check color="primary" />}
38
47
  </SettingsListItemButton>
39
48
  ))}
@@ -15,8 +15,8 @@ import { useDefaultElementId } from '../hooks/useDefaultElementId.js'
15
15
  import { useHeader } from '../hooks/useHeader.js'
16
16
  import { useScrollableContainer } from '../hooks/useScrollableContainer.js'
17
17
  import { useTools } from '../hooks/useTools.js'
18
+ import { useSettingsStore } from '../stores/settings/SettingsStore.js'
18
19
  import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
19
- import { useSettingsStore } from '../stores/settings/useSettingsStore.js'
20
20
 
21
21
  interface SelectAllCheckboxProps {
22
22
  allCheckboxesSelected: boolean
@@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom'
5
5
  import { CardButton } from '../../components/Card/CardButton.js'
6
6
  import { useSettingMonitor } from '../../hooks/useSettingMonitor.js'
7
7
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
8
- import { useSettingsStore } from '../../stores/settings/useSettingsStore.js'
8
+ import { useSettingsStore } from '../../stores/settings/SettingsStore.js'
9
9
  import { HiddenUI } from '../../types/widget.js'
10
10
  import { navigationRoutes } from '../../utils/navigationRoutes.js'
11
11
  import { BadgedValue } from './SettingsCard/BadgedValue.js'
@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom'
4
4
  import { CardButton } from '../../components/Card/CardButton.js'
5
5
  import { CardValue } from '../../components/Card/CardButton.style.js'
6
6
  import { useLanguages } from '../../hooks/useLanguages.js'
7
+ import { languageNames } from '../../providers/I18nProvider/constants.js'
7
8
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
8
9
  import { HiddenUI } from '../../types/widget.js'
9
10
  import { navigationRoutes } from '../../utils/navigationRoutes.js'
@@ -12,7 +13,7 @@ export const LanguageSetting: React.FC = () => {
12
13
  const { t } = useTranslation()
13
14
  const navigate = useNavigate()
14
15
  const { hiddenUI } = useWidgetConfig()
15
- const { selectedLanguageDisplayName } = useLanguages()
16
+ const { selectedLanguageCode } = useLanguages()
16
17
 
17
18
  if (hiddenUI?.includes(HiddenUI.Language)) {
18
19
  return null
@@ -28,7 +29,13 @@ export const LanguageSetting: React.FC = () => {
28
29
  icon={<Language />}
29
30
  title={t('language.title')}
30
31
  >
31
- <CardValue>{selectedLanguageDisplayName}</CardValue>
32
+ <CardValue>
33
+ {languageNames[selectedLanguageCode as keyof typeof languageNames] ||
34
+ t('language.name', {
35
+ lng: selectedLanguageCode,
36
+ }) ||
37
+ selectedLanguageCode}
38
+ </CardValue>
32
39
  </CardButton>
33
40
  )
34
41
  }
@@ -5,9 +5,9 @@ import type { ChangeEventHandler, FocusEventHandler } from 'react'
5
5
  import { useRef, useState } from 'react'
6
6
  import { useTranslation } from 'react-i18next'
7
7
  import { useSettingMonitor } from '../../../hooks/useSettingMonitor.js'
8
+ import { defaultSlippage } from '../../../stores/settings/createSettingsStore.js'
8
9
  import { useSettings } from '../../../stores/settings/useSettings.js'
9
10
  import { useSettingsActions } from '../../../stores/settings/useSettingsActions.js'
10
- import { defaultSlippage } from '../../../stores/settings/useSettingsStore.js'
11
11
  import { formatInputAmount, formatSlippage } from '../../../utils/format.js'
12
12
  import { BadgedValue } from '../SettingsCard/BadgedValue.js'
13
13
  import { SettingCardExpandable } from '../SettingsCard/SettingCardExpandable.js'
@@ -1,67 +1,82 @@
1
- import type { i18n } from 'i18next'
2
- import { createInstance } from 'i18next'
3
- import { useMemo } from 'react'
1
+ import { createInstance, type i18n } from 'i18next'
2
+ import { useMemo, useRef } from 'react'
4
3
  import { I18nextProvider } from 'react-i18next'
5
- import * as supportedLanguages from '../../i18n/index.js'
6
4
  import { useSettings } from '../../stores/settings/useSettings.js'
7
5
  import { compactNumberFormatter } from '../../utils/compactNumberFormatter.js'
8
6
  import { currencyExtendedFormatter } from '../../utils/currencyExtendedFormatter.js'
9
- import { deepMerge } from '../../utils/deepMerge.js'
10
- import { getConfigItemSets, isItemAllowedForSets } from '../../utils/item.js'
11
7
  import { percentFormatter } from '../../utils/percentFormatter.js'
12
8
  import { useWidgetConfig } from '../WidgetProvider/WidgetProvider.js'
13
- import type { LanguageKey, LanguageTranslationResources } from './types.js'
9
+ import { allLanguages } from './constants.js'
10
+ import { enResource } from './enResource.js'
11
+ import { mergeWithLanguageResources } from './i18n.js'
12
+ import type { LanguageKey, LanguageResource, PartialResource } from './types.js'
14
13
 
15
14
  export const I18nProvider: React.FC<React.PropsWithChildren> = ({
16
15
  children,
17
16
  }) => {
18
- const { languageResources, languages } = useWidgetConfig()
19
- const { language } = useSettings(['language'])
17
+ const { languages, languageResources } = useWidgetConfig()
18
+ const { language, languageCache } = useSettings(['language', 'languageCache'])
19
+ const i18nInstanceRef = useRef<i18n | null>(null)
20
20
 
21
- const i18n = useMemo(() => {
22
- const languagesConfigSets = getConfigItemSets(
23
- languages,
24
- (languages) => new Set(languages)
25
- )
26
- let resources = (Object.keys(supportedLanguages) as LanguageKey[])
27
- .filter((lng) => isItemAllowedForSets(lng, languagesConfigSets))
28
- .reduce((resources, lng) => {
29
- resources[lng] = {
30
- translation: languageResources?.[lng]
31
- ? (deepMerge(
32
- // biome-ignore lint/performance/noDynamicNamespaceImportAccess: TODO: make it dynamic
33
- supportedLanguages[lng],
34
- languageResources[lng]
35
- ) as LanguageTranslationResources)
36
- : // biome-ignore lint/performance/noDynamicNamespaceImportAccess: TODO: make it dynamic
37
- supportedLanguages[lng],
38
- }
39
- return resources
40
- }, {} as LanguageTranslationResources)
41
-
42
- if (languageResources) {
43
- resources = Object.keys(languageResources).reduce((resources, lng) => {
44
- if (!resources[lng]) {
45
- resources[lng] = {
46
- translation: languageResources[lng as LanguageKey]!,
47
- }
48
- }
49
- return resources
50
- }, resources)
21
+ const i18nInstance = useMemo(() => {
22
+ if (i18nInstanceRef.current) {
23
+ // Update i18n instance with language and language cache updates
24
+ if (language && languageCache && language !== 'en') {
25
+ i18nInstanceRef.current.addResourceBundle(
26
+ language,
27
+ 'translation',
28
+ languageCache,
29
+ true,
30
+ true
31
+ )
32
+ }
33
+ return i18nInstanceRef.current
51
34
  }
52
35
 
36
+ // Custom language resources (of non-default languages) are added statically.
37
+ // If custom language resources extend existing languages, they are merged with dynamically loaded resources.
38
+ const customLanguageKeys = languageResources
39
+ ? Object.keys(languageResources).filter(
40
+ (key: string) => !allLanguages.includes(key as LanguageKey)
41
+ )
42
+ : []
43
+ const initialLanguage = language || languages?.default
53
44
  const i18n = createInstance({
54
- lng: languages?.default || language,
55
- fallbackLng: resources.en
56
- ? 'en'
57
- : languages?.default ||
58
- languages?.allow?.[0] ||
59
- Object.keys(resources)?.[0],
45
+ lng: initialLanguage,
46
+ fallbackLng: 'en',
60
47
  lowerCaseLng: true,
61
- interpolation: {
62
- escapeValue: false,
48
+ interpolation: { escapeValue: false },
49
+ resources: {
50
+ en: {
51
+ translation: mergeWithLanguageResources(
52
+ enResource,
53
+ languageResources?.en
54
+ ),
55
+ },
56
+ ...(initialLanguage &&
57
+ initialLanguage !== 'en' &&
58
+ languageCache && {
59
+ [initialLanguage]: {
60
+ translation: languageCache,
61
+ },
62
+ }),
63
+ // Add non-existing custom language resources
64
+ ...customLanguageKeys.reduce(
65
+ (acc, key) => {
66
+ const customResource = languageResources?.[key as LanguageKey]
67
+ if (customResource) {
68
+ acc[key] = {
69
+ translation: customResource,
70
+ }
71
+ }
72
+ return acc
73
+ },
74
+ {} as Record<
75
+ string,
76
+ { translation: PartialResource<LanguageResource> }
77
+ >
78
+ ),
63
79
  },
64
- resources,
65
80
  detection: {
66
81
  caches: [],
67
82
  },
@@ -73,9 +88,9 @@ export const I18nProvider: React.FC<React.PropsWithChildren> = ({
73
88
  i18n.services.formatter?.addCached('numberExt', compactNumberFormatter)
74
89
  i18n.services.formatter?.addCached('currencyExt', currencyExtendedFormatter)
75
90
  i18n.services.formatter?.addCached('percent', percentFormatter)
76
-
91
+ i18nInstanceRef.current = i18n
77
92
  return i18n
78
- }, [language, languageResources, languages])
93
+ }, [language, languageResources, languages?.default, languageCache])
79
94
 
80
- return <I18nextProvider i18n={i18n as i18n}>{children}</I18nextProvider>
95
+ return <I18nextProvider i18n={i18nInstance}>{children}</I18nextProvider>
81
96
  }
@@ -0,0 +1,41 @@
1
+ import type { LanguageKey } from './types.js'
2
+
3
+ export const allLanguages: LanguageKey[] = [
4
+ 'en',
5
+ 'es',
6
+ 'fr',
7
+ 'de',
8
+ 'it',
9
+ 'pt',
10
+ 'ja',
11
+ 'ko',
12
+ 'zh',
13
+ 'hi',
14
+ 'bn',
15
+ 'th',
16
+ 'vi',
17
+ 'tr',
18
+ 'uk',
19
+ 'id',
20
+ 'pl',
21
+ ]
22
+
23
+ export const languageNames: Record<LanguageKey, string> = {
24
+ en: 'English',
25
+ es: 'Español',
26
+ fr: 'Français',
27
+ de: 'Deutsch',
28
+ it: 'Italiano',
29
+ pt: 'Português',
30
+ ja: '日本語',
31
+ ko: '한국어',
32
+ zh: '中文',
33
+ hi: 'हिन्दी',
34
+ bn: 'বাংলা',
35
+ th: 'ไทย',
36
+ vi: 'Tiếng Việt',
37
+ tr: 'Türkçe',
38
+ uk: 'Українська',
39
+ id: 'Bahasa Indonesia',
40
+ pl: 'Polski',
41
+ }
@@ -0,0 +1,4 @@
1
+ import en from '../../i18n/en.json' with { type: 'json' }
2
+
3
+ // Used as a fallback language resource
4
+ export { en as enResource }
@@ -0,0 +1,26 @@
1
+ import { deepMerge } from '../../utils/deepMerge.js'
2
+ import type { LanguageKey, LanguageResource, PartialResource } from './types.js'
3
+
4
+ // Dynamically import the JSON file for the specified language
5
+ export async function loadLocale(
6
+ lng: LanguageKey,
7
+ customLanguageResource?: PartialResource<LanguageResource>
8
+ ) {
9
+ let languageResource: LanguageResource
10
+ try {
11
+ const languageResourceModule = await import(`../../i18n/${lng}.json`)
12
+ languageResource = languageResourceModule.default as LanguageResource
13
+ } catch {
14
+ languageResource = {} as LanguageResource
15
+ }
16
+ return mergeWithLanguageResources(languageResource, customLanguageResource)
17
+ }
18
+
19
+ export function mergeWithLanguageResources(
20
+ languageResource: LanguageResource,
21
+ customLanguageResource?: PartialResource<LanguageResource>
22
+ ) {
23
+ return customLanguageResource
24
+ ? deepMerge(languageResource, customLanguageResource)
25
+ : languageResource
26
+ }
@@ -1,14 +1,31 @@
1
- import type * as languages from '../../i18n/index.js'
1
+ import type { enResource } from './enResource.js'
2
2
 
3
- type PartialResource<T> = T extends object
3
+ export type PartialResource<T> = T extends object
4
4
  ? {
5
5
  [P in keyof T]?: PartialResource<T[P]>
6
6
  }
7
7
  : T
8
8
 
9
- export type LanguageKey = keyof typeof languages
9
+ export type LanguageKey =
10
+ | 'en'
11
+ | 'es'
12
+ | 'fr'
13
+ | 'de'
14
+ | 'it'
15
+ | 'pt'
16
+ | 'ja'
17
+ | 'ko'
18
+ | 'zh'
19
+ | 'hi'
20
+ | 'bn'
21
+ | 'th'
22
+ | 'vi'
23
+ | 'tr'
24
+ | 'uk'
25
+ | 'id'
26
+ | 'pl'
10
27
 
11
- type LanguageResource = typeof languages.en
28
+ export type LanguageResource = typeof enResource
12
29
 
13
30
  export type LanguageResources =
14
31
  | {
@@ -17,11 +34,3 @@ export type LanguageResources =
17
34
  | {
18
35
  [language: string]: PartialResource<LanguageResource>
19
36
  }
20
-
21
- type LanguageTranslationResource = {
22
- [N in 'translation']: PartialResource<LanguageResource>
23
- }
24
-
25
- export type LanguageTranslationResources = {
26
- [language: string]: LanguageTranslationResource
27
- }
@@ -38,7 +38,8 @@ export const SDKProviders = () => {
38
38
  if (!hasConfiguredEVMProvider) {
39
39
  providers.push(
40
40
  EVM({
41
- getWalletClient: () => getWagmiConnectorClient(wagmiConfig),
41
+ getWalletClient: () =>
42
+ getWagmiConnectorClient(wagmiConfig, { assertChainId: false }),
42
43
  switchChain: async (chainId: number) => {
43
44
  const chain = await switchChain(wagmiConfig, { chainId })
44
45
  return getWagmiConnectorClient(wagmiConfig, { chainId: chain.id })
@@ -1,6 +1,5 @@
1
- import type { StateCreator } from 'zustand'
1
+ import { create } from 'zustand'
2
2
  import { persist } from 'zustand/middleware'
3
- import { createWithEqualityFn } from 'zustand/traditional'
4
3
  import type { ToAddress } from '../../types/widget.js'
5
4
  import type { PersistStoreProps } from '../types.js'
6
5
  import type { BookmarkState } from './types.js'
@@ -14,7 +13,7 @@ export const createBookmarksStore = ({
14
13
  namePrefix,
15
14
  toAddress,
16
15
  }: PersistBookmarkProps) =>
17
- createWithEqualityFn<BookmarkState>(
16
+ create<BookmarkState>()(
18
17
  persist(
19
18
  (set, get) => ({
20
19
  selectedBookmark: toAddress,
@@ -76,6 +75,5 @@ export const createBookmarksStore = ({
76
75
  }
77
76
  },
78
77
  }
79
- ) as StateCreator<BookmarkState, [], [], BookmarkState>,
80
- Object.is
78
+ )
81
79
  )
@@ -64,7 +64,9 @@ export function ChainOrderStoreProvider({
64
64
 
65
65
  // Show "All networks" button if there are multiple networks
66
66
  const showAllNetworks = filteredChains.length > 1
67
- storeRef.current?.getState().setIsAllNetworks(showAllNetworks, key)
67
+ if (!showAllNetworks) {
68
+ storeRef.current?.getState().setIsAllNetworks(false, key)
69
+ }
68
70
  storeRef.current?.getState().setShowAllNetworks(showAllNetworks, key)
69
71
 
70
72
  const [chainValue] = getFieldValues(`${key}Chain`)