From a5d18538367944490e4d524eb2e2d5f39724480b Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Mon, 20 Jan 2025 19:20:22 -0300 Subject: [PATCH 01/16] Translate dates POC --- .../desktop-client/src/components/App.tsx | 3 ++ .../src/components/budget/MonthPicker.tsx | 5 ++- .../components/settings/LanguageSettings.tsx | 6 +++- .../desktop-client/src/hooks/useSyncLocale.ts | 15 ++++++++ packages/loot-core/src/client/app/appSlice.ts | 35 ++++++++++++++++++- packages/loot-core/src/shared/months.ts | 4 +-- 6 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 packages/desktop-client/src/hooks/useSyncLocale.ts diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx index 1051fce30e0..920c8aa1691 100644 --- a/packages/desktop-client/src/components/App.tsx +++ b/packages/desktop-client/src/components/App.tsx @@ -43,6 +43,7 @@ import { Modals } from './Modals'; import { ResponsiveProvider } from './responsive/ResponsiveProvider'; import { SidebarProvider } from './sidebar/SidebarProvider'; import { UpdateNotification } from './UpdateNotification'; +import { useSyncLocale } from '../hooks/useSyncLocale'; function AppInner() { const [budgetId] = useMetadataPref('id'); @@ -160,6 +161,8 @@ function ErrorFallback({ error }: FallbackProps) { } export function App() { + useSyncLocale(); + const [hiddenScrollbars, setHiddenScrollbars] = useState( hasHiddenScrollbars(), ); diff --git a/packages/desktop-client/src/components/budget/MonthPicker.tsx b/packages/desktop-client/src/components/budget/MonthPicker.tsx index 8acfa858188..fbd6e519c5d 100644 --- a/packages/desktop-client/src/components/budget/MonthPicker.tsx +++ b/packages/desktop-client/src/components/budget/MonthPicker.tsx @@ -11,6 +11,8 @@ import { Link } from '../common/Link'; import { View } from '../common/View'; import { type MonthBounds } from './MonthsContext'; +import { RootState } from 'loot-core/client/store'; +import { useSelector } from '../../redux'; type MonthPickerProps = { startMonth: string; @@ -27,6 +29,7 @@ export const MonthPicker = ({ style, onSelect, }: MonthPickerProps) => { + const locale = useSelector(state => state.app.locale); const { t } = useTranslation(); const [hoverId, setHoverId] = useState(null); const [targetMonthCount, setTargetMonthCount] = useState(12); @@ -101,7 +104,7 @@ export const MonthPicker = ({ {range.map((month, idx) => { - const monthName = monthUtils.format(month, 'MMM'); + const monthName = monthUtils.format(month, 'MMM', locale); const selected = idx >= firstSelectedIndex && idx <= lastSelectedIndex; diff --git a/packages/desktop-client/src/components/settings/LanguageSettings.tsx b/packages/desktop-client/src/components/settings/LanguageSettings.tsx index 28aec19f048..08856c69487 100644 --- a/packages/desktop-client/src/components/settings/LanguageSettings.tsx +++ b/packages/desktop-client/src/components/settings/LanguageSettings.tsx @@ -12,6 +12,10 @@ import { Text } from '../common/Text'; import { Setting } from './UI'; +const languageDisplayNameOverride = { + 'pt-BR': 'Português (Brasil)' +} + const languageOptions = (t: TFunction): SelectOption[] => [ ['', t('System default')] as [string, string], @@ -19,7 +23,7 @@ const languageOptions = (t: TFunction): SelectOption[] => ].concat( availableLanguages.map(lang => [ lang, - new Intl.DisplayNames([lang], { + languageDisplayNameOverride[lang] || new Intl.DisplayNames([lang], { type: 'language', }).of(lang) || lang, ]), diff --git a/packages/desktop-client/src/hooks/useSyncLocale.ts b/packages/desktop-client/src/hooks/useSyncLocale.ts new file mode 100644 index 00000000000..e828cb59152 --- /dev/null +++ b/packages/desktop-client/src/hooks/useSyncLocale.ts @@ -0,0 +1,15 @@ +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { useGlobalPref } from './useGlobalPref'; +import { fetchLocale } from 'loot-core/client/app/appSlice'; + +export const useSyncLocale = () => { + const dispatch = useDispatch(); + const [language] = useGlobalPref('language'); + + useEffect(() => { + if (language) { + dispatch(fetchLocale({ language })); + } + }, [language, dispatch]); +}; diff --git a/packages/loot-core/src/client/app/appSlice.ts b/packages/loot-core/src/client/app/appSlice.ts index 819027feb0f..43b0063036e 100644 --- a/packages/loot-core/src/client/app/appSlice.ts +++ b/packages/loot-core/src/client/app/appSlice.ts @@ -1,4 +1,4 @@ -import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; +import { createAsyncThunk, createSlice, type PayloadAction } from '@reduxjs/toolkit'; import { send } from '../../platform/client/fetch'; import { getUploadError } from '../../shared/errors'; @@ -6,6 +6,7 @@ import { type AccountEntity } from '../../types/models'; import { syncAccounts } from '../accounts/accountsSlice'; import { loadPrefs, pushModal } from '../actions'; import { createAppAsyncThunk } from '../redux'; +import { enUS, ptBR } from 'date-fns/locale'; const sliceName = 'app'; @@ -18,6 +19,7 @@ type AppState = { } | null; showUpdateNotification: boolean; managerHasInitialized: boolean; + locale: Locale; }; const initialState: AppState = { @@ -25,6 +27,7 @@ const initialState: AppState = { updateInfo: null, showUpdateNotification: true, managerHasInitialized: false, + locale: enUS }; export const updateApp = createAppAsyncThunk( @@ -117,6 +120,28 @@ export const syncAndDownload = createAppAsyncThunk( }, ); +export const fetchLocale = createAppAsyncThunk( + 'app/fetchLocale', + async ({ language }: { language: string }, {}) => { + try { + debugger; + switch(language) { + case 'pt-BR': + return ptBR; + + default: + return enUS; + } + // const localeModule = await import(/* @vite-ignore */ `date-fns/locale/${language.replace('-', '')}`); + // return localeModule.default; + } catch (error) { + console.error(`Locale for language "${language}" not found. Falling back to default.`); + return enUS; + } + } +); + + type SetAppStatePayload = Partial; const appSlice = createSlice({ @@ -130,8 +155,15 @@ const appSlice = createSlice({ }; }, }, + extraReducers: (builder) => { + builder.addCase(fetchLocale.fulfilled, (state, action) => { + debugger; + state.locale = action.payload; // Update the locale when loaded + }); + }, }); + export const { name, reducer, getInitialState } = appSlice; export const actions = { @@ -140,6 +172,7 @@ export const actions = { resetSync, sync, syncAndDownload, + fetchLocale }; export const { setAppState } = actions; diff --git a/packages/loot-core/src/shared/months.ts b/packages/loot-core/src/shared/months.ts index f8c2db27266..065da91e942 100644 --- a/packages/loot-core/src/shared/months.ts +++ b/packages/loot-core/src/shared/months.ts @@ -397,8 +397,8 @@ export function nameForMonth(month: DateLike): string { return d.format(_parse(month), 'MMMM ‘yy'); } -export function format(month: DateLike, format: string): string { - return d.format(_parse(month), format); +export function format(month: DateLike, format: string, locale?: Locale): string { + return d.format(_parse(month), format, { locale }); } export const getDateFormatRegex = memoizeOne((format: string) => { From 3b716eaa13d638fc113952e2754bbca257308d26 Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Mon, 20 Jan 2025 19:26:35 -0300 Subject: [PATCH 02/16] more dynamic loading of locales --- packages/loot-core/src/client/app/appSlice.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/loot-core/src/client/app/appSlice.ts b/packages/loot-core/src/client/app/appSlice.ts index 43b0063036e..bf568ab2e71 100644 --- a/packages/loot-core/src/client/app/appSlice.ts +++ b/packages/loot-core/src/client/app/appSlice.ts @@ -6,7 +6,7 @@ import { type AccountEntity } from '../../types/models'; import { syncAccounts } from '../accounts/accountsSlice'; import { loadPrefs, pushModal } from '../actions'; import { createAppAsyncThunk } from '../redux'; -import { enUS, ptBR } from 'date-fns/locale'; +import { enUS } from 'date-fns/locale'; const sliceName = 'app'; @@ -124,15 +124,14 @@ export const fetchLocale = createAppAsyncThunk( 'app/fetchLocale', async ({ language }: { language: string }, {}) => { try { - debugger; - switch(language) { - case 'pt-BR': - return ptBR; + const localeModule = await import(/* @vite-ignore */ `date-fns/locale`); - default: + if(localeModule[language.replace('-', '')]) { + return localeModule[language.replace('-', '')] + } else { return enUS; } - // const localeModule = await import(/* @vite-ignore */ `date-fns/locale/${language.replace('-', '')}`); + // return localeModule.default; } catch (error) { console.error(`Locale for language "${language}" not found. Falling back to default.`); @@ -157,7 +156,6 @@ const appSlice = createSlice({ }, extraReducers: (builder) => { builder.addCase(fetchLocale.fulfilled, (state, action) => { - debugger; state.locale = action.payload; // Update the locale when loaded }); }, From e2121c86dc03fc608b0419da376dfea1c4dce19d Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Tue, 21 Jan 2025 15:16:58 -0300 Subject: [PATCH 03/16] added locale --- .../budget/envelope/budgetsummary/BudgetSummary.tsx | 8 +++++--- .../budget/tracking/budgetsummary/BudgetSummary.tsx | 6 ++++-- .../src/components/mobile/budget/BudgetTable.jsx | 5 +++-- .../mobile/budget/CategoryTransactions.tsx | 5 +++-- .../src/components/mobile/budget/index.tsx | 5 +++-- .../mobile/transactions/TransactionList.tsx | 5 +++-- .../modals/EnvelopeBudgetMonthMenuModal.tsx | 4 +++- .../modals/EnvelopeBudgetSummaryModal.tsx | 5 +++-- .../modals/ScheduledTransactionMenuModal.tsx | 4 +++- .../modals/TrackingBudgetMonthMenuModal.tsx | 4 +++- .../src/components/reports/DateRange.tsx | 8 +++++--- .../src/components/reports/Header.tsx | 4 +++- .../src/components/reports/ReportSummary.tsx | 12 +++++++----- .../src/components/reports/graphs/CashFlowGraph.tsx | 9 ++++++--- .../src/components/reports/reports/Calendar.tsx | 5 +++-- .../src/components/reports/reports/CashFlow.tsx | 9 +++++---- .../src/components/reports/reports/CustomReport.tsx | 4 +++- .../src/components/reports/reports/NetWorth.tsx | 7 ++++--- .../src/components/reports/reports/NetWorthCard.tsx | 5 ++++- .../src/components/reports/reports/Spending.tsx | 13 +++++++------ .../src/components/reports/reports/Summary.tsx | 7 +++++-- .../src/components/reports/reports/SummaryCard.tsx | 5 ++++- .../reports/spreadsheets/cash-flow-spreadsheet.tsx | 6 ++++-- .../reports/spreadsheets/net-worth-spreadsheet.ts | 8 +++++--- .../reports/spreadsheets/summary-spreadsheet.ts | 5 +++-- .../src/components/schedules/ScheduleDetails.tsx | 5 +++-- .../components/select/RecurringSchedulePicker.tsx | 4 +++- packages/desktop-client/src/hooks/useSyncLocale.ts | 2 +- packages/loot-core/src/shared/months.ts | 4 ++-- 29 files changed, 110 insertions(+), 63 deletions(-) diff --git a/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx b/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx index 9b40609c41c..3e5265e459c 100644 --- a/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx +++ b/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx @@ -19,12 +19,14 @@ import { useEnvelopeBudget } from '../EnvelopeBudgetContext'; import { BudgetMonthMenu } from './BudgetMonthMenu'; import { ToBudget } from './ToBudget'; import { TotalsList } from './TotalsList'; +import { useSelector } from '../../../../redux'; type BudgetSummaryProps = { month: string; isGoalTemplatesEnabled?: boolean; }; export function BudgetSummary({ month }: BudgetSummaryProps) { + const locale = useSelector(state => state.app.locale); const { currentMonth, summaryCollapsed: collapsed, @@ -44,13 +46,13 @@ export function BudgetSummary({ month }: BudgetSummaryProps) { setMenuOpen(false); } - const prevMonthName = monthUtils.format(monthUtils.prevMonth(month), 'MMM'); + const prevMonthName = monthUtils.format(monthUtils.prevMonth(month), 'MMM', locale); const ExpandOrCollapseIcon = collapsed ? SvgArrowButtonDown1 : SvgArrowButtonUp1; - const displayMonth = monthUtils.format(month, 'MMMM ‘yy'); + const displayMonth = monthUtils.format(month, 'MMMM ‘yy', locale); const { t } = useTranslation(); return ( @@ -124,7 +126,7 @@ export function BudgetSummary({ month }: BudgetSummaryProps) { currentMonth === month && { fontWeight: 'bold' }, ])} > - {monthUtils.format(month, 'MMMM')} + {monthUtils.format(month, 'MMMM', locale)} state.app.locale); const { t } = useTranslation(); const { currentMonth, @@ -51,7 +53,7 @@ export function BudgetSummary({ month }: BudgetSummaryProps) { ? SvgArrowButtonDown1 : SvgArrowButtonUp1; - const displayMonth = monthUtils.format(month, 'MMMM ‘yy'); + const displayMonth = monthUtils.format(month, 'MMMM ‘yy', locale); return ( - {monthUtils.format(month, 'MMMM')} + {monthUtils.format(month, 'MMMM', locale)} state.app.locale); const { t } = useTranslation(); const prevEnabled = month > monthBounds.start; const nextEnabled = month < monthUtils.subMonths(monthBounds.end, 1); @@ -2017,7 +2018,7 @@ function MonthSelector({ data-month={month} > - {monthUtils.format(month, 'MMMM ‘yy')} + {monthUtils.format(month, 'MMMM ‘yy', locale)} { - try { - const localeModule = await import(/* @vite-ignore */ `date-fns/locale`); - const localeKey = language.replace('-', '') as keyof typeof localeModule; - - if (localeModule[localeKey]) { - return localeModule[localeKey] as Locale; - } else { - return enUS; - } - - // return localeModule.default; - } catch (error) { - console.error( - `Locale for language ${language} not found. Falling back to default.`, - ); - return enUS; - } + const locale = await getLocale(language); + return locale; }, ); diff --git a/packages/loot-core/src/server/budget/actions.ts b/packages/loot-core/src/server/budget/actions.ts index e09511ca33b..269b2e029e8 100644 --- a/packages/loot-core/src/server/budget/actions.ts +++ b/packages/loot-core/src/server/budget/actions.ts @@ -1,5 +1,7 @@ // @ts-strict-ignore +import * as asyncStorage from '../../platform/server/asyncStorage'; +import { getLocale } from '../../shared/locale'; import * as monthUtils from '../../shared/months'; import { integerToCurrency, safeNumber } from '../../shared/util'; import * as db from '../db'; @@ -552,7 +554,12 @@ async function addMovementNotes({ ])?.note, ); - const displayDay = monthUtils.format(monthUtils.currentDate(), 'MMMM dd'); + const locale = await getLocale(await asyncStorage.getItem('language')); + const displayDay = monthUtils.format( + monthUtils.currentDate(), + 'MMMM dd', + locale, + ); const categories = await db.getCategories( [from, to].filter(c => c !== 'to-be-budgeted' && c !== 'overbudgeted'), ); diff --git a/packages/loot-core/src/shared/locale.ts b/packages/loot-core/src/shared/locale.ts new file mode 100644 index 00000000000..6fbb5a773bb --- /dev/null +++ b/packages/loot-core/src/shared/locale.ts @@ -0,0 +1,19 @@ +import { enUS } from 'date-fns/locale'; + +export const getLocale = async (language: string) => { + const localeModule = await import(/* @vite-ignore */ `date-fns/locale`); + try { + const localeKey = language.replace('-', '') as keyof typeof localeModule; + + if (localeModule[localeKey]) { + return localeModule[localeKey] as Locale; + } else { + return enUS; + } + } catch (error) { + console.error( + `Locale for language ${language} not found. Falling back to default.`, + ); + return enUS; + } +}; diff --git a/packages/loot-core/src/shared/rules.ts b/packages/loot-core/src/shared/rules.ts index 82c837fe177..0fd8c6a74e0 100644 --- a/packages/loot-core/src/shared/rules.ts +++ b/packages/loot-core/src/shared/rules.ts @@ -140,6 +140,22 @@ export function mapField(field, opts?) { return t('amount (inflow)'); case 'amount-outflow': return t('amount (outflow)'); + case 'account': + return t('account'); + case 'date': + return t('date'); + case 'category': + return t('category'); + case 'notes': + return t('notes'); + case 'payee': + return t('payee'); + case 'saved': + return t('saved'); + case 'cleared': + return t('cleared'); + case 'reconciled': + return t('reconciled'); default: return field; } @@ -204,11 +220,11 @@ export function friendlyOp(op, type?) { case 'and': return t('and'); case 'or': - return 'or'; + return t('or'); case 'onBudget': - return 'is on budget'; + return t('is on budget'); case 'offBudget': - return 'is off budget'; + return t('is off budget'); default: return ''; } diff --git a/packages/loot-core/src/shared/schedules.test.ts b/packages/loot-core/src/shared/schedules.test.ts index 67f82e6ce81..fe64bd9119f 100644 --- a/packages/loot-core/src/shared/schedules.test.ts +++ b/packages/loot-core/src/shared/schedules.test.ts @@ -1,3 +1,4 @@ +import { enUS } from 'date-fns/locale'; import MockDate from 'mockdate'; import * as monthUtils from './months'; @@ -66,6 +67,7 @@ describe('schedules', () => { getRecurringDescription( { start: '2021-05-17', frequency: 'weekly' }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every week on Monday'); @@ -77,6 +79,7 @@ describe('schedules', () => { interval: 2, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 weeks on Monday'); }); @@ -86,6 +89,7 @@ describe('schedules', () => { getRecurringDescription( { start: '2021-04-25', frequency: 'monthly' }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 25th'); @@ -97,6 +101,7 @@ describe('schedules', () => { interval: 2, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 months on the 25th'); @@ -108,6 +113,7 @@ describe('schedules', () => { patterns: [{ type: 'day', value: 25 }], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 25th'); @@ -120,6 +126,7 @@ describe('schedules', () => { patterns: [{ type: 'day', value: 25 }], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 months on the 25th'); @@ -132,6 +139,7 @@ describe('schedules', () => { patterns: [{ type: 'day', value: 31 }], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 31st'); @@ -144,6 +152,7 @@ describe('schedules', () => { patterns: [{ type: 'day', value: -1 }], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the last day'); @@ -156,6 +165,7 @@ describe('schedules', () => { patterns: [{ type: 'FR', value: 2 }], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 2nd Friday'); @@ -167,6 +177,7 @@ describe('schedules', () => { patterns: [{ type: 'FR', value: -1 }], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the last Friday'); }); @@ -185,6 +196,7 @@ describe('schedules', () => { ], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 3rd, 15th, and 20th'); @@ -200,6 +212,7 @@ describe('schedules', () => { ], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 3rd, 20th, and last day'); @@ -216,6 +229,7 @@ describe('schedules', () => { ], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 2nd Friday, 3rd, and last day'); @@ -233,6 +247,7 @@ describe('schedules', () => { ], }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every month on the 1st Saturday, 3rd Friday, 2nd, and 10th'); }); @@ -242,6 +257,7 @@ describe('schedules', () => { getRecurringDescription( { start: '2021-05-17', frequency: 'yearly' }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every year on May 17th'); @@ -253,6 +269,7 @@ describe('schedules', () => { interval: 2, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 years on May 17th'); }); @@ -268,6 +285,7 @@ describe('schedules', () => { endOccurrences: 2, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 weeks on Monday, 2 times'); @@ -281,6 +299,7 @@ describe('schedules', () => { endOccurrences: 1, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 weeks on Monday, once'); @@ -294,6 +313,7 @@ describe('schedules', () => { endOccurrences: 2, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 months on the 17th, 2 times'); @@ -307,6 +327,7 @@ describe('schedules', () => { endOccurrences: 2, }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 years on May 17th, 2 times'); }); @@ -322,6 +343,7 @@ describe('schedules', () => { endDate: '2021-06-01', }, 'MM/dd/yyyy', + enUS, ), ).toBe('Every 2 weeks on Monday, until 06/01/2021'); @@ -335,6 +357,7 @@ describe('schedules', () => { endDate: '2021-06-01', }, 'yyyy-MM-dd', + enUS, ), ).toBe('Every 2 months on the 17th, until 2021-06-01'); }); diff --git a/packages/loot-core/src/shared/schedules.ts b/packages/loot-core/src/shared/schedules.ts index 43e91e09d47..ffed1776b87 100644 --- a/packages/loot-core/src/shared/schedules.ts +++ b/packages/loot-core/src/shared/schedules.ts @@ -1,6 +1,7 @@ // @ts-strict-ignore import type { IRuleOptions } from '@rschedule/core'; import * as d from 'date-fns'; +import { t } from 'i18next'; import { Condition } from '../server/accounts/rules'; @@ -56,66 +57,67 @@ export function getHasTransactionsQuery(schedules) { .select(['schedule', 'date']); } -function makeNumberSuffix(num: number) { +function makeNumberSuffix(num: number, locale: Locale) { // Slight abuse of date-fns to turn a number like "1" into the full // form "1st" but formatting a date with that number - return monthUtils.format(new Date(2020, 0, num, 12), 'do'); + return monthUtils.format(new Date(2020, 0, num, 12), 'do', locale); } function prettyDayName(day) { const days = { - SU: 'Sunday', - MO: 'Monday', - TU: 'Tuesday', - WE: 'Wednesday', - TH: 'Thursday', - FR: 'Friday', - SA: 'Saturday', + SU: t('Sunday'), + MO: t('Monday'), + TU: t('Tuesday'), + WE: t('Wednesday'), + TH: t('Thursday'), + FR: t('Friday'), + SA: t('Saturday'), }; return days[day]; } -export function getRecurringDescription(config, dateFormat) { +export function getRecurringDescription(config, dateFormat, locale: Locale) { const interval = config.interval || 1; let endModeSuffix = ''; switch (config.endMode) { case 'after_n_occurrences': if (config.endOccurrences === 1) { - endModeSuffix = `, once`; + endModeSuffix = `, ${t('once')}`; } else { - endModeSuffix = `, ${config.endOccurrences} times`; + endModeSuffix = `, ${t('{{endOccurrences}} times', { endOccurences: config.endOccurrences })}`; } break; case 'on_date': - endModeSuffix = `, until ${monthUtils.format( - config.endDate, - dateFormat, - )}`; + endModeSuffix = `, ${t('until {{date}}', { + date: monthUtils.format(config.endDate, dateFormat), + })}`; break; default: } const weekendSolveSuffix = config.skipWeekend - ? ` (${config.weekendSolveMode} weekend) ` + ? ` (${config.weekendSolveMode} ${t('weekend')}) ` : ''; const suffix = endModeSuffix + weekendSolveSuffix; switch (config.frequency) { case 'daily': { - let desc = 'Every '; - desc += interval !== 1 ? `${interval} days` : 'day'; + let desc = `${t('Every')} `; + desc += interval !== 1 ? t(`{{interval}} days`, { interval }) : t('day'); return desc + suffix; } case 'weekly': { - let desc = 'Every '; - desc += interval !== 1 ? `${interval} weeks` : 'week'; - desc += ' on ' + monthUtils.format(config.start, 'EEEE'); + let desc = `${t('Every')} `; + desc += + interval !== 1 ? t(`{{interval}} weeks`, { interval }) : t('week'); + desc += ` ${t('on')} ` + monthUtils.format(config.start, 'EEEE', locale); return desc + suffix; } case 'monthly': { - let desc = 'Every '; - desc += interval !== 1 ? `${interval} months` : 'month'; + let desc = `${t('Every')} `; + desc += + interval !== 1 ? t(`{{interval}} months`, { interval }) : t('month'); if (config.patterns && config.patterns.length > 0) { // Sort the days ascending. We filter out -1 because that @@ -137,55 +139,57 @@ export function getRecurringDescription(config, dateFormat) { // Add on all -1 values to the end patterns = patterns.concat(config.patterns.filter(p => p.value === -1)); - desc += ' on the '; + desc += ` ${t('on the')} `; const strs: string[] = []; const uniqueDays = new Set(patterns.map(p => p.type)); const isSameDay = uniqueDays.size === 1 && !uniqueDays.has('day'); - for (const pattern of patterns) { if (pattern.type === 'day') { if (pattern.value === -1) { - strs.push('last day'); + strs.push(t('last day')); } else { // Example: 15th day - strs.push(makeNumberSuffix(pattern.value)); + strs.push(makeNumberSuffix(pattern.value, locale)); } } else { const dayName = isSameDay ? '' : ' ' + prettyDayName(pattern.type); if (pattern.value === -1) { // Example: last Monday - strs.push('last' + dayName); + strs.push(t('last') + dayName); } else { // Example: 3rd Monday - strs.push(makeNumberSuffix(pattern.value) + dayName); + strs.push(makeNumberSuffix(pattern.value, locale) + dayName); } } } if (strs.length > 2) { desc += strs.slice(0, strs.length - 1).join(', '); - desc += ', and '; + desc += `, ${t('and')} `; desc += strs[strs.length - 1]; } else { - desc += strs.join(' and '); + desc += strs.join(` ${t('and')} `); } if (isSameDay) { desc += ' ' + prettyDayName(patterns[0].type); } } else { - desc += ' on the ' + monthUtils.format(config.start, 'do'); + desc += + ` ${t('on the')} ` + monthUtils.format(config.start, 'do', locale); } return desc + suffix; } case 'yearly': { - let desc = 'Every '; - desc += interval !== 1 ? `${interval} years` : 'year'; - desc += ' on ' + monthUtils.format(config.start, 'LLL do'); + let desc = `${t('Every')} `; + desc += + interval !== 1 ? t(`{{interval}} years`, { interval }) : t('year'); + desc += + ` ${t('on')} ` + monthUtils.format(config.start, 'LLL do', locale); return desc + suffix; } default: @@ -346,7 +350,7 @@ export function describeSchedule(schedule, payee) { if (payee) { return `${payee.name} (${schedule.next_date})`; } else { - return `Next: ${schedule.next_date}`; + return `${t('Next:')} ${schedule.next_date}`; } } From b681ee541b61e81f7fe850098e5423185d7115fb Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 08:46:02 -0300 Subject: [PATCH 09/16] removed redux state for locale --- .../desktop-client/src/components/App.tsx | 3 --- .../src/components/budget/MonthPicker.tsx | 3 ++- .../envelope/budgetsummary/BudgetSummary.tsx | 3 ++- .../tracking/budgetsummary/BudgetSummary.tsx | 3 ++- .../components/mobile/budget/BudgetTable.jsx | 3 ++- .../mobile/budget/CategoryTransactions.tsx | 3 ++- .../src/components/mobile/budget/index.tsx | 3 ++- .../mobile/transactions/TransactionList.tsx | 3 ++- .../modals/EnvelopeBudgetMonthMenuModal.tsx | 3 ++- .../modals/EnvelopeBudgetSummaryModal.tsx | 3 ++- .../modals/ScheduledTransactionMenuModal.tsx | 3 ++- .../modals/TrackingBudgetMonthMenuModal.tsx | 3 ++- .../src/components/reports/DateRange.tsx | 3 ++- .../src/components/reports/Header.tsx | 3 ++- .../src/components/reports/ReportSummary.tsx | 3 ++- .../reports/graphs/CashFlowGraph.tsx | 5 +++-- .../components/reports/reports/Calendar.tsx | 3 ++- .../components/reports/reports/CashFlow.tsx | 3 ++- .../reports/reports/CustomReport.tsx | 3 ++- .../components/reports/reports/NetWorth.tsx | 3 ++- .../reports/reports/NetWorthCard.tsx | 3 ++- .../components/reports/reports/Spending.tsx | 3 ++- .../components/reports/reports/Summary.tsx | 3 ++- .../reports/reports/SummaryCard.tsx | 3 ++- .../src/components/rules/Value.tsx | 3 ++- .../schedules/DiscoverSchedules.tsx | 3 ++- .../components/schedules/ScheduleDetails.tsx | 3 ++- .../src/components/select/DateSelect.tsx | 3 ++- .../select/RecurringSchedulePicker.tsx | 5 +++-- .../desktop-client/src/hooks/useLocale.ts | 9 ++++++++ .../desktop-client/src/hooks/useSyncLocale.ts | 18 --------------- packages/loot-core/src/client/app/appSlice.ts | 18 --------------- .../loot-core/src/server/budget/actions.ts | 2 +- packages/loot-core/src/shared/locale.ts | 22 ++++++------------- 34 files changed, 75 insertions(+), 85 deletions(-) create mode 100644 packages/desktop-client/src/hooks/useLocale.ts delete mode 100644 packages/desktop-client/src/hooks/useSyncLocale.ts diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx index 585bf48d95b..74000f5d37c 100644 --- a/packages/desktop-client/src/components/App.tsx +++ b/packages/desktop-client/src/components/App.tsx @@ -27,7 +27,6 @@ import { } from 'loot-core/src/platform/client/fetch'; import { useMetadataPref } from '../hooks/useMetadataPref'; -import { useSyncLocale } from '../hooks/useSyncLocale'; import { installPolyfills } from '../polyfills'; import { useDispatch, useSelector } from '../redux'; import { styles, hasHiddenScrollbars, ThemeStyle, useTheme } from '../style'; @@ -161,8 +160,6 @@ function ErrorFallback({ error }: FallbackProps) { } export function App() { - useSyncLocale(); - const [hiddenScrollbars, setHiddenScrollbars] = useState( hasHiddenScrollbars(), ); diff --git a/packages/desktop-client/src/components/budget/MonthPicker.tsx b/packages/desktop-client/src/components/budget/MonthPicker.tsx index 16ad530913a..3e016d25abb 100644 --- a/packages/desktop-client/src/components/budget/MonthPicker.tsx +++ b/packages/desktop-client/src/components/budget/MonthPicker.tsx @@ -12,6 +12,7 @@ import { Link } from '../common/Link'; import { View } from '../common/View'; import { type MonthBounds } from './MonthsContext'; +import { useLocale } from '../../hooks/useLocale'; type MonthPickerProps = { startMonth: string; @@ -28,7 +29,7 @@ export const MonthPicker = ({ style, onSelect, }: MonthPickerProps) => { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const [hoverId, setHoverId] = useState(null); const [targetMonthCount, setTargetMonthCount] = useState(12); diff --git a/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx b/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx index ecfb2769cd3..be7f3c969ac 100644 --- a/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx +++ b/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx @@ -20,13 +20,14 @@ import { useEnvelopeBudget } from '../EnvelopeBudgetContext'; import { BudgetMonthMenu } from './BudgetMonthMenu'; import { ToBudget } from './ToBudget'; import { TotalsList } from './TotalsList'; +import { useLocale } from '../../../../hooks/useLocale'; type BudgetSummaryProps = { month: string; isGoalTemplatesEnabled?: boolean; }; export function BudgetSummary({ month }: BudgetSummaryProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { currentMonth, summaryCollapsed: collapsed, diff --git a/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx b/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx index 312ad6b8a48..e72cbfb2b42 100644 --- a/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx +++ b/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx @@ -23,12 +23,13 @@ import { BudgetMonthMenu } from './BudgetMonthMenu'; import { ExpenseTotal } from './ExpenseTotal'; import { IncomeTotal } from './IncomeTotal'; import { Saved } from './Saved'; +import { useLocale } from '../../../../hooks/useLocale'; type BudgetSummaryProps = { month?: string; }; export function BudgetSummary({ month }: BudgetSummaryProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const { currentMonth, diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index 53adecdb4d6..90ca013a345 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -50,6 +50,7 @@ import { MOBILE_NAV_HEIGHT } from '../MobileNavTabs'; import { PullToRefresh } from '../PullToRefresh'; import { ListItem } from './ListItem'; +import { useLocale } from '../../../hooks/useLocale'; const PILL_STYLE = { borderRadius: 16, @@ -1973,7 +1974,7 @@ function MonthSelector({ onPrevMonth, onNextMonth, }) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const prevEnabled = month > monthBounds.start; const nextEnabled = month < monthUtils.subMonths(monthBounds.end, 1); diff --git a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx index 0ee1b82f543..105337c164e 100644 --- a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx @@ -24,6 +24,7 @@ import { MobilePageHeader, Page } from '../../Page'; import { MobileBackButton } from '../MobileBackButton'; import { AddTransactionButton } from '../transactions/AddTransactionButton'; import { TransactionListWithBalances } from '../transactions/TransactionListWithBalances'; +import { useLocale } from '../../../hooks/useLocale'; type CategoryTransactionsProps = { category: CategoryEntity; @@ -34,7 +35,7 @@ export function CategoryTransactions({ category, month, }: CategoryTransactionsProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const dispatch = useDispatch(); const navigate = useNavigate(); diff --git a/packages/desktop-client/src/components/mobile/budget/index.tsx b/packages/desktop-client/src/components/mobile/budget/index.tsx index b49da043c26..511a57e9e1d 100644 --- a/packages/desktop-client/src/components/mobile/budget/index.tsx +++ b/packages/desktop-client/src/components/mobile/budget/index.tsx @@ -31,13 +31,14 @@ import { NamespaceContext } from '../../spreadsheet/NamespaceContext'; import { SyncRefresh } from '../../SyncRefresh'; import { BudgetTable } from './BudgetTable'; +import { useLocale } from '../../../hooks/useLocale'; function isBudgetType(input?: string): input is 'rollover' | 'report' { return ['rollover', 'report'].includes(input); } export function Budget() { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { list: categories, grouped: categoryGroups } = useCategories(); const [budgetTypePref] = useSyncedPref('budgetType'); const budgetType = isBudgetType(budgetTypePref) ? budgetTypePref : 'rollover'; diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx b/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx index 0bd7fae96ee..f3a7209ee80 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx @@ -39,6 +39,7 @@ import { useScrollListener } from '../../ScrollProvider'; import { FloatingActionBar } from '../FloatingActionBar'; import { TransactionListItem } from './TransactionListItem'; +import { useLocale } from '../../../hooks/useLocale'; const NOTIFICATION_BOTTOM_INSET = 75; @@ -79,7 +80,7 @@ export function TransactionList({ isLoadingMore, onLoadMore, }: TransactionListProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const sections = useMemo(() => { // Group by date. We can assume transactions is ordered diff --git a/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx b/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx index b42ac1079b7..d5a22d685a9 100644 --- a/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx @@ -17,6 +17,7 @@ import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; import { View } from '../common/View'; import { Notes } from '../Notes'; +import { useLocale } from '../../hooks/useLocale'; type EnvelopeBudgetMonthMenuModalProps = { month: string; @@ -29,7 +30,7 @@ export function EnvelopeBudgetMonthMenuModal({ onBudgetAction, onEditNotes, }: EnvelopeBudgetMonthMenuModalProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const originalNotes = useNotes(`budget-${month}`); const { showUndoNotification } = useUndo(); diff --git a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx index f984fdd72a9..f8ab5ccdaae 100644 --- a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx +++ b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx @@ -15,6 +15,7 @@ import { TotalsList } from '../budget/envelope/budgetsummary/TotalsList'; import { useEnvelopeSheetValue } from '../budget/envelope/EnvelopeBudgetComponents'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; import { NamespaceContext } from '../spreadsheet/NamespaceContext'; +import { useLocale } from '../../hooks/useLocale'; type EnvelopeBudgetSummaryModalProps = { onBudgetAction: (month: string, action: string, arg?: unknown) => void; @@ -27,7 +28,7 @@ export function EnvelopeBudgetSummaryModal({ }: EnvelopeBudgetSummaryModalProps) { const { t } = useTranslation(); - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const dispatch = useDispatch(); const prevMonthName = format(prevMonth(month), 'MMM', locale); const sheetValue = diff --git a/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx b/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx index f5d514d4fe9..e079832579e 100644 --- a/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx @@ -20,6 +20,7 @@ import { } from '../common/Modal'; import { Text } from '../common/Text'; import { View } from '../common/View'; +import { useLocale } from '../../hooks/useLocale'; type ScheduledTransactionMenuModalProps = ScheduledTransactionMenuProps; @@ -28,7 +29,7 @@ export function ScheduledTransactionMenuModal({ onSkip, onPost, }: ScheduledTransactionMenuModalProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const defaultMenuItemStyle: CSSProperties = { ...styles.mobileMenuItem, diff --git a/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx b/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx index 689650272ff..52f150ac0e0 100644 --- a/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx @@ -17,6 +17,7 @@ import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; import { View } from '../common/View'; import { Notes } from '../Notes'; +import { useLocale } from '../../hooks/useLocale'; type TrackingBudgetMonthMenuModalProps = { month: string; @@ -29,7 +30,7 @@ export function TrackingBudgetMonthMenuModal({ onBudgetAction, onEditNotes, }: TrackingBudgetMonthMenuModalProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const originalNotes = useNotes(`budget-${month}`); const { showUndoNotification } = useUndo(); diff --git a/packages/desktop-client/src/components/reports/DateRange.tsx b/packages/desktop-client/src/components/reports/DateRange.tsx index 4bef9f5c4d0..87e44cda89b 100644 --- a/packages/desktop-client/src/components/reports/DateRange.tsx +++ b/packages/desktop-client/src/components/reports/DateRange.tsx @@ -10,6 +10,7 @@ import { theme } from '../../style'; import { styles } from '../../style/styles'; import { Block } from '../common/Block'; import { Text } from '../common/Text'; +import { useLocale } from '../../hooks/useLocale'; type DateRangeProps = { start: string; @@ -27,7 +28,7 @@ function checkDate(date: string) { } export function DateRange({ start, end, type }: DateRangeProps): ReactElement { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const checkStart = checkDate(start); const checkEnd = checkDate(end); diff --git a/packages/desktop-client/src/components/reports/Header.tsx b/packages/desktop-client/src/components/reports/Header.tsx index b3a7b6f212b..5e76009faf1 100644 --- a/packages/desktop-client/src/components/reports/Header.tsx +++ b/packages/desktop-client/src/components/reports/Header.tsx @@ -25,6 +25,7 @@ import { validateEnd, validateStart, } from './reportRanges'; +import { useLocale } from '../../hooks/useLocale'; type HeaderProps = { start: TimeFrame['start']; @@ -67,7 +68,7 @@ export function Header({ onConditionsOpChange, children, }: HeaderProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const { isNarrowWidth } = useResponsive(); function convertToMonth( diff --git a/packages/desktop-client/src/components/reports/ReportSummary.tsx b/packages/desktop-client/src/components/reports/ReportSummary.tsx index b900a19061e..655e4d58267 100644 --- a/packages/desktop-client/src/components/reports/ReportSummary.tsx +++ b/packages/desktop-client/src/components/reports/ReportSummary.tsx @@ -19,6 +19,7 @@ import { View } from '../common/View'; import { PrivacyFilter } from '../PrivacyFilter'; import { ReportOptions } from './ReportOptions'; +import { useLocale } from '../../hooks/useLocale'; type ReportSummaryProps = { startDate: string; @@ -37,7 +38,7 @@ export function ReportSummary({ interval, intervalsCount, }: ReportSummaryProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const net = balanceTypeOp === 'netAssets' diff --git a/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx b/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx index 0c2437e14f6..f1b5a2109e5 100644 --- a/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx @@ -27,6 +27,7 @@ import { theme } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { chartTheme } from '../chart-theme'; import { Container } from '../Container'; +import { useLocale } from '../../../hooks/useLocale'; const MAX_BAR_SIZE = 50; const ANIMATION_DURATION = 1000; // in ms @@ -36,7 +37,7 @@ type CustomTooltipProps = TooltipProps & { }; function CustomTooltip({ active, payload, isConcise }: CustomTooltipProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); if (!active || !payload || !Array.isArray(payload) || !payload[0]) { @@ -112,7 +113,7 @@ export function CashFlowGraph({ showBalance = true, style, }: CashFlowGraphProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const privacyMode = usePrivacyMode(); const [yAxisIsHovered, setYAxisIsHovered] = useState(false); diff --git a/packages/desktop-client/src/components/reports/reports/Calendar.tsx b/packages/desktop-client/src/components/reports/reports/Calendar.tsx index a972ac58205..488a47d3160 100644 --- a/packages/desktop-client/src/components/reports/reports/Calendar.tsx +++ b/packages/desktop-client/src/components/reports/reports/Calendar.tsx @@ -70,6 +70,7 @@ import { } from '../spreadsheets/calendar-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; +import { useLocale } from '../../../hooks/useLocale'; const CHEVRON_HEIGHT = 42; const SUMMARY_HEIGHT = 140; @@ -95,7 +96,7 @@ type CalendarInnerProps = { }; function CalendarInner({ widget, parameters }: CalendarInnerProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const [initialStart, initialEnd, initialMode] = calculateTimeRange( widget?.meta?.timeFrame, diff --git a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx index ae717180eb7..c77324112e8 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx +++ b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx @@ -38,6 +38,7 @@ import { LoadingIndicator } from '../LoadingIndicator'; import { calculateTimeRange } from '../reportRanges'; import { cashFlowByDate } from '../spreadsheets/cash-flow-spreadsheet'; import { useReport } from '../useReport'; +import { useLocale } from '../../../hooks/useLocale'; export const defaultTimeFrame = { start: monthUtils.dayFromDate(monthUtils.currentMonth()), @@ -64,7 +65,7 @@ type CashFlowInnerProps = { }; function CashFlowInner({ widget }: CashFlowInnerProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const dispatch = useDispatch(); const { t } = useTranslation(); diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.tsx b/packages/desktop-client/src/components/reports/reports/CustomReport.tsx index a399c9b5f45..ca692cbb1ee 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.tsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.tsx @@ -62,6 +62,7 @@ import { createCustomSpreadsheet } from '../spreadsheets/custom-spreadsheet'; import { createGroupedSpreadsheet } from '../spreadsheets/grouped-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; +import { useLocale } from '../../../hooks/useLocale'; /** * Transform `selectedCategories` into `conditions`. @@ -122,7 +123,7 @@ type CustomReportInnerProps = { }; function CustomReportInner({ report: initialReport }: CustomReportInnerProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const categories = useCategories(); const { isNarrowWidth } = useResponsive(); diff --git a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx index 84e1a7e0b26..593cffacfbb 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx @@ -33,6 +33,7 @@ import { calculateTimeRange } from '../reportRanges'; import { createSpreadsheet as netWorthSpreadsheet } from '../spreadsheets/net-worth-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; +import { useLocale } from '../../../hooks/useLocale'; export function NetWorth() { const params = useParams(); @@ -53,7 +54,7 @@ type NetWorthInnerProps = { }; function NetWorthInner({ widget }: NetWorthInnerProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const dispatch = useDispatch(); const { t } = useTranslation(); diff --git a/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx index ff8ab7065a4..c47976ff456 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx @@ -22,6 +22,7 @@ import { ReportCardName } from '../ReportCardName'; import { calculateTimeRange } from '../reportRanges'; import { createSpreadsheet as netWorthSpreadsheet } from '../spreadsheets/net-worth-spreadsheet'; import { useReport } from '../useReport'; +import { useLocale } from '../../../hooks/useLocale'; type NetWorthCardProps = { widgetId: string; @@ -40,7 +41,7 @@ export function NetWorthCard({ onMetaChange, onRemove, }: NetWorthCardProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const { isNarrowWidth } = useResponsive(); diff --git a/packages/desktop-client/src/components/reports/reports/Spending.tsx b/packages/desktop-client/src/components/reports/reports/Spending.tsx index d72d50aa8ef..e1cb1b0cd55 100644 --- a/packages/desktop-client/src/components/reports/reports/Spending.tsx +++ b/packages/desktop-client/src/components/reports/reports/Spending.tsx @@ -40,6 +40,7 @@ import { calculateSpendingReportTimeRange } from '../reportRanges'; import { createSpendingSpreadsheet } from '../spreadsheets/spending-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; +import { useLocale } from '../../../hooks/useLocale'; export function Spending() { const params = useParams(); @@ -60,7 +61,7 @@ type SpendingInternalProps = { }; function SpendingInternal({ widget }: SpendingInternalProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const dispatch = useDispatch(); const { t } = useTranslation(); diff --git a/packages/desktop-client/src/components/reports/reports/Summary.tsx b/packages/desktop-client/src/components/reports/reports/Summary.tsx index 680fb21215b..1a583ce605d 100644 --- a/packages/desktop-client/src/components/reports/reports/Summary.tsx +++ b/packages/desktop-client/src/components/reports/reports/Summary.tsx @@ -43,6 +43,7 @@ import { calculateTimeRange } from '../reportRanges'; import { summarySpreadsheet } from '../spreadsheets/summary-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; +import { useLocale } from '../../../hooks/useLocale'; export function Summary() { const params = useParams(); @@ -65,7 +66,7 @@ type SummaryInnerProps = { type FilterObject = ReturnType; function SummaryInner({ widget }: SummaryInnerProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const [initialStart, initialEnd, initialMode] = calculateTimeRange( widget?.meta?.timeFrame, diff --git a/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx b/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx index 54e080ef919..337af056d10 100644 --- a/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx +++ b/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx @@ -17,6 +17,7 @@ import { calculateTimeRange } from '../reportRanges'; import { summarySpreadsheet } from '../spreadsheets/summary-spreadsheet'; import { SummaryNumber } from '../SummaryNumber'; import { useReport } from '../useReport'; +import { useLocale } from '../../../hooks/useLocale'; type SummaryCardProps = { widgetId: string; @@ -33,7 +34,7 @@ export function SummaryCard({ onMetaChange, onRemove, }: SummaryCardProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const [start, end] = calculateTimeRange(meta?.timeFrame, { start: monthUtils.dayFromDate(monthUtils.currentMonth()), diff --git a/packages/desktop-client/src/components/rules/Value.tsx b/packages/desktop-client/src/components/rules/Value.tsx index 241689c1566..88ce26545f8 100644 --- a/packages/desktop-client/src/components/rules/Value.tsx +++ b/packages/desktop-client/src/components/rules/Value.tsx @@ -16,6 +16,7 @@ import { useSelector } from '../../redux'; import { theme } from '../../style'; import { Link } from '../common/Link'; import { Text } from '../common/Text'; +import { useLocale } from '../../hooks/useLocale'; type ValueProps = { value: T; @@ -46,7 +47,7 @@ export function Value({ color: theme.pageTextPositive, ...style, }; - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const data = dataProp || diff --git a/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx b/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx index 736557c909c..79081205069 100644 --- a/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx +++ b/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx @@ -27,6 +27,7 @@ import { Table, TableHeader, Row, Field, SelectCell } from '../table'; import { DisplayId } from '../util/DisplayId'; import { ScheduleAmountCell } from './SchedulesTable'; +import { useLocale } from '../../hooks/useLocale'; const ROW_HEIGHT = 43; @@ -42,7 +43,7 @@ function DiscoverSchedulesTable({ const selectedItems = useSelectedItems(); const dispatchSelected = useSelectedDispatch(); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); function renderItem({ item }: { item: DiscoverScheduleEntity }) { const selected = selectedItems.has(item.id); diff --git a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx index 6f0c80dd516..ea2972410da 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx @@ -39,6 +39,7 @@ import { SelectedItemsButton } from '../table'; import { SimpleTransactionsTable } from '../transactions/SimpleTransactionsTable'; import { AmountInput, BetweenAmountInput } from '../util/AmountInput'; import { GenericInput } from '../util/GenericInput'; +import { useLocale } from '../../hooks/useLocale'; type Fields = { payee: null | string; @@ -106,7 +107,7 @@ type ScheduleDetailsProps = { }; export function ScheduleDetails({ id, transaction }: ScheduleDetailsProps) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const { t } = useTranslation(); const adding = id == null; diff --git a/packages/desktop-client/src/components/select/DateSelect.tsx b/packages/desktop-client/src/components/select/DateSelect.tsx index 4e4836f4470..b352bca3068 100644 --- a/packages/desktop-client/src/components/select/DateSelect.tsx +++ b/packages/desktop-client/src/components/select/DateSelect.tsx @@ -35,6 +35,7 @@ import { View } from '../common/View'; import DateSelectLeft from './DateSelect.left.png'; import DateSelectRight from './DateSelect.right.png'; +import { useLocale } from '../../hooks/useLocale'; const pickerStyles: CSSProperties = { '& .pika-single.actual-date-picker': { @@ -127,7 +128,7 @@ type DatePickerForwardedRef = { }; const DatePicker = forwardRef( ({ value, firstDayOfWeekIdx, dateFormat, onUpdate, onSelect }, ref) => { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const picker = useRef(null); const mountPoint = useRef(null); diff --git a/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx b/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx index 085f14ef36a..0b1f6354a40 100644 --- a/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx +++ b/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx @@ -34,6 +34,7 @@ import { View } from '../common/View'; import { Checkbox } from '../forms'; import { DateSelect } from './DateSelect'; +import { useLocale } from '../../hooks/useLocale'; // ex: There is no 6th Friday of the Month const MAX_DAY_OF_WEEK_INTERVAL = 5; @@ -241,7 +242,7 @@ function SchedulePreview({ }: { previewDates: string[] | string; }) { - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); const dateFormat = (useDateFormat() || 'MM/dd/yyyy') .replace('MM', 'M') .replace('dd', 'd'); @@ -593,7 +594,7 @@ export function RecurringSchedulePicker({ const triggerRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const dateFormat = useDateFormat() || 'MM/dd/yyyy'; - const locale = useSelector(state => state.app.locale); + const locale = useLocale(); function onSave(config: RecurConfig) { onChange(config); diff --git a/packages/desktop-client/src/hooks/useLocale.ts b/packages/desktop-client/src/hooks/useLocale.ts new file mode 100644 index 00000000000..077e76afceb --- /dev/null +++ b/packages/desktop-client/src/hooks/useLocale.ts @@ -0,0 +1,9 @@ +import { useMemo } from 'react'; +import { useGlobalPref } from './useGlobalPref'; +import { getLocale } from 'loot-core/shared/locale'; + +export function useLocale() { + const [language] = useGlobalPref('language'); + const locale = useMemo(() => getLocale(language), [language]); + return locale; +} diff --git a/packages/desktop-client/src/hooks/useSyncLocale.ts b/packages/desktop-client/src/hooks/useSyncLocale.ts deleted file mode 100644 index ef102c780db..00000000000 --- a/packages/desktop-client/src/hooks/useSyncLocale.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useEffect } from 'react'; - -import { fetchLocale } from 'loot-core/client/app/appSlice'; - -import { useDispatch } from '../redux'; - -import { useGlobalPref } from './useGlobalPref'; - -export const useSyncLocale = () => { - const dispatch = useDispatch(); - const [language] = useGlobalPref('language'); - - useEffect(() => { - if (language) { - dispatch(fetchLocale({ language })); - } - }, [language, dispatch]); -}; diff --git a/packages/loot-core/src/client/app/appSlice.ts b/packages/loot-core/src/client/app/appSlice.ts index 1d3a2104913..819027feb0f 100644 --- a/packages/loot-core/src/client/app/appSlice.ts +++ b/packages/loot-core/src/client/app/appSlice.ts @@ -1,9 +1,7 @@ import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; -import { enUS } from 'date-fns/locale'; import { send } from '../../platform/client/fetch'; import { getUploadError } from '../../shared/errors'; -import { getLocale } from '../../shared/locale'; import { type AccountEntity } from '../../types/models'; import { syncAccounts } from '../accounts/accountsSlice'; import { loadPrefs, pushModal } from '../actions'; @@ -20,7 +18,6 @@ type AppState = { } | null; showUpdateNotification: boolean; managerHasInitialized: boolean; - locale: Locale; }; const initialState: AppState = { @@ -28,7 +25,6 @@ const initialState: AppState = { updateInfo: null, showUpdateNotification: true, managerHasInitialized: false, - locale: enUS, }; export const updateApp = createAppAsyncThunk( @@ -121,14 +117,6 @@ export const syncAndDownload = createAppAsyncThunk( }, ); -export const fetchLocale = createAppAsyncThunk( - 'app/fetchLocale', - async ({ language }: { language: string }) => { - const locale = await getLocale(language); - return locale; - }, -); - type SetAppStatePayload = Partial; const appSlice = createSlice({ @@ -142,11 +130,6 @@ const appSlice = createSlice({ }; }, }, - extraReducers: builder => { - builder.addCase(fetchLocale.fulfilled, (state, action) => { - state.locale = action.payload; // Update the locale when loaded - }); - }, }); export const { name, reducer, getInitialState } = appSlice; @@ -157,7 +140,6 @@ export const actions = { resetSync, sync, syncAndDownload, - fetchLocale, }; export const { setAppState } = actions; diff --git a/packages/loot-core/src/server/budget/actions.ts b/packages/loot-core/src/server/budget/actions.ts index 269b2e029e8..b169b82b33f 100644 --- a/packages/loot-core/src/server/budget/actions.ts +++ b/packages/loot-core/src/server/budget/actions.ts @@ -554,7 +554,7 @@ async function addMovementNotes({ ])?.note, ); - const locale = await getLocale(await asyncStorage.getItem('language')); + const locale = getLocale(await asyncStorage.getItem('language')); const displayDay = monthUtils.format( monthUtils.currentDate(), 'MMMM dd', diff --git a/packages/loot-core/src/shared/locale.ts b/packages/loot-core/src/shared/locale.ts index 6fbb5a773bb..bd3e17aa055 100644 --- a/packages/loot-core/src/shared/locale.ts +++ b/packages/loot-core/src/shared/locale.ts @@ -1,19 +1,11 @@ -import { enUS } from 'date-fns/locale'; +import { enUS, ptBR } from 'date-fns/locale'; -export const getLocale = async (language: string) => { - const localeModule = await import(/* @vite-ignore */ `date-fns/locale`); - try { - const localeKey = language.replace('-', '') as keyof typeof localeModule; +export function getLocale(language: string) { + switch (language) { + case 'pt-BR': + return ptBR; - if (localeModule[localeKey]) { - return localeModule[localeKey] as Locale; - } else { + default: return enUS; - } - } catch (error) { - console.error( - `Locale for language ${language} not found. Falling back to default.`, - ); - return enUS; } -}; +} \ No newline at end of file From 74926f4427ed5187ea3ef156f0050f0621751584 Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 08:58:05 -0300 Subject: [PATCH 10/16] linter --- packages/desktop-client/src/components/budget/MonthPicker.tsx | 3 +-- .../budget/envelope/budgetsummary/BudgetSummary.tsx | 3 +-- .../budget/tracking/budgetsummary/BudgetSummary.tsx | 3 +-- .../src/components/mobile/budget/BudgetTable.jsx | 4 ++-- .../src/components/mobile/budget/CategoryTransactions.tsx | 4 ++-- .../desktop-client/src/components/mobile/budget/index.tsx | 4 ++-- .../src/components/mobile/transactions/TransactionList.tsx | 4 ++-- .../src/components/modals/EnvelopeBudgetMonthMenuModal.tsx | 3 +-- .../src/components/modals/EnvelopeBudgetSummaryModal.tsx | 4 ++-- .../src/components/modals/ScheduledTransactionMenuModal.tsx | 3 +-- .../src/components/modals/TrackingBudgetMonthMenuModal.tsx | 3 +-- packages/desktop-client/src/components/reports/DateRange.tsx | 3 +-- packages/desktop-client/src/components/reports/Header.tsx | 3 +-- .../desktop-client/src/components/reports/ReportSummary.tsx | 3 +-- .../src/components/reports/graphs/CashFlowGraph.tsx | 3 +-- .../src/components/reports/reports/Calendar.tsx | 4 ++-- .../src/components/reports/reports/CashFlow.tsx | 4 ++-- .../src/components/reports/reports/CustomReport.tsx | 3 +-- .../src/components/reports/reports/NetWorth.tsx | 4 ++-- .../src/components/reports/reports/NetWorthCard.tsx | 3 +-- .../src/components/reports/reports/Spending.tsx | 4 ++-- .../desktop-client/src/components/reports/reports/Summary.tsx | 4 ++-- .../src/components/reports/reports/SummaryCard.tsx | 3 +-- packages/desktop-client/src/components/rules/Value.tsx | 3 +-- .../src/components/schedules/DiscoverSchedules.tsx | 3 +-- .../src/components/schedules/ScheduleDetails.tsx | 4 ++-- packages/desktop-client/src/components/select/DateSelect.tsx | 3 +-- .../src/components/select/RecurringSchedulePicker.tsx | 3 +-- packages/desktop-client/src/hooks/useLocale.ts | 4 +++- packages/loot-core/src/shared/locale.ts | 2 +- 30 files changed, 43 insertions(+), 58 deletions(-) diff --git a/packages/desktop-client/src/components/budget/MonthPicker.tsx b/packages/desktop-client/src/components/budget/MonthPicker.tsx index 3e016d25abb..30788cebd11 100644 --- a/packages/desktop-client/src/components/budget/MonthPicker.tsx +++ b/packages/desktop-client/src/components/budget/MonthPicker.tsx @@ -4,15 +4,14 @@ import { useTranslation } from 'react-i18next'; import * as monthUtils from 'loot-core/src/shared/months'; +import { useLocale } from '../../hooks/useLocale'; import { useResizeObserver } from '../../hooks/useResizeObserver'; import { SvgCalendar } from '../../icons/v2'; -import { useSelector } from '../../redux'; import { styles, theme } from '../../style'; import { Link } from '../common/Link'; import { View } from '../common/View'; import { type MonthBounds } from './MonthsContext'; -import { useLocale } from '../../hooks/useLocale'; type MonthPickerProps = { startMonth: string; diff --git a/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx b/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx index be7f3c969ac..12ebd297b27 100644 --- a/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx +++ b/packages/desktop-client/src/components/budget/envelope/budgetsummary/BudgetSummary.tsx @@ -5,10 +5,10 @@ import { css } from '@emotion/css'; import * as monthUtils from 'loot-core/src/shared/months'; +import { useLocale } from '../../../../hooks/useLocale'; import { useUndo } from '../../../../hooks/useUndo'; import { SvgDotsHorizontalTriple } from '../../../../icons/v1'; import { SvgArrowButtonDown1, SvgArrowButtonUp1 } from '../../../../icons/v2'; -import { useSelector } from '../../../../redux'; import { theme, styles } from '../../../../style'; import { Button } from '../../../common/Button2'; import { Popover } from '../../../common/Popover'; @@ -20,7 +20,6 @@ import { useEnvelopeBudget } from '../EnvelopeBudgetContext'; import { BudgetMonthMenu } from './BudgetMonthMenu'; import { ToBudget } from './ToBudget'; import { TotalsList } from './TotalsList'; -import { useLocale } from '../../../../hooks/useLocale'; type BudgetSummaryProps = { month: string; diff --git a/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx b/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx index e72cbfb2b42..ab4bc305854 100644 --- a/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx +++ b/packages/desktop-client/src/components/budget/tracking/budgetsummary/BudgetSummary.tsx @@ -6,10 +6,10 @@ import { css } from '@emotion/css'; import * as monthUtils from 'loot-core/src/shared/months'; +import { useLocale } from '../../../../hooks/useLocale'; import { useUndo } from '../../../../hooks/useUndo'; import { SvgDotsHorizontalTriple } from '../../../../icons/v1'; import { SvgArrowButtonDown1, SvgArrowButtonUp1 } from '../../../../icons/v2'; -import { useSelector } from '../../../../redux'; import { theme, styles } from '../../../../style'; import { Button } from '../../../common/Button2'; import { Popover } from '../../../common/Popover'; @@ -23,7 +23,6 @@ import { BudgetMonthMenu } from './BudgetMonthMenu'; import { ExpenseTotal } from './ExpenseTotal'; import { IncomeTotal } from './IncomeTotal'; import { Saved } from './Saved'; -import { useLocale } from '../../../../hooks/useLocale'; type BudgetSummaryProps = { month?: string; diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index 90ca013a345..841c336002c 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -16,6 +16,7 @@ import * as monthUtils from 'loot-core/src/shared/months'; import { useCategories } from '../../../hooks/useCategories'; import { useFeatureFlag } from '../../../hooks/useFeatureFlag'; +import { useLocale } from '../../../hooks/useLocale'; import { useLocalPref } from '../../../hooks/useLocalPref'; import { useNavigate } from '../../../hooks/useNavigate'; import { useNotes } from '../../../hooks/useNotes'; @@ -30,7 +31,7 @@ import { SvgCheveronRight, } from '../../../icons/v1'; import { SvgViewShow } from '../../../icons/v2'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { BalanceWithCarryover } from '../../budget/BalanceWithCarryover'; import { makeAmountGrey, makeBalanceAmountStyle } from '../../budget/util'; @@ -50,7 +51,6 @@ import { MOBILE_NAV_HEIGHT } from '../MobileNavTabs'; import { PullToRefresh } from '../PullToRefresh'; import { ListItem } from './ListItem'; -import { useLocale } from '../../../hooks/useLocale'; const PILL_STYLE = { borderRadius: 16, diff --git a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx index 105337c164e..a622a684993 100644 --- a/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx +++ b/packages/desktop-client/src/components/mobile/budget/CategoryTransactions.tsx @@ -16,15 +16,15 @@ import { } from 'loot-core/types/models'; import { useDateFormat } from '../../../hooks/useDateFormat'; +import { useLocale } from '../../../hooks/useLocale'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { TextOneLine } from '../../common/TextOneLine'; import { View } from '../../common/View'; import { MobilePageHeader, Page } from '../../Page'; import { MobileBackButton } from '../MobileBackButton'; import { AddTransactionButton } from '../transactions/AddTransactionButton'; import { TransactionListWithBalances } from '../transactions/TransactionListWithBalances'; -import { useLocale } from '../../../hooks/useLocale'; type CategoryTransactionsProps = { category: CategoryEntity; diff --git a/packages/desktop-client/src/components/mobile/budget/index.tsx b/packages/desktop-client/src/components/mobile/budget/index.tsx index 511a57e9e1d..29bbfdf7c68 100644 --- a/packages/desktop-client/src/components/mobile/budget/index.tsx +++ b/packages/desktop-client/src/components/mobile/budget/index.tsx @@ -20,10 +20,11 @@ import { send, listen } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; import { useCategories } from '../../../hooks/useCategories'; +import { useLocale } from '../../../hooks/useLocale'; import { useLocalPref } from '../../../hooks/useLocalPref'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { prewarmMonth } from '../../budget/util'; import { View } from '../../common/View'; @@ -31,7 +32,6 @@ import { NamespaceContext } from '../../spreadsheet/NamespaceContext'; import { SyncRefresh } from '../../SyncRefresh'; import { BudgetTable } from './BudgetTable'; -import { useLocale } from '../../../hooks/useLocale'; function isBudgetType(input?: string): input is 'rollover' | 'report' { return ['rollover', 'report'].includes(input); diff --git a/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx b/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx index f3a7209ee80..3e286486078 100644 --- a/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx +++ b/packages/desktop-client/src/components/mobile/transactions/TransactionList.tsx @@ -17,6 +17,7 @@ import { type TransactionEntity } from 'loot-core/types/models/transaction'; import { useAccounts } from '../../../hooks/useAccounts'; import { useCategories } from '../../../hooks/useCategories'; +import { useLocale } from '../../../hooks/useLocale'; import { useNavigate } from '../../../hooks/useNavigate'; import { usePayees } from '../../../hooks/usePayees'; import { @@ -28,7 +29,7 @@ import { useUndo } from '../../../hooks/useUndo'; import { AnimatedLoading } from '../../../icons/AnimatedLoading'; import { SvgDelete } from '../../../icons/v0'; import { SvgDotsHorizontalTriple } from '../../../icons/v1'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Menu, type MenuItemObject } from '../../common/Menu'; @@ -39,7 +40,6 @@ import { useScrollListener } from '../../ScrollProvider'; import { FloatingActionBar } from '../FloatingActionBar'; import { TransactionListItem } from './TransactionListItem'; -import { useLocale } from '../../../hooks/useLocale'; const NOTIFICATION_BOTTOM_INSET = 75; diff --git a/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx b/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx index d5a22d685a9..5ce4561db76 100644 --- a/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/EnvelopeBudgetMonthMenuModal.tsx @@ -6,18 +6,17 @@ import { css } from '@emotion/css'; import * as monthUtils from 'loot-core/src/shared/months'; +import { useLocale } from '../../hooks/useLocale'; import { useNotes } from '../../hooks/useNotes'; import { useUndo } from '../../hooks/useUndo'; import { SvgCheveronDown, SvgCheveronUp } from '../../icons/v1'; import { SvgNotesPaper } from '../../icons/v2'; -import { useSelector } from '../../redux'; import { styles, theme, type CSSProperties } from '../../style'; import { BudgetMonthMenu } from '../budget/envelope/budgetsummary/BudgetMonthMenu'; import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; import { View } from '../common/View'; import { Notes } from '../Notes'; -import { useLocale } from '../../hooks/useLocale'; type EnvelopeBudgetMonthMenuModalProps = { month: string; diff --git a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx index f8ab5ccdaae..c0638418d34 100644 --- a/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx +++ b/packages/desktop-client/src/components/modals/EnvelopeBudgetSummaryModal.tsx @@ -7,15 +7,15 @@ import { groupById, integerToCurrency } from 'loot-core/shared/util'; import { format, sheetForMonth, prevMonth } from 'loot-core/src/shared/months'; import { useCategories } from '../../hooks/useCategories'; +import { useLocale } from '../../hooks/useLocale'; import { useUndo } from '../../hooks/useUndo'; -import { useDispatch, useSelector } from '../../redux'; +import { useDispatch } from '../../redux'; import { styles } from '../../style'; import { ToBudgetAmount } from '../budget/envelope/budgetsummary/ToBudgetAmount'; import { TotalsList } from '../budget/envelope/budgetsummary/TotalsList'; import { useEnvelopeSheetValue } from '../budget/envelope/EnvelopeBudgetComponents'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; import { NamespaceContext } from '../spreadsheet/NamespaceContext'; -import { useLocale } from '../../hooks/useLocale'; type EnvelopeBudgetSummaryModalProps = { onBudgetAction: (month: string, action: string, arg?: unknown) => void; diff --git a/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx b/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx index e079832579e..9cdf788e6fe 100644 --- a/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/ScheduledTransactionMenuModal.tsx @@ -9,7 +9,7 @@ import { useSchedules } from 'loot-core/client/data-hooks/schedules'; import { format } from 'loot-core/shared/months'; import { q } from 'loot-core/shared/query'; -import { useSelector } from '../../redux'; +import { useLocale } from '../../hooks/useLocale'; import { theme, styles } from '../../style'; import { Menu } from '../common/Menu'; import { @@ -20,7 +20,6 @@ import { } from '../common/Modal'; import { Text } from '../common/Text'; import { View } from '../common/View'; -import { useLocale } from '../../hooks/useLocale'; type ScheduledTransactionMenuModalProps = ScheduledTransactionMenuProps; diff --git a/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx b/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx index 52f150ac0e0..9e446b85d33 100644 --- a/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/TrackingBudgetMonthMenuModal.tsx @@ -6,18 +6,17 @@ import { css } from '@emotion/css'; import * as monthUtils from 'loot-core/src/shared/months'; +import { useLocale } from '../../hooks/useLocale'; import { useNotes } from '../../hooks/useNotes'; import { useUndo } from '../../hooks/useUndo'; import { SvgCheveronDown, SvgCheveronUp } from '../../icons/v1'; import { SvgNotesPaper } from '../../icons/v2'; -import { useSelector } from '../../redux'; import { type CSSProperties, styles, theme } from '../../style'; import { BudgetMonthMenu } from '../budget/tracking/budgetsummary/BudgetMonthMenu'; import { Button } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; import { View } from '../common/View'; import { Notes } from '../Notes'; -import { useLocale } from '../../hooks/useLocale'; type TrackingBudgetMonthMenuModalProps = { month: string; diff --git a/packages/desktop-client/src/components/reports/DateRange.tsx b/packages/desktop-client/src/components/reports/DateRange.tsx index 87e44cda89b..de6bbc49426 100644 --- a/packages/desktop-client/src/components/reports/DateRange.tsx +++ b/packages/desktop-client/src/components/reports/DateRange.tsx @@ -5,12 +5,11 @@ import * as d from 'date-fns'; import * as monthUtils from 'loot-core/src/shared/months'; -import { useSelector } from '../../redux'; +import { useLocale } from '../../hooks/useLocale'; import { theme } from '../../style'; import { styles } from '../../style/styles'; import { Block } from '../common/Block'; import { Text } from '../common/Text'; -import { useLocale } from '../../hooks/useLocale'; type DateRangeProps = { start: string; diff --git a/packages/desktop-client/src/components/reports/Header.tsx b/packages/desktop-client/src/components/reports/Header.tsx index 5e76009faf1..49b6c0b3c5c 100644 --- a/packages/desktop-client/src/components/reports/Header.tsx +++ b/packages/desktop-client/src/components/reports/Header.tsx @@ -8,7 +8,7 @@ import { } from 'loot-core/types/models'; import { type SyncedPrefs } from 'loot-core/types/prefs'; -import { useSelector } from '../../redux'; +import { useLocale } from '../../hooks/useLocale'; import { Button } from '../common/Button2'; import { Select } from '../common/Select'; import { SpaceBetween } from '../common/SpaceBetween'; @@ -25,7 +25,6 @@ import { validateEnd, validateStart, } from './reportRanges'; -import { useLocale } from '../../hooks/useLocale'; type HeaderProps = { start: TimeFrame['start']; diff --git a/packages/desktop-client/src/components/reports/ReportSummary.tsx b/packages/desktop-client/src/components/reports/ReportSummary.tsx index 655e4d58267..eb9a92d3817 100644 --- a/packages/desktop-client/src/components/reports/ReportSummary.tsx +++ b/packages/desktop-client/src/components/reports/ReportSummary.tsx @@ -12,14 +12,13 @@ import { type DataEntity, } from 'loot-core/src/types/models/reports'; -import { useSelector } from '../../redux'; +import { useLocale } from '../../hooks/useLocale'; import { theme, styles } from '../../style'; import { Text } from '../common/Text'; import { View } from '../common/View'; import { PrivacyFilter } from '../PrivacyFilter'; import { ReportOptions } from './ReportOptions'; -import { useLocale } from '../../hooks/useLocale'; type ReportSummaryProps = { startDate: string; diff --git a/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx b/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx index f1b5a2109e5..d13c8289ea7 100644 --- a/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx @@ -21,13 +21,12 @@ import { amountToCurrencyNoDecimal, } from 'loot-core/src/shared/util'; +import { useLocale } from '../../../hooks/useLocale'; import { usePrivacyMode } from '../../../hooks/usePrivacyMode'; -import { useSelector } from '../../../redux'; import { theme } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { chartTheme } from '../chart-theme'; import { Container } from '../Container'; -import { useLocale } from '../../../hooks/useLocale'; const MAX_BAR_SIZE = 50; const ANIMATION_DURATION = 1000; // in ms diff --git a/packages/desktop-client/src/components/reports/reports/Calendar.tsx b/packages/desktop-client/src/components/reports/reports/Calendar.tsx index 488a47d3160..361e5664312 100644 --- a/packages/desktop-client/src/components/reports/reports/Calendar.tsx +++ b/packages/desktop-client/src/components/reports/reports/Calendar.tsx @@ -34,6 +34,7 @@ import { useAccounts } from '../../../hooks/useAccounts'; import { useCategories } from '../../../hooks/useCategories'; import { useDateFormat } from '../../../hooks/useDateFormat'; import { useFilters } from '../../../hooks/useFilters'; +import { useLocale } from '../../../hooks/useLocale'; import { useMergedRefs } from '../../../hooks/useMergedRefs'; import { useNavigate } from '../../../hooks/useNavigate'; import { usePayees } from '../../../hooks/usePayees'; @@ -47,7 +48,7 @@ import { SvgCheveronDown, SvgCheveronUp, } from '../../../icons/v1'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { styles, theme } from '../../../style'; import { Button } from '../../common/Button2'; import { View } from '../../common/View'; @@ -70,7 +71,6 @@ import { } from '../spreadsheets/calendar-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; -import { useLocale } from '../../../hooks/useLocale'; const CHEVRON_HEIGHT = 42; const SUMMARY_HEIGHT = 140; diff --git a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx index c77324112e8..736f97ba88b 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlow.tsx +++ b/packages/desktop-client/src/components/reports/reports/CashFlow.tsx @@ -16,9 +16,10 @@ import { } from 'loot-core/types/models'; import { useFilters } from '../../../hooks/useFilters'; +import { useLocale } from '../../../hooks/useLocale'; import { useNavigate } from '../../../hooks/useNavigate'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Block } from '../../common/Block'; @@ -38,7 +39,6 @@ import { LoadingIndicator } from '../LoadingIndicator'; import { calculateTimeRange } from '../reportRanges'; import { cashFlowByDate } from '../spreadsheets/cash-flow-spreadsheet'; import { useReport } from '../useReport'; -import { useLocale } from '../../../hooks/useLocale'; export const defaultTimeFrame = { start: monthUtils.dayFromDate(monthUtils.currentMonth()), diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.tsx b/packages/desktop-client/src/components/reports/reports/CustomReport.tsx index ca692cbb1ee..2c48ca2ffe6 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.tsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.tsx @@ -22,11 +22,11 @@ import { type TransObjectLiteral } from 'loot-core/types/util'; import { useAccounts } from '../../../hooks/useAccounts'; import { useCategories } from '../../../hooks/useCategories'; import { useFilters } from '../../../hooks/useFilters'; +import { useLocale } from '../../../hooks/useLocale'; import { useLocalPref } from '../../../hooks/useLocalPref'; import { useNavigate } from '../../../hooks/useNavigate'; import { usePayees } from '../../../hooks/usePayees'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; -import { useSelector } from '../../../redux'; import { theme, styles } from '../../../style'; import { Warning } from '../../alerts'; import { AlignedText } from '../../common/AlignedText'; @@ -62,7 +62,6 @@ import { createCustomSpreadsheet } from '../spreadsheets/custom-spreadsheet'; import { createGroupedSpreadsheet } from '../spreadsheets/grouped-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; -import { useLocale } from '../../../hooks/useLocale'; /** * Transform `selectedCategories` into `conditions`. diff --git a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx index 593cffacfbb..ad8fb3d1f1c 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorth.tsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorth.tsx @@ -13,9 +13,10 @@ import { type TimeFrame, type NetWorthWidget } from 'loot-core/types/models'; import { useAccounts } from '../../../hooks/useAccounts'; import { useFilters } from '../../../hooks/useFilters'; +import { useLocale } from '../../../hooks/useLocale'; import { useNavigate } from '../../../hooks/useNavigate'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { Button } from '../../common/Button2'; import { Paragraph } from '../../common/Paragraph'; @@ -33,7 +34,6 @@ import { calculateTimeRange } from '../reportRanges'; import { createSpreadsheet as netWorthSpreadsheet } from '../spreadsheets/net-worth-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; -import { useLocale } from '../../../hooks/useLocale'; export function NetWorth() { const params = useParams(); diff --git a/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx index c47976ff456..f876e0249bd 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx @@ -7,7 +7,7 @@ import { type NetWorthWidget, } from 'loot-core/src/types/models'; -import { useSelector } from '../../../redux'; +import { useLocale } from '../../../hooks/useLocale'; import { styles } from '../../../style'; import { Block } from '../../common/Block'; import { View } from '../../common/View'; @@ -22,7 +22,6 @@ import { ReportCardName } from '../ReportCardName'; import { calculateTimeRange } from '../reportRanges'; import { createSpreadsheet as netWorthSpreadsheet } from '../spreadsheets/net-worth-spreadsheet'; import { useReport } from '../useReport'; -import { useLocale } from '../../../hooks/useLocale'; type NetWorthCardProps = { widgetId: string; diff --git a/packages/desktop-client/src/components/reports/reports/Spending.tsx b/packages/desktop-client/src/components/reports/reports/Spending.tsx index e1cb1b0cd55..4f5d12def02 100644 --- a/packages/desktop-client/src/components/reports/reports/Spending.tsx +++ b/packages/desktop-client/src/components/reports/reports/Spending.tsx @@ -13,8 +13,9 @@ import { type SpendingWidget } from 'loot-core/types/models'; import { type RuleConditionEntity } from 'loot-core/types/models/rule'; import { useFilters } from '../../../hooks/useFilters'; +import { useLocale } from '../../../hooks/useLocale'; import { useNavigate } from '../../../hooks/useNavigate'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme, styles } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Block } from '../../common/Block'; @@ -40,7 +41,6 @@ import { calculateSpendingReportTimeRange } from '../reportRanges'; import { createSpendingSpreadsheet } from '../spreadsheets/spending-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; -import { useLocale } from '../../../hooks/useLocale'; export function Spending() { const params = useParams(); diff --git a/packages/desktop-client/src/components/reports/reports/Summary.tsx b/packages/desktop-client/src/components/reports/reports/Summary.tsx index 1a583ce605d..93cc972c2a7 100644 --- a/packages/desktop-client/src/components/reports/reports/Summary.tsx +++ b/packages/desktop-client/src/components/reports/reports/Summary.tsx @@ -16,13 +16,14 @@ import { } from 'loot-core/types/models'; import { useFilters } from '../../../hooks/useFilters'; +import { useLocale } from '../../../hooks/useLocale'; import { useNavigate } from '../../../hooks/useNavigate'; import { useSyncedPref } from '../../../hooks/useSyncedPref'; import { SvgEquals } from '../../../icons/v1'; import { SvgCloseParenthesis } from '../../../icons/v2/CloseParenthesis'; import { SvgOpenParenthesis } from '../../../icons/v2/OpenParenthesis'; import { SvgSum } from '../../../icons/v2/Sum'; -import { useDispatch, useSelector } from '../../../redux'; +import { useDispatch } from '../../../redux'; import { theme } from '../../../style'; import { Button } from '../../common/Button2'; import { Text } from '../../common/Text'; @@ -43,7 +44,6 @@ import { calculateTimeRange } from '../reportRanges'; import { summarySpreadsheet } from '../spreadsheets/summary-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; -import { useLocale } from '../../../hooks/useLocale'; export function Summary() { const params = useParams(); diff --git a/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx b/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx index 337af056d10..ba7baa4c130 100644 --- a/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx +++ b/packages/desktop-client/src/components/reports/reports/SummaryCard.tsx @@ -7,7 +7,7 @@ import { type SummaryWidget, } from 'loot-core/types/models'; -import { useSelector } from '../../../redux'; +import { useLocale } from '../../../hooks/useLocale'; import { View } from '../../common/View'; import { DateRange } from '../DateRange'; import { LoadingIndicator } from '../LoadingIndicator'; @@ -17,7 +17,6 @@ import { calculateTimeRange } from '../reportRanges'; import { summarySpreadsheet } from '../spreadsheets/summary-spreadsheet'; import { SummaryNumber } from '../SummaryNumber'; import { useReport } from '../useReport'; -import { useLocale } from '../../../hooks/useLocale'; type SummaryCardProps = { widgetId: string; diff --git a/packages/desktop-client/src/components/rules/Value.tsx b/packages/desktop-client/src/components/rules/Value.tsx index 88ce26545f8..705a24c242c 100644 --- a/packages/desktop-client/src/components/rules/Value.tsx +++ b/packages/desktop-client/src/components/rules/Value.tsx @@ -11,12 +11,11 @@ import { integerToCurrency } from 'loot-core/src/shared/util'; import { useAccounts } from '../../hooks/useAccounts'; import { useCategories } from '../../hooks/useCategories'; import { useDateFormat } from '../../hooks/useDateFormat'; +import { useLocale } from '../../hooks/useLocale'; import { usePayees } from '../../hooks/usePayees'; -import { useSelector } from '../../redux'; import { theme } from '../../style'; import { Link } from '../common/Link'; import { Text } from '../common/Text'; -import { useLocale } from '../../hooks/useLocale'; type ValueProps = { value: T; diff --git a/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx b/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx index 79081205069..b0bd2432f39 100644 --- a/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx +++ b/packages/desktop-client/src/components/schedules/DiscoverSchedules.tsx @@ -9,6 +9,7 @@ import { getRecurringDescription } from 'loot-core/src/shared/schedules'; import type { DiscoverScheduleEntity } from 'loot-core/src/types/models'; import { useDateFormat } from '../../hooks/useDateFormat'; +import { useLocale } from '../../hooks/useLocale'; import { useSelected, useSelectedDispatch, @@ -16,7 +17,6 @@ import { SelectedProvider, } from '../../hooks/useSelected'; import { useSendPlatformRequest } from '../../hooks/useSendPlatformRequest'; -import { useSelector } from '../../redux'; import { theme } from '../../style'; import { ButtonWithLoading } from '../common/Button2'; import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; @@ -27,7 +27,6 @@ import { Table, TableHeader, Row, Field, SelectCell } from '../table'; import { DisplayId } from '../util/DisplayId'; import { ScheduleAmountCell } from './SchedulesTable'; -import { useLocale } from '../../hooks/useLocale'; const ROW_HEIGHT = 43; diff --git a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx index ea2972410da..4e5369350de 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleDetails.tsx @@ -19,9 +19,10 @@ import { } from 'loot-core/types/models'; import { useDateFormat } from '../../hooks/useDateFormat'; +import { useLocale } from '../../hooks/useLocale'; import { usePayees } from '../../hooks/usePayees'; import { useSelected, SelectedProvider } from '../../hooks/useSelected'; -import { useDispatch, useSelector } from '../../redux'; +import { useDispatch } from '../../redux'; import { theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { PayeeAutocomplete } from '../autocomplete/PayeeAutocomplete'; @@ -39,7 +40,6 @@ import { SelectedItemsButton } from '../table'; import { SimpleTransactionsTable } from '../transactions/SimpleTransactionsTable'; import { AmountInput, BetweenAmountInput } from '../util/AmountInput'; import { GenericInput } from '../util/GenericInput'; -import { useLocale } from '../../hooks/useLocale'; type Fields = { payee: null | string; diff --git a/packages/desktop-client/src/components/select/DateSelect.tsx b/packages/desktop-client/src/components/select/DateSelect.tsx index b352bca3068..44df84ff591 100644 --- a/packages/desktop-client/src/components/select/DateSelect.tsx +++ b/packages/desktop-client/src/components/select/DateSelect.tsx @@ -26,8 +26,8 @@ import { currentDate, } from 'loot-core/src/shared/months'; +import { useLocale } from '../../hooks/useLocale'; import { useSyncedPref } from '../../hooks/useSyncedPref'; -import { useSelector } from '../../redux'; import { styles, theme, type CSSProperties } from '../../style'; import { Input } from '../common/Input'; import { Popover } from '../common/Popover'; @@ -35,7 +35,6 @@ import { View } from '../common/View'; import DateSelectLeft from './DateSelect.left.png'; import DateSelectRight from './DateSelect.right.png'; -import { useLocale } from '../../hooks/useLocale'; const pickerStyles: CSSProperties = { '& .pika-single.actual-date-picker': { diff --git a/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx b/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx index 0b1f6354a40..2cee6c6070c 100644 --- a/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx +++ b/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx @@ -19,8 +19,8 @@ import { } from 'loot-core/types/util'; import { useDateFormat } from '../../hooks/useDateFormat'; +import { useLocale } from '../../hooks/useLocale'; import { SvgAdd, SvgSubtract } from '../../icons/v0'; -import { useSelector } from '../../redux'; import { theme } from '../../style'; import { Button } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; @@ -34,7 +34,6 @@ import { View } from '../common/View'; import { Checkbox } from '../forms'; import { DateSelect } from './DateSelect'; -import { useLocale } from '../../hooks/useLocale'; // ex: There is no 6th Friday of the Month const MAX_DAY_OF_WEEK_INTERVAL = 5; diff --git a/packages/desktop-client/src/hooks/useLocale.ts b/packages/desktop-client/src/hooks/useLocale.ts index 077e76afceb..36477f03909 100644 --- a/packages/desktop-client/src/hooks/useLocale.ts +++ b/packages/desktop-client/src/hooks/useLocale.ts @@ -1,7 +1,9 @@ import { useMemo } from 'react'; -import { useGlobalPref } from './useGlobalPref'; + import { getLocale } from 'loot-core/shared/locale'; +import { useGlobalPref } from './useGlobalPref'; + export function useLocale() { const [language] = useGlobalPref('language'); const locale = useMemo(() => getLocale(language), [language]); diff --git a/packages/loot-core/src/shared/locale.ts b/packages/loot-core/src/shared/locale.ts index bd3e17aa055..6d96f11aa72 100644 --- a/packages/loot-core/src/shared/locale.ts +++ b/packages/loot-core/src/shared/locale.ts @@ -8,4 +8,4 @@ export function getLocale(language: string) { default: return enUS; } -} \ No newline at end of file +} From 4167105592668e85b8e3901fa1bf5a0f8deb693c Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 09:04:32 -0300 Subject: [PATCH 11/16] typecheck fix --- packages/desktop-client/src/hooks/useLocale.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-client/src/hooks/useLocale.ts b/packages/desktop-client/src/hooks/useLocale.ts index 36477f03909..0028673babf 100644 --- a/packages/desktop-client/src/hooks/useLocale.ts +++ b/packages/desktop-client/src/hooks/useLocale.ts @@ -6,6 +6,6 @@ import { useGlobalPref } from './useGlobalPref'; export function useLocale() { const [language] = useGlobalPref('language'); - const locale = useMemo(() => getLocale(language), [language]); + const locale = useMemo(() => getLocale(language ?? 'en-US'), [language]); return locale; } From fba050761d494ae284570f248160b98a2d239423 Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 09:25:16 -0300 Subject: [PATCH 12/16] fix test for schedules.ts --- .../loot-core/src/shared/schedules.test.ts | 49 +++++++++++++++++++ packages/loot-core/src/shared/schedules.ts | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/loot-core/src/shared/schedules.test.ts b/packages/loot-core/src/shared/schedules.test.ts index fe64bd9119f..d9c70b84a17 100644 --- a/packages/loot-core/src/shared/schedules.test.ts +++ b/packages/loot-core/src/shared/schedules.test.ts @@ -3,6 +3,55 @@ import MockDate from 'mockdate'; import * as monthUtils from './months'; import { getRecurringDescription, getStatus } from './schedules'; +import i18next from 'i18next'; + +i18next.init({ + lng: 'en', + fallbackLng: 'en', + resources: { + en: { + translation: { + Every: 'Every', + day: 'day', + week: 'week', + month: 'month', + year: 'year', + on: 'on', + 'on the': 'on the', + and: 'and', + 'until {{date}}': 'until {{date}}', + once: 'once', + times: '{{endOccurrences}} times', + weekend: 'weekend', + last: 'last', + 'Next:': 'Next:', + 'last day': 'last day', + '{{interval}} days': '{{interval}} days', + '{{interval}} weeks': '{{interval}} weeks', + '{{interval}} months': '{{interval}} months', + '{{interval}} years': '{{interval}} years', + + Sunday: 'Sunday', + Monday: 'Monday', + Tuesday: 'Tuesday', + Wednesday: 'Wednesday', + Thursday: 'Thursday', + Friday: 'Friday', + Saturday: 'Saturday', + + '{{value}}th day': '{{value}}th day', + '{{value}}th': '{{value}}th', + '{{value}}th {{dayName}}': '{{value}}th {{dayName}}', + 'last {{dayName}}': 'last {{dayName}}', + + '({{weekendSolveMode}} weekend)': '({{weekendSolveMode}} weekend)', + }, + }, + }, + interpolation: { + escapeValue: false, + }, +}); describe('schedules', () => { const today = new Date(2017, 0, 1); // Global date when testing is set to 2017-01-01 per monthUtils.currentDay() diff --git a/packages/loot-core/src/shared/schedules.ts b/packages/loot-core/src/shared/schedules.ts index ffed1776b87..c750d44bebe 100644 --- a/packages/loot-core/src/shared/schedules.ts +++ b/packages/loot-core/src/shared/schedules.ts @@ -85,7 +85,7 @@ export function getRecurringDescription(config, dateFormat, locale: Locale) { if (config.endOccurrences === 1) { endModeSuffix = `, ${t('once')}`; } else { - endModeSuffix = `, ${t('{{endOccurrences}} times', { endOccurences: config.endOccurrences })}`; + endModeSuffix = `, ${t('{{endOccurrences}} times', { endOccurrences: config.endOccurrences })}`; } break; case 'on_date': From b6078c1bc2926a5bb276c3a228af3cab5c592e61 Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 09:30:49 -0300 Subject: [PATCH 13/16] linter --- packages/loot-core/src/shared/schedules.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loot-core/src/shared/schedules.test.ts b/packages/loot-core/src/shared/schedules.test.ts index d9c70b84a17..c40ab1de732 100644 --- a/packages/loot-core/src/shared/schedules.test.ts +++ b/packages/loot-core/src/shared/schedules.test.ts @@ -1,9 +1,9 @@ import { enUS } from 'date-fns/locale'; +import i18next from 'i18next'; import MockDate from 'mockdate'; import * as monthUtils from './months'; import { getRecurringDescription, getStatus } from './schedules'; -import i18next from 'i18next'; i18next.init({ lng: 'en', From a11ae36a5a24d9eec477d1b71d4ce3d37c0416a2 Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 13:18:36 -0300 Subject: [PATCH 14/16] fixed System Default language --- packages/desktop-client/src/hooks/useLocale.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/desktop-client/src/hooks/useLocale.ts b/packages/desktop-client/src/hooks/useLocale.ts index 0028673babf..256befc38e2 100644 --- a/packages/desktop-client/src/hooks/useLocale.ts +++ b/packages/desktop-client/src/hooks/useLocale.ts @@ -6,6 +6,9 @@ import { useGlobalPref } from './useGlobalPref'; export function useLocale() { const [language] = useGlobalPref('language'); - const locale = useMemo(() => getLocale(language ?? 'en-US'), [language]); + const locale = useMemo( + () => getLocale(language ?? navigator.language ?? 'en-US'), + [language], + ); return locale; } From d75322734c4338619936ee77de404474d58ff1ba Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 23:32:43 -0300 Subject: [PATCH 15/16] loading all locales at once --- packages/loot-core/src/shared/locale.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/loot-core/src/shared/locale.ts b/packages/loot-core/src/shared/locale.ts index 6d96f11aa72..714a76eb6a9 100644 --- a/packages/loot-core/src/shared/locale.ts +++ b/packages/loot-core/src/shared/locale.ts @@ -1,11 +1,11 @@ -import { enUS, ptBR } from 'date-fns/locale'; +import * as locales from 'date-fns/locale'; export function getLocale(language: string) { - switch (language) { - case 'pt-BR': - return ptBR; + const localeKey = language.replace('-', '') as keyof typeof locales; - default: - return enUS; + if (localeKey in locales) { + return locales[localeKey]; } + + return locales.enUS; } From 86c5682eaa71ff986297076a163fca668169b88d Mon Sep 17 00:00:00 2001 From: Leandro Menezes Date: Wed, 22 Jan 2025 23:53:39 -0300 Subject: [PATCH 16/16] added some fallback cases --- packages/loot-core/src/shared/locale.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/loot-core/src/shared/locale.ts b/packages/loot-core/src/shared/locale.ts index 714a76eb6a9..49c48014d89 100644 --- a/packages/loot-core/src/shared/locale.ts +++ b/packages/loot-core/src/shared/locale.ts @@ -1,7 +1,18 @@ import * as locales from 'date-fns/locale'; export function getLocale(language: string) { - const localeKey = language.replace('-', '') as keyof typeof locales; + if (!language || typeof language !== 'string') { + return locales.enUS; + } + + let localeKey = language.replace('-', '') as keyof typeof locales; + + if (localeKey in locales) { + return locales[localeKey]; + } + + //if language was not found with four letters, try with two + localeKey = language.replace('-', '').substring(0, 2) as keyof typeof locales; if (localeKey in locales) { return locales[localeKey];