diff --git a/packages/desktop-client/src/components/FatalError.tsx b/packages/desktop-client/src/components/FatalError.tsx
index 663fd6abb4..682ba97aa2 100644
--- a/packages/desktop-client/src/components/FatalError.tsx
+++ b/packages/desktop-client/src/components/FatalError.tsx
@@ -1,4 +1,5 @@
import React, { useState, type ReactNode } from 'react';
+import { useTranslation, Trans } from 'react-i18next';
import { LazyLoadFailedError } from 'loot-core/src/shared/errors';
@@ -32,10 +33,12 @@ function RenderSimple({ error }: RenderSimpleProps) {
// IndexedDB wasn't able to open the database
msg = (
- Your browser doesn’t support IndexedDB in this environment, a feature
- that Actual requires to run. This might happen if you are in private
- browsing mode. Please try a different browser or turn off private
- browsing.
+
+ Your browser doesn’t support IndexedDB in this environment, a feature
+ that Actual requires to run. This might happen if you are in private
+ browsing mode. Please try a different browser or turn off private
+ browsing.
+
);
} else if (
@@ -45,18 +48,20 @@ function RenderSimple({ error }: RenderSimpleProps) {
// SharedArrayBuffer isn't available
msg = (
- Actual requires access to SharedArrayBuffer in order to
- function properly. If you’re seeing this error, either your browser does
- not support SharedArrayBuffer, or your server is not
- sending the appropriate headers, or you are not using HTTPS. See{' '}
-
- our troubleshooting documentation
- {' '}
- to learn more.
+
+ Actual requires access to SharedArrayBuffer in order to
+ function properly. If you’re seeing this error, either your browser
+ does not support SharedArrayBuffer, or your server is not
+ sending the appropriate headers, or you are not using HTTPS. See{' '}
+
+ our troubleshooting documentation
+ {' '}
+ to learn more.
+
);
} else {
@@ -64,7 +69,11 @@ function RenderSimple({ error }: RenderSimpleProps) {
// user something at least so they aren't looking at a blank
// screen
msg = (
- There was a problem loading the app in this browser version.
+
+
+ There was a problem loading the app in this browser version.
+
+
);
}
@@ -78,15 +87,17 @@ function RenderSimple({ error }: RenderSimpleProps) {
>
{msg}
- Please get{' '}
-
- in touch
- {' '}
- for support
+
+ Please get{' '}
+
+ in touch
+ {' '}
+ for support
+
);
@@ -102,10 +113,12 @@ function RenderLazyLoadError() {
}}
>
- There was a problem loading one of the chunks of the application. Please
- reload the page and try again. If the issue persists - there might be an
- issue with either your internet connection and/or the server where the
- app is hosted.
+
+ There was a problem loading one of the chunks of the application.
+ Please reload the page and try again. If the issue persists - there
+ might be an issue with either your internet connection and/or the
+ server where the app is hosted.
+
);
@@ -114,13 +127,17 @@ function RenderLazyLoadError() {
function RenderUIError() {
return (
<>
- There was an unrecoverable error in the UI. Sorry!
- If this error persists, please get{' '}
-
- in touch
- {' '}
- so it can be investigated.
+ There was an unrecoverable error in the UI. Sorry!
+
+
+
+ If this error persists, please get{' '}
+
+ in touch
+ {' '}
+ so it can be investigated.
+
>
);
@@ -133,11 +150,13 @@ function SharedArrayBufferOverride() {
return expanded ? (
<>
- Actual uses SharedArrayBuffer to allow usage from multiple
- tabs at once and to ensure correct behavior when switching files. While
- it can run without access to SharedArrayBuffer, you may
- encounter data loss or notice multiple budget files being merged with
- each other.
+
+ Actual uses SharedArrayBuffer to allow usage from
+ multiple tabs at once and to ensure correct behavior when switching
+ files. While it can run without access to
+ SharedArrayBuffer, you may encounter data loss or notice
+ multiple budget files being merged with each other.
+
>
) : (
@@ -164,12 +185,14 @@ function SharedArrayBufferOverride() {
onClick={() => setExpanded(true)}
style={{ marginLeft: 5 }}
>
- Advanced options
+ Advanced options
);
}
export function FatalError({ error }: FatalErrorProps) {
+ const { t } = useTranslation();
+
const [showError, setShowError] = useState(false);
const showSimpleRender = 'type' in error && error.type === 'app-init-failure';
@@ -177,7 +200,9 @@ export function FatalError({ error }: FatalErrorProps) {
return (
-
+
-
+
setShowError(state => !state)}>
- Show Error
+ Show Error
{showError && (
state.user.data);
const { getUserData, signOut, closeBudget } = useActions();
const [loading, setLoading] = useState(true);
@@ -64,14 +67,14 @@ export function LoggedInUser({
function serverMessage() {
if (!serverUrl) {
- return 'No server';
+ return t('No server');
}
if (userData?.offline) {
- return 'Server offline';
+ return t('Server offline');
}
- return 'Server online';
+ return t('Server online');
}
if (hideIfNoServer && !serverUrl) {
@@ -88,7 +91,7 @@ export function LoggedInUser({
...style,
}}
>
- Connecting...
+ Connecting...
);
}
@@ -115,12 +118,14 @@ export function LoggedInUser({
serverUrl &&
!userData?.offline && {
name: 'change-password',
- text: 'Change password',
+ text: t('Change password'),
},
- serverUrl && { name: 'sign-out', text: 'Sign out' },
+ serverUrl && { name: 'sign-out', text: t('Sign out') },
{
name: 'config-server',
- text: serverUrl ? 'Change server URL' : 'Start using a server',
+ text: serverUrl
+ ? t('Change server URL')
+ : t('Start using a server'),
},
]}
/>
diff --git a/packages/desktop-client/src/components/gocardless/GoCardlessLink.tsx b/packages/desktop-client/src/components/gocardless/GoCardlessLink.tsx
index a40f31ef01..2a3312242e 100644
--- a/packages/desktop-client/src/components/gocardless/GoCardlessLink.tsx
+++ b/packages/desktop-client/src/components/gocardless/GoCardlessLink.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { Trans } from 'react-i18next';
import { Modal, ModalHeader } from '../common/Modal';
import { Paragraph } from '../common/Paragraph';
@@ -10,10 +11,14 @@ export function GoCardlessLink() {
- Please wait...
- The window should close automatically. If nothing happened you can
- close this window or tab.
+ Please wait...
+
+
+
+ The window should close automatically. If nothing happened you can
+ close this window or tab.
+
diff --git a/packages/desktop-client/src/components/manager/BudgetList.tsx b/packages/desktop-client/src/components/manager/BudgetList.tsx
index d9e7a3fd1a..c12700fa9a 100644
--- a/packages/desktop-client/src/components/manager/BudgetList.tsx
+++ b/packages/desktop-client/src/components/manager/BudgetList.tsx
@@ -1,4 +1,5 @@
import React, { useState, useRef, type CSSProperties } from 'react';
+import { Trans, useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import {
@@ -38,19 +39,19 @@ import { Popover } from '../common/Popover';
import { Text } from '../common/Text';
import { View } from '../common/View';
-function getFileDescription(file: File) {
+function getFileDescription(file: File, t: (key: string) => string) {
if (file.state === 'unknown') {
- return (
+ return t(
'This is a cloud-based file but its state is unknown because you ' +
- 'are offline.'
+ 'are offline.',
);
}
if (file.encryptKeyId) {
if (file.hasKey) {
- return 'This file is encrypted and you have key to access it.';
+ return t('This file is encrypted and you have key to access it.');
}
- return 'This file is encrypted and you do not have the key for it.';
+ return t('This file is encrypted and you do not have the key for it.');
}
return null;
@@ -74,7 +75,9 @@ function FileMenu({
}
}
- const items = [{ name: 'delete', text: 'Delete' }];
+ const { t } = useTranslation();
+
+ const items = [{ name: 'delete', text: t('Delete') }];
const { isNarrowWidth } = useResponsive();
const defaultMenuItemStyle = isNarrowWidth
@@ -124,6 +127,8 @@ function FileMenuButton({ onDelete }: { onDelete: () => void }) {
}
function FileState({ file }: { file: File }) {
+ const { t } = useTranslation();
+
let Icon;
let status;
let color;
@@ -131,21 +136,21 @@ function FileState({ file }: { file: File }) {
switch (file.state) {
case 'unknown':
Icon = SvgCloudUnknown;
- status = 'Network unavailable';
+ status = t('Network unavailable');
color = theme.buttonNormalDisabledText;
break;
case 'remote':
Icon = SvgCloudDownload;
- status = 'Available for download';
+ status = t('Available for download');
break;
case 'local':
case 'broken':
Icon = SvgFileDouble;
- status = 'Local';
+ status = t('Local');
break;
default:
Icon = SvgCloudCheck;
- status = 'Syncing';
+ status = t('Syncing');
break;
}
@@ -182,6 +187,8 @@ function FileItem({
onSelect: (file: File) => void;
onDelete: (file: File) => void;
}) {
+ const { t } = useTranslation();
+
const selecting = useRef(false);
async function _onSelect(file: File) {
@@ -197,7 +204,7 @@ function FileItem({
return (
_onSelect(file)}
- title={getFileDescription(file) || ''}
+ title={getFileDescription(file, t) || ''}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
@@ -277,7 +284,7 @@ function BudgetFiles({
color: theme.pageTextSubdued,
}}
>
- No budget files
+ No budget files
) : (
files.map(file => (
@@ -343,7 +350,7 @@ function BudgetListHeader({
...styles.veryLargeText,
}}
>
- Files
+ Files
{!quickSwitchMode && }
@@ -452,7 +459,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
dispatch(pushModal('import'));
}}
>
- Import file
+ Import file
{isNonProductionEnvironment() && (
@@ -475,7 +482,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
marginLeft: 10,
}}
>
- Create test file
+ Create test file
)}
diff --git a/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx b/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx
index 51b2c4af16..37d4913f22 100644
--- a/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx
+++ b/packages/desktop-client/src/components/modals/GoCardlessExternalMsgModal.tsx
@@ -1,5 +1,6 @@
// @ts-strict-ignore
import React, { useEffect, useState, useRef } from 'react';
+import { Trans, useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { pushModal } from 'loot-core/src/client/actions/modals';
@@ -61,12 +62,12 @@ function useAvailableBanks(country: string) {
};
}
-function renderError(error: 'unknown' | 'timeout') {
+function renderError(error: 'unknown' | 'timeout', t: (key: string) => string) {
return (
{error === 'timeout'
- ? 'Timed out. Please try again.'
- : 'An error occurred while linking your account, sorry!'}
+ ? t('Timed out. Please try again.')
+ : t('An error occurred while linking your account, sorry!')}
);
}
@@ -84,6 +85,8 @@ export function GoCardlessExternalMsgModal({
onSuccess,
onClose,
}: GoCardlessExternalMsgProps) {
+ const { t } = useTranslation();
+
const dispatch = useDispatch();
const [waiting, setWaiting] = useState(null);
@@ -140,37 +143,45 @@ export function GoCardlessExternalMsgModal({
return (
-
+
{isBankOptionError ? (
- Failed loading available banks: GoCardless access credentials might
- be misconfigured. Please{' '}
-
- set them up
- {' '}
- again.
+
+ Failed loading available banks: GoCardless access credentials
+ might be misconfigured. Please{' '}
+
+ set them up
+ {' '}
+ again.
+
) : (
country &&
(isBankOptionsLoading ? (
- 'Loading banks...'
+ t('Loading banks...')
) : (
-
+
@@ -188,18 +199,20 @@ export function GoCardlessExternalMsgModal({
)}
- By enabling bank-sync, you will be granting GoCardless (a third party
- service) read-only access to your entire account’s transaction
- history. This service is not affiliated with Actual in any way. Make
- sure you’ve read and understand GoCardless’s{' '}
-
- Privacy Policy
- {' '}
- before proceeding.
+
+ By enabling bank-sync, you will be granting GoCardless (a third
+ party service) read-only access to your entire account’s transaction
+ history. This service is not affiliated with Actual in any way. Make
+ sure you’ve read and understand GoCardless’s{' '}
+
+ Privacy Policy
+ {' '}
+ before proceeding.
+
@@ -215,7 +228,7 @@ export function GoCardlessExternalMsgModal({
onPress={onJump}
isDisabled={!institutionId || !country}
>
- Link bank in browser →
+ Link bank in browser →
@@ -231,17 +244,19 @@ export function GoCardlessExternalMsgModal({
{({ state: { close } }) => (
<>
}
/>
- To link your bank account, you will be redirected to a new page
- where GoCardless will ask to connect to your bank. GoCardless will
- not be able to withdraw funds from your accounts.
+
+ To link your bank account, you will be redirected to a new page
+ where GoCardless will ask to connect to your bank. GoCardless
+ will not be able to withdraw funds from your accounts.
+
- {error && renderError(error)}
+ {error && renderError(error, t)}
{waiting || isConfigurationLoading ? (
@@ -251,11 +266,11 @@ export function GoCardlessExternalMsgModal({
/>
{isConfigurationLoading
- ? 'Checking GoCardless configuration..'
+ ? t('Checking GoCardless configuration..')
: waiting === 'browser'
- ? 'Waiting on GoCardless...'
+ ? t('Waiting on GoCardless...')
: waiting === 'accounts'
- ? 'Loading accounts...'
+ ? t('Loading accounts...')
: null}
@@ -265,7 +280,9 @@ export function GoCardlessExternalMsgModal({
onClick={onJump}
style={{ marginTop: 10 }}
>
- (Account linking not opening in a new tab? Click here)
+
+ (Account linking not opening in a new tab? Click here)
+
)}
@@ -281,17 +298,19 @@ export function GoCardlessExternalMsgModal({
}}
onPress={onContinue}
>
- Success! Click to continue →
+ Success! Click to continue →
) : isConfigured || isGoCardlessSetupComplete ? (
renderLinkButton()
) : (
<>
- GoCardless integration has not yet been configured.
+
+ GoCardless integration has not yet been configured.
+
>
)}
diff --git a/upcoming-release-notes/3430.md b/upcoming-release-notes/3430.md
new file mode 100644
index 0000000000..9412881953
--- /dev/null
+++ b/upcoming-release-notes/3430.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [a-gradina]
+---
+
+Support translations for component files