🌍 add translations for desktop-client/components/accounts (#3277)

* add translations for `desktop-client/components/accounts`

* release note

* fix lint

* fix quotes

* feedback

* Update 3277.md
This commit is contained in:
Matt Fiddaman
2024-09-03 19:20:55 +01:00
committed by GitHub
parent 44375e72ad
commit 1aa65946c2
6 changed files with 134 additions and 71 deletions

View File

@@ -1,8 +1,10 @@
import React, { PureComponent, createRef, useMemo } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { Navigate, useParams, useLocation } from 'react-router-dom';
import { debounce } from 'debounce';
import { t } from 'i18next';
import { v4 as uuidv4 } from 'uuid';
import { validForTransfer } from 'loot-core/client/transfer';
@@ -70,9 +72,11 @@ function EmptyMessage({ onAdd }) {
}}
>
<Text style={{ textAlign: 'center', lineHeight: '1.4em' }}>
For Actual to be useful, you need to <strong>add an account</strong>.
You can link an account to automatically download transactions, or
manage it locally yourself.
<Trans>
For Actual to be useful, you need to <strong>add an account</strong>
. You can link an account to automatically download transactions, or
manage it locally yourself.
</Trans>
</Text>
<Button
@@ -81,13 +85,13 @@ function EmptyMessage({ onAdd }) {
autoFocus
onPress={onAdd}
>
Add account
<Trans>Add account</Trans>
</Button>
<View
style={{ marginTop: 20, fontSize: 13, color: theme.tableTextLight }}
>
In the future, you can add accounts from the sidebar.
<Trans>In the future, you can add accounts from the sidebar.</Trans>
</View>
</View>
</View>
@@ -456,6 +460,8 @@ class AccountInternal extends PureComponent {
};
onImport = async () => {
const { t } = useTranslation();
const accountId = this.props.accountId;
const account = this.props.accounts.find(acct => acct.id === accountId);
const categories = await this.props.getCategories();
@@ -464,7 +470,7 @@ class AccountInternal extends PureComponent {
const res = await window.Actual?.openFileDialog({
filters: [
{
name: 'Financial Files',
name: t('Financial Files'),
extensions: ['qif', 'ofx', 'qfx', 'csv', 'tsv', 'xml'],
},
],
@@ -486,6 +492,8 @@ class AccountInternal extends PureComponent {
};
onExport = async accountName => {
const { t } = useTranslation();
const exportedTransactions = await send('transactions-export-query', {
query: this.currentQuery.serialize(),
});
@@ -496,7 +504,7 @@ class AccountInternal extends PureComponent {
window.Actual?.saveFile(
exportedTransactions,
filename,
'Export Transactions',
t('Export Transactions'),
);
};
@@ -676,13 +684,13 @@ class AccountInternal extends PureComponent {
if (!account) {
if (id === 'budgeted') {
return 'Budgeted Accounts';
return t('Budgeted Accounts');
} else if (id === 'offbudget') {
return 'Off Budget Accounts';
return t('Off Budget Accounts');
} else if (id === 'uncategorized') {
return 'Uncategorized';
return t('Uncategorized');
} else if (!id) {
return 'All Accounts';
return t('All Accounts');
}
return null;
}
@@ -793,6 +801,8 @@ class AccountInternal extends PureComponent {
};
onCreateReconciliationTransaction = async diff => {
const { t } = useTranslation();
// Create a new reconciliation transaction
const reconciliationTransactions = realizeTempTransactions([
{
@@ -802,7 +812,7 @@ class AccountInternal extends PureComponent {
reconciled: false,
amount: diff,
date: currentDay(),
notes: 'Reconciliation balance adjustment',
notes: t('Reconciliation balance adjustment'),
},
]);
@@ -819,8 +829,10 @@ class AccountInternal extends PureComponent {
};
onShowTransactions = async ids => {
const { t } = useTranslation();
this.onApplyFilter({
customName: 'Selected transactions',
customName: t('Selected transactions'),
queryFilter: { id: { $oneof: ids } },
});
};

View File

@@ -1,7 +1,10 @@
import React, { useRef, useState } from 'react';
import { Trans } from 'react-i18next';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { t } from 'i18next';
import { authorizeBank } from '../../gocardless';
import { useAccounts } from '../../hooks/useAccounts';
import { useActions } from '../../hooks/useActions';
@@ -17,9 +20,13 @@ function getErrorMessage(type, code) {
case 'ITEM_ERROR':
switch (code.toUpperCase()) {
case 'NO_ACCOUNTS':
return 'No open accounts could be found. Did you close the account? If so, unlink the account.';
return t(
'No open accounts could be found. Did you close the account? If so, unlink the account.',
);
case 'ITEM_LOGIN_REQUIRED':
return 'Your password or something else has changed with your bank and you need to login again.';
return t(
'Your password or something else has changed with your bank and you need to login again.',
);
default:
}
break;
@@ -27,39 +34,41 @@ function getErrorMessage(type, code) {
case 'INVALID_INPUT':
switch (code.toUpperCase()) {
case 'INVALID_ACCESS_TOKEN':
return 'Item is no longer authorized. You need to login again.';
return t('Item is no longer authorized. You need to login again.');
default:
}
break;
case 'RATE_LIMIT_EXCEEDED':
return 'Rate limit exceeded for this item. Please try again later.';
return t('Rate limit exceeded for this item. Please try again later.');
case 'INVALID_ACCESS_TOKEN':
return 'Your SimpleFIN Access Token is no longer valid. Please reset and generate a new token.';
return t(
'Your SimpleFIN Access Token is no longer valid. Please reset and generate a new token.',
);
case 'ACCOUNT_NEEDS_ATTENTION':
return (
<>
<Trans>
The account needs your attention at{' '}
<Link variant="external" to="https://bridge.simplefin.org/auth/login">
SimpleFIN
</Link>
.
</>
</Trans>
);
default:
}
return (
<>
<Trans>
An internal error occurred. Try to login again, or get{' '}
<Link variant="external" to="https://actualbudget.org/contact/">
in touch
</Link>{' '}
for support.
</>
</Trans>
);
}
@@ -116,7 +125,9 @@ export function AccountSyncCheck() {
<SvgExclamationOutline
style={{ width: 14, height: 14, marginRight: 5 }}
/>{' '}
This account is experiencing connection problems. Lets fix it.
<Trans>
This account is experiencing connection problems. Lets fix it.
</Trans>
</Button>
<Popover
@@ -127,7 +138,7 @@ export function AccountSyncCheck() {
style={{ fontSize: 14, padding: 15, maxWidth: 400 }}
>
<div style={{ marginBottom: '1.15em' }}>
The server returned the following error:
<Trans>The server returned the following error:</Trans>
</div>
<div style={{ marginBottom: '1.25em', color: theme.errorText }}>
@@ -137,18 +148,22 @@ export function AccountSyncCheck() {
<View style={{ justifyContent: 'flex-end', flexDirection: 'row' }}>
{showAuth ? (
<>
<Button onPress={unlink}>Unlink</Button>
<Button onPress={unlink}>
<Trans>Unlink</Trans>
</Button>
<Button
variant="primary"
autoFocus
onPress={reauth}
style={{ marginLeft: 5 }}
>
Reauthorize
<Trans>Reauthorize</Trans>
</Button>
</>
) : (
<Button onPress={unlink}>Unlink account</Button>
<Button onPress={unlink}>
<Trans>Unlink account</Trans>
</Button>
)}
</View>
</Popover>

View File

@@ -1,4 +1,5 @@
import React, { useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useHover } from 'usehooks-ts';
@@ -42,6 +43,8 @@ function DetailedBalance({ name, balance, isExactBalance = true }) {
}
function SelectedBalance({ selectedItems, account }) {
const { t } = useTranslation();
const name = `selected-balance-${[...selectedItems].join('-')}`;
const rows = useSheetValue({
@@ -99,7 +102,7 @@ function SelectedBalance({ selectedItems, account }) {
return (
<DetailedBalance
name="Selected balance:"
name={t('Selected balance:')}
balance={balance}
isExactBalance={isExactBalance}
/>
@@ -107,9 +110,11 @@ function SelectedBalance({ selectedItems, account }) {
}
function FilteredBalance({ filteredAmount }) {
const { t } = useTranslation();
return (
<DetailedBalance
name="Filtered balance:"
name={t('Filtered balance:')}
balance={filteredAmount || 0}
isExactBalance={true}
/>
@@ -117,6 +122,8 @@ function FilteredBalance({ filteredAmount }) {
}
function MoreBalances({ balanceQuery }) {
const { t } = useTranslation();
const cleared = useSheetValue({
name: balanceQuery.name + '-cleared',
query: balanceQuery.query.filter({ cleared: true }),
@@ -128,8 +135,8 @@ function MoreBalances({ balanceQuery }) {
return (
<View style={{ flexDirection: 'row' }}>
<DetailedBalance name="Cleared total:" balance={cleared} />
<DetailedBalance name="Uncleared total:" balance={uncleared} />
<DetailedBalance name={t('Cleared total:')} balance={cleared} />
<DetailedBalance name={t('Uncleared total:')} balance={uncleared} />
</View>
);
}

View File

@@ -1,5 +1,6 @@
import React, { useState, useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { Trans, useTranslation } from 'react-i18next';
import { useLocalPref } from '../../hooks/useLocalPref';
import { useSplitsExpanded } from '../../hooks/useSplitsExpanded';
@@ -88,6 +89,8 @@ export function AccountHeader({
onMakeAsSplitTransaction,
onMakeAsNonSplitTransactions,
}) {
const { t } = useTranslation();
const [menuOpen, setMenuOpen] = useState(false);
const searchInput = useRef(null);
const triggerRef = useRef(null);
@@ -231,7 +234,7 @@ export function AccountHeader({
data-testid="account-name"
>
{account && account.closed
? 'Closed: ' + accountName
? t('Closed: {{ accountName }}', { accountName })
: accountName}
</View>
@@ -243,7 +246,7 @@ export function AccountHeader({
)}
<Button
variant="bare"
aria-label="Edit account name"
aria-label={t('Edit account name')}
className="hover-visible"
onPress={() => onExposeName(true)}
>
@@ -262,7 +265,7 @@ export function AccountHeader({
data-testid="account-name"
>
{account && account.closed
? 'Closed: ' + accountName
? t('Closed: {{ accountName }}', { accountName })
: accountName}
</View>
)}
@@ -302,7 +305,7 @@ export function AccountHeader({
}
style={{ marginRight: 4 }}
/>{' '}
{isServerOffline ? 'Bank Sync Offline' : 'Bank Sync'}
{isServerOffline ? t('Bank Sync Offline') : t('Bank Sync')}
</>
) : (
<>
@@ -311,15 +314,15 @@ export function AccountHeader({
height={13}
style={{ marginRight: 4 }}
/>{' '}
Import
<Trans>Import</Trans>
</>
)}
</Button>
)}
{!showEmptyMessage && (
<Button variant="bare" onPress={onAddTransaction}>
<SvgAdd width={10} height={10} style={{ marginRight: 3 }} /> Add
New
<SvgAdd width={10} height={10} style={{ marginRight: 3 }} />
<Trans>Add New</Trans>
</Button>
)}
<View style={{ flexShrink: 0 }}>
@@ -327,7 +330,7 @@ export function AccountHeader({
</View>
<View style={{ flex: 1 }} />
<Search
placeholder="Search"
placeholder={t('Search')}
value={search}
onChange={onSearch}
inputRef={searchInput}
@@ -359,8 +362,8 @@ export function AccountHeader({
variant="bare"
aria-label={
splitsExpanded.state.mode === 'collapse'
? 'Collapse split transactions'
: 'Expand split transactions'
? t('Collapse split transactions')
: t('Expand split transactions')
}
isDisabled={search !== '' || filterConditions.length > 0}
style={{ padding: 6, marginLeft: 10 }}
@@ -369,8 +372,8 @@ export function AccountHeader({
<View
title={
splitsExpanded.state.mode === 'collapse'
? 'Collapse split transactions'
: 'Expand split transactions'
? t('Collapse split transactions')
: t('Expand split transactions')
}
>
{splitsExpanded.state.mode === 'collapse' ? (
@@ -432,9 +435,9 @@ export function AccountHeader({
items={[
isSorted && {
name: 'remove-sorting',
text: 'Remove all sorting',
text: t('Remove all sorting'),
},
{ name: 'export', text: 'Export' },
{ name: 'export', text: t('Export') },
]}
/>
</Popover>
@@ -480,6 +483,8 @@ function AccountMenu({
onReconcile,
onMenuSelect,
}) {
const { t } = useTranslation();
const [tooltip, setTooltip] = useState('default');
const syncServerStatus = useSyncServerStatus();
@@ -501,36 +506,42 @@ function AccountMenu({
items={[
isSorted && {
name: 'remove-sorting',
text: 'Remove all sorting',
text: t('Remove all sorting'),
},
canShowBalances && {
name: 'toggle-balance',
text: (showBalances ? 'Hide' : 'Show') + ' running balance',
text: showBalances
? t('Hide running balance')
: t('Show running balance'),
},
{
name: 'toggle-cleared',
text: (showCleared ? 'Hide' : 'Show') + ' “cleared” checkboxes',
text: showCleared
? t('Hide “cleared” checkboxes')
: t('Show “cleared” checkboxes'),
},
{
name: 'toggle-reconciled',
text: (showReconciled ? 'Hide' : 'Show') + ' reconciled transactions',
text: showReconciled
? t('Hide reconciled transactions')
: t('Show reconciled transactions'),
},
{ name: 'export', text: 'Export' },
{ name: 'reconcile', text: 'Reconcile' },
{ name: 'export', text: t('Export') },
{ name: 'reconcile', text: t('Reconcile') },
account &&
!account.closed &&
(canSync
? {
name: 'unlink',
text: 'Unlink account',
text: t('Unlink account'),
}
: syncServerStatus === 'online' && {
name: 'link',
text: 'Link account',
text: t('Link account'),
}),
account.closed
? { name: 'reopen', text: 'Reopen account' }
: { name: 'close', text: 'Close account' },
? { name: 'reopen', text: t('Reopen account') }
: { name: 'close', text: t('Close account') },
].filter(x => x)}
/>
);

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import { Trans } from 'react-i18next';
import * as queries from 'loot-core/src/client/queries';
import { currencyToInteger } from 'loot-core/src/shared/util';
@@ -59,33 +60,42 @@ export function ReconcilingMessage({
marginRight: 3,
}}
/>
All reconciled!
<Trans>All reconciled!</Trans>
</View>
) : (
<View style={{ color: theme.tableText }}>
<Text style={{ fontStyle: 'italic', textAlign: 'center' }}>
Your cleared balance{' '}
<strong>{format(cleared, 'financial')}</strong> needs{' '}
<strong>
{(targetDiff > 0 ? '+' : '') + format(targetDiff, 'financial')}
</strong>{' '}
to match
<br /> your banks balance of{' '}
<Text style={{ fontWeight: 700 }}>
{format(targetBalance, 'financial')}
</Text>
<Trans>
Your cleared balance{' '}
<strong>
{{ clearedBalance: format(cleared, 'financial') }}
</strong>{' '}
needs{' '}
<strong>
{{
difference:
(targetDiff > 0 ? '+' : '') +
format(targetDiff, 'financial'),
}}
</strong>{' '}
to match
<br /> your bank&apos;s balance of{' '}
<Text style={{ fontWeight: 700 }}>
{{ bankBalance: format(targetBalance, 'financial') }}
</Text>
</Trans>
</Text>
</View>
)}
<View style={{ marginLeft: 15 }}>
<Button variant="primary" onPress={onDone}>
Done Reconciling
<Trans>Done Reconciling</Trans>
</Button>
</View>
{targetDiff !== 0 && (
<View style={{ marginLeft: 15 }}>
<Button onPress={() => onCreateTransaction(targetDiff)}>
Create Reconciliation Transaction
<Trans>Create Reconciliation Transaction</Trans>
</Button>
</View>
)}
@@ -121,8 +131,10 @@ export function ReconcileMenu({ account, onReconcile, onClose }) {
return (
<View style={{ padding: '5px 8px' }}>
<Text>
Enter the current balance of your bank account that you want to
reconcile with:
<Trans>
Enter the current balance of your bank account that you want to
reconcile with:
</Trans>
</Text>
{clearedBalance != null && (
<InitialFocus>
@@ -136,7 +148,7 @@ export function ReconcileMenu({ account, onReconcile, onClose }) {
</InitialFocus>
)}
<Button variant="primary" onPress={onSubmit}>
Reconcile
<Trans>Reconcile</Trans>
</Button>
</View>
);

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [matt-fidd]
---
Support translations in desktop-client/components/accounts.