From 4c192d7e1e7e7ef639c15856fc321ce39c98a887 Mon Sep 17 00:00:00 2001
From: Robert Dyer <rdyer@unl.edu>
Date: Fri, 16 Aug 2024 07:35:57 -0500
Subject: [PATCH] Translation: desktop-client/components/filters (#3270)

* Translation: desktop-client/components/filters

* add release note

* fix linter

* add missing t definition
---
 .../components/filters/FilterExpression.tsx   |  4 ++-
 .../src/components/filters/FilterMenu.tsx     | 27 +++++++++--------
 .../src/components/filters/FiltersButton.tsx  |  5 +++-
 .../src/components/filters/FiltersMenu.jsx    | 29 ++++++++++++-------
 .../src/components/filters/NameFilter.tsx     |  6 ++--
 .../filters/SavedFilterMenuButton.tsx         |  8 +++--
 upcoming-release-notes/3270.md                |  6 ++++
 7 files changed, 56 insertions(+), 29 deletions(-)
 create mode 100644 upcoming-release-notes/3270.md

diff --git a/packages/desktop-client/src/components/filters/FilterExpression.tsx b/packages/desktop-client/src/components/filters/FilterExpression.tsx
index 55accb1c8..d649fd09c 100644
--- a/packages/desktop-client/src/components/filters/FilterExpression.tsx
+++ b/packages/desktop-client/src/components/filters/FilterExpression.tsx
@@ -1,4 +1,5 @@
 import React, { useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
 
 import { mapField, friendlyOp } from 'loot-core/src/shared/rules';
 import { integerToCurrency } from 'loot-core/src/shared/util';
@@ -38,6 +39,7 @@ export function FilterExpression<T extends RuleConditionEntity>({
   onChange,
   onDelete,
 }: FilterExpressionProps<T>) {
+  const { t } = useTranslation();
   const [editing, setEditing] = useState(false);
   const triggerRef = useRef(null);
 
@@ -84,7 +86,7 @@ export function FilterExpression<T extends RuleConditionEntity>({
           )}
         </div>
       </Button>
-      <Button type="bare" onClick={onDelete} aria-label="Delete filter">
+      <Button type="bare" onClick={onDelete} aria-label={t('Delete filter')}>
         <SvgDelete
           style={{
             width: 8,
diff --git a/packages/desktop-client/src/components/filters/FilterMenu.tsx b/packages/desktop-client/src/components/filters/FilterMenu.tsx
index b56649567..c3adb2048 100644
--- a/packages/desktop-client/src/components/filters/FilterMenu.tsx
+++ b/packages/desktop-client/src/components/filters/FilterMenu.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { useTranslation } from 'react-i18next';
 
 import { Menu } from '../common/Menu';
 
@@ -11,6 +12,8 @@ export function FilterMenu({
   filterId: SavedFilter;
   onFilterMenuSelect: (item: string) => void;
 }) {
+  const { t } = useTranslation();
+
   return (
     <Menu
       onMenuSelect={item => {
@@ -19,29 +22,29 @@ export function FilterMenu({
       items={
         !filterId.id
           ? [
-              { name: 'save-filter', text: 'Save new filter' },
-              { name: 'clear-filter', text: 'Clear all conditions' },
+              { name: 'save-filter', text: t('Save new filter') },
+              { name: 'clear-filter', text: t('Clear all conditions') },
             ]
           : filterId.id !== null && filterId.status === 'saved'
             ? [
-                { name: 'rename-filter', text: 'Rename' },
-                { name: 'delete-filter', text: 'Delete' },
+                { name: 'rename-filter', text: t('Rename') },
+                { name: 'delete-filter', text: t('Delete') },
                 Menu.line,
                 {
                   name: 'save-filter',
-                  text: 'Save new filter',
+                  text: t('Save new filter'),
                   disabled: true,
                 },
-                { name: 'clear-filter', text: 'Clear all conditions' },
+                { name: 'clear-filter', text: t('Clear all conditions') },
               ]
             : [
-                { name: 'rename-filter', text: 'Rename' },
-                { name: 'update-filter', text: 'Update condtions' },
-                { name: 'reload-filter', text: 'Revert changes' },
-                { name: 'delete-filter', text: 'Delete' },
+                { name: 'rename-filter', text: t('Rename') },
+                { name: 'update-filter', text: t('Update condtions') },
+                { name: 'reload-filter', text: t('Revert changes') },
+                { name: 'delete-filter', text: t('Delete') },
                 Menu.line,
-                { name: 'save-filter', text: 'Save new filter' },
-                { name: 'clear-filter', text: 'Clear all conditions' },
+                { name: 'save-filter', text: t('Save new filter') },
+                { name: 'clear-filter', text: t('Clear all conditions') },
               ]
       }
     />
diff --git a/packages/desktop-client/src/components/filters/FiltersButton.tsx b/packages/desktop-client/src/components/filters/FiltersButton.tsx
index 6d82904ee..35be24986 100644
--- a/packages/desktop-client/src/components/filters/FiltersButton.tsx
+++ b/packages/desktop-client/src/components/filters/FiltersButton.tsx
@@ -1,11 +1,14 @@
 import React from 'react';
+import { useTranslation } from 'react-i18next';
 
 import { SvgFilter } from '../../icons/v1/Filter';
 import { Button } from '../common/Button';
 
 export function FiltersButton({ onClick }: { onClick: () => void }) {
+  const { t } = useTranslation();
+
   return (
-    <Button type="bare" onClick={onClick} title="Filters">
+    <Button type="bare" onClick={onClick} title={t('Filters')}>
       <SvgFilter style={{ width: 12, height: 12, marginRight: 5 }} /> Filter
     </Button>
   );
diff --git a/packages/desktop-client/src/components/filters/FiltersMenu.jsx b/packages/desktop-client/src/components/filters/FiltersMenu.jsx
index fc971664e..61720dff6 100644
--- a/packages/desktop-client/src/components/filters/FiltersMenu.jsx
+++ b/packages/desktop-client/src/components/filters/FiltersMenu.jsx
@@ -1,5 +1,6 @@
 import React, { useState, useRef, useEffect, useReducer } from 'react';
 import { useHotkeys } from 'react-hotkeys-hook';
+import { Trans, useTranslation } from 'react-i18next';
 
 import { FocusScope } from '@react-aria/focus';
 import {
@@ -62,6 +63,7 @@ function ConfigureField({
   dispatch,
   onApply,
 }) {
+  const { t } = useTranslation();
   const [subfield, setSubfield] = useState(initialSubfield);
   const inputRef = useRef();
   const prevOp = useRef(null);
@@ -91,15 +93,15 @@ function ConfigureField({
               options={
                 field === 'amount'
                   ? [
-                      ['amount', 'Amount'],
-                      ['amount-inflow', 'Amount (inflow)'],
-                      ['amount-outflow', 'Amount (outflow)'],
+                      ['amount', t('Amount')],
+                      ['amount-inflow', t('Amount (inflow)')],
+                      ['amount-outflow', t('Amount (outflow)')],
                     ]
                   : field === 'date'
                     ? [
-                        ['date', 'Date'],
-                        ['month', 'Month'],
-                        ['year', 'Year'],
+                        ['date', t('Date')],
+                        ['month', t('Month')],
+                        ['year', t('Year')],
                       ]
                     : null
               }
@@ -125,7 +127,7 @@ function ConfigureField({
           marginBottom: 10,
         }}
       >
-        {field === 'saved' && 'Existing filters will be cleared'}
+        {field === 'saved' && t('Existing filters will be cleared')}
       </View>
 
       <Stack
@@ -231,7 +233,7 @@ function ConfigureField({
               });
             }}
           >
-            Apply
+            <Trans>Apply</Trans>
           </Button>
         </Stack>
       </form>
@@ -240,6 +242,7 @@ function ConfigureField({
 }
 
 export function FilterButton({ onApply, compact, hover, exclude }) {
+  const { t } = useTranslation();
   const filters = useFilters();
   const triggerRef = useRef(null);
 
@@ -285,7 +288,7 @@ export function FilterButton({ onApply, compact, hover, exclude }) {
         if (isDateValid(date)) {
           cond.value = formatDate(date, 'yyyy-MM');
         } else {
-          alert('Invalid date format');
+          alert(t('Invalid date format'));
           return;
         }
       } else if (cond.options.year) {
@@ -293,7 +296,7 @@ export function FilterButton({ onApply, compact, hover, exclude }) {
         if (isDateValid(date)) {
           cond.value = formatDate(date, 'yyyy');
         } else {
-          alert('Invalid date format');
+          alert(t('Invalid date format'));
           return;
         }
       }
@@ -329,7 +332,11 @@ export function FilterButton({ onApply, compact, hover, exclude }) {
             lineHeight: 1.5,
             padding: '6px 10px',
           }}
-          content={<Text>Filters</Text>}
+          content={
+            <Text>
+              <Trans>Filters</Trans>
+            </Text>
+          }
           placement="bottom start"
           triggerProps={{
             isDisabled: !hover,
diff --git a/packages/desktop-client/src/components/filters/NameFilter.tsx b/packages/desktop-client/src/components/filters/NameFilter.tsx
index 0e4285e85..b8ad19ac0 100644
--- a/packages/desktop-client/src/components/filters/NameFilter.tsx
+++ b/packages/desktop-client/src/components/filters/NameFilter.tsx
@@ -1,4 +1,5 @@
 import React, { useRef, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
 
 import { theme } from '../../style';
 import { Button } from '../common/Button';
@@ -22,6 +23,7 @@ export function NameFilter({
   onAddUpdate: () => void;
   err: string | null;
 }) {
+  const { t } = useTranslation();
   const inputRef = useRef<HTMLInputElement>(null);
 
   useEffect(() => {
@@ -42,7 +44,7 @@ export function NameFilter({
           >
             <FormField style={{ flex: 1 }}>
               <FormLabel
-                title="Filter Name"
+                title={t('Filter Name')}
                 htmlFor="name-field"
                 style={{ userSelect: 'none' }}
               />
@@ -61,7 +63,7 @@ export function NameFilter({
                 onAddUpdate();
               }}
             >
-              {adding ? 'Add' : 'Update'}
+              {adding ? t('Add') : t('Update')}
             </Button>
           </Stack>
         </form>
diff --git a/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx b/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx
index ba5658f6b..1a2d68e52 100644
--- a/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx
+++ b/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx
@@ -1,4 +1,5 @@
 import React, { useRef, useState } from 'react';
+import { Trans, useTranslation } from 'react-i18next';
 
 import { send, sendCatch } from 'loot-core/src/platform/client/fetch';
 import { type TransactionFilterEntity } from 'loot-core/types/models';
@@ -36,6 +37,7 @@ export function SavedFilterMenuButton({
   onReloadSavedFilter: (savedFilter: SavedFilter, value?: string) => void;
   savedFilters: TransactionFilterEntity[];
 }) {
+  const { t } = useTranslation();
   const [nameOpen, setNameOpen] = useState(false);
   const [adding, setAdding] = useState(false);
   const [menuOpen, setMenuOpen] = useState(false);
@@ -176,10 +178,12 @@ export function SavedFilterMenuButton({
               flexShrink: 0,
             }}
           >
-            {!filterId.id ? 'Unsaved filter' : filterId.name}&nbsp;
+            {!filterId.id ? t('Unsaved filter') : filterId.name}&nbsp;
           </Text>
           {filterId.id && filterId.status !== 'saved' && (
-            <Text>(modified)&nbsp;</Text>
+            <Text>
+              <Trans>(modified)</Trans>&nbsp;
+            </Text>
           )}
           <SvgExpandArrow width={8} height={8} style={{ marginRight: 5 }} />
         </Button>
diff --git a/upcoming-release-notes/3270.md b/upcoming-release-notes/3270.md
new file mode 100644
index 000000000..85a2c1b25
--- /dev/null
+++ b/upcoming-release-notes/3270.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [psybers]
+---
+
+Support translations in desktop-client/components/filters.
-- 
GitLab