Skip to content

Commit

Permalink
add translation keys (#246)
Browse files Browse the repository at this point in the history
* Fix i18n tsc issue

"t" can return null by default.
This was already disabled in the config, but it had no effect on tsc.

* Fix translation in "DefaultNavBar"

key was missing and since this is a constant the translation wouldn't update in case the language got changed after first load.

* Enable typing for i18n translation keys

* Add translation keys
  • Loading branch information
schroda authored Mar 23, 2023
1 parent 8c129f2 commit d58bd6c
Show file tree
Hide file tree
Showing 56 changed files with 1,029 additions and 415 deletions.
19 changes: 16 additions & 3 deletions src/components/ExtensionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import Typography from '@mui/material/Typography';
import client from 'util/client';
import useLocalStorage from 'util/useLocalStorage';
import { Box } from '@mui/system';
import { IExtension } from 'typings';
import { IExtension, TranslationKey } from 'typings';
import { useTranslation } from 'react-i18next';

interface IProps {
extension: IExtension;
Expand Down Expand Up @@ -50,7 +51,19 @@ const EXTENSION_ACTION_TO_NEXT_ACTION_MAP: { [action in ExtensionAction]: Extens
[ExtensionAction.INSTALL]: ExtensionAction.UNINSTALL,
} as const;

const INSTALLED_STATE_TO_TRANSLATION_KEY_MAP: { [installedState in InstalledStates]: TranslationKey } = {
[InstalledState.UNINSTALL]: 'extension.action.label.uninstall',
[InstalledState.INSTALL]: 'extension.action.label.install',
[InstalledState.UPDATE]: 'extension.action.label.update',
[InstalledState.OBSOLETE]: 'extension.state.label.obsolete',
[InstalledState.UPDATING]: 'extension.state.label.updating',
[InstalledState.UNINSTALLING]: 'extension.state.label.uninstalling',
[InstalledState.INSTALLING]: 'extension.state.label.installing',
} as const;

export default function ExtensionCard(props: IProps) {
const { t } = useTranslation();

const {
extension: { name, lang, versionName, installed, hasUpdate, obsolete, pkgName, iconUrl, isNsfw },
notifyInstall,
Expand All @@ -68,7 +81,7 @@ export default function ExtensionCard(props: IProps) {
const [serverAddress] = useLocalStorage<String>('serverBaseURL', '');
const [useCache] = useLocalStorage<boolean>('useCache', true);

const langPress = lang === 'all' ? 'All' : lang.toUpperCase();
const langPress = lang === 'all' ? t('extension.language.all') : lang.toUpperCase();

const requestExtensionAction = async (action: ExtensionAction): Promise<void> => {
const nextAction = EXTENSION_ACTION_TO_NEXT_ACTION_MAP[action];
Expand Down Expand Up @@ -137,7 +150,7 @@ export default function ExtensionCard(props: IProps) {
sx={{ color: installedState === InstalledState.OBSOLETE ? 'red' : 'inherit' }}
onClick={() => handleButtonClick()}
>
{installedState}
{t(INSTALLED_STATE_TO_TRANSLATION_KEY_MAP[installedState])}
</Button>
</CardContent>
</Card>
Expand Down
9 changes: 7 additions & 2 deletions src/components/MangaCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Box, styled } from '@mui/system';
import { GridLayout, useLibraryOptionsContext } from 'components/context/LibraryOptionsContext';
import { BACK } from 'util/useBackTo';
import { IMangaCard } from 'typings';
import { useTranslation } from 'react-i18next';

const BottomGradient = styled('div')({
position: 'absolute',
Expand Down Expand Up @@ -73,6 +74,8 @@ interface IProps {
}

const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref) => {
const { t } = useTranslation();

const {
manga: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -131,7 +134,7 @@ const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref)
>
{inLibraryIndicator && inLibrary && (
<Typography sx={{ backgroundColor: 'primary.dark', zIndex: '1' }}>
In library
{t('manga.button.in_library')}
</Typography>
)}
{showUnreadBadge && unread! > 0 && (
Expand Down Expand Up @@ -252,7 +255,9 @@ const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref)
</Box>
<BadgeContainer>
{inLibraryIndicator && inLibrary && (
<Typography sx={{ backgroundColor: 'primary.dark' }}>In library</Typography>
<Typography sx={{ backgroundColor: 'primary.dark' }}>
{t('manga.button.in_library')}
</Typography>
)}
{showUnreadBadge && unread! > 0 && (
<Typography sx={{ backgroundColor: 'primary.dark' }}>{unread}</Typography>
Expand Down
9 changes: 6 additions & 3 deletions src/components/SourceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import { Box, styled } from '@mui/system';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useHistory } from 'react-router-dom';
import { langCodeToName } from 'util/language';
import useLocalStorage from 'util/useLocalStorage';
Expand Down Expand Up @@ -41,6 +42,8 @@ interface IProps {
}

const SourceCard: React.FC<IProps> = (props: IProps) => {
const { t } = useTranslation();

const {
source: { id, name, lang, iconUrl, supportsLatest, isNsfw },
} = props;
Expand Down Expand Up @@ -110,18 +113,18 @@ const SourceCard: React.FC<IProps> = (props: IProps) => {
<MobileWidthButtons>
{supportsLatest && (
<Button variant="outlined" onClick={(e) => redirectTo(e, `/sources/${id}/latest/`)}>
Latest
{t('global.button.latest')}
</Button>
)}
</MobileWidthButtons>
<WiderWidthButtons>
{supportsLatest && (
<Button component={Link} to={`/sources/${id}/latest/`} variant="outlined">
Latest
{t('global.button.latest')}
</Button>
)}
<Button component={Link} to={`/sources/${id}/popular/`} variant="outlined">
Browse
{t('global.button.browse')}
</Button>
</WiderWidthButtons>
</>
Expand Down
7 changes: 4 additions & 3 deletions src/components/library/LibraryMangaGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import { StringParam, useQueryParam } from 'use-query-params';
import { useMediaQuery, useTheme } from '@mui/material';
import { IMangaCard, LibrarySortMode, NullAndUndefined } from 'typings';
import { useSearchSettings } from 'util/searchSettings';

const FILTERED_OUT_MESSAGE = 'There are no Manga matching this filter';
import { useTranslation } from 'react-i18next';

const unreadFilter = (unread: NullAndUndefined<boolean>, { unreadCount }: IMangaCard): boolean => {
switch (unread) {
Expand Down Expand Up @@ -118,6 +117,8 @@ const LibraryMangaGrid: React.FC<LibraryMangaGridProps & { lastLibraryUpdate: nu
message,
lastLibraryUpdate,
}) => {
const { t } = useTranslation();

const [query] = useQueryParam('query', StringParam);
const { options } = useLibraryOptionsContext();
const { unread, downloaded } = options;
Expand Down Expand Up @@ -155,7 +156,7 @@ const LibraryMangaGrid: React.FC<LibraryMangaGridProps & { lastLibraryUpdate: nu
hasNextPage={lastPageNum < totalPages}
lastPageNum={lastPageNum}
setLastPageNum={setLastPageNum}
message={showFilteredOutMessage ? FILTERED_OUT_MESSAGE : message}
message={showFilteredOutMessage ? t('library.error.label.no_matches') : message}
gridLayout={options.gridLayout}
/>
);
Expand Down
44 changes: 23 additions & 21 deletions src/components/library/LibraryOptionsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ import ThreeStateCheckboxInput from 'components/atoms/ThreeStateCheckboxInput';
import { GridLayout, useLibraryOptionsContext } from 'components/context/LibraryOptionsContext';
import OptionsTabs from 'components/molecules/OptionsTabs';
import React from 'react';
import { LibraryOptions, LibrarySortMode } from 'typings';
import { LibraryOptions, LibrarySortMode, TranslationKey } from 'typings';
import { useTranslation } from 'react-i18next';

const TITLES = {
filter: 'Filter',
sort: 'Sort',
display: 'Display',
const TITLES: { [key in 'filter' | 'sort' | 'display']: TranslationKey } = {
filter: 'global.label.filter',
sort: 'global.label.sort',
display: 'global.label.display',
};

const SORT_OPTIONS: [LibrarySortMode, string][] = [
['sortToRead', 'By Unread chapters'],
['sortAlph', 'Alphabetically'],
['sortDateAdded', 'Date Added'],
['sortLastRead', 'Last Read'],
const SORT_OPTIONS: [LibrarySortMode, TranslationKey][] = [
['sortToRead', 'library.option.sort.label.by_unread_chapters'],
['sortAlph', 'library.option.sort.label.alphabetically'],
['sortDateAdded', 'library.option.sort.label.by_date_added'],
['sortLastRead', 'library.option.sort.label.by_last_read'],
];

interface IProps {
Expand All @@ -34,6 +35,7 @@ interface IProps {
}

const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
const { t } = useTranslation();
const { options, setOptions } = useLibraryOptionsContext();

const handleFilterChange = <T extends keyof LibraryOptions>(key: T, value: LibraryOptions[T]) => {
Expand All @@ -45,18 +47,18 @@ const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
open={open}
onClose={onClose}
tabs={['filter', 'sort', 'display']}
tabTitle={(key) => TITLES[key]}
tabTitle={(key) => t(TITLES[key])}
tabContent={(key) => {
if (key === 'filter') {
return (
<>
<ThreeStateCheckboxInput
label="Unread"
label={t('global.filter.label.unread')}
checked={options.unread}
onChange={(c) => handleFilterChange('unread', c)}
/>
<ThreeStateCheckboxInput
label="Downloaded"
label={t('global.filter.label.downloaded')}
checked={options.downloaded}
onChange={(c) => handleFilterChange('downloaded', c)}
/>
Expand All @@ -67,7 +69,7 @@ const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
return SORT_OPTIONS.map(([mode, label]) => (
<SortRadioInput
key={mode}
label={label}
label={t(label) as string}
checked={options.sorts === mode}
sortDescending={options.sortDesc}
onClick={() =>
Expand All @@ -82,36 +84,36 @@ const LibraryOptionsPanel: React.FC<IProps> = ({ open, onClose }) => {
const { gridLayout, showDownloadBadge, showUnreadBadge } = options;
return (
<>
<FormLabel>Display mode</FormLabel>
<FormLabel>{t('global.grid_layout.title')}</FormLabel>
<RadioGroup
onChange={(e) => handleFilterChange('gridLayout', Number(e.target.value))}
value={gridLayout}
>
<RadioInput
label="Compact grid"
label={t('global.grid_layout.label.compact_grid')}
value={GridLayout.Compact}
checked={gridLayout == null || gridLayout === GridLayout.Compact}
/>
<RadioInput
label="Comfortable grid"
label={t('global.grid_layout.label.comfortable_grid')}
value={GridLayout.Comfortable}
checked={gridLayout === GridLayout.Comfortable}
/>
<RadioInput
label="List"
label={t('global.grid_layout.label.list')}
value={GridLayout.List}
checked={gridLayout === GridLayout.List}
/>
</RadioGroup>

<FormLabel sx={{ mt: 2 }}>Badges</FormLabel>
<FormLabel sx={{ mt: 2 }}>{t('library.option.display.badge.title')}</FormLabel>
<CheckboxInput
label="Unread Badges"
label={t('library.option.display.badge.label.unread_badges')}
checked={showUnreadBadge === true}
onChange={() => handleFilterChange('showUnreadBadge', !showUnreadBadge)}
/>
<CheckboxInput
label="Download Badges"
label={t('library.option.display.badge.label.download_badges')}
checked={showDownloadBadge === true}
onChange={() => handleFilterChange('showDownloadBadge', !showDownloadBadge)}
/>
Expand Down
5 changes: 4 additions & 1 deletion src/components/library/UpdateChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Typography from '@mui/material/Typography';
import client from 'util/client';
import makeToast from 'components/util/Toast';
import { IUpdateStatus } from 'typings';
import { useTranslation } from 'react-i18next';

interface IProgressProps {
progress: number;
Expand All @@ -30,6 +31,8 @@ interface IUpdateCheckerProps {
}

function UpdateChecker({ handleFinishedUpdate }: IUpdateCheckerProps) {
const { t } = useTranslation();

const [loading, setLoading] = useState(false);
const [progress, setProgress] = useState(0);

Expand All @@ -39,7 +42,7 @@ function UpdateChecker({ handleFinishedUpdate }: IUpdateCheckerProps) {
setProgress(0);
await client.post('/api/v1/update/fetch');
} catch (e) {
makeToast('Checking for updates failed!', 'error');
makeToast(t('global.error.label.update_failed'), 'error');
setLoading(false);
}
};
Expand Down
22 changes: 12 additions & 10 deletions src/components/manga/ChapterCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import client from 'util/client';
import { BACK } from 'util/useBackTo';
import { getUploadDateString } from 'util/date';
import { IChapter, IDownloadChapter } from 'typings';
import { useTranslation } from 'react-i18next';

interface IProps {
chapter: IChapter;
Expand All @@ -41,6 +42,7 @@ interface IProps {
}

const ChapterCard: React.FC<IProps> = (props: IProps) => {
const { t } = useTranslation();
const theme = useTheme();

const { chapter, triggerChaptersUpdate, downloadChapter: dc, showChapterNumber, onSelect, selected } = props;
Expand Down Expand Up @@ -135,12 +137,12 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
sx={{ mr: 0.5, position: 'relative', top: '0.15em' }}
/>
)}
{showChapterNumber ? `Chapter ${chapter.chapterNumber}` : chapter.name}
{showChapterNumber ? `${t('chapter.title')} ${chapter.chapterNumber}` : chapter.name}
</Typography>
<Typography variant="caption">{chapter.scanlator}</Typography>
<Typography variant="caption">
{getUploadDateString(chapter.uploadDate)}
{isDownloaded && 'Downloaded'}
{isDownloaded && `${t('chapter.status.label.downloaded')}`}
</Typography>
</Stack>

Expand All @@ -160,22 +162,22 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
<ListItemIcon>
<CheckBoxOutlineBlank fontSize="small" />
</ListItemIcon>
<ListItemText>Select</ListItemText>
<ListItemText>{t('chapter.action.label.select')}</ListItemText>
</MenuItem>
{isDownloaded && (
<MenuItem onClick={deleteChapter}>
<ListItemIcon>
<Delete fontSize="small" />
</ListItemIcon>
<ListItemText>Delete</ListItemText>
<ListItemText>{t('chapter.action.download.delete.label.action')}</ListItemText>
</MenuItem>
)}
{canBeDownloaded && (
<MenuItem onClick={downloadChapter}>
<ListItemIcon>
<Download fontSize="small" />
</ListItemIcon>
<ListItemText>Download</ListItemText>
<ListItemText>{t('chapter.action.download.add.label.action')}</ListItemText>
</MenuItem>
)}
<MenuItem onClick={() => sendChange('bookmarked', !chapter.bookmarked)}>
Expand All @@ -184,8 +186,8 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
{!chapter.bookmarked && <BookmarkAdd fontSize="small" />}
</ListItemIcon>
<ListItemText>
{chapter.bookmarked && 'Remove bookmark'}
{!chapter.bookmarked && 'Add bookmark'}
{chapter.bookmarked && t('chapter.action.bookmark.remove.label.action')}
{!chapter.bookmarked && t('chapter.action.bookmark.add.label.action')}
</ListItemText>
</MenuItem>
<MenuItem onClick={() => sendChange('read', !chapter.read)}>
Expand All @@ -194,15 +196,15 @@ const ChapterCard: React.FC<IProps> = (props: IProps) => {
{!chapter.read && <Done fontSize="small" />}
</ListItemIcon>
<ListItemText>
{chapter.read && 'Mark as unread'}
{!chapter.read && 'Mark as read'}
{chapter.read && t('chapter.action.mark_as_read.remove.label.action')}
{!chapter.read && t('chapter.action.mark_as_read.add.label.action.current')}
</ListItemText>
</MenuItem>
<MenuItem onClick={() => sendChange('markPrevRead', true)}>
<ListItemIcon>
<DoneAll fontSize="small" />
</ListItemIcon>
<ListItemText>Mark previous as Read</ListItemText>
<ListItemText>{t('chapter.action.mark_as_read.add.label.action.previous')}</ListItemText>
</MenuItem>
</Menu>
</Card>
Expand Down
Loading

0 comments on commit d58bd6c

Please sign in to comment.