diff --git a/packages/desktop-client/src/components/schedules/EditSchedule.js b/packages/desktop-client/src/components/schedules/EditSchedule.js
index 0e25e3b..60c8277 100644
--- a/packages/desktop-client/src/components/schedules/EditSchedule.js
+++ b/packages/desktop-client/src/components/schedules/EditSchedule.js
@@ -27,6 +27,7 @@ import { usePageType } from '../Page';
import { Page } from '../Page';
import { OpSelect } from '../modals/EditRule';
import { AmountInput, BetweenAmountInput } from '../util/AmountInput';
+import { useTranslation } from 'react-i18next';
function mergeFields(defaults, initial) {
let res = { ...defaults };
@@ -92,6 +93,7 @@ export default function ScheduleDetails() {
});
let pageType = usePageType();
+ const { t } = useTranslation();
let [state, dispatch] = useReducer(
(state, action) => {
@@ -365,8 +367,10 @@ export default function ScheduleDetails() {
if (res.error) {
dispatch({
type: 'form-error',
- error:
- 'An error occurred while saving. Please contact help@actualbudget.com for support.'
+ // Note: email is outside of translation to be easily replace on future
+ error: t('support.anErrorOccuredWhileSaving', {
+ email: 'help@actualbudget.com'
+ })
});
} else {
if (adding) {
@@ -423,15 +427,19 @@ export default function ScheduleDetails() {
return (
-
+
dispatch({ type: 'set-field', field: 'payee', value: id })
}
@@ -439,10 +447,10 @@ export default function ScheduleDetails() {
-
+
dispatch({ type: 'set-field', field: 'account', value: id })
}
@@ -451,18 +459,21 @@ export default function ScheduleDetails() {
-
+
{
switch (op) {
case 'is':
- return 'is exactly';
+ return t('schedules.isExactly');
case 'isapprox':
- return 'is approximately';
+ return t('schedules.isApproximately');
case 'isbetween':
- return 'is between';
+ return t('schedules.isBetween');
default:
throw new Error('Invalid op for select: ' + op);
}
@@ -504,7 +515,7 @@ export default function ScheduleDetails() {
-
+
@@ -529,7 +540,7 @@ export default function ScheduleDetails() {
{state.upcomingDates && (
- Upcoming dates
+ {t('schedules.upcomingDates')}
@@ -592,7 +603,7 @@ export default function ScheduleDetails() {
}}
/>
@@ -606,8 +617,7 @@ export default function ScheduleDetails() {
lineHeight: '1.4em'
}}
>
- If checked, the schedule will automatically create transactions for
- you in the specified account
+ {t('schedules.automaticallyAddTransactionAdvice')}
{!adding && state.schedule.rule && (
@@ -621,11 +631,11 @@ export default function ScheduleDetails() {
width: 350
}}
>
- This schedule has custom conditions and actions
+ {t('schedules.thisScheduleHasCustomConditionsAndActions')}
)}
)}
@@ -637,11 +647,11 @@ export default function ScheduleDetails() {
{adding ? (
- These transactions match this schedule:
+ {t('schedules.theseTransactionsMatchThisSchedule')}
- Select transactions to link on save
+ {t('schedules.selectTransactionsToLinkOnSave')}
) : (
@@ -656,7 +666,7 @@ export default function ScheduleDetails() {
}}
onClick={() => onSwitchTransactions('linked')}
>
- Linked transactions
+ {t('schedules.linkedTransactions')}
{' '}
{
switch (name) {
@@ -715,10 +730,10 @@ export default function ScheduleDetails() {
>
{state.error && {state.error}}
diff --git a/packages/desktop-client/src/components/schedules/SchedulesTable.js b/packages/desktop-client/src/components/schedules/SchedulesTable.js
index fa2a339..77d6cd3 100644
--- a/packages/desktop-client/src/components/schedules/SchedulesTable.js
+++ b/packages/desktop-client/src/components/schedules/SchedulesTable.js
@@ -22,12 +22,15 @@ import DotsHorizontalTriple from 'loot-design/src/svg/v1/DotsHorizontalTriple';
import Check from 'loot-design/src/svg/v2/Check';
import DisplayId from '../util/DisplayId';
import { StatusBadge } from './StatusBadge';
+import { useTranslation } from 'react-i18next';
export let ROW_HEIGHT = 43;
function OverflowMenu({ schedule, status, onAction }) {
let [open, setOpen] = useState(false);
+ const { t } = useTranslation();
+
return (
@@ -80,6 +83,8 @@ export function ScheduleAmountCell({ amount, op }) {
let str = integerToCurrency(Math.abs(num || 0));
let isApprox = op === 'isapprox' || op === 'isbetween';
+ const { t } = useTranslation();
+
return (
~
|
@@ -112,7 +117,9 @@ export function ScheduleAmountCell({ amount, op }) {
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
- title={(isApprox ? 'Approximately ' : '') + str}
+ title={
+ isApprox ? t('general.approximatelyWithAmount', { amount: str }) : str
+ }
>
{num > 0 ? `+${str}` : `${str}`}
@@ -135,6 +142,8 @@ export function SchedulesTable({
let [showCompleted, setShowCompleted] = useState(false);
+ const { t } = useTranslation();
+
let items = useMemo(() => {
if (!allowCompleted) {
return schedules.filter(s => !s.completed);
@@ -219,7 +228,7 @@ export function SchedulesTable({
color: colors.n6
}}
>
- Show completed schedules
+ {t('schedules.showCompletedSchedules')}
);
@@ -230,16 +239,16 @@ export function SchedulesTable({
return (
<>
- Payee
- Account
- Next date
- Status
+ {t('general.payee')}
+ {t('general.account')}
+ {t('schedules.nextDate')}
+ {t('general.status')}
- Amount
+ {t('general.amount')}
{!minimal && (
- Recurring
+ {t('general.recurring')}
)}
{!minimal && }
@@ -251,7 +260,7 @@ export function SchedulesTable({
style={[{ flex: 1, backgroundColor: 'transparent' }, style]}
items={items}
renderItem={renderItem}
- renderEmpty="No schedules"
+ renderEmpty={t('schedules.noSchedules')}
allowPopupsEscape={items.length < 6}
/>
>
diff --git a/packages/desktop-client/src/components/schedules/StatusBadge.js b/packages/desktop-client/src/components/schedules/StatusBadge.js
index 8a2eb5f..244cc97 100644
--- a/packages/desktop-client/src/components/schedules/StatusBadge.js
+++ b/packages/desktop-client/src/components/schedules/StatusBadge.js
@@ -9,54 +9,65 @@ import CalendarIcon from 'loot-design/src/svg/v2/Calendar';
import ValidationCheck from 'loot-design/src/svg/v2/ValidationCheck';
import FavoriteStar from 'loot-design/src/svg/v2/FavoriteStar';
import CheckCircle1 from 'loot-design/src/svg/v2/CheckCircle1';
+import { useTranslation } from 'react-i18next';
export function getStatusProps(status) {
- let color, backgroundColor, Icon;
+ let color, backgroundColor, Icon, title;
+
+ const { t } = useTranslation();
switch (status) {
case 'missed':
color = colors.r1;
backgroundColor = colors.r10;
Icon = EditSkull1;
+ title = t('status.missed');
break;
case 'due':
color = colors.y1;
backgroundColor = colors.y9;
Icon = AlertTriangle;
+ title = t('status.due');
break;
case 'upcoming':
color = colors.p1;
backgroundColor = colors.p10;
Icon = CalendarIcon;
+ title = t('status.upcoming');
break;
case 'paid':
color = colors.g2;
backgroundColor = colors.g10;
Icon = ValidationCheck;
+ title = t('status.paid');
break;
case 'completed':
color = colors.n4;
backgroundColor = colors.n11;
Icon = FavoriteStar;
+ title = t('status.completed');
break;
case 'pending':
color = colors.g4;
backgroundColor = colors.g11;
Icon = CalendarIcon;
+ title = t('status.pending');
break;
case 'scheduled':
color = colors.n1;
backgroundColor = colors.n11;
Icon = CalendarIcon;
+ title = t('status.scheduled');
break;
default:
color = colors.n1;
backgroundColor = colors.n11;
Icon = CheckCircle1;
+ title = status;
break;
}
- return { color, backgroundColor, Icon };
+ return { title, color, backgroundColor, Icon };
}
export function StatusIcon({ status }) {
@@ -66,7 +77,7 @@ export function StatusIcon({ status }) {
}
export function StatusBadge({ status, style }) {
- let { color, backgroundColor, Icon } = getStatusProps(status);
+ let { title, color, backgroundColor, Icon } = getStatusProps(status);
return (
- {titleFirst(status)}
+ {titleFirst(title)}
);
}
diff --git a/packages/desktop-client/src/components/schedules/index.js b/packages/desktop-client/src/components/schedules/index.js
index ede1a8c..13127ed 100644
--- a/packages/desktop-client/src/components/schedules/index.js
+++ b/packages/desktop-client/src/components/schedules/index.js
@@ -5,12 +5,15 @@ import { send } from 'loot-core/src/platform/client/fetch';
import { useSchedules } from 'loot-core/src/client/data-hooks/schedules';
import { Page } from '../Page';
import { SchedulesTable, ROW_HEIGHT } from './SchedulesTable';
+import { useTranslation } from 'react-i18next';
export default function Schedules() {
let history = useHistory();
let scheduleData = useSchedules();
+ const { t } = useTranslation();
+
if (scheduleData == null) {
return null;
}
@@ -50,7 +53,7 @@ export default function Schedules() {
}
return (
-
+
diff --git a/packages/desktop-client/src/locales/en-GB.json b/packages/desktop-client/src/locales/en-GB.json
index fc1b4e5..51ae70d 100644
--- a/packages/desktop-client/src/locales/en-GB.json
+++ b/packages/desktop-client/src/locales/en-GB.json
@@ -8,8 +8,62 @@
"needAccountMessage": "For Actual to be useful, you need to add an account. You can link an account to automatically download transactions, or manage it locally yourself."
},
"bootstrap": {
- "title": "Bootstrap this Actual instance",
"setPassword": "Set a password for this server instance",
+ "title": "Bootstrap this Actual instance",
"tryDemo": "Try Demo"
+ },
+ "general": {
+ "account": "Account",
+ "add": "Add",
+ "amount": "Amount",
+ "approximatelyWithAmount": "Approximately {{amount}}",
+ "cancel": "Cancel",
+ "complete": "Complete",
+ "date": "Date",
+ "delete": "Delete",
+ "payee": "Payee",
+ "recurring": "Recurring",
+ "repeats": "Repeats",
+ "restart": "Restart",
+ "save": "Save",
+ "schedule": "Schedule",
+ "schedules": "Schedules",
+ "status": "Status"
+ },
+ "schedules": {
+ "addNewSchedule": "Add new schedule",
+ "automaticallyAddTransaction": "Automatically add transaction",
+ "automaticallyAddTransactionAdvice": "If checked, the schedule will automatically create transactions for you in the specified account",
+ "editAsRule": "Edit as rule",
+ "findMatchingTransactions": "Find matching transactions",
+ "isApproximately": "is approximately",
+ "isBetween": "is between",
+ "isExactly": "is exactly",
+ "linkedTransactions": "Linked transactions",
+ "linkToSchedule": "Link to schedule",
+ "nextDate": "Next date",
+ "none": "(none)",
+ "noSchedules": "No schedules",
+ "postTransaction": "Post transaction",
+ "scheduleNamed": "Schedule: {{name}}",
+ "selectTransactionsToLinkOnSave": "Select transactions to link on save",
+ "showCompletedSchedules": "Show completed schedules",
+ "skipNextDate": "Skip next date",
+ "theseTransactionsMatchThisSchedule": "These transactions match this schedule:",
+ "thisScheduleHasCustomConditionsAndActions": "This schedule has custom conditions and actions",
+ "unlinkFromSchedule": "Unlink from schedule",
+ "upcomingDates": "Upcoming dates"
+ },
+ "status": {
+ "completed": "completed",
+ "due": "due",
+ "missed": "missed",
+ "paid": "paid",
+ "pending": "pending",
+ "scheduled": "scheduled",
+ "upcoming": "upcoming"
+ },
+ "support": {
+ "anErrorOccuredWhileSaving": "An error occurred while saving. Please contact {{email}} for support."
}
}
diff --git a/packages/desktop-client/src/locales/es-ES.json b/packages/desktop-client/src/locales/es-ES.json
index 64d1208..57b6dea 100644
--- a/packages/desktop-client/src/locales/es-ES.json
+++ b/packages/desktop-client/src/locales/es-ES.json
@@ -11,5 +11,59 @@
"setPassword": "Establecer una contraseña para esta instancia de servidor",
"title": "Bootstrap esta instancia de Actual",
"tryDemo": "Probar Demo"
+ },
+ "general": {
+ "account": "Cuenta",
+ "add": "Agregar",
+ "amount": "Monto",
+ "approximatelyWithAmount": "Aproximadamente {{amount}}",
+ "cancel": "Cancelar",
+ "complete": "Completar",
+ "date": "Fecha",
+ "delete": "Borrar",
+ "payee": "Beneficiario",
+ "recurring": "Periódico",
+ "repeats": "Repetir",
+ "restart": "Reiniciar",
+ "save": "Guardar",
+ "schedule": "Agenda",
+ "schedules": "Agendas",
+ "status": "Estado"
+ },
+ "schedules": {
+ "addNewSchedule": "Agregar nuevo agenda",
+ "automaticallyAddTransaction": "Agregar transacción automáticamente",
+ "automaticallyAddTransactionAdvice": "Si se selecciona, la agenda creará automáticamente una transacción para la cuenta especificada",
+ "editAsRule": "Editar como regla",
+ "findMatchingTransactions": "Encontrar transacciones que coincidan",
+ "isApproximately": "es aproximadamente",
+ "isBetween": "está entre",
+ "isExactly": "es exactamente",
+ "linkedTransactions": "Transacciones vinculadas",
+ "linkToSchedule": "Vincular a agenda",
+ "nextDate": "Próxima fecha",
+ "none": "(ninguno)",
+ "noSchedules": "Sin agendas",
+ "postTransaction": "Publicar transacción",
+ "scheduleNamed": "Agenda: {{name}}",
+ "selectTransactionsToLinkOnSave": "Seleccionar transacciones para vincular al guardar",
+ "showCompletedSchedules": "Mostrar agendas completadas",
+ "skipNextDate": "Saltar próxima fecha",
+ "theseTransactionsMatchThisSchedule": "Éstas transacciones coinciden con la agenda",
+ "thisScheduleHasCustomConditionsAndActions": "Ésta agenda tiene condiciones y acciones personalizadas",
+ "unlinkFromSchedule": "Desvincular de la agenda",
+ "upcomingDates": "Próximas fechas"
+ },
+ "status": {
+ "completed": "completo",
+ "due": "adeudado",
+ "missed": "omitido",
+ "paid": "pago",
+ "pending": "pendiente",
+ "scheduled": "agendado",
+ "upcoming": "próximo"
+ },
+ "support": {
+ "anErrorOccuredWhileSaving": "Ocurrió un error al guardar. Por favor, póngase en contacto con {{email}} para obtener asistencia."
}
}