@vendure/dashboard 3.5.0-minor-202509261210 → 3.5.0-minor-202510031341

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 (263) hide show
  1. package/README.md +4 -0
  2. package/dist/plugin/dashboard.plugin.d.ts +25 -6
  3. package/dist/plugin/dashboard.plugin.js +184 -27
  4. package/dist/plugin/default-page.html +188 -0
  5. package/dist/vite/vite-plugin-config.js +13 -9
  6. package/dist/vite/vite-plugin-translations.d.ts +22 -0
  7. package/dist/vite/vite-plugin-translations.js +66 -0
  8. package/dist/vite/vite-plugin-vendure-dashboard.js +8 -6
  9. package/lingui.config.js +25 -2
  10. package/package.json +159 -156
  11. package/src/app/app-providers.tsx +0 -4
  12. package/src/app/common/delete-bulk-action.tsx +6 -5
  13. package/src/app/common/duplicate-bulk-action.tsx +4 -5
  14. package/src/app/common/duplicate-entity-dialog.tsx +1 -1
  15. package/src/app/common/set-document-direction.ts +7 -0
  16. package/src/app/main.tsx +50 -17
  17. package/src/app/routes/_authenticated/_administrators/administrators.tsx +8 -6
  18. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +17 -6
  19. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +2 -2
  20. package/src/app/routes/_authenticated/_assets/assets.tsx +1 -1
  21. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +4 -4
  22. package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +8 -6
  23. package/src/app/routes/_authenticated/_assets/components/asset-tag-filter.tsx +1 -1
  24. package/src/app/routes/_authenticated/_assets/components/asset-tags-editor.tsx +1 -1
  25. package/src/app/routes/_authenticated/_assets/components/manage-tags-dialog.tsx +3 -8
  26. package/src/app/routes/_authenticated/_channels/channels.tsx +3 -6
  27. package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +5 -5
  28. package/src/app/routes/_authenticated/_collections/collections.tsx +3 -4
  29. package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +4 -6
  30. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +1 -1
  31. package/src/app/routes/_authenticated/_collections/components/collection-contents-sheet.tsx +1 -1
  32. package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +6 -6
  33. package/src/app/routes/_authenticated/_countries/countries.graphql.ts +2 -0
  34. package/src/app/routes/_authenticated/_countries/countries.tsx +2 -3
  35. package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +4 -4
  36. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-sheet.tsx +1 -1
  37. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-table.tsx +4 -4
  38. package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +2 -4
  39. package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +13 -6
  40. package/src/app/routes/_authenticated/_customers/components/customer-address-card.tsx +8 -8
  41. package/src/app/routes/_authenticated/_customers/components/customer-address-form.tsx +3 -3
  42. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-container.tsx +1 -1
  43. package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-utils.tsx +1 -1
  44. package/src/app/routes/_authenticated/_customers/components/customer-history/default-customer-history-components.tsx +1 -1
  45. package/src/app/routes/_authenticated/_customers/components/customer-history/use-customer-history.ts +1 -1
  46. package/src/app/routes/_authenticated/_customers/components/customer-status-badge.tsx +1 -1
  47. package/src/app/routes/_authenticated/_customers/customers.graphql.ts +4 -0
  48. package/src/app/routes/_authenticated/_customers/customers.tsx +23 -11
  49. package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +10 -8
  50. package/src/app/routes/_authenticated/_facets/components/edit-facet-value.tsx +1 -1
  51. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +6 -5
  52. package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +1 -1
  53. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +1 -1
  54. package/src/app/routes/_authenticated/_facets/facets.tsx +5 -5
  55. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +7 -5
  56. package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +4 -4
  57. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +5 -5
  58. package/src/app/routes/_authenticated/_orders/components/add-manual-payment-dialog.tsx +19 -21
  59. package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +1 -1
  60. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +22 -22
  61. package/src/app/routes/_authenticated/_orders/components/fulfill-order-dialog.tsx +6 -6
  62. package/src/app/routes/_authenticated/_orders/components/fulfillment-details.tsx +15 -9
  63. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +1 -1
  64. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +11 -9
  65. package/src/app/routes/_authenticated/_orders/components/order-history/default-order-history-components.tsx +1 -1
  66. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx +1 -1
  67. package/src/app/routes/_authenticated/_orders/components/order-history/order-history-utils.tsx +1 -1
  68. package/src/app/routes/_authenticated/_orders/components/order-history/use-order-history.ts +1 -1
  69. package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +1 -1
  70. package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +4 -4
  71. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +1 -1
  72. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +27 -27
  73. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +2 -2
  74. package/src/app/routes/_authenticated/_orders/components/order-tax-summary.tsx +1 -1
  75. package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +26 -20
  76. package/src/app/routes/_authenticated/_orders/components/seller-orders-card.tsx +3 -1
  77. package/src/app/routes/_authenticated/_orders/components/settle-refund-dialog.tsx +6 -6
  78. package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +1 -1
  79. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +1 -1
  80. package/src/app/routes/_authenticated/_orders/components/use-transition-order-to-state.tsx +3 -2
  81. package/src/app/routes/_authenticated/_orders/orders.tsx +5 -9
  82. package/src/app/routes/_authenticated/_orders/orders_.$aggregateOrderId_.seller-orders.$sellerOrderId.tsx +1 -1
  83. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +1 -1
  84. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +4 -4
  85. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +17 -17
  86. package/src/app/routes/_authenticated/_orders/utils/order-detail-loaders.tsx +1 -1
  87. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +5 -6
  88. package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +13 -6
  89. package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +1 -1
  90. package/src/app/routes/_authenticated/_product-variants/components/variant-price-detail.tsx +1 -1
  91. package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +9 -2
  92. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +13 -6
  93. package/src/app/routes/_authenticated/_products/components/add-option-group-dialog.tsx +5 -5
  94. package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +5 -5
  95. package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +5 -4
  96. package/src/app/routes/_authenticated/_products/components/create-product-options-dialog.tsx +9 -12
  97. package/src/app/routes/_authenticated/_products/components/create-product-variants-dialog.tsx +1 -1
  98. package/src/app/routes/_authenticated/_products/components/create-product-variants.tsx +4 -4
  99. package/src/app/routes/_authenticated/_products/components/option-groups-editor.tsx +1 -1
  100. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +1 -1
  101. package/src/app/routes/_authenticated/_products/components/product-option-select.tsx +3 -3
  102. package/src/app/routes/_authenticated/_products/components/product-options-table.tsx +9 -6
  103. package/src/app/routes/_authenticated/_products/products.graphql.ts +31 -31
  104. package/src/app/routes/_authenticated/_products/products.tsx +11 -6
  105. package/src/app/routes/_authenticated/_products/products_.$id.tsx +4 -4
  106. package/src/app/routes/_authenticated/_products/products_.$id_.variants.tsx +11 -11
  107. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$id.tsx +8 -12
  108. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +2 -2
  109. package/src/app/routes/_authenticated/_profile/profile.tsx +4 -4
  110. package/src/app/routes/_authenticated/_promotions/promotions.tsx +2 -4
  111. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +16 -9
  112. package/src/app/routes/_authenticated/_roles/components/permissions-table-grid.tsx +1 -1
  113. package/src/app/routes/_authenticated/_roles/roles.tsx +3 -6
  114. package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +4 -6
  115. package/src/app/routes/_authenticated/_sellers/sellers.tsx +3 -4
  116. package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +4 -4
  117. package/src/app/routes/_authenticated/_shipping-methods/components/price-display.tsx +5 -5
  118. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-test-result-wrapper.tsx +1 -1
  119. package/src/app/routes/_authenticated/_shipping-methods/components/test-address-form.tsx +4 -3
  120. package/src/app/routes/_authenticated/_shipping-methods/components/test-order-builder.tsx +4 -3
  121. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-result.tsx +8 -8
  122. package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-sheet.tsx +1 -1
  123. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-method-result.tsx +8 -8
  124. package/src/app/routes/_authenticated/_shipping-methods/components/test-single-shipping-method-sheet.tsx +4 -4
  125. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +2 -3
  126. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +2 -2
  127. package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +3 -4
  128. package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +13 -6
  129. package/src/app/routes/_authenticated/_system/healthchecks.tsx +10 -4
  130. package/src/app/routes/_authenticated/_system/job-queue.tsx +10 -13
  131. package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +18 -16
  132. package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +2 -4
  133. package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +13 -6
  134. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +8 -12
  135. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +6 -4
  136. package/src/app/routes/_authenticated/_zones/zones.tsx +4 -4
  137. package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +8 -5
  138. package/src/app/routes/_authenticated/index.tsx +6 -2
  139. package/src/app/styles.css +4 -0
  140. package/src/i18n/common-strings.ts +111 -0
  141. package/src/i18n/locales/ar.po +4777 -0
  142. package/src/i18n/locales/cs.po +4777 -0
  143. package/src/i18n/locales/de.po +4299 -1101
  144. package/src/i18n/locales/en.po +3857 -659
  145. package/src/i18n/locales/es.po +4777 -0
  146. package/src/i18n/locales/fa.po +4777 -0
  147. package/src/i18n/locales/fr.po +4777 -0
  148. package/src/i18n/locales/he.po +4777 -0
  149. package/src/i18n/locales/hr.po +4777 -0
  150. package/src/i18n/locales/it.po +4777 -0
  151. package/src/i18n/locales/ja.po +4777 -0
  152. package/src/i18n/locales/ko.po +4628 -0
  153. package/src/i18n/locales/nb.po +4777 -0
  154. package/src/i18n/locales/ne.po +4777 -0
  155. package/src/i18n/locales/nl.po +4628 -0
  156. package/src/i18n/locales/pl.po +4777 -0
  157. package/src/i18n/locales/pt_BR.po +4777 -0
  158. package/src/i18n/locales/pt_PT.po +4777 -0
  159. package/src/i18n/locales/ru.po +4777 -0
  160. package/src/i18n/locales/sv.po +4777 -0
  161. package/src/i18n/locales/tr.po +4777 -0
  162. package/src/i18n/locales/uk.po +4777 -0
  163. package/src/i18n/locales/zh_Hans.po +4777 -0
  164. package/src/i18n/locales/zh_Hant.po +4777 -0
  165. package/src/lib/components/data-input/combination-mode-input.tsx +1 -1
  166. package/src/lib/components/data-input/custom-field-list-input.tsx +11 -7
  167. package/src/lib/components/data-input/customer-group-input.tsx +27 -33
  168. package/src/lib/components/data-input/datetime-input.tsx +40 -1
  169. package/src/lib/components/data-input/default-relation-input.tsx +5 -4
  170. package/src/lib/components/data-input/product-multi-selector-input.tsx +14 -14
  171. package/src/lib/components/data-input/relation-selector.tsx +1 -1
  172. package/src/lib/components/data-input/select-with-options.tsx +1 -1
  173. package/src/lib/components/data-input/slug-input.tsx +9 -15
  174. package/src/lib/components/data-table/add-filter-menu.tsx +4 -4
  175. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +8 -8
  176. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  177. package/src/lib/components/data-table/data-table-column-header.tsx +13 -8
  178. package/src/lib/components/data-table/data-table-faceted-filter.tsx +2 -1
  179. package/src/lib/components/data-table/data-table-filter-dialog.tsx +1 -1
  180. package/src/lib/components/data-table/data-table-utils.ts +21 -4
  181. package/src/lib/components/data-table/data-table-view-options.tsx +4 -2
  182. package/src/lib/components/data-table/data-table.tsx +3 -3
  183. package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +4 -4
  184. package/src/lib/components/data-table/global-views-bar.tsx +1 -1
  185. package/src/lib/components/data-table/human-readable-operator.tsx +1 -1
  186. package/src/lib/components/data-table/manage-global-views-button.tsx +1 -1
  187. package/src/lib/components/data-table/my-views-button.tsx +13 -13
  188. package/src/lib/components/data-table/refresh-button.tsx +1 -1
  189. package/src/lib/components/data-table/save-view-button.tsx +11 -11
  190. package/src/lib/components/data-table/use-generated-columns.tsx +10 -7
  191. package/src/lib/components/data-table/views-sheet.tsx +79 -71
  192. package/src/lib/components/date-range-picker.tsx +36 -34
  193. package/src/lib/components/layout/app-sidebar.tsx +3 -1
  194. package/src/lib/components/layout/channel-switcher.tsx +8 -10
  195. package/src/lib/components/layout/dev-mode-indicator.tsx +1 -1
  196. package/src/lib/components/layout/generated-breadcrumbs.tsx +10 -8
  197. package/src/lib/components/layout/language-dialog.tsx +34 -13
  198. package/src/lib/components/layout/manage-languages-dialog.tsx +1 -1
  199. package/src/lib/components/layout/nav-main.tsx +23 -13
  200. package/src/lib/components/layout/nav-user.tsx +19 -23
  201. package/src/lib/components/login/login-form.tsx +1 -1
  202. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +4 -4
  203. package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +1 -1
  204. package/src/lib/components/shared/asset/asset-gallery.tsx +15 -14
  205. package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +11 -11
  206. package/src/lib/components/shared/assign-to-channel-dialog.tsx +6 -5
  207. package/src/lib/components/shared/channel-code-label.tsx +1 -1
  208. package/src/lib/components/shared/channel-selector.tsx +4 -4
  209. package/src/lib/components/shared/configurable-operation-multi-selector.tsx +16 -14
  210. package/src/lib/components/shared/configurable-operation-selector.tsx +1 -1
  211. package/src/lib/components/shared/confirmation-dialog.tsx +8 -8
  212. package/src/lib/components/shared/country-selector.tsx +1 -1
  213. package/src/lib/components/shared/currency-selector.tsx +4 -4
  214. package/src/lib/components/shared/custom-fields-form.tsx +8 -24
  215. package/src/lib/components/shared/customer-address-form.tsx +3 -3
  216. package/src/lib/components/shared/customer-group-selector.tsx +1 -1
  217. package/src/lib/components/shared/customer-selector.tsx +1 -1
  218. package/src/lib/components/shared/error-page.tsx +1 -1
  219. package/src/lib/components/shared/facet-value-selector.tsx +10 -10
  220. package/src/lib/components/shared/history-timeline/history-note-checkbox.tsx +1 -1
  221. package/src/lib/components/shared/history-timeline/history-note-editor.tsx +1 -1
  222. package/src/lib/components/shared/history-timeline/history-note-entry.tsx +1 -1
  223. package/src/lib/components/shared/language-selector.tsx +4 -4
  224. package/src/lib/components/shared/navigation-confirmation.tsx +1 -1
  225. package/src/lib/components/shared/paginated-list-data-table.tsx +21 -18
  226. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +6 -5
  227. package/src/lib/components/shared/rich-text-editor/image-dialog.tsx +1 -1
  228. package/src/lib/components/shared/rich-text-editor/link-dialog.tsx +1 -1
  229. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +1 -1
  230. package/src/lib/components/shared/rich-text-editor/table-edit-icons.tsx +1 -1
  231. package/src/lib/components/shared/role-code-label.tsx +1 -1
  232. package/src/lib/components/shared/role-selector.tsx +4 -4
  233. package/src/lib/components/shared/seller-selector.tsx +1 -1
  234. package/src/lib/components/shared/stock-level-label.tsx +3 -5
  235. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +3 -1
  236. package/src/lib/components/shared/tax-category-selector.tsx +1 -1
  237. package/src/lib/components/shared/translatable-form-field.tsx +15 -15
  238. package/src/lib/components/shared/zone-selector.tsx +1 -1
  239. package/src/lib/constants.ts +10 -0
  240. package/src/lib/framework/dashboard-widget/base-widget.tsx +11 -9
  241. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +6 -4
  242. package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +8 -5
  243. package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +7 -4
  244. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +3 -1
  245. package/src/lib/framework/defaults.ts +34 -63
  246. package/src/lib/framework/layout-engine/page-layout.tsx +36 -36
  247. package/src/lib/framework/page/detail-page.tsx +10 -10
  248. package/src/lib/framework/page/use-extended-router.tsx +48 -23
  249. package/src/lib/graphql/api.ts +22 -7
  250. package/src/lib/graphql/graphql-env.d.ts +13 -25
  251. package/src/lib/hooks/use-display-locale.ts +40 -0
  252. package/src/lib/hooks/use-dynamic-translations.ts +46 -0
  253. package/src/lib/hooks/use-extended-detail-query.ts +1 -1
  254. package/src/lib/hooks/use-extended-list-query.ts +1 -1
  255. package/src/lib/hooks/use-local-format.ts +15 -1
  256. package/src/lib/hooks/use-saved-views.ts +7 -0
  257. package/src/lib/hooks/use-ui-language-loader.ts +30 -0
  258. package/src/lib/lib/load-i18n-messages.ts +17 -0
  259. package/src/lib/lib/trans.tsx +15 -11
  260. package/src/lib/providers/auth.tsx +2 -2
  261. package/src/lib/providers/channel-provider.tsx +3 -2
  262. package/src/lib/providers/i18n-provider.tsx +7 -14
  263. package/src/lib/providers/user-settings.tsx +46 -5
@@ -1,21 +1,14 @@
1
1
  'use client';
2
2
 
3
+ import { useDayPickerLocale } from '@/vdb/components/data-input/index.js';
3
4
  import { Button } from '@/vdb/components/ui/button.js';
4
5
  import { Calendar } from '@/vdb/components/ui/calendar.js';
5
6
  import { Popover, PopoverContent, PopoverTrigger } from '@/vdb/components/ui/popover.js';
6
7
  import { DefinedDateRange } from '@/vdb/framework/dashboard-widget/widget-filters-context.js';
8
+ import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
7
9
  import { cn } from '@/vdb/lib/utils.js';
8
- import {
9
- addDays,
10
- endOfDay,
11
- endOfMonth,
12
- endOfWeek,
13
- format,
14
- startOfDay,
15
- startOfMonth,
16
- startOfWeek,
17
- subDays
18
- } from 'date-fns';
10
+ import { Trans, useLingui } from '@lingui/react/macro';
11
+ import { endOfDay, endOfMonth, endOfWeek, startOfDay, startOfMonth, startOfWeek, subDays } from 'date-fns';
19
12
  import { CalendarIcon } from 'lucide-react';
20
13
  import * as React from 'react';
21
14
  import { DateRange } from 'react-day-picker';
@@ -28,56 +21,64 @@ interface DateRangePickerProps {
28
21
 
29
22
  const presets = [
30
23
  {
31
- label: 'Today',
24
+ id: 'today',
25
+ label: <Trans context="date-range">Today</Trans>,
32
26
  getValue: () => ({
33
27
  from: startOfDay(new Date()),
34
28
  to: endOfDay(new Date()),
35
29
  }),
36
30
  },
37
31
  {
38
- label: 'Yesterday',
32
+ id: 'yesterday',
33
+ label: <Trans context="date-range">Yesterday</Trans>,
39
34
  getValue: () => ({
40
35
  from: startOfDay(subDays(new Date(), 1)),
41
36
  to: endOfDay(subDays(new Date(), 1)),
42
37
  }),
43
38
  },
44
39
  {
45
- label: 'Last 7 days',
40
+ id: 'last-7-days',
41
+ label: <Trans context="date-range">Last 7 days</Trans>,
46
42
  getValue: () => ({
47
43
  from: startOfDay(subDays(new Date(), 6)),
48
44
  to: endOfDay(new Date()),
49
45
  }),
50
46
  },
51
47
  {
52
- label: 'This week',
48
+ id: 'this-week',
49
+ label: <Trans context="date-range">This week</Trans>,
53
50
  getValue: () => ({
54
51
  from: startOfWeek(new Date(), { weekStartsOn: 1 }),
55
52
  to: endOfWeek(new Date(), { weekStartsOn: 1 }),
56
53
  }),
57
54
  },
58
55
  {
59
- label: 'Last 30 days',
56
+ id: 'last-30-days',
57
+ label: <Trans context="date-range">Last 30 days</Trans>,
60
58
  getValue: () => ({
61
59
  from: startOfDay(subDays(new Date(), 29)),
62
60
  to: endOfDay(new Date()),
63
61
  }),
64
62
  },
65
63
  {
66
- label: 'Month to date',
64
+ id: 'month-to-date',
65
+ label: <Trans context="date-range">Month to date</Trans>,
67
66
  getValue: () => ({
68
67
  from: startOfMonth(new Date()),
69
68
  to: endOfDay(new Date()),
70
69
  }),
71
70
  },
72
71
  {
73
- label: 'This month',
72
+ id: 'this-month',
73
+ label: <Trans context="date-range">This month</Trans>,
74
74
  getValue: () => ({
75
75
  from: startOfMonth(new Date()),
76
76
  to: endOfMonth(new Date()),
77
77
  }),
78
78
  },
79
79
  {
80
- label: 'Last month',
80
+ id: 'last-month',
81
+ label: <Trans context="date-range">Last month</Trans>,
81
82
  getValue: () => ({
82
83
  from: startOfMonth(subDays(new Date(), 30)),
83
84
  to: endOfMonth(subDays(new Date(), 30)),
@@ -87,16 +88,19 @@ const presets = [
87
88
 
88
89
  export function DateRangePicker({ className, dateRange, onDateRangeChange }: DateRangePickerProps) {
89
90
  const [open, setOpen] = React.useState(false);
91
+ const { t } = useLingui();
92
+ const { formatDate } = useLocalFormat();
93
+ const locale = useDayPickerLocale();
90
94
  // Internal state uses react-day-picker's DateRange type for Calendar compatibility
91
95
  const [selectedRange, setSelectedRange] = React.useState<DateRange>({
92
96
  from: dateRange.from,
93
- to: dateRange.to
97
+ to: dateRange.to,
94
98
  });
95
99
 
96
100
  React.useEffect(() => {
97
101
  setSelectedRange({
98
102
  from: dateRange.from,
99
- to: dateRange.to
103
+ to: dateRange.to,
100
104
  });
101
105
  }, [dateRange]);
102
106
 
@@ -106,14 +110,14 @@ export function DateRangePicker({ className, dateRange, onDateRangeChange }: Dat
106
110
  const to = range.to || range.from;
107
111
  const finalRange: DefinedDateRange = {
108
112
  from: range.from,
109
- to: to
113
+ to: to,
110
114
  };
111
115
  setSelectedRange({ from: range.from, to });
112
116
  onDateRangeChange(finalRange);
113
117
  }
114
118
  };
115
119
 
116
- const handlePresetClick = (preset: typeof presets[number]) => {
120
+ const handlePresetClick = (preset: (typeof presets)[number]) => {
117
121
  const range = preset.getValue();
118
122
  handleSelect(range);
119
123
  setOpen(false);
@@ -121,15 +125,15 @@ export function DateRangePicker({ className, dateRange, onDateRangeChange }: Dat
121
125
 
122
126
  const formatDateRange = () => {
123
127
  if (!selectedRange.from) {
124
- return 'Select date range';
128
+ return t`Select date range`;
125
129
  }
126
130
  if (!selectedRange.to || selectedRange.from === selectedRange.to) {
127
- return format(selectedRange.from, 'MMM dd, yyyy');
131
+ return formatDate(selectedRange.from, { dateStyle: 'medium' });
128
132
  }
129
- return `${format(selectedRange.from, 'MMM dd, yyyy')} - ${format(selectedRange.to, 'MMM dd, yyyy')}`;
133
+ return `${formatDate(selectedRange.from, { dateStyle: 'medium' })} - ${formatDate(selectedRange.to, { dateStyle: 'medium' })}`;
130
134
  };
131
135
 
132
- const isPresetActive = (preset: typeof presets[number]) => {
136
+ const isPresetActive = (preset: (typeof presets)[number]) => {
133
137
  if (!selectedRange.from || !selectedRange.to) return false;
134
138
  const presetRange = preset.getValue();
135
139
  return (
@@ -143,10 +147,7 @@ export function DateRangePicker({ className, dateRange, onDateRangeChange }: Dat
143
147
  <PopoverTrigger asChild>
144
148
  <Button
145
149
  variant="outline"
146
- className={cn(
147
- 'w-[280px] justify-start text-left font-normal',
148
- className
149
- )}
150
+ className={cn('w-[280px] justify-start text-left font-normal', className)}
150
151
  >
151
152
  <CalendarIcon className="mr-2 h-4 w-4" />
152
153
  {formatDateRange()}
@@ -155,9 +156,9 @@ export function DateRangePicker({ className, dateRange, onDateRangeChange }: Dat
155
156
  <PopoverContent className="w-auto p-0" align="start">
156
157
  <div className="flex">
157
158
  <div className="border-r p-2 space-y-0.5 min-w-0 w-32">
158
- {presets.map((preset) => (
159
+ {presets.map(preset => (
159
160
  <Button
160
- key={preset.label}
161
+ key={preset.id}
161
162
  variant={isPresetActive(preset) ? 'default' : 'ghost'}
162
163
  size="sm"
163
164
  className="w-full justify-start font-normal text-xs h-7 px-2"
@@ -175,10 +176,11 @@ export function DateRangePicker({ className, dateRange, onDateRangeChange }: Dat
175
176
  onSelect={handleSelect}
176
177
  numberOfMonths={2}
177
178
  showOutsideDays={false}
179
+ locale={locale}
178
180
  />
179
181
  </div>
180
182
  </div>
181
183
  </PopoverContent>
182
184
  </Popover>
183
185
  );
184
- }
186
+ }
@@ -9,16 +9,18 @@ import {
9
9
  } from '@/vdb/components/ui/sidebar.js';
10
10
  import { useDashboardExtensions } from '@/vdb/framework/extension-api/use-dashboard-extensions.js';
11
11
  import { getNavMenuConfig } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
12
+ import { useDisplayLocale } from '@/vdb/hooks/use-display-locale.js';
12
13
  import * as React from 'react';
13
14
  import { ChannelSwitcher } from './channel-switcher.js';
14
15
 
15
16
  export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
16
17
  const { extensionsLoaded } = useDashboardExtensions();
18
+ const { isRTL } = useDisplayLocale();
17
19
  const { sections } = getNavMenuConfig();
18
20
 
19
21
  return (
20
22
  extensionsLoaded && (
21
- <Sidebar collapsible="icon" {...props}>
23
+ <Sidebar collapsible="icon" {...props} side={isRTL ? 'right' : 'left'}>
22
24
  <SidebarHeader>
23
25
  <ChannelSwitcher />
24
26
  </SidebarHeader>
@@ -18,8 +18,8 @@ import { useChannel } from '@/vdb/hooks/use-channel.js';
18
18
  import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
19
19
  import { useServerConfig } from '@/vdb/hooks/use-server-config.js';
20
20
  import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
21
- import { Trans } from '@/vdb/lib/trans.js';
22
21
  import { cn } from '@/vdb/lib/utils.js';
22
+ import { Trans } from '@lingui/react/macro';
23
23
  import { Link } from '@tanstack/react-router';
24
24
  import { useEffect, useState } from 'react';
25
25
  import { ManageLanguagesDialog } from './manage-languages-dialog.js';
@@ -99,13 +99,9 @@ export function ChannelSwitcher() {
99
99
  <ChannelCodeLabel code={displayChannel?.code} />
100
100
  </span>
101
101
  <span className="truncate text-xs">
102
- {hasMultipleLanguages ? (
103
- <span className="cursor-pointer hover:text-foreground">
104
- Language: {formatLanguageName(contentLanguage)}
105
- </span>
106
- ) : (
107
- <span>Language: {formatLanguageName(contentLanguage)}</span>
108
- )}
102
+ <span>
103
+ <Trans>Language: {formatLanguageName(contentLanguage)}</Trans>
104
+ </span>
109
105
  </span>
110
106
  </div>
111
107
  <ChevronsUpDown className="ml-auto" />
@@ -139,7 +135,7 @@ export function ChannelSwitcher() {
139
135
  <ChannelCodeLabel code={channel.code} />
140
136
  {channel.id === displayChannel?.id && (
141
137
  <span className="ml-auto text-xs text-muted-foreground">
142
- Current
138
+ <Trans context="current channel">Current</Trans>
143
139
  </span>
144
140
  )}
145
141
  </DropdownMenuItem>
@@ -168,7 +164,9 @@ export function ChannelSwitcher() {
168
164
  <span>{formatLanguageName(languageCode)}</span>
169
165
  {contentLanguage === languageCode && (
170
166
  <span className="ml-auto text-xs text-muted-foreground">
171
- Active
167
+ <Trans context="active language">
168
+ Active
169
+ </Trans>
172
170
  </span>
173
171
  )}
174
172
  </DropdownMenuItem>
@@ -1,7 +1,7 @@
1
1
  import { Badge } from '@/vdb/components/ui/badge.js';
2
2
  import { Button } from '@/vdb/components/ui/button.js';
3
3
  import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
4
- import { Trans } from '@/vdb/lib/trans.js';
4
+ import { Trans } from '@lingui/react/macro';
5
5
  import { CodeXmlIcon, XIcon } from 'lucide-react';
6
6
 
7
7
  export function DevModeIndicator() {
@@ -5,11 +5,13 @@ import {
5
5
  BreadcrumbList,
6
6
  BreadcrumbSeparator,
7
7
  } from '@/vdb/components/ui/breadcrumb.js';
8
+ import type { NavMenuItem, NavMenuSection } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
9
+ import { getNavMenuConfig } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
10
+ import { useDisplayLocale } from '@/vdb/hooks/use-display-locale.js';
11
+ import { useLingui } from '@lingui/react';
8
12
  import { Link, useRouter, useRouterState } from '@tanstack/react-router';
9
13
  import * as React from 'react';
10
14
  import { Fragment } from 'react';
11
- import { getNavMenuConfig } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
12
- import type { NavMenuItem, NavMenuSection } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
13
15
 
14
16
  export interface BreadcrumbPair {
15
17
  label: string | React.ReactElement;
@@ -24,7 +26,9 @@ export function GeneratedBreadcrumbs() {
24
26
  const matches = useRouterState({ select: s => s.matches });
25
27
  const currentPath = useRouterState({ select: s => s.location.pathname });
26
28
  const router = useRouter();
29
+ const { i18n } = useLingui();
27
30
  const navMenuConfig = getNavMenuConfig();
31
+ const { bcp47Tag } = useDisplayLocale();
28
32
  const basePath = router.basepath || '';
29
33
 
30
34
  const normalizeBreadcrumb = (breadcrumb: any, pathname: string): BreadcrumbPair[] => {
@@ -51,9 +55,7 @@ export function GeneratedBreadcrumbs() {
51
55
  const rawCrumbs: BreadcrumbPair[] = React.useMemo(() => {
52
56
  return matches
53
57
  .filter(match => match.loaderData?.breadcrumb)
54
- .flatMap(({ pathname, loaderData }) =>
55
- normalizeBreadcrumb(loaderData.breadcrumb, pathname)
56
- );
58
+ .flatMap(({ pathname, loaderData }) => normalizeBreadcrumb(loaderData.breadcrumb, pathname));
57
59
  }, [matches]);
58
60
 
59
61
  const isBaseRoute = (p: string) => p === basePath || p === `${basePath}/`;
@@ -119,8 +121,8 @@ export function GeneratedBreadcrumbs() {
119
121
  );
120
122
  const breadcrumbs: BreadcrumbPair[] = React.useMemo(() => {
121
123
  const arr = sectionCrumb ? [sectionCrumb, ...pageCrumbs] : pageCrumbs;
122
- return arr.filter((c, i, self) =>
123
- self.findIndex(x => x.path === c.path && x.label === c.label) === i,
124
+ return arr.filter(
125
+ (c, i, self) => self.findIndex(x => x.path === c.path && x.label === c.label) === i,
124
126
  );
125
127
  }, [sectionCrumb, pageCrumbs]);
126
128
  return (
@@ -130,7 +132,7 @@ export function GeneratedBreadcrumbs() {
130
132
  <Fragment key={`${path}-${index}`}>
131
133
  <BreadcrumbItem className="hidden md:block">
132
134
  <BreadcrumbLink asChild>
133
- <Link to={path}>{label}</Link>
135
+ <Link to={path}>{typeof label === 'string' ? i18n.t(label) : label}</Link>
134
136
  </BreadcrumbLink>
135
137
  </BreadcrumbItem>
136
138
  {index < arr.length - 1 && <BreadcrumbSeparator className="hidden md:block" />}
@@ -1,7 +1,9 @@
1
1
  import { CurrencyCode } from '@/vdb/constants.js';
2
+ import { useDisplayLocale } from '@/vdb/hooks/use-display-locale.js';
2
3
  import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
4
+ import { useUiLanguageLoader } from '@/vdb/hooks/use-ui-language-loader.js';
3
5
  import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
4
- import { Trans } from '@/vdb/lib/trans.js';
6
+ import { Trans } from '@lingui/react/macro';
5
7
  import { useState } from 'react';
6
8
  import { uiConfig } from 'virtual:vendure-ui-config';
7
9
  import { Button } from '../ui/button.js';
@@ -11,12 +13,22 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '.
11
13
 
12
14
  export function LanguageDialog() {
13
15
  const { i18n } = uiConfig;
16
+ const { loadAndActivateLocale } = useUiLanguageLoader();
14
17
  const { availableLocales, availableLanguages } = i18n;
15
18
  const { settings, setDisplayLanguage, setDisplayLocale } = useUserSettings();
19
+ const { humanReadableLanguageAndLocale } = useDisplayLocale();
16
20
  const availableCurrencyCodes = Object.values(CurrencyCode);
17
- const { formatCurrency, formatLanguageName, formatCurrencyName, formatDate } = useLocalFormat();
21
+ const { formatCurrency, formatLanguageName, formatRegionName, formatCurrencyName, formatDate } =
22
+ useLocalFormat();
18
23
  const [selectedCurrency, setSelectedCurrency] = useState<string>('USD');
19
24
 
25
+ const orderedAvailableLanguages = availableLanguages.slice().sort((a, b) => a.localeCompare(b));
26
+ const orderedAvailableLocales = availableLocales.slice().sort((a, b) => a.localeCompare(b));
27
+ const handleLanguageChange = async (value: string) => {
28
+ setDisplayLanguage(value);
29
+ void loadAndActivateLocale(value);
30
+ };
31
+
20
32
  return (
21
33
  <DialogContent>
22
34
  <DialogHeader>
@@ -29,14 +41,15 @@ export function LanguageDialog() {
29
41
  <Label>
30
42
  <Trans>Display language</Trans>
31
43
  </Label>
32
- <Select defaultValue={settings.displayLanguage} onValueChange={setDisplayLanguage}>
44
+ <Select defaultValue={settings.displayLanguage} onValueChange={handleLanguageChange}>
33
45
  <SelectTrigger className="w-full">
34
46
  <SelectValue placeholder="Select a language" />
35
47
  </SelectTrigger>
36
48
  <SelectContent>
37
- {availableLanguages.map(language => (
38
- <SelectItem key={language} value={language}>
39
- {formatLanguageName(language)}
49
+ {orderedAvailableLanguages.map(language => (
50
+ <SelectItem key={language} value={language} className="flex gap-1">
51
+ <span className="uppercase text-muted-foreground">{language}</span>
52
+ <span>{formatLanguageName(language)}</span>
40
53
  </SelectItem>
41
54
  ))}
42
55
  </SelectContent>
@@ -51,9 +64,10 @@ export function LanguageDialog() {
51
64
  <SelectValue placeholder="Select a locale" />
52
65
  </SelectTrigger>
53
66
  <SelectContent>
54
- {availableLocales.map(locale => (
55
- <SelectItem key={locale} value={locale}>
56
- {formatLanguageName(locale)}
67
+ {orderedAvailableLocales.map(locale => (
68
+ <SelectItem key={locale} value={locale} className="flex gap-1">
69
+ <span className="uppercase text-muted-foreground">{locale}</span>
70
+ <span>{formatRegionName(locale)}</span>
57
71
  </SelectItem>
58
72
  ))}
59
73
  </SelectContent>
@@ -62,7 +76,8 @@ export function LanguageDialog() {
62
76
  </div>
63
77
  <div className="bg-sidebar border border-border rounded-md px-6 py-4 space-y-4">
64
78
  <span className="font-medium block text-accent-foreground">
65
- <Trans>Sample Formatting</Trans>: {settings.displayLocale} {settings.displayLanguage}
79
+ <Trans>Sample Formatting</Trans>:{' '}
80
+ <span className="text-muted-foreground">{humanReadableLanguageAndLocale}</span>
66
81
  </span>
67
82
  <Select defaultValue={selectedCurrency} onValueChange={setSelectedCurrency}>
68
83
  <SelectTrigger>
@@ -77,15 +92,21 @@ export function LanguageDialog() {
77
92
  </SelectContent>
78
93
  </Select>
79
94
  <div className="flex flex-col">
80
- <span className="text-muted-foreground text-sm font-medium">Medium date</span>
95
+ <span className="text-muted-foreground text-sm font-medium">
96
+ <Trans>Medium date</Trans>
97
+ </span>
81
98
  <span>{formatDate(new Date('2025-03-14'), { dateStyle: 'medium' })}</span>
82
99
  </div>
83
100
  <div className="flex flex-col">
84
- <span className="text-muted-foreground text-sm font-medium">Short date</span>
101
+ <span className="text-muted-foreground text-sm font-medium">
102
+ <Trans>Short date</Trans>
103
+ </span>
85
104
  <span>{formatDate(new Date('2025-03-14'), { dateStyle: 'short' })}</span>
86
105
  </div>
87
106
  <div className="flex flex-col">
88
- <span className="text-muted-foreground text-sm font-medium">Price</span>
107
+ <span className="text-muted-foreground text-sm font-medium">
108
+ <Trans>Price</Trans>
109
+ </span>
89
110
  <span>{formatCurrency(100.0, selectedCurrency)}</span>
90
111
  </div>
91
112
  </div>
@@ -17,7 +17,7 @@ import { graphql } from '@/vdb/graphql/graphql.js';
17
17
  import { useChannel } from '@/vdb/hooks/use-channel.js';
18
18
  import { useLocalFormat } from '@/vdb/hooks/use-local-format.js';
19
19
  import { usePermissions } from '@/vdb/hooks/use-permissions.js';
20
- import { Trans } from '@/vdb/lib/trans.js';
20
+ import { Trans } from '@lingui/react/macro';
21
21
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
22
22
  import { AlertCircle, Lock } from 'lucide-react';
23
23
  import { useEffect, useState } from 'react';
@@ -15,6 +15,7 @@ import {
15
15
  NavMenuSectionPlacement,
16
16
  } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
17
17
  import { usePermissions } from '@/vdb/hooks/use-permissions.js';
18
+ import { useLingui } from '@lingui/react';
18
19
  import { Link, useRouter, useRouterState } from '@tanstack/react-router';
19
20
  import { ChevronRight } from 'lucide-react';
20
21
  import * as React from 'react';
@@ -41,6 +42,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
41
42
  const router = useRouter();
42
43
  const routerState = useRouterState();
43
44
  const { hasPermissions } = usePermissions();
45
+ const { i18n } = useLingui();
44
46
  const currentPath = routerState.location.pathname;
45
47
  const basePath = router.basepath || '';
46
48
 
@@ -183,12 +185,16 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
183
185
  const renderTopSection = (item: NavMenuSection | NavMenuItem) => {
184
186
  if ('url' in item) {
185
187
  return (
186
- <NavItemWrapper key={item.title} locationId={item.id} order={item.order} offset={true}>
188
+ <NavItemWrapper key={item.id} locationId={item.id} order={item.order} offset={true}>
187
189
  <SidebarMenuItem>
188
- <SidebarMenuButton tooltip={item.title} asChild isActive={isPathActive(item.url)}>
190
+ <SidebarMenuButton
191
+ tooltip={i18n.t(item.title)}
192
+ asChild
193
+ isActive={isPathActive(item.url)}
194
+ >
189
195
  <Link to={item.url}>
190
196
  {item.icon && <item.icon />}
191
- <span>{item.title}</span>
197
+ <span>{i18n.t(item.title)}</span>
192
198
  </Link>
193
199
  </SidebarMenuButton>
194
200
  </SidebarMenuItem>
@@ -197,7 +203,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
197
203
  }
198
204
 
199
205
  return (
200
- <NavItemWrapper key={item.title} locationId={item.id} order={item.order} offset={true}>
206
+ <NavItemWrapper key={item.id} locationId={item.id} order={item.order} offset={true}>
201
207
  <Collapsible
202
208
  asChild
203
209
  open={openTopSectionIds.has(item.id)}
@@ -208,7 +214,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
208
214
  <CollapsibleTrigger asChild>
209
215
  <SidebarMenuButton tooltip={item.title}>
210
216
  {item.icon && <item.icon />}
211
- <span>{item.title}</span>
217
+ <span>{i18n.t(item.title)}</span>
212
218
  <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
213
219
  </SidebarMenuButton>
214
220
  </CollapsibleTrigger>
@@ -216,7 +222,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
216
222
  <SidebarMenuSub>
217
223
  {item.items?.map(subItem => (
218
224
  <NavItemWrapper
219
- key={subItem.title}
225
+ key={subItem.id}
220
226
  locationId={subItem.id}
221
227
  order={subItem.order}
222
228
  parentLocationId={item.id}
@@ -227,7 +233,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
227
233
  isActive={isPathActive(subItem.url)}
228
234
  >
229
235
  <Link to={subItem.url}>
230
- <span>{subItem.title}</span>
236
+ <span>{i18n.t(subItem.title)}</span>
231
237
  </Link>
232
238
  </SidebarMenuSubButton>
233
239
  </SidebarMenuSubItem>
@@ -247,10 +253,14 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
247
253
  return (
248
254
  <NavItemWrapper key={item.title} locationId={item.id} order={item.order} offset={true}>
249
255
  <SidebarMenuItem>
250
- <SidebarMenuButton tooltip={item.title} asChild isActive={isPathActive(item.url)}>
256
+ <SidebarMenuButton
257
+ tooltip={i18n.t(item.title)}
258
+ asChild
259
+ isActive={isPathActive(item.url)}
260
+ >
251
261
  <Link to={item.url}>
252
262
  {item.icon && <item.icon />}
253
- <span>{item.title}</span>
263
+ <span>{i18n.t(item.title)}</span>
254
264
  </Link>
255
265
  </SidebarMenuButton>
256
266
  </SidebarMenuItem>
@@ -267,9 +277,9 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
267
277
  >
268
278
  <SidebarMenuItem>
269
279
  <CollapsibleTrigger asChild>
270
- <SidebarMenuButton tooltip={item.title}>
280
+ <SidebarMenuButton tooltip={i18n.t(item.title)}>
271
281
  {item.icon && <item.icon />}
272
- <span>{item.title}</span>
282
+ <span>{i18n.t(item.title)}</span>
273
283
  <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
274
284
  </SidebarMenuButton>
275
285
  </CollapsibleTrigger>
@@ -277,7 +287,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
277
287
  <SidebarMenuSub>
278
288
  {item.items?.map(subItem => (
279
289
  <NavItemWrapper
280
- key={subItem.title}
290
+ key={i18n.t(subItem.title)}
281
291
  locationId={subItem.id}
282
292
  order={subItem.order}
283
293
  parentLocationId={item.id}
@@ -288,7 +298,7 @@ export function NavMain({ items }: Readonly<{ items: Array<NavMenuSection | NavM
288
298
  isActive={isPathActive(subItem.url)}
289
299
  >
290
300
  <Link to={subItem.url}>
291
- <span>{subItem.title}</span>
301
+ <span>{i18n.t(subItem.title)}</span>
292
302
  </Link>
293
303
  </SidebarMenuSubButton>
294
304
  </SidebarMenuSubItem>