mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-28 10:33:02 -05:00
🌍 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:
@@ -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 } },
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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. Let’s fix it.
|
||||
<Trans>
|
||||
This account is experiencing connection problems. Let’s 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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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 bank’s 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'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>
|
||||
);
|
||||
|
||||
6
upcoming-release-notes/3277.md
Normal file
6
upcoming-release-notes/3277.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [matt-fidd]
|
||||
---
|
||||
|
||||
Support translations in desktop-client/components/accounts.
|
||||
Reference in New Issue
Block a user