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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) : ''}
|
||||
|
||||
@@ -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));
|
||||
}}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 }}>
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'))}
|
||||
/>
|
||||
|
||||
@@ -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)',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user