🚧 wip extract to translations file
This commit is contained in:
@@ -5,6 +5,7 @@ import { ColorSelector } from './ColorSelector';
|
||||
import { OpacitySelector } from './OpacitySelector';
|
||||
import { AppCardWidthSelector } from './AppCardWidthSelector';
|
||||
import { ShadeSelector } from './ShadeSelector';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export default function TitleChanger() {
|
||||
const { config, setConfig } = useConfig();
|
||||
@@ -40,19 +41,27 @@ export default function TitleChanger() {
|
||||
<Stack mb="md" mr="sm" mt="xs">
|
||||
<form onSubmit={form.onSubmit((values) => saveChanges(values))}>
|
||||
<Stack>
|
||||
<TextInput label="Page title" placeholder="Homarr 🦞" {...form.getInputProps('title')} />
|
||||
<TextInput label="Logo" placeholder="/img/logo.png" {...form.getInputProps('logo')} />
|
||||
<TextInput
|
||||
label="Favicon"
|
||||
placeholder="/favicon.png"
|
||||
label={t('settings.tabs.customizations.settings.pageTitle.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.pageTitle.placeholder')}
|
||||
{...form.getInputProps('title')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('settings.tabs.customizations.settings.logo.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.logo.placeholder')}
|
||||
{...form.getInputProps('logo')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('settings.tabs.customizations.settings.favicon.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.favicon.placeholder')}
|
||||
{...form.getInputProps('favicon')}
|
||||
/>
|
||||
<TextInput
|
||||
label="Background"
|
||||
placeholder="/img/background.png"
|
||||
label={t('settings.tabs.customizations.settings.background.label')}
|
||||
placeholder={t('settings.tabs.customizations.settings.background.placeholder')}
|
||||
{...form.getInputProps('background')}
|
||||
/>
|
||||
<Button type="submit">Save</Button>
|
||||
<Button type="submit">{t('settings.tabs.customizations.settings.buttons.submit')}</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
<ColorSelector type="primary" />
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||
import { ColorSwatch, Grid, Group, Popover, Text, useMantineTheme } from '@mantine/core';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { useColorTheme } from '../../tools/color';
|
||||
import { t } from 'i18next';
|
||||
|
||||
interface ColorControlProps {
|
||||
type: string;
|
||||
@@ -82,7 +83,11 @@ export function ColorSelector({ type }: ColorControlProps) {
|
||||
</Grid>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
<Text>{type[0].toUpperCase() + type.slice(1)} color</Text>
|
||||
<Text>
|
||||
{t('settings.tabs.customizations.settings.colorSelector.suffix', {
|
||||
color: type[0].toUpperCase() + type.slice(1),
|
||||
})}
|
||||
</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import ConfigChanger from '../Config/ConfigChanger';
|
||||
import SaveConfigComponent from '../Config/SaveConfig';
|
||||
import ModuleEnabler from './ModuleEnabler';
|
||||
import Tip from '../layout/Tip';
|
||||
import { t } from 'i18next';
|
||||
import LanguageSwitch from './LanguageSwitch';
|
||||
|
||||
export default function CommonSettings(args: any) {
|
||||
const { config, setConfig } = useConfig();
|
||||
@@ -26,15 +28,12 @@ export default function CommonSettings(args: any) {
|
||||
return (
|
||||
<Stack mb="md" mr="sm">
|
||||
<Stack spacing={0} mt="xs">
|
||||
<Text>Search engine</Text>
|
||||
<Tip>
|
||||
Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube or
|
||||
for a Torrent respectively.
|
||||
</Tip>
|
||||
<Text>{t('settings.tabs.common.settings.searchEngine.title')}</Text>
|
||||
<Tip>{t('settings.tabs.common.settings.searchEngine.tips.generalTip')}</Tip>
|
||||
<SegmentedControl
|
||||
fullWidth
|
||||
mb="sm"
|
||||
title="Search engine"
|
||||
title={t('settings.tabs.common.settings.searchEngine.title')}
|
||||
value={
|
||||
// Match config.settings.searchUrl with a key in the matches array
|
||||
searchUrl
|
||||
@@ -56,10 +55,10 @@ export default function CommonSettings(args: any) {
|
||||
/>
|
||||
{searchUrl === 'Custom' && (
|
||||
<>
|
||||
<Tip>%s can be used as a placeholder for the query.</Tip>
|
||||
<Tip>{t('settings.tabs.common.settings.searchEngine.tips.placeholderTip')}</Tip>
|
||||
<TextInput
|
||||
label="Query URL"
|
||||
placeholder="Custom query URL"
|
||||
label={t('settings.tabs.common.settings.searchEngine.customEngine.label')}
|
||||
placeholder={t('settings.tabs.common.settings.searchEngine.customEngine.placeholder')}
|
||||
value={customSearchUrl}
|
||||
onChange={(event) => {
|
||||
setCustomSearchUrl(event.currentTarget.value);
|
||||
@@ -78,9 +77,10 @@ export default function CommonSettings(args: any) {
|
||||
<ColorSchemeSwitch />
|
||||
<WidgetsPositionSwitch />
|
||||
<ModuleEnabler />
|
||||
<LanguageSwitch />
|
||||
<ConfigChanger />
|
||||
<SaveConfigComponent />
|
||||
<Tip>Upload your config file by dragging and dropping it onto the page!</Tip>
|
||||
<Tip>{t('settings.tabs.common.settings.configTip')}</Tip>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Group, ActionIcon, Anchor, Text } from '@mantine/core';
|
||||
import { IconBrandDiscord, IconBrandGithub } from '@tabler/icons';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { CURRENT_VERSION } from '../../../data/constants';
|
||||
|
||||
export default function Credits(props: any) {
|
||||
@@ -27,7 +29,7 @@ export default function Credits(props: any) {
|
||||
color: 'gray',
|
||||
}}
|
||||
>
|
||||
Made with ❤️ by @
|
||||
{t('settings.credits.madeWithLove')}
|
||||
<Anchor
|
||||
href="https://github.com/ajnart"
|
||||
style={{ color: 'inherit', fontStyle: 'inherit', fontSize: 'inherit' }}
|
||||
|
||||
78
src/components/Settings/LanguageSwitch.tsx
Normal file
78
src/components/Settings/LanguageSwitch.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Group, Image, Select, Stack, Text } from '@mantine/core';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { IconLanguage } from '@tabler/icons';
|
||||
|
||||
import { forwardRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { convertCodeToName } from '../../translations/i18n';
|
||||
|
||||
export default function LanguageSwitch() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { language, languages, changeLanguage } = i18n;
|
||||
|
||||
const [selectedLanguage, setSelectedLanguage] = useState<string | null>(language);
|
||||
|
||||
const data = languages.map((language) => ({
|
||||
image: `https://countryflagsapi.com/png/${language.split('-').pop()}`,
|
||||
label: convertCodeToName(language),
|
||||
value: language,
|
||||
}));
|
||||
|
||||
const onChangeSelect = (value: string) => {
|
||||
setSelectedLanguage(value);
|
||||
|
||||
const languageName = convertCodeToName(value);
|
||||
|
||||
changeLanguage(value)
|
||||
.then(() => {
|
||||
showNotification({
|
||||
title: 'Language changed',
|
||||
message: `You changed the language to '${languageName}'`,
|
||||
color: 'green',
|
||||
autoClose: 5000,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
showNotification({
|
||||
title: 'Failed to change language',
|
||||
message: `Failed to change to '${languageName}', Error:'${err}`,
|
||||
color: 'red',
|
||||
autoClose: 5000,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Select
|
||||
icon={<IconLanguage size={18} />}
|
||||
label={t('settings.tabs.common.settings.language.title')}
|
||||
data={data}
|
||||
itemComponent={SelectItem}
|
||||
nothingFound="Nothing found"
|
||||
onChange={onChangeSelect}
|
||||
value={selectedLanguage}
|
||||
defaultValue={language}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
interface ItemProps extends React.ComponentPropsWithoutRef<'div'> {
|
||||
image: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const SelectItem = forwardRef<HTMLDivElement, ItemProps>(
|
||||
({ image, label, ...others }: ItemProps, ref) => (
|
||||
<div ref={ref} {...others}>
|
||||
<Group noWrap>
|
||||
<Image src={image} width={30} height={20} radius="xs" />
|
||||
|
||||
<div>
|
||||
<Text size="sm">{label}</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Checkbox, SimpleGrid, Stack, Title } from '@mantine/core';
|
||||
import { t } from 'i18next';
|
||||
import * as Modules from '../../modules';
|
||||
import { useConfig } from '../../tools/state';
|
||||
|
||||
@@ -7,7 +8,7 @@ export default function ModuleEnabler(props: any) {
|
||||
const modules = Object.values(Modules).map((module) => module);
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={4}>Module enabler</Title>
|
||||
<Title order={4}>{t('settings.tabs.common.settings.moduleEnabler.title')}</Title>
|
||||
<SimpleGrid cols={3} spacing="xs">
|
||||
{modules.map((module) => (
|
||||
<Checkbox
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Text, Slider, Stack } from '@mantine/core';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export function OpacitySelector() {
|
||||
const { config, setConfig } = useConfig();
|
||||
@@ -30,7 +31,7 @@ export function OpacitySelector() {
|
||||
|
||||
return (
|
||||
<Stack spacing="xs">
|
||||
<Text>App Opacity</Text>
|
||||
<Text>{t('settings.tabs.customizations.settings.opacitySelector.label')}</Text>
|
||||
<Slider
|
||||
defaultValue={config.settings.appOpacity || 100}
|
||||
step={10}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { ActionIcon, Title, Tooltip, Drawer, Tabs, ScrollArea } from '@mantine/c
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
import { IconSettings } from '@tabler/icons';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import AdvancedSettings from './AdvancedSettings';
|
||||
import CommonSettings from './CommonSettings';
|
||||
import Credits from './Credits';
|
||||
@@ -10,8 +12,8 @@ function SettingsMenu(props: any) {
|
||||
return (
|
||||
<Tabs defaultValue="Common">
|
||||
<Tabs.List grow>
|
||||
<Tabs.Tab value="Common">Common</Tabs.Tab>
|
||||
<Tabs.Tab value="Customizations">Customizations</Tabs.Tab>
|
||||
<Tabs.Tab value="Common">{t('settings.tabs.common.title')}</Tabs.Tab>
|
||||
<Tabs.Tab value="Customizations">{t('settings.tabs.customizations.title')}</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel data-autofocus value="Common">
|
||||
<ScrollArea style={{ height: '78vh' }} offsetScrollbars>
|
||||
@@ -37,14 +39,14 @@ export function SettingsMenuButton(props: any) {
|
||||
size="xl"
|
||||
padding="lg"
|
||||
position="right"
|
||||
title={<Title order={5}>Settings</Title>}
|
||||
title={<Title order={5}>{t('settings.title')}</Title>}
|
||||
opened={props.opened || opened}
|
||||
onClose={() => setOpened(false)}
|
||||
>
|
||||
<SettingsMenu />
|
||||
<Credits />
|
||||
</Drawer>
|
||||
<Tooltip label="Settings">
|
||||
<Tooltip label={t('settings.tooltip')}>
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
radius="md"
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@mantine/core';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { useColorTheme } from '../../tools/color';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export function ShadeSelector() {
|
||||
const { config, setConfig } = useConfig();
|
||||
@@ -94,7 +95,7 @@ export function ShadeSelector() {
|
||||
</Stack>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
<Text>Shade</Text>
|
||||
<Text>{t('settings.tabs.customizations.settings.shadeSelector.label')}</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user