Migrate to lingui

Switch to "lingui" for better DX.
Tried to persist existing languages as much as possible.

Removed "vite-plugin-node-polyfills" because it's incompatible with "lingui"
This commit is contained in:
schroda
2026-01-10 19:45:02 +01:00
parent c1ac58f4d6
commit 65a9a905be
287 changed files with 120766 additions and 37748 deletions

View File

@@ -6,7 +6,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
@@ -14,6 +13,7 @@ import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';
import { useLingui } from '@lingui/react/macro';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { useUpdateChecker } from '@/features/app-updates/hooks/useUpdateChecker.tsx';
import { VersionUpdateInfoDialog } from '@/features/app-updates/components/VersionUpdateInfoDialog.tsx';
@@ -24,7 +24,7 @@ import { AppRoutes } from '@/base/AppRoute.constants.ts';
const disabledUpdateCheck = () => Promise.resolve();
export const ServerUpdateChecker = () => {
const { t } = useTranslation();
const { t } = useLingui();
const [serverVersion, setServerVersion] = useLocalStorage<string>('serverVersion');
const [open, setOpen] = useState(false);
@@ -98,11 +98,8 @@ export const ServerUpdateChecker = () => {
return (
<VersionUpdateInfoDialog
info={t('global.update.label.info', {
channel: selectedServerChannelInfo.channel,
version: selectedServerChannelInfo.tag,
})}
actionTitle={t('global.button.download')}
info={t`Server version ${selectedServerChannelInfo.tag} (${selectedServerChannelInfo.channel}) available for download`}
actionTitle={t`Download`}
actionUrl={selectedServerChannelInfo.url}
updateCheckerProps={['server', checkForUpdate, selectedServerChannelInfo?.tag]}
/>
@@ -115,20 +112,14 @@ export const ServerUpdateChecker = () => {
return (
<Dialog open={open}>
<DialogTitle>{t('settings.about.webui.label.updated')}</DialogTitle>
<DialogTitle>{t`Updated version`}</DialogTitle>
<DialogContent>
<DialogContentText>
{t('global.update.label.update_success', {
name: t('settings.server.title.server'),
version,
channel: aboutServer?.buildType,
})}
</DialogContentText>
<DialogContentText>{t`Server was updated to version ${version} (${aboutServer?.buildType})`}</DialogContentText>
</DialogContent>
<DialogActions>
{changelogUrl && (
<Button href={changelogUrl} target="_blank" rel="noreferrer">
{t('global.button.changelog')}
{t`Changelog`}
</Button>
)}
<Button
@@ -138,7 +129,7 @@ export const ServerUpdateChecker = () => {
}}
variant="contained"
>
{t('global.button.ok')}
{t`Ok`}
</Button>
</DialogActions>
</Dialog>

View File

@@ -13,8 +13,8 @@ import Typography from '@mui/material/Typography';
import RefreshIcon from '@mui/icons-material/Refresh';
import DownloadIcon from '@mui/icons-material/Download';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { t as translate } from 'i18next';
import DownloadingIcon from '@mui/icons-material/Downloading';
import { t } from '@lingui/core/macro';
import { UpdateState } from '@/lib/graphql/generated/graphql.ts';
export type BaseVersionInfoProps = {
@@ -69,27 +69,27 @@ const getUpdateCheckButtonText = (
) => {
const isUpdating = updateState === UpdateState.Downloading;
if (isUpdating) {
return translate('global.update.label.updating', { progress });
return t`${progress}% | Updating…`;
}
const didUpdateFail = updateState === UpdateState.Error;
if (didUpdateFail) {
return translate('global.update.label.update_failure');
return t`Update failed`;
}
if (isLoading) {
return translate('global.update.label.checking');
return t`Checking for update`;
}
if (error) {
return translate('global.update.label.check_failure');
return t`Could not check for update`;
}
if (isUpdateAvailable) {
return translate('global.update.label.available');
return t`Update available`;
}
return translate('global.update.label.up_to_date');
return t`This is the latest version`;
};
export const VersionInfo = ({

View File

@@ -15,8 +15,8 @@ import DialogContentText from '@mui/material/DialogContentText';
import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { useTranslation } from 'react-i18next';
import Stack from '@mui/material/Stack';
import { useLingui } from '@lingui/react/macro';
import { useUpdateChecker } from '@/features/app-updates/hooks/useUpdateChecker.tsx';
interface BaseProps {
@@ -48,7 +48,7 @@ export const VersionUpdateInfoDialog = ({
changelogUrl,
disabled,
}: VersionUpdateInfoDialogProps) => {
const { t } = useTranslation();
const { t } = useLingui();
const updateChecker = useUpdateChecker(...updateCheckerProps);
@@ -58,7 +58,7 @@ export const VersionUpdateInfoDialog = ({
return (
<Dialog open>
<DialogTitle>{t('global.update.label.available')}</DialogTitle>
<DialogTitle>{t`Update available`}</DialogTitle>
<DialogContent>
<DialogContentText>{info}</DialogContentText>
</DialogContent>
@@ -72,7 +72,7 @@ export const VersionUpdateInfoDialog = ({
>
{changelogUrl && (
<Button href={changelogUrl} target="_blank" rel="noreferrer">
{t('global.button.changelog')}
{t`Changelog`}
</Button>
)}
<Stack direction="row">
@@ -80,7 +80,7 @@ export const VersionUpdateInfoDialog = ({
{(popupState) => (
<>
<Button disabled={disabled} {...bindTrigger(popupState)}>
{t('global.label.close')}
{t`Close`}
</Button>
<Menu {...bindMenu(popupState)}>
<MenuItem
@@ -89,7 +89,7 @@ export const VersionUpdateInfoDialog = ({
popupState.close();
}}
>
{t('global.button.remind_later')}
{t`Remind later`}
</MenuItem>
<MenuItem
onClick={() => {
@@ -97,7 +97,7 @@ export const VersionUpdateInfoDialog = ({
popupState.close();
}}
>
{t('global.button.ignore')}
{t`Ignore`}
</MenuItem>
</Menu>
</>

View File

@@ -6,7 +6,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useEffect, useState } from 'react';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
@@ -14,6 +13,7 @@ import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';
import { useLingui } from '@lingui/react/macro';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { UpdateState, WebUiChannel, WebUiUpdateStatus } from '@/lib/graphql/generated/graphql.ts';
import { useLocalStorage, useSessionStorage } from '@/base/hooks/useStorage.tsx';
@@ -36,7 +36,7 @@ if (BrowserUtil.isActualPageLoad()) {
}
export const WebUIUpdateChecker = () => {
const { t } = useTranslation();
const { t } = useLingui();
const [webUIVersion, setWebUIVersion] = useLocalStorage<string>('webUIVersion');
const [initialLoadTimestamp] = useSessionStorage<number>(INITIAL_LOAD_TIMESTAMP_KEY, Date.now());
@@ -93,7 +93,7 @@ export const WebUIUpdateChecker = () => {
useEffect(() => {
const isError = webUIUpdateState === UpdateState.Error;
if (isError) {
makeToast(t('settings.about.webui.label.update_failure'), 'error');
makeToast(t`Could not update WebUI`, 'error');
}
const updateFinished = webUIUpdateState === UpdateState.Finished;
@@ -139,24 +139,15 @@ export const WebUIUpdateChecker = () => {
return (
<VersionUpdateInfoDialog
info={t('settings.about.webui.label.info', {
version: webUIUpdateData?.checkForWebUIUpdate.tag,
channel: webUIUpdateData?.checkForWebUIUpdate.channel,
})}
info={t`WebUI version ${webUIUpdateData?.checkForWebUIUpdate.tag} (${webUIUpdateData?.checkForWebUIUpdate.channel}) available for download`}
changelogUrl={changelogUrl}
disabled={isUpdateInProgress}
onAction={() =>
requestManager
.updateWebUI()
.response.catch((e) =>
makeToast(t('settings.about.webui.label.update_failure'), 'error', getErrorMessage(e)),
)
}
actionTitle={
isUpdateInProgress
? t('global.update.label.updating', { progress: updateStatus.progress })
: t('extension.action.label.update')
.response.catch((e) => makeToast(t`Could not update WebUI`, 'error', getErrorMessage(e)))
}
actionTitle={isUpdateInProgress ? t`${updateStatus.progress}% | Updating…` : t`Update`}
updateCheckerProps={[
'webUI',
isAutoUpdateEnabled ? disabledUpdateCheck : checkForUpdate,
@@ -172,19 +163,13 @@ export const WebUIUpdateChecker = () => {
return (
<Dialog open={open} onClose={shouldForceRefresh ? () => setOpen(false) : noOp}>
<DialogTitle>{t('settings.about.webui.label.updated')}</DialogTitle>
<DialogTitle>{t`Updated version`}</DialogTitle>
<DialogContent>
<DialogContentText>
{t('global.update.label.update_success', {
name: t('settings.webui.title.webui'),
version: newVersion,
channel: aboutWebUI?.channel,
})}
</DialogContentText>
<DialogContentText>{t`{name} was updated to version ${newVersion} (${aboutWebUI?.channel})`}</DialogContentText>
</DialogContent>
<DialogActions>
<Button href={changelogUrl} target="_blank" rel="noreferrer">
{t('global.button.changelog')}
{t`Changelog`}
</Button>
<Button
onClick={() => {
@@ -197,7 +182,7 @@ export const WebUIUpdateChecker = () => {
}}
variant="contained"
>
{t(shouldForceRefresh ? 'global.button.refresh' : 'global.button.ok')}
{shouldForceRefresh ? t`Refresh` : t`Ok`}
</Button>
</DialogActions>
</Dialog>