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,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { plural } from '@lingui/core/macro';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
@@ -18,18 +19,21 @@ export const ReaderSettingAutoScroll = ({
}: Pick<IReaderSettings, 'autoScroll'> & {
setAutoScroll: (updatedAutoScroll: IReaderSettings['autoScroll'], commit: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<>
<CheckboxInput
label={t('reader.settings.auto_scroll.smooth')}
label={t`Smooth auto scrolling`}
checked={autoScroll.smooth}
onChange={(_, checked) => setAutoScroll({ ...autoScroll, smooth: checked }, true)}
/>
<SliderInput
label={t('reader.settings.auto_scroll.speed')}
value={t('global.time.seconds.value', { count: autoScroll.value })}
label={t`Auto scroll speed`}
value={plural(autoScroll.value, {
one: '# second',
other: '# seconds',
})}
onDefault={() =>
setAutoScroll({ ...autoScroll, value: DEFAULT_READER_SETTINGS.autoScroll.value }, true)
}

View File

@@ -6,7 +6,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import Button from '@mui/material/Button';
import PauseCircleFilledIcon from '@mui/icons-material/PauseCircleFilled';
import PlayCircleFilledIcon from '@mui/icons-material/PlayCircleFilled';
@@ -15,6 +14,8 @@ import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';
import { useRef } from 'react';
import { d } from 'koration';
import { useLingui } from '@lingui/react/macro';
import { plural } from '@lingui/core/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { AUTO_SCROLL_SPEED } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { coerceIn } from '@/lib/HelperFunctions.ts';
@@ -26,7 +27,7 @@ export const ReaderNavBarDesktopAutoScroll = ({
}: Pick<IReaderSettings, 'autoScroll'> & {
setAutoScroll: (newAutoScroll: IReaderSettings['autoScroll'], commit: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const { isActive, toggleActive } = useReaderAutoScrollStore((state) => ({
isActive: state.autoScroll.isActive,
toggleActive: state.autoScroll.toggleActive,
@@ -44,7 +45,7 @@ export const ReaderNavBarDesktopAutoScroll = ({
variant="contained"
startIcon={isActive ? <PauseCircleFilledIcon /> : <PlayCircleFilledIcon />}
>
{t('reader.settings.auto_scroll.title')}
{t`Auto scroll`}
</Button>
<TextField
value={autoScroll.value}
@@ -73,7 +74,10 @@ export const ReaderNavBarDesktopAutoScroll = ({
inputProps: AUTO_SCROLL_SPEED,
endAdornment: (
<InputAdornment position="end">
{t('global.time.seconds.second', { count: autoScroll.value })}
{plural(autoScroll.value, {
one: 'Second',
other: 'Seconds',
})}
</InputAdornment>
),
},

View File

@@ -7,11 +7,10 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
import { CUSTOM_FILTER, DEFAULT_READER_SETTINGS } from '@/features/reader/settings/ReaderSettings.constants.tsx';
export const ReaderSettingBrightness = ({
@@ -24,18 +23,18 @@ export const ReaderSettingBrightness = ({
commit: boolean,
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.brightness')}
label={t`Custom brightness`}
checked={brightness.enabled}
onChange={(_, checked) => updateSetting('brightness', { ...brightness, enabled: checked }, true)}
/>
{brightness.enabled && (
<SliderInput
label={t('reader.settings.custom_filter.brightness')}
label={t`Custom brightness`}
value={brightness.value}
onDefault={() =>
updateSetting(

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
@@ -23,18 +23,18 @@ export const ReaderSettingContrast = ({
commit: boolean,
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.contrast')}
label={t`Custom contrast`}
checked={contrast.enabled}
onChange={(_, checked) => updateSetting('contrast', { ...contrast, enabled: checked }, true)}
/>
{contrast.enabled && (
<SliderInput
label={t('reader.settings.custom_filter.contrast')}
label={t`Custom contrast`}
value={contrast.value}
onDefault={() =>
updateSetting(

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
@@ -17,15 +17,11 @@ export const ReaderSettingGrayscale = ({
}: Pick<IReaderSettings['customFilter'], 'grayscale'> & {
updateSetting: (grayscale: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.grayscale')}
checked={grayscale}
onChange={(_, checked) => updateSetting(checked)}
/>
<CheckboxInput label={t`Grayscale`} checked={grayscale} onChange={(_, checked) => updateSetting(checked)} />
</Stack>
);
};

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
@@ -23,18 +23,18 @@ export const ReaderSettingHue = ({
commit: boolean,
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.hue')}
label={t`Custom hue`}
checked={hue.enabled}
onChange={(_, checked) => updateSetting('hue', { ...hue, enabled: checked }, true)}
/>
{hue.enabled && (
<SliderInput
label={t('reader.settings.custom_filter.hue')}
label={t`Custom hue`}
value={hue.value}
onDefault={() =>
updateSetting('hue', { ...hue, value: DEFAULT_READER_SETTINGS.customFilter.hue.value }, true)

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
@@ -17,15 +17,11 @@ export const ReaderSettingInvert = ({
}: Pick<IReaderSettings['customFilter'], 'invert'> & {
updateSetting: (invert: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.invert')}
checked={invert}
onChange={(_, checked) => updateSetting(checked)}
/>
<CheckboxInput label={t`Invert`} checked={invert} onChange={(_, checked) => updateSetting(checked)} />
</Stack>
);
};

View File

@@ -6,28 +6,29 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { MessageDescriptor } from '@lingui/core';
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
import {
CUSTOM_FILTER,
DEFAULT_READER_SETTINGS,
READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA,
READER_BLEND_MODE_VALUES,
} from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
import { TranslationKey } from '@/base/Base.types.ts';
type RGBAType = Exclude<keyof IReaderSettings['customFilter']['rgba']['value'], 'blendMode'>;
const RGBA_TYPE_TO_TRANSLATION_KEY: Record<RGBAType, TranslationKey> = {
red: 'reader.settings.custom_filter.rgba.red',
green: 'reader.settings.custom_filter.rgba.green',
blue: 'reader.settings.custom_filter.rgba.blue',
alpha: 'reader.settings.custom_filter.rgba.alpha',
const RGBA_TYPE_TO_TRANSLATION: Record<RGBAType, MessageDescriptor> = {
red: msg`Red`,
green: msg`Green`,
blue: msg`Blue`,
alpha: msg`Alpha`,
};
export const ReaderSettingRGBA = ({
@@ -40,12 +41,12 @@ export const ReaderSettingRGBA = ({
commit: boolean,
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.rgba.title')}
label={t`Custom color filters`}
checked={rgba.enabled}
onChange={(_, checked) => updateSetting('rgba', { ...rgba, enabled: checked }, true)}
/>
@@ -59,7 +60,7 @@ export const ReaderSettingRGBA = ({
return (
<SliderInput
key={key}
label={t(RGBA_TYPE_TO_TRANSLATION_KEY[key as RGBAType])}
label={t(RGBA_TYPE_TO_TRANSLATION[key as RGBAType])}
value={value}
onDefault={() =>
updateSetting(
@@ -101,7 +102,7 @@ export const ReaderSettingRGBA = ({
);
})}
<ButtonSelectInput
label={t('reader.settings.custom_filter.rgba.blend_mode.title')}
label={t`Color filter blend mode`}
value={rgba.value.blendMode}
values={READER_BLEND_MODE_VALUES}
setValue={(value) =>

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
@@ -23,18 +23,18 @@ export const ReaderSettingSaturate = ({
commit: boolean,
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.saturate')}
label={t`Custom saturate`}
checked={saturate.enabled}
onChange={(_, checked) => updateSetting('saturate', { ...saturate, enabled: checked }, true)}
/>
{saturate.enabled && (
<SliderInput
label={t('reader.settings.custom_filter.saturate')}
label={t`Custom saturate`}
value={saturate.value}
onDefault={() =>
updateSetting(

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
@@ -17,15 +17,11 @@ export const ReaderSettingSepia = ({
}: Pick<IReaderSettings['customFilter'], 'sepia'> & {
updateSetting: (sepia: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack>
<CheckboxInput
label={t('reader.settings.custom_filter.sepia')}
checked={sepia}
onChange={(_, checked) => updateSetting(checked)}
/>
<CheckboxInput label={t`Sepia`} checked={sepia} onChange={(_, checked) => updateSetting(checked)} />
</Stack>
);
};

View File

@@ -6,14 +6,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { MessageDescriptor } from '@lingui/core';
import { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { d } from 'koration';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings, IReaderSettingsWithDefaultFlag } from '@/features/reader/Reader.types.ts';
import { makeToast } from '@/base/utils/Toast.ts';
import { READING_MODE_VALUE_TO_DISPLAY_DATA } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { TReaderTapZoneContext } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
import { TranslationKey } from '@/base/Base.types.ts';
const HIDE_PREVIEW_TIMEOUT = d(5).seconds.inWholeMilliseconds;
@@ -28,7 +28,7 @@ export const useReaderShowSettingPreviewOnChange = (
shouldShowTapZoneLayoutPreview: IReaderSettings['shouldShowTapZoneLayoutPreview'],
setShowPreview: TReaderTapZoneContext['setShowPreview'],
) => {
const { t } = useTranslation();
const { t } = useLingui();
// show setting previews on change or when open reader
const previousReadingMode = useRef<IReaderSettingsWithDefaultFlag['readingMode']>(undefined);
@@ -44,7 +44,7 @@ export const useReaderShowSettingPreviewOnChange = (
const didReadingModeChange = JSON.stringify(readingMode) !== JSON.stringify(previousReadingMode.current);
const showReadingModePreview = shouldShowReadingModePreview && didReadingModeChange;
if (showReadingModePreview) {
makeToast(t(READING_MODE_VALUE_TO_DISPLAY_DATA[readingMode.value].title as TranslationKey), {
makeToast(t(READING_MODE_VALUE_TO_DISPLAY_DATA[readingMode.value].title as MessageDescriptor), {
autoHideDuration: HIDE_PREVIEW_TIMEOUT,
});
}

View File

@@ -7,20 +7,20 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import Typography from '@mui/material/Typography';
import { useLingui } from '@lingui/react/macro';
import { ReaderSettingsTypeProps } from '@/features/reader/Reader.types.ts';
import { ReaderSettingHotkey } from '@/features/reader/hotkeys/settings/components/ReaderSettingHotkey.tsx';
import { READER_HOTKEYS } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { ResetButton } from '@/base/components/buttons/ResetButton.tsx';
export const ReaderHotkeysSettings = ({ settings, updateSetting, onDefault }: ReaderSettingsTypeProps) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ gap: 2 }}>
<Stack sx={{ alignItems: 'end' }}>
<Typography variant="caption">{t('hotkeys.info.delete')}</Typography>
<Typography variant="caption">{t`Click key to remove binding`}</Typography>
</Stack>
{READER_HOTKEYS.map((hotkey) => (
<ReaderSettingHotkey

View File

@@ -8,18 +8,18 @@
import { Fragment } from 'react';
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { Kbd } from '@/base/components/texts/Kbd.tsx';
export const Hotkey = ({ keys, removeKey }: { keys: string[]; removeKey?: (key: string) => void }) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ flexDirection: 'row', flexWrap: 'wrap', gap: 1 }}>
{keys.map((key, index) => (
<Fragment key={key}>
<CustomTooltip title={t('global.button.delete')} hidden={!removeKey}>
<CustomTooltip title={t`Delete`} hidden={!removeKey}>
<Stack
sx={{
flexDirection: 'row',

View File

@@ -6,37 +6,38 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { MessageDescriptor } from '@lingui/core';
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import AddIcon from '@mui/icons-material/Add';
import Typography from '@mui/material/Typography';
import { bindTrigger, usePopupState } from 'material-ui-popup-state/hooks';
import IconButton from '@mui/material/IconButton';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { ReaderHotkey } from '@/features/reader/Reader.types.ts';
import { DEFAULT_READER_SETTINGS } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import { RecordHotkey } from '@/features/reader/hotkeys/settings/components/RecordHotkey.tsx';
import { Hotkey } from '@/features/reader/hotkeys/settings/components/Hotkey.tsx';
import { ResetButton } from '@/base/components/buttons/ResetButton.tsx';
import { TranslationKey } from '@/base/Base.types.ts';
const READER_HOTKEY_TO_TITLE: Record<ReaderHotkey, TranslationKey> = {
[ReaderHotkey.PREVIOUS_PAGE]: 'reader.settings.hotkey.previous_page',
[ReaderHotkey.NEXT_PAGE]: 'reader.settings.hotkey.next_page',
[ReaderHotkey.SCROLL_BACKWARD]: 'reader.settings.hotkey.scroll_backward',
[ReaderHotkey.SCROLL_FORWARD]: 'reader.settings.hotkey.scroll_forward',
[ReaderHotkey.PREVIOUS_CHAPTER]: 'reader.settings.hotkey.previous_chapter',
[ReaderHotkey.NEXT_CHAPTER]: 'reader.settings.hotkey.next_chapter',
[ReaderHotkey.TOGGLE_MENU]: 'reader.settings.hotkey.menu',
[ReaderHotkey.CYCLE_SCALE_TYPE]: 'reader.settings.hotkey.scale_type',
[ReaderHotkey.STRETCH_IMAGE]: 'reader.settings.hotkey.stretch_image',
[ReaderHotkey.OFFSET_SPREAD_PAGES]: 'reader.settings.hotkey.offset_spread_pages',
[ReaderHotkey.CYCLE_READING_MODE]: 'reader.settings.hotkey.reading_mode',
[ReaderHotkey.CYCLE_READING_DIRECTION]: 'reader.settings.hotkey.reading_direction',
[ReaderHotkey.TOGGLE_AUTO_SCROLL]: 'reader.settings.hotkey.auto_scroll',
[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE]: 'reader.settings.hotkey.auto_scroll_speed_increase',
[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE]: 'reader.settings.hotkey.auto_scroll_speed_decrease',
[ReaderHotkey.EXIT_READER]: 'reader.button.exit',
const READER_HOTKEY_TO_TITLE: Record<ReaderHotkey, MessageDescriptor> = {
[ReaderHotkey.PREVIOUS_PAGE]: msg`Previous page`,
[ReaderHotkey.NEXT_PAGE]: msg`Next page`,
[ReaderHotkey.SCROLL_BACKWARD]: msg`Scroll backward`,
[ReaderHotkey.SCROLL_FORWARD]: msg`Scroll forward`,
[ReaderHotkey.PREVIOUS_CHAPTER]: msg`Previous chapter`,
[ReaderHotkey.NEXT_CHAPTER]: msg`Next chapter`,
[ReaderHotkey.TOGGLE_MENU]: msg`Toggle menu`,
[ReaderHotkey.CYCLE_SCALE_TYPE]: msg`Cycle image scale type`,
[ReaderHotkey.STRETCH_IMAGE]: msg`Toggle stretch image`,
[ReaderHotkey.OFFSET_SPREAD_PAGES]: msg`Toggle offset spread pages`,
[ReaderHotkey.CYCLE_READING_MODE]: msg`Cycle reading mode`,
[ReaderHotkey.CYCLE_READING_DIRECTION]: msg`Cycle reading direction`,
[ReaderHotkey.TOGGLE_AUTO_SCROLL]: msg`Toggle auto scroll`,
[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE]: msg`Increase auto scroll speed`,
[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE]: msg`Decrease auto scroll speed`,
[ReaderHotkey.EXIT_READER]: msg`Exit reader`,
};
export const ReaderSettingHotkey = ({
@@ -50,7 +51,7 @@ export const ReaderSettingHotkey = ({
existingKeys: string[];
updateSetting: (keys: string[]) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const popupState = usePopupState({ popupId: 'reader-setting-record-hotkey', variant: 'dialog' });
return (
@@ -61,7 +62,7 @@ export const ReaderSettingHotkey = ({
keys={keys}
removeKey={(keyToRemove) => updateSetting(keys.filter((key) => key !== keyToRemove))}
/>
<CustomTooltip title={t('global.button.add')}>
<CustomTooltip title={t`Add`}>
<IconButton {...bindTrigger(popupState)} color="inherit">
<AddIcon />
</IconButton>

View File

@@ -8,7 +8,6 @@
import { useEffect } from 'react';
import Stack from '@mui/material/Stack';
import { Trans, useTranslation } from 'react-i18next';
import { useRecordHotkeys } from 'react-hotkeys-hook';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
@@ -16,6 +15,7 @@ import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import { Trans, useLingui } from '@lingui/react/macro';
import { Hotkey } from '@/features/reader/hotkeys/settings/components/Hotkey.tsx';
export const RecordHotkey = ({
@@ -27,7 +27,7 @@ export const RecordHotkey = ({
onCreate: (keys: string[]) => void;
existingKeys: string[];
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const [recordedKeys, { start, stop, resetKeys }] = useRecordHotkeys();
const keys = [[...recordedKeys].join('+')];
@@ -45,23 +45,15 @@ export const RecordHotkey = ({
return (
<Dialog open onClose={onClose} fullWidth>
<DialogTitle>{t('hotkeys.create.dialog.title')}</DialogTitle>
<DialogTitle>{t`Record keybind`}</DialogTitle>
<DialogContent>
<Stack sx={{ flexDirection: 'row', gap: 1 }}>
<Trans
i18nKey="hotkeys.create.dialog.label"
components={{
Keys: recordedKeys.size ? (
<Hotkey keys={keys} />
) : (
<Typography>{t('hotkeys.create.dialog.placeholder')}</Typography>
),
}}
>
Recorded keys:
<Trans>
Recorded hotkeys:{' '}
{recordedKeys.size ? <Hotkey keys={keys} /> : <Typography>{t`Press keys`}</Typography>}
</Trans>
</Stack>
{isExistingKey && <Typography color="error">{t('hotkeys.create.error.exists')}</Typography>}
{isExistingKey && <Typography color="error">{t`Hotkey already exists`}</Typography>}
</DialogContent>
<DialogActions>
<Stack
@@ -71,9 +63,9 @@ export const RecordHotkey = ({
width: '100%',
}}
>
<Button onClick={resetKeys}>{t('global.button.reset')}</Button>
<Button onClick={resetKeys}>{t`Reset`}</Button>
<Stack direction="row">
<Button onClick={onClose}>{t('global.button.cancel')}</Button>
<Button onClick={onClose}>{t`Cancel`}</Button>
<Button
disabled={isExistingKey}
onClick={() => {
@@ -81,7 +73,7 @@ export const RecordHotkey = ({
onCreate(keys);
}}
>
{t('global.button.create')}
{t`Create`}
</Button>
</Stack>
</Stack>

View File

@@ -11,13 +11,13 @@ import Stack from '@mui/material/Stack';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { bindMenu, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks';
import MenuItem from '@mui/material/MenuItem';
import { useTranslation } from 'react-i18next';
import Menu from '@mui/material/Menu';
import Link from '@mui/material/Link';
import { Link as RouterLink } from 'react-router-dom';
import { alpha } from '@mui/material/styles';
import Slide from '@mui/material/Slide';
import { memo, Ref } from 'react';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { TypographyMaxLines } from '@/base/components/texts/TypographyMaxLines.tsx';
import { makeToast } from '@/base/utils/Toast.ts';
@@ -39,7 +39,7 @@ import {
const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' };
const BaseReaderOverlayHeaderMobile = ({ isVisible, ref }: MobileHeaderProps & { ref?: Ref<HTMLDivElement> }) => {
const { t } = useTranslation();
const { t } = useLingui();
const popupState = usePopupState({ popupId: 'reader-overlay-more-menu', variant: 'popover' });
const currentChapter = useReaderChaptersStore((state) => state.chapters.currentChapter);
@@ -102,7 +102,7 @@ const BaseReaderOverlayHeaderMobile = ({ isVisible, ref }: MobileHeaderProps & {
rel="noreferrer"
target="_blank"
>
{t('global.button.open_browser')}
{t`Open in browser`}
</MenuItem>
<MenuItem
component={Link}
@@ -111,16 +111,16 @@ const BaseReaderOverlayHeaderMobile = ({ isVisible, ref }: MobileHeaderProps & {
rel="noreferrer"
target="_blank"
>
{t('global.button.open_webview')}
{t`Open in WebView`}
</MenuItem>
<MenuItem
disabled={!realUrl}
onClick={async () => {
await navigator.clipboard.writeText(title);
makeToast(t('global.label.copied_clipboard'), 'info');
makeToast(t`Copied to clipboard`, 'info');
}}
>
{t('global.label.share')}
{t`Share`}
</MenuItem>
</Menu>
</Stack>

View File

@@ -7,17 +7,17 @@
*/
import IconButton from '@mui/material/IconButton';
import { useTranslation } from 'react-i18next';
import { memo } from 'react';
import BookmarkIcon from '@mui/icons-material/Bookmark';
import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { Chapters } from '@/features/chapter/services/Chapters.ts';
import { ChapterAction, TChapterReader } from '@/features/chapter/Chapter.types.ts';
import { CHAPTER_ACTION_TO_TRANSLATION } from '@/features/chapter/Chapter.constants.ts';
const BaseReaderBookmarkButton = ({ id, isBookmarked }: Pick<TChapterReader, 'id' | 'isBookmarked'>) => {
const { t } = useTranslation();
const { t } = useLingui();
const bookmarkAction: Extract<ChapterAction, 'unbookmark' | 'bookmark'> = isBookmarked ? 'unbookmark' : 'bookmark';

View File

@@ -7,21 +7,21 @@
*/
import IconButton from '@mui/material/IconButton';
import { useTranslation } from 'react-i18next';
import ArrowBack from '@mui/icons-material/ArrowBack';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import { memo } from 'react';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { useGetOptionForDirection } from '@/features/theme/services/ThemeCreator.ts';
import { withPropsFrom } from '@/base/hoc/withPropsFrom.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
const BaseReaderExitButton = ({ exit }: { exit: ReturnType<typeof ReaderService.useExit> }) => {
const { t } = useTranslation();
const { t } = useLingui();
const getOptionForDirection = useGetOptionForDirection();
return (
<CustomTooltip title={t('reader.button.exit')}>
<CustomTooltip title={t`Exit reader`}>
<IconButton sx={{ marginRight: 2 }} onClick={exit} color="inherit">
{getOptionForDirection(<ArrowBack />, <ArrowForwardIcon />)}
</IconButton>

View File

@@ -7,10 +7,10 @@
*/
import IconButton from '@mui/material/IconButton';
import { useTranslation } from 'react-i18next';
import FavoriteIcon from '@mui/icons-material/Favorite';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import { memo } from 'react';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { useManageMangaLibraryState } from '@/features/manga/hooks/useManageMangaLibraryState.tsx';
import { FALLBACK_MANGA } from '@/features/manga/Manga.constants.ts';
@@ -27,13 +27,11 @@ export const ReaderLibraryButton = memo(() => {
const { inLibrary } = manga ?? ACTION_FALLBACK_MANGA;
const { t } = useTranslation();
const { t } = useLingui();
const { updateLibraryState } = useManageMangaLibraryState(manga ?? ACTION_FALLBACK_MANGA, true);
return (
<CustomTooltip
title={inLibrary ? t('manga.action.library.remove.label.action') : t('manga.button.add_to_library')}
>
<CustomTooltip title={inLibrary ? t`Remove from the library` : t`Add To Library`}>
<IconButton onClick={updateLibraryState} color="inherit">
{inLibrary ? <FavoriteIcon /> : <FavoriteBorderIcon />}
</IconButton>

View File

@@ -7,13 +7,13 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import IconButton from '@mui/material/IconButton';
import PushPinIcon from '@mui/icons-material/PushPin';
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
import Divider from '@mui/material/Divider';
import { memo, useCallback, useLayoutEffect, useRef, useState } from 'react';
import Drawer from '@mui/material/Drawer';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { ReaderNavBarDesktopProps } from '@/features/reader/overlay/ReaderOverlay.types.ts';
import { ReaderNavContainer } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavContainer.tsx';
@@ -58,7 +58,7 @@ const BaseReaderNavBarDesktop = ({
openSettings,
setReaderNavBarWidth,
}: ReaderNavBarDesktopProps & Pick<NavbarContextType, 'setReaderNavBarWidth'>) => {
const { t } = useTranslation();
const { t } = useLingui();
const manga = useReaderStore((state) => state.manga);
const {
chapters,
@@ -112,7 +112,7 @@ const BaseReaderNavBarDesktop = ({
<Stack sx={{ p: 2, gap: 2, backgroundColor: 'action.hover' }}>
<Stack sx={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
<ReaderExitButton />
<CustomTooltip title={t('reader.settings.label.static_navigation')}>
<CustomTooltip title={t`Static navigation`}>
<IconButton
onClick={() => {
setReaderNavBarWidth(0);

View File

@@ -7,12 +7,12 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import IconButton from '@mui/material/IconButton';
import DownloadIcon from '@mui/icons-material/Download';
import ReplayIcon from '@mui/icons-material/Replay';
import { memo, useMemo, useRef } from 'react';
import DeleteIcon from '@mui/icons-material/Delete';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { Chapters } from '@/features/chapter/services/Chapters.ts';
import { DownloadStateIndicator } from '@/base/components/downloads/DownloadStateIndicator.tsx';
@@ -26,7 +26,7 @@ import { useReaderChaptersStore, useReaderPagesStore } from '@/features/reader/s
import { ChapterDownloadInfo, ChapterIdInfo } from '@/features/chapter/Chapter.types.ts';
const DownloadButton = ({ id = -1, isDownloaded }: ChapterIdInfo & ChapterDownloadInfo) => {
const { t } = useTranslation();
const { t } = useLingui();
const downloadStatus = Chapters.useDownloadStatusFromCache(id);
@@ -67,7 +67,7 @@ export const ReaderNavBarDesktopActions = memo(() => {
realUrl: state.chapters.currentChapter?.realUrl ?? FALLBACK_CHAPTER.realUrl,
}));
const { t } = useTranslation();
const { t } = useLingui();
const { pageLoadStates, setPageLoadStates, setRetryFailedPagesKeyPrefix } = useReaderPagesStore((state) => ({
pageLoadStates: state.pages.pageLoadStates,
setPageLoadStates: state.pages.setPageLoadStates,
@@ -85,7 +85,7 @@ export const ReaderNavBarDesktopActions = memo(() => {
<Stack sx={{ flexDirection: 'row', justifyContent: 'center', gap: 1 }}>
<ReaderLibraryButton />
<ReaderBookmarkButton id={id} isBookmarked={isBookmarked} />
<CustomTooltip title={t('reader.button.retry_load_pages')} disabled={!haveSomePagesFailedToLoad}>
<CustomTooltip title={t`Retry errored pages`} disabled={!haveSomePagesFailedToLoad}>
<IconButton
onClick={() => {
setPageLoadStates((statePageLoadStates) =>
@@ -104,12 +104,12 @@ export const ReaderNavBarDesktopActions = memo(() => {
</IconButton>
</CustomTooltip>
<DownloadButton id={id} isDownloaded={isDownloaded} />
<CustomTooltip title={t('global.button.open_browser')} disabled={!realUrl}>
<CustomTooltip title={t`Open in browser`} disabled={!realUrl}>
<IconButton disabled={!realUrl} href={realUrl ?? ''} rel="noreferrer" target="_blank" color="inherit">
<IconBrowser />
</IconButton>
</CustomTooltip>
<CustomTooltip title={t('global.button.open_webview')} disabled={!realUrl}>
<CustomTooltip title={t`Open in WebView`} disabled={!realUrl}>
<IconButton
disabled={!realUrl}
href={realUrl ? requestManager.getWebviewUrl(realUrl) : ''}

View File

@@ -7,7 +7,6 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import Box from '@mui/material/Box';
import MenuItem from '@mui/material/MenuItem';
import { memo, useLayoutEffect } from 'react';
@@ -16,6 +15,7 @@ import { bindPopover, bindTrigger, usePopupState } from 'material-ui-popup-state
import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel';
import { Link } from 'react-router-dom';
import { useLingui } from '@lingui/react/macro';
import { Select } from '@/base/components/inputs/Select.tsx';
import { ReaderChapterList } from '@/features/reader/overlay/navigation/components/ReaderChapterList.tsx';
import { ReaderNavBarDesktopNextPreviousButton } from '@/features/reader/overlay/navigation/desktop/components/ReaderNavBarDesktopNextPreviousButton.tsx';
@@ -41,7 +41,7 @@ const BaseReaderNavBarDesktopChapterNavigation = ({
} & Pick<ReaderStateChapters, 'chapters' | 'previousChapter' | 'nextChapter'> & {
readerThemeDirection: ReturnType<typeof ReaderService.useGetThemeDirection>;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const popupState = usePopupState({ variant: 'popover', popupId: 'reader-nav-bar-desktop-chapter-list' });
@@ -53,27 +53,21 @@ const BaseReaderNavBarDesktopChapterNavigation = ({
<Stack sx={{ flexDirection: 'row', gap: 1 }} dir="ltr">
<ReaderNavBarDesktopNextPreviousButton
type="previous"
title={t(
getOptionForDirection(
'reader.button.previous_chapter',
'reader.button.next_chapter',
readerThemeDirection,
),
)}
title={getOptionForDirection(t`Previous chapter`, t`Next chapter`, readerThemeDirection)}
onClick={() => {
ReaderControls.openChapter(getOptionForDirection('previous', 'next', readerThemeDirection));
}}
disabled={getOptionForDirection(!previousChapter, !nextChapter, readerThemeDirection)}
/>
<FormControl sx={{ flexBasis: '70%', flexGrow: 0, flexShrink: 0 }}>
<InputLabel id="reader-nav-bar-desktop-chapter-select">{t('chapter.title_one')}</InputLabel>
<InputLabel id="reader-nav-bar-desktop-chapter-select">{t`Chapter`}</InputLabel>
<Select
{...bindTrigger(popupState)}
open={popupState.isOpen}
value={currentChapterId ?? 0}
// hide actual select menu
MenuProps={{ sx: { visibility: 'hidden' } }}
label={t('chapter.title_one')}
label={t`Chapter`}
labelId="reader-nav-bar-desktop-chapter-select"
>
{/* hacky way to use the select component with a custom menu, the only possible value that is needed is the current chapter */}
@@ -85,13 +79,7 @@ const BaseReaderNavBarDesktopChapterNavigation = ({
<ReaderNavBarDesktopNextPreviousButton
component={Link}
type="next"
title={t(
getOptionForDirection(
'reader.button.next_chapter',
'reader.button.previous_chapter',
readerThemeDirection,
),
)}
title={getOptionForDirection(t`Next chapter`, t`Previous chapter`, readerThemeDirection)}
onClick={() => {
ReaderControls.openChapter(getOptionForDirection('next', 'previous', readerThemeDirection));
}}

View File

@@ -7,11 +7,11 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import MenuItem from '@mui/material/MenuItem';
import { memo, useMemo } from 'react';
import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel';
import { useLingui } from '@lingui/react/macro';
import { Select } from '@/base/components/inputs/Select.tsx';
import { getNextIndexFromPage, getPage } from '@/features/reader/overlay/progress-bar/ReaderProgressBar.utils.tsx';
import { useGetOptionForDirection } from '@/features/theme/services/ThemeCreator.ts';
@@ -21,7 +21,7 @@ import { useReaderPagesStore, useReaderSettingsStore } from '@/features/reader/s
import { ReaderControls } from '@/features/reader/services/ReaderControls.ts';
const BaseReaderNavBarDesktopPageNavigation = () => {
const { t } = useTranslation();
const { t } = useLingui();
const getOptionForDirection = useGetOptionForDirection();
const { currentPageIndex, pages } = useReaderPagesStore((state) => ({
currentPageIndex: state.pages.currentPageIndex,
@@ -36,7 +36,7 @@ const BaseReaderNavBarDesktopPageNavigation = () => {
<Stack sx={{ flexDirection: 'row', gap: 1 }} dir="ltr">
<ReaderNavBarDesktopNextPreviousButton
type="previous"
title={t(getOptionForDirection('reader.button.previous_page', 'reader.button.next_page', direction))}
title={getOptionForDirection(t`Previous page`, t`Next page`, direction)}
disabled={getOptionForDirection(
!currentPage.primary.index,
getNextIndexFromPage(currentPage) === getNextIndexFromPage(pages.slice(-1)[0]),
@@ -45,10 +45,10 @@ const BaseReaderNavBarDesktopPageNavigation = () => {
onClick={() => ReaderControls.openPage('previous', undefined, false)}
/>
<FormControl sx={{ flexBasis: '70%', flexGrow: 0, flexShrink: 0 }}>
<InputLabel id="reader-nav-bar-desktop-page-select">{t('reader.page_info.label.page')}</InputLabel>
<InputLabel id="reader-nav-bar-desktop-page-select">{t`Page`}</InputLabel>
<Select
labelId="reader-nav-bar-desktop-page-select"
label={t('reader.page_info.label.page')}
label={t`Page`}
value={getNextIndexFromPage(currentPage)}
onChange={(e) => ReaderControls.openPage(e.target.value as number, undefined, false)}
>
@@ -61,7 +61,7 @@ const BaseReaderNavBarDesktopPageNavigation = () => {
</FormControl>
<ReaderNavBarDesktopNextPreviousButton
type="next"
title={t(getOptionForDirection('reader.button.next_page', 'reader.button.previous_page', direction))}
title={getOptionForDirection(t`Next page`, t`Previous page`, direction)}
disabled={getOptionForDirection(
getNextIndexFromPage(currentPage) === getNextIndexFromPage(pages.slice(-1)[0]),
!currentPage.primary.index,

View File

@@ -7,10 +7,10 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import Button from '@mui/material/Button';
import SettingsIcon from '@mui/icons-material/Settings';
import { memo } from 'react';
import { useLingui } from '@lingui/react/macro';
import { ReaderNavBarDesktopPageScale } from '@/features/reader/overlay/navigation/desktop/quick-settings/components/ReaderNavBarDesktopPageScale.tsx';
import { ReaderNavBarDesktopReadingMode } from '@/features/reader/overlay/navigation/desktop/quick-settings/components/ReaderNavBarDesktopReadingMode.tsx';
import { ReaderNavBarDesktopOffsetDoubleSpread } from '@/features/reader/overlay/navigation/desktop/quick-settings/components/ReaderNavBarDesktopOffsetDoubleSpread.tsx';
@@ -21,7 +21,7 @@ import { ReaderNavBarDesktopAutoScroll } from '@/features/reader/auto-scroll/set
import { useReaderSettingsStore } from '@/features/reader/stores/ReaderStore.ts';
const BaseReaderNavBarDesktopQuickSettings = ({ openSettings }: Pick<ReaderNavBarDesktopProps, 'openSettings'>) => {
const { t } = useTranslation();
const { t } = useLingui();
const { readingMode, shouldOffsetDoubleSpreads, pageScaleMode, shouldStretchPage, readingDirection, autoScroll } =
useReaderSettingsStore((state) => ({
readingMode: state.settings.readingMode,
@@ -71,7 +71,7 @@ const BaseReaderNavBarDesktopQuickSettings = ({ openSettings }: Pick<ReaderNavBa
variant="contained"
startIcon={<SettingsIcon />}
>
{t('settings.title')}
{t`Settings`}
</Button>
</Stack>
);

View File

@@ -6,8 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import Button from '@mui/material/Button';
import { useLingui } from '@lingui/react/macro';
import { OffsetDoubleSpreadIcon } from '@/assets/icons/svg/OffsetDoubleSpreadIcon.tsx';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { isOffsetDoubleSpreadPagesEditable } from '@/features/reader/settings/ReaderSettings.utils.tsx';
@@ -19,7 +19,7 @@ export const ReaderNavBarDesktopOffsetDoubleSpread = ({
}: Pick<IReaderSettings, 'readingMode' | 'shouldOffsetDoubleSpreads'> & {
setShouldOffsetDoubleSpreads: (offset: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
if (!isOffsetDoubleSpreadPagesEditable(readingMode)) {
return null;
@@ -34,7 +34,7 @@ export const ReaderNavBarDesktopOffsetDoubleSpread = ({
variant="contained"
startIcon={<OffsetDoubleSpreadIcon />}
>
{t('reader.settings.label.offset_double_spread')}
{t`Offset double spreads`}
</Button>
);
};

View File

@@ -7,8 +7,8 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import FitScreenIcon from '@mui/icons-material/FitScreen';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { ValueRotationButton } from '@/base/components/buttons/ValueRotationButton.tsx';
import {
@@ -36,13 +36,13 @@ export const ReaderNavBarDesktopPageScale = ({
value: IReaderSettings[Setting],
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ flexDirection: 'row', gap: 1 }}>
<ValueRotationButton
{...buttonSelectInputProps}
tooltip={t('reader.settings.page_scale.title')}
tooltip={t`Scale type`}
value={pageScaleMode.isDefault ? undefined : pageScaleMode.value}
defaultValue={pageScaleMode.isDefault ? pageScaleMode.value : undefined}
values={READER_PAGE_SCALE_MODE_VALUES}
@@ -51,7 +51,7 @@ export const ReaderNavBarDesktopPageScale = ({
defaultIcon={PAGE_SCALE_VALUE_TO_DISPLAY_DATA[pageScaleMode.value].icon}
/>
{READER_PAGE_SCALE_MODE_TO_SCALING_ALLOWED[pageScaleMode.value] && (
<CustomTooltip title={t('reader.settings.page_scale.stretch')}>
<CustomTooltip title={t`Stretch small pages`}>
<CustomButtonIcon
onClick={() => updateSetting('shouldStretchPage', !shouldStretchPage.value)}
sx={{ px: undefined }}

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { ValueRotationButton } from '@/base/components/buttons/ValueRotationButton.tsx';
import { IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/features/reader/Reader.types.ts';
import {
@@ -23,12 +23,12 @@ export const ReaderNavBarDesktopReadingDirection = ({
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
setReadingDirection: (readingDirection: ReadingDirection) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ValueRotationButton
{...buttonSelectInputProps}
tooltip={t('reader.settings.label.reading_direction')}
tooltip={t`Reading direction`}
value={readingDirection.isDefault ? undefined : readingDirection.value}
defaultValue={readingDirection.isDefault ? readingDirection.value : undefined}
values={READING_DIRECTION_VALUES}

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { ValueRotationButton } from '@/base/components/buttons/ValueRotationButton.tsx';
import { IReaderSettingsWithDefaultFlag, ReadingMode } from '@/features/reader/Reader.types.ts';
import {
@@ -23,12 +23,12 @@ export const ReaderNavBarDesktopReadingMode = ({
Pick<MultiValueButtonDefaultableProps<ReadingMode>, 'isDefaultable' | 'onDefault'> & {
setReadingMode: (mode: ReadingMode) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ValueRotationButton
{...buttonSelectInputProps}
tooltip={t('reader.settings.label.reading_mode')}
tooltip={t`Reading mode`}
value={readingMode.isDefault ? undefined : readingMode.value}
defaultValue={readingMode.isDefault ? readingMode.value : undefined}
values={READING_MODE_VALUES}

View File

@@ -15,9 +15,9 @@ import { alpha } from '@mui/material/styles';
import { bindDialog, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks';
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
import { useTranslation } from 'react-i18next';
import Slide from '@mui/material/Slide';
import { memo, useCallback, useLayoutEffect, useRef, useState } from 'react';
import { useLingui } from '@lingui/react/macro';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { ReaderBottomBarMobileProps } from '@/features/reader/overlay/ReaderOverlay.types.ts';
import { MobileReaderProgressBar } from '@/features/reader/overlay/progress-bar/mobile/MobileReaderProgressBar.tsx';
@@ -31,7 +31,7 @@ const BaseReaderBottomBarMobile = ({
isVisible,
topOffset = 0,
}: ReaderBottomBarMobileProps & { topOffset?: number }) => {
const { t } = useTranslation();
const { t } = useLingui();
const { currentChapterId, chapters } = useReaderChaptersStore((state) => ({
currentChapterId: state.chapters.currentChapter?.id,
chapters: state.chapters.chapters,
@@ -85,17 +85,17 @@ const BaseReaderBottomBarMobile = ({
alignItems: 'center',
}}
>
<CustomTooltip title={t('reader.button.chapter_list')}>
<CustomTooltip title={t`Chapter list`}>
<IconButton {...bindTrigger(chapterListPopupState)} color="inherit">
<FormatListBulletedIcon />
</IconButton>
</CustomTooltip>
<CustomTooltip title={t('reader.settings.title.quick_settings')}>
<CustomTooltip title={t`Quick settings`}>
<IconButton {...bindTrigger(quickSettingsPopupState)} color="inherit">
<AppSettingsAltIcon />
</IconButton>
</CustomTooltip>
<CustomTooltip title={t('settings.title')}>
<CustomTooltip title={t`Settings`}>
<IconButton onClick={openSettings} color="inherit">
<SettingsIcon />
</IconButton>

View File

@@ -8,7 +8,7 @@
import Stack from '@mui/material/Stack';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { ReaderSettingReadingMode } from '@/features/reader/settings/layout/components/ReaderSettingReadingMode.tsx';
import { ReaderSettingReadingDirection } from '@/features/reader/settings/layout/components/ReaderSettingReadingDirection.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
@@ -18,7 +18,7 @@ import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { useReaderAutoScrollStore, useReaderSettingsStore } from '@/features/reader/stores/ReaderStore.ts';
const BaseReaderBottomBarMobileQuickSettings = () => {
const { t } = useTranslation();
const { t } = useLingui();
const { isActive, toggleActive } = useReaderAutoScrollStore((state) => ({
isActive: state.autoScroll.isActive,
toggleActive: state.autoScroll.toggleActive,
@@ -44,11 +44,7 @@ const BaseReaderBottomBarMobileQuickSettings = () => {
isDefaultable
onDefault={() => ReaderService.deleteSetting('readingDirection')}
/>
<CheckboxInput
label={t('reader.settings.auto_scroll.title')}
checked={isActive}
onChange={() => toggleActive()}
/>
<CheckboxInput label={t`Auto scroll`} checked={isActive} onChange={() => toggleActive()} />
<ReaderSettingAutoScroll
autoScroll={autoScroll}
setAutoScroll={(...args) => ReaderService.updateSetting('autoScroll', ...args)}

View File

@@ -6,10 +6,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import AutoModeIcon from '@mui/icons-material/AutoMode';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import {
IReaderSettings,
ProgressBarPosition,
@@ -20,19 +21,19 @@ import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.ts
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ProgressBarPosition> = {
[ProgressBarPosition.AUTO]: {
title: 'global.label.auto',
title: msg`Auto`,
icon: <AutoModeIcon />,
},
[ProgressBarPosition.BOTTOM]: {
title: 'global.label.bottom',
title: msg`Bottom`,
icon: <ArrowBackIosNewIcon sx={{ transform: 'rotate(90deg)' }} />,
},
[ProgressBarPosition.LEFT]: {
title: 'global.label.left',
title: msg`Left`,
icon: <ArrowForwardIosIcon />,
},
[ProgressBarPosition.RIGHT]: {
title: 'global.label.right',
title: msg`Right`,
icon: <ArrowBackIosNewIcon />,
},
};
@@ -55,14 +56,14 @@ export const ReaderSettingProgressBarPosition = ({
value: IReaderSettings[Position],
) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const isAutoPosition = progressBarPosition === ProgressBarPosition.AUTO;
return (
<>
<ButtonSelectInput
label={t('reader.settings.progress_bar.position')}
label={t`Progress bar position`}
value={progressBarPosition}
values={PROGRESS_BAR_POSITION_VALUES}
setValue={(position) => updateSetting('progressBarPosition', position)}
@@ -70,7 +71,7 @@ export const ReaderSettingProgressBarPosition = ({
/>
{isAutoPosition && (
<ButtonSelectInput
label={t('reader.settings.progress_bar.auto_vertical_position.title')}
label={t`Automatic progress bar vertical position`}
value={progressBarPositionAutoVertical}
values={PROGRESS_BAR_AUTO_VERTICAL_POSITION_VALUES}
setValue={(position) => updateSetting('progressBarPositionAutoVertical', position)}

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings, ProgressBarType, ReaderOverlayMode } from '@/features/reader/Reader.types.ts';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
import { DEFAULT_READER_SETTINGS, PROGRESS_BAR_SIZE } from '@/features/reader/settings/ReaderSettings.constants.tsx';
@@ -21,7 +21,7 @@ export const ReaderSettingProgressBarSize = ({
setProgressBarSize: (size: number, commit: boolean) => void;
onDefault: () => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const isChangeable = overlayMode === ReaderOverlayMode.DESKTOP && progressBarType === ProgressBarType.STANDARD;
if (!isChangeable) {
@@ -30,8 +30,8 @@ export const ReaderSettingProgressBarSize = ({
return (
<SliderInput
label={t('reader.settings.progress_bar.size')}
value={t('global.value', { value: progressBarSize, unit: t('global.unit.px') })}
label={t`Progress bar size`}
value={`${progressBarSize}${t`px`}`}
onDefault={onDefault}
slotProps={{
slider: {

View File

@@ -6,7 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { IReaderSettings, ProgressBarType, ReaderOverlayMode } from '@/features/reader/Reader.types.ts';
import { ValueToDisplayData } from '@/base/Base.types.ts';
import { HiddenProgressBarIcon } from '@/assets/icons/svg/HiddenProgressBarIcon.tsx';
@@ -15,11 +16,11 @@ import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.ts
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ProgressBarType> = {
[ProgressBarType.HIDDEN]: {
title: 'global.label.hidden',
title: msg`Hidden`,
icon: <HiddenProgressBarIcon />,
},
[ProgressBarType.STANDARD]: {
title: 'global.label.standard',
title: msg`Standard`,
icon: <StandardProgressBarIcon />,
},
};
@@ -33,7 +34,7 @@ export const ReaderSettingProgressBarType = ({
}: Pick<IReaderSettings, 'progressBarType' | 'overlayMode'> & {
setProgressBarType: (progressBarType: ProgressBarType) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
if (overlayMode !== ReaderOverlayMode.DESKTOP) {
return null;
@@ -41,7 +42,7 @@ export const ReaderSettingProgressBarType = ({
return (
<ButtonSelectInput
label={t('reader.settings.progress_bar.style')}
label={t`Progress bar style`}
value={progressBarType}
values={PROGRESS_BAR_TYPE_VALUES}
setValue={setProgressBarType}

View File

@@ -6,25 +6,26 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import PhoneIphoneIcon from '@mui/icons-material/PhoneIphone';
import ComputerIcon from '@mui/icons-material/Computer';
import AutoModeIcon from '@mui/icons-material/AutoMode';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { IReaderSettings, ReaderOverlayMode } from '@/features/reader/Reader.types.ts';
import { ValueToDisplayData } from '@/base/Base.types.ts';
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderOverlayMode> = {
[ReaderOverlayMode.AUTO]: {
title: 'global.label.auto',
title: msg`Auto`,
icon: <AutoModeIcon />,
},
[ReaderOverlayMode.DESKTOP]: {
title: 'global.label.desktop',
title: msg`Desktop`,
icon: <ComputerIcon />,
},
[ReaderOverlayMode.MOBILE]: {
title: 'global.label.mobile',
title: msg`Mobile`,
icon: <PhoneIphoneIcon />,
},
};
@@ -37,11 +38,11 @@ export const ReaderSettingOverlayMode = ({
}: Pick<IReaderSettings, 'overlayMode'> & {
setOverlayMode: (mode: ReaderOverlayMode) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
label={t('reader.settings.overlay_mode')}
label={t`Overlay mode`}
value={overlayMode}
values={READING_MODE_VALUES}
setValue={setOverlayMode}

View File

@@ -9,7 +9,7 @@
import Box from '@mui/material/Box';
import { memo, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { useDefaultReaderSettings } from '@/features/reader/settings/ReaderSettingsMetadata.ts';
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
import { ReaderOverlay } from '@/features/reader/overlay/ReaderOverlay.tsx';
@@ -43,14 +43,13 @@ import {
useReaderStore,
useReaderTapZoneStore,
} from '@/features/reader/stores/ReaderStore.ts';
import { ReaderAutoScroll } from '@/features/reader/auto-scroll/ReaderAutoScroll.tsx';
const BaseReader = ({
setOverride,
readerNavBarWidth,
}: Pick<NavbarContextType, 'setOverride' | 'readerNavBarWidth'>) => {
const { t } = useTranslation();
const { t } = useLingui();
const manga = useReaderStore((state) => state.manga);
const { mangaChapters, initialChapter, chapterForDuplicatesHandling, currentChapter } = useReaderChaptersStore(
(state) => ({
@@ -99,7 +98,7 @@ const BaseReader = ({
useAppTitle(
!manga || !currentChapter
? t('reader.title', { mangaId, chapterIndex: chapterSourceOrder })
? t`Reader — Manga ${mangaId} Chapter ${chapterSourceOrder}`
: `${manga.title}: ${currentChapter.name}`,
);
@@ -183,7 +182,7 @@ const BaseReader = ({
if (error) {
return (
<EmptyViewAbsoluteCentered
message={t('global.error.label.failed_to_load_data')}
message={t`Unable to load data`}
messageExtra={getErrorMessage(error)}
retry={() => {
if (mangaResponse.error) {
@@ -220,7 +219,7 @@ const BaseReader = ({
}
if (currentChapter === null) {
return <EmptyViewAbsoluteCentered message={t('reader.error.label.chapter_not_found')} />;
return <EmptyViewAbsoluteCentered message={t`Chapter does not exist`} />;
}
if (!manga || !currentChapter) {

View File

@@ -6,10 +6,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { MessageDescriptor } from '@lingui/core';
import { MutableRefObject, RefObject, useCallback, useEffect } from 'react';
import { Direction } from '@mui/material/styles';
import { t as translate } from 'i18next';
import { d } from 'koration';
import { msg, t } from '@lingui/core/macro';
import {
getNextIndexFromPage,
getNextPageIndex,
@@ -28,7 +29,7 @@ import {
ReadingDirection,
ReadingMode,
} from '@/features/reader/Reader.types.ts';
import { DirectionOffset, ScrollDirection, ScrollOffset, TranslationKey } from '@/base/Base.types.ts';
import { DirectionOffset, ScrollDirection, ScrollOffset } from '@/base/Base.types';
import { READING_DIRECTION_TO_THEME_DIRECTION } from '@/features/reader/settings/ReaderSettings.constants.tsx';
import {
isATransitionPageVisible,
@@ -57,6 +58,7 @@ import {
getReaderTapZoneStore,
} from '@/features/reader/stores/ReaderStore.ts';
import { Confirmation } from '@/base/AppAwaitableComponent.ts';
import { i18n } from '@/i18n';
const getScrollDirectionInvert = (
scrollDirection: ScrollDirection,
@@ -279,39 +281,47 @@ export class ReaderControls {
return;
}
const offsetToTranslationKeys: Record<typeof offset, Record<'scanlator' | 'chapter_number', TranslationKey>> = {
const offsetToTranslations: Record<typeof offset, Record<'scanlator' | 'chapter_number', MessageDescriptor>> = {
previous: {
scanlator: 'reader.chapter_transition.warning.previous.scanlator',
chapter_number: 'reader.chapter_transition.warning.previous.chapter_number',
scanlator: msg`The previous chapter has a different scanlator then the current one.\nPrevious: {nextScanlator}\nCurrent: {currentScanlator}`,
chapter_number: msg`{count, plural, one {There is # missing chapter.\nPrevious: {nextChapter}\nCurrent: {currentChapter}} other {There are # missing chapters.\nPrevious: {nextChapter}\nCurrent: {currentChapter}}}`,
},
next: {
scanlator: 'reader.chapter_transition.warning.next.scanlator',
chapter_number: 'reader.chapter_transition.warning.next.chapter_number',
scanlator: msg`The next chapter has a different scanlator then the current one.\nCurrent: {currentScanlator}\nNext: {nextScanlator}`,
chapter_number: msg`{count, plural, one {There is # missing chapter.\nCurrent: {currentChapter}\nNext: {nextChapter}} other {There are # missing chapters.\nCurrent: {currentChapter}\nNext: {nextChapter}}}`,
},
};
const sameScanlator = isSameScanlator
? ''
: translate(offsetToTranslationKeys[offset].scanlator, {
nextScanlator: chapterToOpen.scanlator,
currentScanlator: currentChapter.scanlator,
: /* lingui-extract-ignore */
i18n.t({
...offsetToTranslations[offset].scanlator,
values: {
nextScanlator: chapterToOpen.scanlator,
currentScanlator: currentChapter.scanlator,
},
});
const continuousChapter = isContinuousChapter
? ''
: translate(offsetToTranslationKeys[offset].chapter_number, {
count: missingChapters,
nextChapter: `#${chapterToOpen.chapterNumber} ${chapterToOpen.name}`,
currentChapter: `#${currentChapter.chapterNumber} ${currentChapter.name}`,
: /* lingui-extract-ignore */
i18n.t({
...offsetToTranslations[offset].chapter_number,
values: {
count: missingChapters,
nextChapter: `#${chapterToOpen.chapterNumber} ${chapterToOpen.name}`,
currentChapter: `#${currentChapter.chapterNumber} ${currentChapter.name}`,
},
});
const warningLineBreak = !isSameScanlator && !isContinuousChapter ? '\n\n' : '';
const warning = `${sameScanlator}${warningLineBreak}${continuousChapter}`;
await Confirmation.show({
title: translate('reader.chapter_transition.warning.title'),
title: t`Chapter transition warning`,
message: warning,
actions: {
confirm: {
title: translate('global.button.open'),
title: t`Open`,
},
},
});

View File

@@ -9,7 +9,7 @@
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Direction, useTheme } from '@mui/material/styles';
import { t as translate } from 'i18next';
import { t } from '@lingui/core/macro';
import { ChapterIdInfo, TChapterReader } from '@/features/chapter/Chapter.types.ts';
import { Chapters } from '@/features/chapter/services/Chapters.ts';
import {
@@ -299,11 +299,7 @@ export class ReaderService {
if (commit) {
updateReaderSettings(manga, setting, value, isGlobal, profile).catch((e) =>
makeToast(
translate('reader.settings.error.label.failed_to_save_settings'),
'error',
getErrorMessage(e),
),
makeToast(t`Could not save the reader settings to the server`, 'error', getErrorMessage(e)),
);
}
}
@@ -335,7 +331,7 @@ export class ReaderService {
? () => requestManager.deleteGlobalMeta(key).response
: () => requestManager.deleteMangaMeta(manga.id, key).response;
deleteSetting().catch((e) =>
makeToast(translate('reader.settings.error.label.failed_to_save_settings'), 'error', getErrorMessage(e)),
makeToast(t`Could not save the reader settings to the server`, 'error', getErrorMessage(e)),
);
}

View File

@@ -6,6 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { MessageDescriptor } from '@lingui/core';
import ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft';
import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight';
import ZoomOutMapIcon from '@mui/icons-material/ZoomOutMap';
@@ -13,7 +14,8 @@ import ExpandIcon from '@mui/icons-material/Expand';
import CropOriginalIcon from '@mui/icons-material/CropOriginal';
import { TooltipProps } from '@mui/material/Tooltip';
import { Direction } from '@mui/material/styles';
import { ScrollDirection, TranslationKey, ValueToDisplayData } from '@/base/Base.types.ts';
import { msg } from '@lingui/core/macro';
import { ScrollDirection, ValueToDisplayData } from '@/base/Base.types';
import {
IReaderSettings,
IReaderSettingsGlobal,
@@ -252,11 +254,11 @@ export const READER_PROGRESS_BAR_POSITION_TO_PLACEMENT: Record<ProgressBarPositi
export const READING_DIRECTION_VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReadingDirection> = {
[ReadingDirection.LTR]: {
title: 'reader.settings.reading_direction.ltr',
title: msg`Left to right`,
icon: <ArrowCircleRightIcon />,
},
[ReadingDirection.RTL]: {
title: 'reader.settings.reading_direction.rtl',
title: msg`Right to left`,
icon: <ArrowCircleLeftIcon />,
},
};
@@ -265,19 +267,19 @@ export const READING_DIRECTION_VALUES = Object.values(ReadingDirection).filter((
export const PAGE_SCALE_VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderPageScaleMode> = {
[ReaderPageScaleMode.WIDTH]: {
title: 'reader.settings.page_scale.width',
title: msg`Fit width`,
icon: <ExpandIcon sx={{ transform: 'rotate(90deg)' }} />,
},
[ReaderPageScaleMode.HEIGHT]: {
title: 'reader.settings.page_scale.height',
title: msg`Fit height`,
icon: <ExpandIcon />,
},
[ReaderPageScaleMode.SCREEN]: {
title: 'reader.settings.page_scale.screen',
title: msg`Fit screen`,
icon: <ZoomOutMapIcon />,
},
[ReaderPageScaleMode.ORIGINAL]: {
title: 'reader.settings.page_scale.original',
title: msg`Original size`,
icon: <CropOriginalIcon />,
},
};
@@ -295,23 +297,23 @@ export const READER_PAGE_SCALE_MODE_TO_SCALING_ALLOWED: Record<ReaderPageScaleMo
export const READING_MODE_VALUE_TO_DISPLAY_DATA = {
[ReadingMode.SINGLE_PAGE]: {
title: 'reader.settings.reader_type.label.single_page',
title: msg`Single page`,
icon: <SinglePageIcon />,
},
[ReadingMode.DOUBLE_PAGE]: {
title: 'reader.settings.reader_type.label.double_page',
title: msg`Double page`,
icon: <DoublePageIcon />,
},
[ReadingMode.CONTINUOUS_VERTICAL]: {
title: 'reader.settings.reader_type.label.continuous_vertical',
title: msg`Continuous vertical`,
icon: <ContinuousVerticalPageIcon />,
},
[ReadingMode.CONTINUOUS_HORIZONTAL]: {
title: 'reader.settings.reader_type.label.continuous_horizontal',
title: msg`Continuous horizontal`,
icon: <ContinuousHorizontalPageIcon />,
},
[ReadingMode.WEBTOON]: {
title: 'reader.settings.reader_type.label.webtoon',
title: msg`Webtoon`,
icon: <WebtoonPageIcon />,
},
} satisfies ValueToDisplayData<ReadingMode>;
@@ -330,33 +332,33 @@ export const READER_SETTING_TABS: Record<
ReaderSettingTab,
{
id: ReaderSettingTab;
label: TranslationKey;
label: MessageDescriptor;
supportsTouchDevices: boolean;
}
> = {
[ReaderSettingTab.LAYOUT]: {
id: ReaderSettingTab.LAYOUT,
label: 'reader.settings.label.layout',
label: msg`Layout`,
supportsTouchDevices: true,
},
[ReaderSettingTab.GENERAL]: {
id: ReaderSettingTab.GENERAL,
label: 'global.label.general',
label: msg`General`,
supportsTouchDevices: true,
},
[ReaderSettingTab.FILTER]: {
id: ReaderSettingTab.FILTER,
label: 'reader.settings.custom_filter.title',
label: msg`Custom filter`,
supportsTouchDevices: true,
},
[ReaderSettingTab.BEHAVIOUR]: {
id: ReaderSettingTab.BEHAVIOUR,
label: 'reader.settings.label.behaviour',
label: msg`Behaviour`,
supportsTouchDevices: true,
},
[ReaderSettingTab.HOTKEYS]: {
id: ReaderSettingTab.HOTKEYS,
label: 'hotkeys.title_other',
label: msg`Keybinds`,
supportsTouchDevices: false,
},
};
@@ -385,27 +387,27 @@ export const CONTINUOUS_READING_MODE_TO_SCROLL_DIRECTION: Record<
export const READER_BLEND_MODE_VALUE_TO_DISPLAY_DATA = {
[ReaderBlendMode.DEFAULT]: {
title: 'reader.settings.custom_filter.rgba.blend_mode.default',
title: msg`Default`,
icon: null,
},
[ReaderBlendMode.MULTIPLY]: {
title: 'reader.settings.custom_filter.rgba.blend_mode.multiply',
title: msg`Multiply`,
icon: null,
},
[ReaderBlendMode.SCREEN]: {
title: 'reader.settings.custom_filter.rgba.blend_mode.screen',
title: msg`Screen`,
icon: null,
},
[ReaderBlendMode.OVERLAY]: {
title: 'reader.settings.custom_filter.rgba.blend_mode.overlay',
title: msg`Overlay`,
icon: null,
},
[ReaderBlendMode.DARKEN]: {
title: 'reader.settings.custom_filter.rgba.blend_mode.darken',
title: msg`Darken`,
icon: null,
},
[ReaderBlendMode.LIGHTEN]: {
title: 'reader.settings.custom_filter.rgba.blend_mode.lighten',
title: msg`Lighten`,
icon: null,
},
} satisfies ValueToDisplayData<ReaderBlendMode>;

View File

@@ -7,9 +7,9 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { useLingui } from '@lingui/react/macro';
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { IReaderSettingsWithDefaultFlag, ReaderSettingsTypeProps } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
@@ -34,7 +34,7 @@ export const ReaderBehaviourSettings = ({
...args: Parameters<typeof ReaderService.updateSetting>
) => ReturnType<typeof ReaderService.updateSetting>;
} & ReaderSettingsTypeProps) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ gap: 2 }}>
@@ -47,17 +47,17 @@ export const ReaderBehaviourSettings = ({
setScrollAmount={(value, commit) => updateSetting('scrollAmount', value, commit)}
/>
<CheckboxInput
label={t('reader.settings.label.skip_dup_chapters')}
label={t`Skip duplicate chapters`}
checked={settings.shouldSkipDupChapters}
onChange={(_, checked) => updateSetting('shouldSkipDupChapters', checked)}
/>
<CheckboxInput
label={
<Box>
<Typography>{t('reader.settings.label.skip_filtered_chapters')}</Typography>
<Typography>{t`Skip filtered chapters`}</Typography>
{isDefaultable && (
<Typography variant="body2" color="textDisabled">
{t('reader.settings.label.unchangeable_in_reader')}
{t`Setting can not be changed while the reader is opened`}
</Typography>
)}
</Box>
@@ -68,7 +68,7 @@ export const ReaderBehaviourSettings = ({
/>
{isOffsetDoubleSpreadPagesEditable(settings.readingMode.value) && (
<CheckboxInput
label={t('reader.settings.label.offset_double_spread')}
label={t`Offset double spreads`}
checked={settings.shouldOffsetDoubleSpreads.value}
onChange={(_, checked) => updateSetting('shouldOffsetDoubleSpreads', checked)}
/>
@@ -76,9 +76,9 @@ export const ReaderBehaviourSettings = ({
<CheckboxInput
label={
<Box>
<Typography>{t('reader.settings.infinite_scroll.title')}</Typography>
<Typography>{t`Continuous chapter display`}</Typography>
<Typography variant="body2" color="textDisabled">
{t('reader.settings.infinite_scroll.description')}
{t`Keep previous and upcoming chapters visible while reading in continuous reading modes, allowing seamless scrolling between chapters.`}
</Typography>
</Box>
}
@@ -88,9 +88,9 @@ export const ReaderBehaviourSettings = ({
<CheckboxInput
label={
<Box>
<Typography>{t('reader.settings.preview.reading_mode.title')}</Typography>
<Typography>{t`Show reading mode`}</Typography>
<Typography variant="body2" color="textDisabled">
{t('reader.settings.preview.reading_mode.description')}
{t`Briefly show current mode when reader is opened or mode got changed`}
</Typography>
</Box>
}
@@ -100,9 +100,9 @@ export const ReaderBehaviourSettings = ({
<CheckboxInput
label={
<Box>
<Typography>{t('reader.settings.preview.tap_zones.title')}</Typography>
<Typography>{t`Show tap zones overlay`}</Typography>
<Typography variant="body2" color="textDisabled">
{t('reader.settings.preview.tap_zones.description')}
{t`Briefly show tap zone layout preview when reader is opened or layout got changed`}
</Typography>
</Box>
}
@@ -112,9 +112,9 @@ export const ReaderBehaviourSettings = ({
<CheckboxInput
label={
<Box>
<Typography>{t('reader.settings.auto_webtoon_mode.title')}</Typography>
<Typography>{t`Auto webtoon mode`}</Typography>
<Typography variant="body2" color="textDisabled">
{t('reader.settings.auto_webtoon_mode.description')}
{t`Automatically use webtoon mode for entries that are detected to likely use the long strip format`}
</Typography>
</Box>
}
@@ -122,17 +122,17 @@ export const ReaderBehaviourSettings = ({
onChange={(_, checked) => updateSetting('shouldUseAutoWebtoonMode', checked)}
/>
<CheckboxInput
label={t('reader.settings.show_transition_page')}
label={t`Show transition page`}
checked={settings.shouldShowTransitionPage}
onChange={(_, checked) => updateSetting('shouldShowTransitionPage', checked)}
/>
<CheckboxInput
label={t('reader.settings.chapter_transition.warning.missing_chapter')}
label={t`Inform about missing chapters on chapter transition`}
checked={settings.shouldInformAboutMissingChapter}
onChange={(_, checked) => updateSetting('shouldInformAboutMissingChapter', checked)}
/>
<CheckboxInput
label={t('reader.settings.chapter_transition.warning.scanlator_change')}
label={t`Inform about changing scanlator on chapter transition`}
checked={settings.shouldInformAboutScanlatorChange}
onChange={(_, checked) => updateSetting('shouldInformAboutScanlatorChange', checked)}
/>
@@ -141,7 +141,7 @@ export const ReaderBehaviourSettings = ({
setAutoScroll={(...args) => updateSetting('autoScroll', ...args)}
/>
<SliderInput
label={t('reader.settings.image_preload_amount')}
label={t`Preload images`}
value={settings.imagePreLoadAmount}
onDefault={() => onDefault?.('imagePreLoadAmount')}
slotProps={{

View File

@@ -6,18 +6,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { IReaderSettings, ReaderExitMode } from '@/features/reader/Reader.types.ts';
import { ValueToDisplayData } from '@/base/Base.types.ts';
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderExitMode> = {
[ReaderExitMode.PREVIOUS]: {
title: 'global.label.previous',
title: msg`Previous`,
icon: null,
},
[ReaderExitMode.MANGA]: {
title: 'manga.title_one',
title: msg`Manga`,
icon: null,
},
};
@@ -30,11 +31,11 @@ export const ReaderSettingExitMode = ({
}: Pick<IReaderSettings, 'exitMode'> & {
setExitMode: (mode: ReaderExitMode) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
label={t('reader.settings.exit_mode')}
label={t`Open page on exit`}
value={exitMode}
values={READER_EXIT_MODE_VALUES}
setValue={setExitMode}

View File

@@ -6,7 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { IReaderSettings, ReaderScrollAmount } from '@/features/reader/Reader.types.ts';
import { ValueToDisplayData } from '@/base/Base.types.ts';
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
@@ -15,19 +16,19 @@ import { DEFAULT_READER_SETTINGS, SCROLL_AMOUNT } from '@/features/reader/settin
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderScrollAmount> = {
[ReaderScrollAmount.TINY]: {
title: 'global.label.tiny',
title: msg`Tiny`,
icon: null,
},
[ReaderScrollAmount.SMALL]: {
title: 'global.label.small',
title: msg`Small`,
icon: null,
},
[ReaderScrollAmount.MEDIUM]: {
title: 'global.label.medium',
title: msg`Medium`,
icon: null,
},
[ReaderScrollAmount.LARGE]: {
title: 'global.label.large',
title: msg`Large`,
icon: null,
},
};
@@ -40,20 +41,20 @@ export const ReaderSettingScrollAmount = ({
}: Pick<IReaderSettings, 'scrollAmount'> & {
setScrollAmount: (amount: ReaderScrollAmount, commit: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<>
<ButtonSelectInput
label={t('reader.settings.scroll_amount')}
label={t`Scroll amount`}
value={scrollAmount}
values={READER_SCROLL_AMOUNT_VALUES}
setValue={(value) => setScrollAmount(value as number, true)}
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
/>
<SliderInput
label={t('reader.settings.label.custom_scroll_amount')}
value={t('global.value', { value: scrollAmount, unit: '%' })}
label={t`Custom scroll amount`}
value={`${scrollAmount}%`}
onDefault={() => setScrollAmount(DEFAULT_READER_SETTINGS.scrollAmount, true)}
slotProps={{
slider: {

View File

@@ -8,11 +8,11 @@
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { MediaQuery } from '@/base/utils/MediaQuery.tsx';
export const DefaultSettingFootnote = ({ areDefaultSettings }: { areDefaultSettings?: boolean }) => {
const { t } = useTranslation();
const { t } = useLingui();
const isTouchDevice = MediaQuery.useIsTouchDevice();
if (!isTouchDevice || areDefaultSettings) {
@@ -21,7 +21,7 @@ export const DefaultSettingFootnote = ({ areDefaultSettings }: { areDefaultSetti
return (
<Stack sx={{ alignItems: 'end' }}>
<Typography variant="caption">{t('reader.settings.default_setting_footnote')}</Typography>
<Typography variant="caption">{t`*: Active setting`}</Typography>
</Stack>
);
};

View File

@@ -8,7 +8,7 @@
import Box from '@mui/material/Box';
import Tab from '@mui/material/Tab';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { TabsMenu } from '@/base/components/tabs/TabsMenu.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
import { MediaQuery } from '@/base/utils/MediaQuery.tsx';
@@ -43,7 +43,7 @@ const BaseReaderSettingsTabs = ({
deleteSetting: (setting: keyof IReaderSettings) => void;
setTransparent?: (transparent: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const isTouchDevice = MediaQuery.useIsTouchDevice();
const setShowPreview = useReaderTapZoneStore((state) => state.tapZone.setShowPreview);

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { ReaderSettingProgressBarType } from '@/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarType.tsx';
import { ReaderSettingProgressBarSize } from '@/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarSize.tsx';
import { ReaderSettingProgressBarPosition } from '@/features/reader/overlay/progress-bar/settings/components/ReaderSettingProgressBarPosition.tsx';
@@ -27,7 +27,7 @@ export const ReaderGeneralSettings = ({
updateSetting,
onDefault,
}: Pick<IReaderSettings, 'overlayMode'> & ReaderSettingsTypeProps) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ gap: 2 }}>
@@ -58,7 +58,7 @@ export const ReaderGeneralSettings = ({
/>
{(settings.progressBarType === ProgressBarType.HIDDEN || overlayMode === ReaderOverlayMode.MOBILE) && (
<CheckboxInput
label={t('reader.settings.label.show_page_number')}
label={t`Show page number`}
checked={settings.shouldShowPageNumber}
onChange={(_, checked) => updateSetting('shouldShowPageNumber', checked)}
/>

View File

@@ -6,26 +6,27 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { ValueToDisplayData } from '@/base/Base.types.ts';
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
import { IReaderSettingsWithDefaultFlag, ReaderBackgroundColor } from '@/features/reader/Reader.types.ts';
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderBackgroundColor> = {
[ReaderBackgroundColor.THEME]: {
title: 'settings.appearance.theme.title',
title: msg`Theme`,
icon: null,
},
[ReaderBackgroundColor.BLACK]: {
title: 'global.colors.black',
title: msg`Black`,
icon: null,
},
[ReaderBackgroundColor.GRAY]: {
title: 'global.colors.gray',
title: msg`Gray`,
icon: null,
},
[ReaderBackgroundColor.WHITE]: {
title: 'global.colors.white',
title: msg`White`,
icon: null,
},
};
@@ -40,11 +41,11 @@ export const ReaderSettingBackgroundColor = ({
}: Pick<IReaderSettingsWithDefaultFlag, 'backgroundColor'> & {
updateSetting: (color: ReaderBackgroundColor) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
label={t('reader.settings.background_color')}
label={t`Background color`}
value={backgroundColor}
values={READER_BACKGROUND_COLOR_VALUES}
setValue={updateSetting}

View File

@@ -8,7 +8,7 @@
import Stack from '@mui/material/Stack';
import { ComponentProps } from 'react';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { ReaderLayoutSettings } from '@/features/reader/settings/layout/ReaderLayoutSettings.tsx';
import { ReaderSettingProfileSettings } from '@/features/reader/settings/layout/components/ReaderSettingProfileSettings.tsx';
import {
@@ -28,7 +28,7 @@ export const ReaderDefaultLayoutSettings = ({
}) => {
const { updateSetting, isSeriesMode } = props;
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ gap: 2, pb: Number(!isSeriesMode) * 2 }}>

View File

@@ -9,7 +9,7 @@
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import Divider from '@mui/material/Divider';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { ReaderSettingReadingMode } from '@/features/reader/settings/layout/components/ReaderSettingReadingMode.tsx';
import { ReaderSettingReadingDirection } from '@/features/reader/settings/layout/components/ReaderSettingReadingDirection.tsx';
import { ReaderSettingTapZoneLayout } from '@/features/reader/settings/layout/components/ReaderSettingTapZoneLayout.tsx';
@@ -35,14 +35,14 @@ export const ReaderLayoutSettings = ({
setShowPreview: TReaderTapZoneContext['setShowPreview'];
isSeriesMode?: boolean;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<Stack sx={{ gap: 2 }}>
<DefaultSettingFootnote areDefaultSettings={!isDefaultable} />
{isSeriesMode && (
<>
<Typography>{t('reader.settings.source_series')}</Typography>
<Typography>{t`Series settings`}</Typography>
<ReaderSettingReadingMode
readingMode={settings.readingMode}
setReadingMode={(value) => updateSetting('readingMode', value)}

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings, IReaderSettingsWithDefaultFlag, ReadingMode } from '@/features/reader/Reader.types.ts';
import { SliderInput } from '@/base/components/inputs/SliderInput.tsx';
import { DEFAULT_READER_SETTINGS, PAGE_GAP } from '@/features/reader/settings/ReaderSettings.constants.tsx';
@@ -23,7 +23,7 @@ export const ReaderSettingPageGap = ({
Pick<MultiValueButtonDefaultableProps<IReaderSettings['pageGap']>, 'isDefaultable' | 'onDefault'> & {
updateSetting: (gap: number, commit: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const isChangeable = readingMode.value !== ReadingMode.WEBTOON && isContinuousReadingMode(readingMode.value);
if (!isChangeable) {
@@ -32,8 +32,8 @@ export const ReaderSettingPageGap = ({
return (
<SliderInput
label={t('reader.settings.label.page_gap')}
value={t('global.value', { value: pageGap.value, unit: t('global.unit.px') })}
label={t`Page gap`}
value={`${pageGap.value}${t`px`}`}
onDefault={isDefaultable ? onDefault : undefined}
slotProps={{
slider: {

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings, IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/features/reader/Reader.types.ts';
import {
PAGE_SCALE_VALUE_TO_DISPLAY_DATA,
@@ -23,12 +23,12 @@ export const ReaderSettingPageScaleMode = ({
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
setPageScaleMode: (mode: IReaderSettings['pageScaleMode']) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
{...buttonSelectInputProps}
label={t('reader.settings.page_scale.title')}
label={t`Scale type`}
value={pageScaleMode.isDefault ? undefined : pageScaleMode.value}
defaultValue={pageScaleMode.isDefault ? pageScaleMode.value : undefined}
values={READER_PAGE_SCALE_MODE_VALUES}

View File

@@ -8,18 +8,19 @@
import ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft';
import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/features/reader/Reader.types.ts';
import { MultiValueButtonDefaultableProps, ValueToDisplayData } from '@/base/Base.types.ts';
import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.tsx';
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReadingDirection> = {
[ReadingDirection.LTR]: {
title: 'reader.settings.reading_direction.ltr',
title: msg`Left to right`,
icon: <ArrowCircleRightIcon />,
},
[ReadingDirection.RTL]: {
title: 'reader.settings.reading_direction.rtl',
title: msg`Right to left`,
icon: <ArrowCircleLeftIcon />,
},
};
@@ -34,12 +35,12 @@ export const ReaderSettingReadingDirection = ({
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
setReadingDirection: (readingDirection: ReadingDirection) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
{...buttonSelectInputProps}
label={t('reader.settings.label.reading_direction')}
label={t`Reading direction`}
value={readingDirection.isDefault ? undefined : readingDirection.value}
defaultValue={readingDirection.isDefault ? readingDirection.value : undefined}
values={READING_DIRECTION_VALUES}

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettingsWithDefaultFlag, ReadingMode } from '@/features/reader/Reader.types.ts';
import {
READING_MODE_VALUE_TO_DISPLAY_DATA,
@@ -23,12 +23,12 @@ export const ReaderSettingReadingMode = ({
Pick<MultiValueButtonDefaultableProps<ReadingMode>, 'isDefaultable' | 'onDefault'> & {
setReadingMode: (mode: ReadingMode) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
{...buttonSelectInputProps}
label={t('reader.settings.label.reading_mode')}
label={t`Reading mode`}
value={readingMode.isDefault ? undefined : readingMode.value}
defaultValue={readingMode.isDefault ? readingMode.value : undefined}
values={READING_MODE_VALUES}

View File

@@ -6,9 +6,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { READER_PAGE_SCALE_MODE_TO_SCALING_ALLOWED } from '@/features/reader/settings/ReaderSettings.constants.tsx';
@@ -19,7 +19,7 @@ export const ReaderSettingStretchPage = ({
}: Pick<IReaderSettings, 'pageScaleMode' | 'shouldStretchPage'> & {
setShouldStretchPage: (mode: IReaderSettings['shouldStretchPage']) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
if (!READER_PAGE_SCALE_MODE_TO_SCALING_ALLOWED[pageScaleMode]) {
return null;
@@ -31,7 +31,7 @@ export const ReaderSettingStretchPage = ({
onClick={() => setShouldStretchPage(!shouldStretchPage)}
variant={shouldStretchPage ? 'contained' : 'outlined'}
>
{t('reader.settings.page_scale.stretch')}
{t`Stretch small pages`}
</Button>
</Box>
);

View File

@@ -6,7 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { MultiValueButtonDefaultableProps, ValueToDisplayData } from '@/base/Base.types.ts';
import { IReaderSettings, IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/features/reader/Reader.types.ts';
import { TapZoneInvertMode } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
@@ -21,19 +22,19 @@ enum TapZonesInvertOption {
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<TapZonesInvertOption> = {
[TapZonesInvertOption.NONE]: {
title: 'global.label.none',
title: msg`None`,
icon: null,
},
[TapZonesInvertOption.HORIZONTAL]: {
title: 'global.label.horizontal',
title: msg`Horizontal`,
icon: null,
},
[TapZonesInvertOption.VERTICAL]: {
title: 'global.label.vertical',
title: msg`Vertical`,
icon: null,
},
[TapZonesInvertOption.BOTH]: {
title: 'global.label.both',
title: msg`Both`,
icon: null,
},
};
@@ -83,14 +84,14 @@ export const ReaderSettingTapZoneInvertMode = ({
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
setTapZoneInvertMode: (invert: IReaderSettings['tapZoneInvertMode']) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const tapZonesInvertOption = convertTapZoneInvertModeToOption(tapZoneInvertMode.value);
return (
<ButtonSelectInput
{...buttonSelectInputProps}
label={t('reader.settings.tap_zones.invert')}
label={t`Invert tap zones`}
value={tapZoneInvertMode.isDefault ? undefined : tapZonesInvertOption}
defaultValue={tapZoneInvertMode.isDefault ? tapZonesInvertOption : undefined}
values={TAP_ZONES_INVERT_OPTION_VALUES}

View File

@@ -6,7 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { msg } from '@lingui/core/macro';
import { TapZoneLayouts } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
import { MultiValueButtonDefaultableProps, ValueToDisplayData } from '@/base/Base.types.ts';
import { IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/features/reader/Reader.types.ts';
@@ -14,23 +15,23 @@ import { ButtonSelectInput } from '@/base/components/inputs/ButtonSelectInput.ts
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<TapZoneLayouts> = {
[TapZoneLayouts.EDGE]: {
title: 'reader.settings.tap_zones.edge',
title: msg`Edge`,
icon: null,
},
[TapZoneLayouts.KINDLE]: {
title: 'reader.settings.tap_zones.kindle',
title: msg`Kindle`,
icon: null,
},
[TapZoneLayouts.L_SHAPE]: {
title: 'reader.settings.tap_zones.l_shape',
title: msg`L-Shape`,
icon: null,
},
[TapZoneLayouts.RIGHT_LEFT]: {
title: 'reader.settings.tap_zones.right_left',
title: msg`Right and left`,
icon: null,
},
[TapZoneLayouts.DISABLED]: {
title: 'global.label.disabled',
title: msg`Disabled`,
icon: null,
},
};
@@ -45,12 +46,12 @@ export const ReaderSettingTapZoneLayout = ({
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
setTapZoneLayout: (layout: TapZoneLayouts) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
return (
<ButtonSelectInput
{...buttonSelectInputProps}
label={t('reader.settings.tap_zones.title')}
label={t`Tap zones`}
value={tapZoneLayout.isDefault ? undefined : tapZoneLayout.value}
defaultValue={tapZoneLayout.isDefault ? tapZoneLayout.value : undefined}
values={READER_TAP_ZONE_LAYOUT_VALUES}

View File

@@ -7,7 +7,7 @@
*/
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings } from '@/features/reader/Reader.types.ts';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
@@ -29,7 +29,7 @@ export const ReaderSettingWidth = ({
updateSetting: (...args: Parameters<typeof ReaderService.updateSetting>) => void;
setTransparent?: (transparent: boolean) => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
if (!isReaderWidthEditable(pageScaleMode)) {
return null;
@@ -39,7 +39,7 @@ export const ReaderSettingWidth = ({
<Stack>
<Stack sx={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<CheckboxInput
label={t('reader.settings.label.limit_reader_width')}
label={t`Limit reader width`}
checked={readerWidth.enabled}
onChange={(_, checked) => updateSetting('readerWidth', { ...readerWidth, enabled: checked }, true)}
/>
@@ -47,8 +47,8 @@ export const ReaderSettingWidth = ({
</Stack>
{readerWidth.enabled && (
<SliderInput
label={t('reader.settings.label.reader_width')}
value={t('global.value', { value: readerWidth.value, unit: '%' })}
label={t`Reader width`}
value={`${readerWidth.value}%`}
slotProps={{
slider: {
defaultValue: DEFAULT_READER_SETTINGS.readerWidth.value,

View File

@@ -6,8 +6,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { useLingui } from '@lingui/react/macro';
import { useDefaultReaderSettingsWithDefaultFlag } from '@/features/reader/settings/ReaderSettingsMetadata.ts';
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx';
import { EmptyViewAbsoluteCentered } from '@/base/components/feedback/EmptyViewAbsoluteCentered.tsx';
@@ -19,9 +19,9 @@ import { getErrorMessage } from '@/lib/HelperFunctions.ts';
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
export const GlobalReaderSettings = () => {
const { t } = useTranslation();
const { t } = useLingui();
useAppTitle(t('reader.settings.title.reader'));
useAppTitle(t`Reader`);
const [activeTab, setActiveTab] = useState(0);
@@ -46,7 +46,7 @@ export const GlobalReaderSettings = () => {
if (error) {
return (
<EmptyViewAbsoluteCentered
message={t('global.error.label.failed_to_load_data')}
message={t`Unable to load data`}
messageExtra={getErrorMessage(error)}
retry={() => refetch().catch(defaultPromiseErrorHandler('DefaultReaderSettings::refetch'))}
/>

View File

@@ -6,10 +6,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { TapZoneLayouts, TapZoneRegion, TapZoneRegionType } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
import { TranslationKey } from '@/base/Base.types.ts';
export const READER_TAP_ZONE_LAYOUTS: Record<TapZoneLayouts, TapZoneRegion[]> = {
/**
* +---+---+---+
@@ -136,17 +136,17 @@ export const READER_TAP_ZONE_LAYOUTS: Record<TapZoneLayouts, TapZoneRegion[]> =
],
};
export const TAP_ZONE_REGION_TYPE_DATA: Record<TapZoneRegionType, { text: TranslationKey; color: string }> = {
export const TAP_ZONE_REGION_TYPE_DATA: Record<TapZoneRegionType, { text: MessageDescriptor; color: string }> = {
[TapZoneRegionType.PREVIOUS]: {
text: 'global.label.previous',
text: msg`Previous`,
color: 'rgba(255, 114, 118, .5)',
},
[TapZoneRegionType.NEXT]: {
text: 'global.label.next',
text: msg`Next`,
color: 'rgba(144, 238, 144, .5)',
},
[TapZoneRegionType.MENU]: {
text: 'global.label.menu',
text: msg`Menu`,
color: 'rgba(0, 0, 0, .5)',
},
};

View File

@@ -6,7 +6,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import i18next, { t as translate } from 'i18next';
import { CSSProperties } from 'react';
import {
ReaderTapZoneRect,
@@ -20,6 +19,8 @@ import {
TAP_ZONE_REGION_TYPE_DATA,
} from '@/features/reader/tap-zones/ReaderTapZone.constants.ts';
import { i18n } from '@/i18n';
interface InvertMode extends TapZoneInvertMode {
isRTL: boolean;
}
@@ -78,7 +79,7 @@ export class ReaderTapZoneService {
const isSameWidth = this.width === canvasWidth;
const isSameHeight = this.height === canvasHeight;
const doesCanvasExist = !!this.canvas;
const isSameLanguage = this.language === i18next.language;
const isSameLanguage = this.language === i18n.locale;
const isSameInvertMode =
this.invertMode?.vertical === invertMode.vertical &&
this.invertMode.horizontal === invertMode.horizontal &&
@@ -104,8 +105,8 @@ export class ReaderTapZoneService {
regions: TapZoneRegion[],
): void {
regions.forEach(({ type, rect: [rectX, rectY, rectWidth, rectHeight] }) => {
const { text: translationKey, color } = TAP_ZONE_REGION_TYPE_DATA[type];
const text = translate(translationKey);
const { text: translation, color } = TAP_ZONE_REGION_TYPE_DATA[type];
const text = i18n._(translation);
const calcActualValue = (value: number, size: number) => (value / 100) * size;
const x = calcActualValue(rectX, canvasWidth);
@@ -168,7 +169,7 @@ export class ReaderTapZoneService {
this.canvas = canvas;
this.width = canvasWidth;
this.height = canvasHeight;
this.language = i18next.language;
this.language = i18n.locale;
this.invertMode = invertMode;
this.regions = regions;
this.fontStyle = fontStyle;

View File

@@ -9,8 +9,8 @@
import { memo, MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Stack from '@mui/material/Stack';
import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import Box from '@mui/material/Box';
import { useLingui } from '@lingui/react/macro';
import {
IReaderSettings,
ReaderPagerProps,
@@ -42,8 +42,8 @@ import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholde
import { ReaderInfiniteScrollUpdateChapter } from '@/features/reader/infinite-scroll/ReaderInfiniteScrollUpdateChapter.tsx';
import { useResizeObserver } from '@/base/hooks/useResizeObserver.tsx';
import { ChapterIdInfo } from '@/features/chapter/Chapter.types.ts';
import { READER_DEFAULT_PAGES_STATE } from '@/features/reader/stores/ReaderPagesStore.ts';
import { getReaderChaptersStore } from '@/features/reader/stores/ReaderStore.ts';
const BaseReaderChapterViewer = ({
@@ -125,7 +125,7 @@ const BaseReaderChapterViewer = ({
minHeight: number;
scrollElement: HTMLElement | null;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const { direction: themeDirection } = useTheme();
const [fetchPages, pagesResponse] = requestManager.useGetChapterPagesFetch(chapterId ?? -1);
@@ -319,7 +319,7 @@ const BaseReaderChapterViewer = ({
}}
>
<EmptyViewAbsoluteCentered
message={t('global.error.label.failed_to_load_data')}
message={t`Unable to load data`}
messageExtra={getErrorMessage(pagesResponse.error)}
retry={() => {
doFetchPages();
@@ -355,7 +355,7 @@ const BaseReaderChapterViewer = ({
return (
<Box sx={{ minWidth: '100%', minHeight: '100%', position: 'relative' }}>
<EmptyViewAbsoluteCentered message={t('reader.error.label.no_pages_found')} retry={doFetchPages} />
<EmptyViewAbsoluteCentered message={t`No pages found`} retry={doFetchPages} />
</Box>
);
}

View File

@@ -8,11 +8,11 @@
import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack';
import { useTranslation } from 'react-i18next';
import Button from '@mui/material/Button';
import { Link } from 'react-router-dom';
import { ComponentProps, memo, useMemo } from 'react';
import { alpha, useTheme } from '@mui/material/styles';
import { useLingui } from '@lingui/react/macro';
import { IReaderSettings, ReaderTransitionPageMode, ReadingMode } from '@/features/reader/Reader.types.ts';
import { isTransitionPageVisible } from '@/features/reader/viewer/pager/ReaderPager.utils.tsx';
import { useBackButton } from '@/base/hooks/useBackButton.ts';
@@ -97,7 +97,7 @@ const BaseReaderTransitionPage = ({
type: Exclude<ReaderTransitionPageMode, ReaderTransitionPageMode.NONE | ReaderTransitionPageMode.BOTH>;
handleBack: () => void;
}) => {
const { t } = useTranslation();
const { t } = useLingui();
const manga = useReaderStore((state) => state.manga);
const scrollbar = useReaderScrollbarStore((state) => state.scrollbar);
const transitionPageMode = useReaderPagesStore((state) => state.pages.transitionPageMode);
@@ -162,12 +162,12 @@ const BaseReaderTransitionPage = ({
}}
>
{isPreviousType && isFirstChapter && (
<Typography variant="h6">{t('reader.transition_page.first_chapter')}</Typography>
<Typography variant="h6">{t`There is no previous chapter`}</Typography>
)}
<Stack sx={{ gap: 5 }}>
{isPreviousType && !isFirstChapter && (
<ChapterInfo
title={t('reader.transition_page.previous')}
title={t`Previous:`}
name={previousChapterName}
scanlator={previousChapterScanlator}
backgroundColor={backgroundColor}
@@ -175,9 +175,7 @@ const BaseReaderTransitionPage = ({
)}
{!!currentChapterName && (
<ChapterInfo
title={t(
isPreviousType ? 'reader.transition_page.current' : 'reader.transition_page.finished',
)}
title={isPreviousType ? t`Current:` : t`Finished:`}
name={currentChapterName}
scanlator={currentChapterScanlator}
backgroundColor={backgroundColor}
@@ -185,16 +183,14 @@ const BaseReaderTransitionPage = ({
)}
{isNextType && !isLastChapter && (
<ChapterInfo
title={t('reader.transition_page.next')}
title={t`Next:`}
name={nextChapterName}
scanlator={nextChapterScanlator}
backgroundColor={backgroundColor}
/>
)}
</Stack>
{isNextType && isLastChapter && (
<Typography variant="h6">{t('reader.transition_page.last_chapter')}</Typography>
)}
{isNextType && isLastChapter && <Typography variant="h6">{t`There is no next chapter`}</Typography>}
{((isPreviousType && isFirstChapter) || (isNextType && isLastChapter)) && (
<Stack sx={{ flexDirection: 'row', flexWrap: 'wrap', gap: 1 }}>
<Button
@@ -205,7 +201,7 @@ const BaseReaderTransitionPage = ({
}}
variant="contained"
>
{t('reader.transition_page.exit.previous_page')}
{t`Exit to previous page`}
</Button>
<Button
sx={{ flexGrow: 1 }}
@@ -216,7 +212,7 @@ const BaseReaderTransitionPage = ({
variant="contained"
to={AppRoutes.manga.path(manga?.id ?? -1)}
>
{t('reader.transition_page.exit.manga_page')}
{t`Exit to manga page`}
</Button>
</Stack>
)}