Marked files for translation (#3430)

This commit is contained in:
Ali Gradina
2024-09-14 22:41:10 +02:00
committed by GitHub
parent 183c4b25a9
commit c5193b6d43
6 changed files with 185 additions and 116 deletions

View File

@@ -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 = (
<Text>
Your browser doesnt 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.
<Trans>
Your browser doesnt 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.
</Trans>
</Text>
);
} else if (
@@ -45,18 +48,20 @@ function RenderSimple({ error }: RenderSimpleProps) {
// SharedArrayBuffer isn't available
msg = (
<Text>
Actual requires access to <code>SharedArrayBuffer</code> in order to
function properly. If youre seeing this error, either your browser does
not support <code>SharedArrayBuffer</code>, or your server is not
sending the appropriate headers, or you are not using HTTPS. See{' '}
<Link
variant="external"
linkColor="muted"
to="https://actualbudget.org/docs/troubleshooting/shared-array-buffer"
>
our troubleshooting documentation
</Link>{' '}
to learn more. <SharedArrayBufferOverride />
<Trans>
Actual requires access to <code>SharedArrayBuffer</code> in order to
function properly. If youre seeing this error, either your browser
does not support <code>SharedArrayBuffer</code>, or your server is not
sending the appropriate headers, or you are not using HTTPS. See{' '}
<Link
variant="external"
linkColor="muted"
to="https://actualbudget.org/docs/troubleshooting/shared-array-buffer"
>
our troubleshooting documentation
</Link>{' '}
to learn more. <SharedArrayBufferOverride />
</Trans>
</Text>
);
} else {
@@ -64,7 +69,11 @@ function RenderSimple({ error }: RenderSimpleProps) {
// user something at least so they aren't looking at a blank
// screen
msg = (
<Text>There was a problem loading the app in this browser version.</Text>
<Text>
<Trans>
There was a problem loading the app in this browser version.
</Trans>
</Text>
);
}
@@ -78,15 +87,17 @@ function RenderSimple({ error }: RenderSimpleProps) {
>
<Text>{msg}</Text>
<Text>
Please get{' '}
<Link
variant="external"
linkColor="muted"
to="https://actualbudget.org/contact"
>
in touch
</Link>{' '}
for support
<Trans>
Please get{' '}
<Link
variant="external"
linkColor="muted"
to="https://actualbudget.org/contact"
>
in touch
</Link>{' '}
for support
</Trans>
</Text>
</Stack>
);
@@ -102,10 +113,12 @@ function RenderLazyLoadError() {
}}
>
<Text>
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.
<Trans>
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.
</Trans>
</Text>
</Stack>
);
@@ -114,13 +127,17 @@ function RenderLazyLoadError() {
function RenderUIError() {
return (
<>
<Paragraph>There was an unrecoverable error in the UI. Sorry!</Paragraph>
<Paragraph>
If this error persists, please get{' '}
<Link variant="external" to="https://actualbudget.org/contact">
in touch
</Link>{' '}
so it can be investigated.
<Trans>There was an unrecoverable error in the UI. Sorry!</Trans>
</Paragraph>
<Paragraph>
<Trans>
If this error persists, please get{' '}
<Link variant="external" to="https://actualbudget.org/contact">
in touch
</Link>{' '}
so it can be investigated.
</Trans>
</Paragraph>
</>
);
@@ -133,11 +150,13 @@ function SharedArrayBufferOverride() {
return expanded ? (
<>
<Paragraph style={{ marginTop: 10 }}>
Actual uses <code>SharedArrayBuffer</code> to allow usage from multiple
tabs at once and to ensure correct behavior when switching files. While
it can run without access to <code>SharedArrayBuffer</code>, you may
encounter data loss or notice multiple budget files being merged with
each other.
<Trans>
Actual uses <code>SharedArrayBuffer</code> to allow usage from
multiple tabs at once and to ensure correct behavior when switching
files. While it can run without access to
<code>SharedArrayBuffer</code>, you may encounter data loss or notice
multiple budget files being merged with each other.
</Trans>
</Paragraph>
<label
style={{ display: 'flex', alignItems: 'center', marginBottom: 10 }}
@@ -146,7 +165,9 @@ function SharedArrayBufferOverride() {
checked={understand}
onChange={() => setUnderstand(!understand)}
/>{' '}
I understand the risks, run Actual in the unsupported fallback mode
<Trans>
I understand the risks, run Actual in the unsupported fallback mode
</Trans>
</label>
<Button
isDisabled={!understand}
@@ -155,7 +176,7 @@ function SharedArrayBufferOverride() {
window.location.reload();
}}
>
Open Actual
<Trans>Open Actual</Trans>
</Button>
</>
) : (
@@ -164,12 +185,14 @@ function SharedArrayBufferOverride() {
onClick={() => setExpanded(true)}
style={{ marginLeft: 5 }}
>
Advanced options
<Trans>Advanced options</Trans>
</Link>
);
}
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 (
<Modal name="fatal-error" isDismissable={false}>
<ModalHeader title={isLazyLoadError ? 'Loading Error' : 'Fatal Error'} />
<ModalHeader
title={isLazyLoadError ? t('Loading Error') : t('Fatal Error')}
/>
<View
style={{
maxWidth: 500,
@@ -192,11 +217,13 @@ export function FatalError({ error }: FatalErrorProps) {
)}
<Paragraph>
<Button onPress={() => window.Actual?.relaunch()}>Restart app</Button>
<Button onPress={() => window.Actual?.relaunch()}>
<Trans>Restart app</Trans>
</Button>
</Paragraph>
<Paragraph isLast={true} style={{ fontSize: 11 }}>
<Link variant="text" onClick={() => setShowError(state => !state)}>
Show Error
<Trans>Show Error</Trans>
</Link>
{showError && (
<Block

View File

@@ -1,5 +1,6 @@
// @ts-strict-ignore
import React, { useState, useEffect, useRef } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { type State } from 'loot-core/src/client/state-types';
@@ -24,6 +25,8 @@ export function LoggedInUser({
style,
color,
}: LoggedInUserProps) {
const { t } = useTranslation();
const userData = useSelector((state: State) => 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...
<Trans>Connecting...</Trans>
</Text>
);
}
@@ -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'),
},
]}
/>

View File

@@ -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() {
<Modal name="gocardless-link" isDismissable={false}>
<ModalHeader title="Account sync" />
<View style={{ maxWidth: 500 }}>
<Paragraph>Please wait...</Paragraph>
<Paragraph>
The window should close automatically. If nothing happened you can
close this window or tab.
<Trans>Please wait...</Trans>
</Paragraph>
<Paragraph>
<Trans>
The window should close automatically. If nothing happened you can
close this window or tab.
</Trans>
</Paragraph>
</View>
</Modal>

View File

@@ -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 (
<View
onClick={() => _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
<Trans>No budget files</Trans>
</Text>
) : (
files.map(file => (
@@ -343,7 +350,7 @@ function BudgetListHeader({
...styles.veryLargeText,
}}
>
Files
<Trans>Files</Trans>
</Text>
{!quickSwitchMode && <RefreshButton onRefresh={onRefresh} />}
</View>
@@ -452,7 +459,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
dispatch(pushModal('import'));
}}
>
Import file
<Trans>Import file</Trans>
</Button>
<Button
@@ -463,7 +470,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
marginLeft: 10,
}}
>
Create new file
<Trans>Create new file</Trans>
</Button>
{isNonProductionEnvironment() && (
@@ -475,7 +482,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {
marginLeft: 10,
}}
>
Create test file
<Trans>Create test file</Trans>
</Button>
)}
</View>

View File

@@ -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 style={{ alignSelf: 'center' }}>
{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!')}
</Error>
);
}
@@ -84,6 +85,8 @@ export function GoCardlessExternalMsgModal({
onSuccess,
onClose,
}: GoCardlessExternalMsgProps) {
const { t } = useTranslation();
const dispatch = useDispatch();
const [waiting, setWaiting] = useState<string | null>(null);
@@ -140,37 +143,45 @@ export function GoCardlessExternalMsgModal({
return (
<View style={{ gap: 10 }}>
<FormField>
<FormLabel title="Choose your country:" htmlFor="country-field" />
<FormLabel
title={t('Choose your country:')}
htmlFor="country-field"
/>
<Autocomplete
strict
highlightFirst
suggestions={COUNTRY_OPTIONS}
onSelect={setCountry}
value={country}
inputProps={{ id: 'country-field', placeholder: '(please select)' }}
inputProps={{
id: 'country-field',
placeholder: t('(please select)'),
}}
/>
</FormField>
{isBankOptionError ? (
<Error>
Failed loading available banks: GoCardless access credentials might
be misconfigured. Please{' '}
<Link
variant="text"
onClick={onGoCardlessInit}
style={{ color: theme.formLabelText, display: 'inline' }}
>
set them up
</Link>{' '}
again.
<Trans>
Failed loading available banks: GoCardless access credentials
might be misconfigured. Please{' '}
<Link
variant="text"
onClick={onGoCardlessInit}
style={{ color: theme.formLabelText, display: 'inline' }}
>
set them up
</Link>{' '}
again.
</Trans>
</Error>
) : (
country &&
(isBankOptionsLoading ? (
'Loading banks...'
t('Loading banks...')
) : (
<FormField>
<FormLabel title="Choose your bank:" htmlFor="bank-field" />
<FormLabel title={t('Choose your bank:')} htmlFor="bank-field" />
<Autocomplete
focused
strict
@@ -180,7 +191,7 @@ export function GoCardlessExternalMsgModal({
value={institutionId}
inputProps={{
id: 'bank-field',
placeholder: '(please select)',
placeholder: t('(please select)'),
}}
/>
</FormField>
@@ -188,18 +199,20 @@ export function GoCardlessExternalMsgModal({
)}
<Warning>
By enabling bank-sync, you will be granting GoCardless (a third party
service) read-only access to your entire accounts transaction
history. This service is not affiliated with Actual in any way. Make
sure youve read and understand GoCardlesss{' '}
<Link
variant="external"
to="https://gocardless.com/privacy/"
linkColor="purple"
>
Privacy Policy
</Link>{' '}
before proceeding.
<Trans>
By enabling bank-sync, you will be granting GoCardless (a third
party service) read-only access to your entire accounts transaction
history. This service is not affiliated with Actual in any way. Make
sure youve read and understand GoCardlesss{' '}
<Link
variant="external"
to="https://gocardless.com/privacy/"
linkColor="purple"
>
Privacy Policy
</Link>{' '}
before proceeding.
</Trans>
</Warning>
<View style={{ flexDirection: 'row', gap: 10, alignItems: 'center' }}>
@@ -215,7 +228,7 @@ export function GoCardlessExternalMsgModal({
onPress={onJump}
isDisabled={!institutionId || !country}
>
Link bank in browser &rarr;
<Trans>Link bank in browser &rarr;</Trans>
</Button>
</View>
</View>
@@ -231,17 +244,19 @@ export function GoCardlessExternalMsgModal({
{({ state: { close } }) => (
<>
<ModalHeader
title="Link Your Bank"
title={t('Link Your Bank')}
rightContent={<ModalCloseButton onPress={close} />}
/>
<View>
<Paragraph style={{ fontSize: 15 }}>
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.
<Trans>
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.
</Trans>
</Paragraph>
{error && renderError(error)}
{error && renderError(error, t)}
{waiting || isConfigurationLoading ? (
<View style={{ alignItems: 'center', marginTop: 15 }}>
@@ -251,11 +266,11 @@ export function GoCardlessExternalMsgModal({
/>
<View style={{ marginTop: 10, color: theme.pageText }}>
{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}
</View>
@@ -265,7 +280,9 @@ export function GoCardlessExternalMsgModal({
onClick={onJump}
style={{ marginTop: 10 }}
>
(Account linking not opening in a new tab? Click here)
<Trans>
(Account linking not opening in a new tab? Click here)
</Trans>
</Link>
)}
</View>
@@ -281,17 +298,19 @@ export function GoCardlessExternalMsgModal({
}}
onPress={onContinue}
>
Success! Click to continue &rarr;
<Trans>Success! Click to continue &rarr;</Trans>
</Button>
) : isConfigured || isGoCardlessSetupComplete ? (
renderLinkButton()
) : (
<>
<Paragraph style={{ color: theme.errorText }}>
GoCardless integration has not yet been configured.
<Trans>
GoCardless integration has not yet been configured.
</Trans>
</Paragraph>
<Button variant="primary" onPress={onGoCardlessInit}>
Configure GoCardless integration
<Trans>Configure GoCardless integration</Trans>
</Button>
</>
)}

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [a-gradina]
---
Support translations for component files