fix(i18n): add missing translation keys, update lang/locale logic
This commit is contained in:
parent
8bc7d2599e
commit
7d8828a358
|
@ -1,15 +1,25 @@
|
|||
{
|
||||
"css.validate": false,
|
||||
"scss.validate": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.wordWrap": "on",
|
||||
"eslint.workingDirectories": ["schema", "client", "server"],
|
||||
"i18n-ally.enabledFrameworks": ["i18next"],
|
||||
"i18n-ally.localesPaths": ["client/public/locales"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.keystyle": "nested"
|
||||
"eslint.workingDirectories": [
|
||||
"schema",
|
||||
"client",
|
||||
"server"
|
||||
],
|
||||
"i18n-ally.enabledFrameworks": [
|
||||
"react"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.localesPaths": [
|
||||
"client/public/locales"
|
||||
],
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"scss.validate": false
|
||||
}
|
|
@ -74,19 +74,19 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||
})}
|
||||
>
|
||||
<div className={styles.controller}>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-in')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-in') as string}>
|
||||
<ButtonBase onClick={() => zoomIn(0.25)}>
|
||||
<ZoomIn fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.zoom-out')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.zoom-out') as string}>
|
||||
<ButtonBase onClick={() => zoomOut(0.25)}>
|
||||
<ZoomOut fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.center-artboard')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.center-artboard') as string}>
|
||||
<ButtonBase onClick={() => centerView(0.95)}>
|
||||
<FilterCenterFocus fontSize="medium" />
|
||||
</ButtonBase>
|
||||
|
@ -96,7 +96,7 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||
|
||||
{isDesktop && (
|
||||
<>
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-orientation')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-orientation') as string}>
|
||||
<ButtonBase onClick={handleTogglePageOrientation}>
|
||||
{orientation === 'vertical' ? (
|
||||
<AlignHorizontalCenter fontSize="medium" />
|
||||
|
@ -106,13 +106,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-page-break-line')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-page-break-line') as string}>
|
||||
<ButtonBase onClick={handleTogglePageBreakLine}>
|
||||
<InsertPageBreak fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.toggle-sidebars')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.toggle-sidebars') as string}>
|
||||
<ButtonBase onClick={handleToggleSidebar}>
|
||||
<ViewSidebar fontSize="medium" />
|
||||
</ButtonBase>
|
||||
|
@ -122,13 +122,13 @@ const ArtboardController: React.FC<ReactZoomPanPinchRef> = ({ zoomIn, zoomOut, c
|
|||
</>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.copy-link')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.copy-link') as string}>
|
||||
<ButtonBase onClick={handleCopyLink}>
|
||||
<Link fontSize="medium" />
|
||||
</ButtonBase>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip arrow placement="top" title={t<string>('builder.controller.tooltip.export-pdf')}>
|
||||
<Tooltip arrow placement="top" title={t('builder.controller.tooltip.export-pdf') as string}>
|
||||
<ButtonBase onClick={handleExportPDF} disabled={isLoading}>
|
||||
<Download fontSize="medium" />
|
||||
</ButtonBase>
|
||||
|
|
|
@ -184,7 +184,7 @@ const Header = () => {
|
|||
<ListItemText>{t('builder.header.menu.share-link')}</ListItemText>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.share-link')}>
|
||||
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.share-link') as string}>
|
||||
<div>
|
||||
<MenuItem>
|
||||
<ListItemIcon>
|
||||
|
@ -196,7 +196,7 @@ const Header = () => {
|
|||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="right" title={t<string>('builder.header.menu.tooltips.delete')}>
|
||||
<Tooltip arrow placement="right" title={t('builder.header.menu.tooltips.delete') as string}>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<ListItemIcon>
|
||||
<Delete className="scale-90" />
|
||||
|
|
|
@ -58,14 +58,14 @@ const PhotoFilters = () => {
|
|||
|
||||
<div className="flex items-center">
|
||||
<FormControlLabel
|
||||
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label')}
|
||||
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.grayscale.label') as string}
|
||||
control={
|
||||
<Checkbox color="secondary" checked={grayscale} onChange={(_, value) => handleSetGrayscale(value)} />
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
label={t<string>('builder.leftSidebar.sections.basics.photo-filters.effects.border.label')}
|
||||
label={t('builder.leftSidebar.sections.basics.photo-filters.effects.border.label') as string}
|
||||
control={<Checkbox color="secondary" checked={border} onChange={(_, value) => handleSetBorder(value)} />}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -67,8 +67,8 @@ const PhotoUpload: React.FC = () => {
|
|||
<Tooltip
|
||||
title={
|
||||
isEmpty(photo.url)
|
||||
? t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload')
|
||||
: t<string>('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove')
|
||||
? (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.upload') as string)
|
||||
: (t('builder.leftSidebar.sections.basics.photo-upload.tooltip.remove') as string)
|
||||
}
|
||||
>
|
||||
<Avatar sx={{ width: 96, height: 96 }} src={photo.url} />
|
||||
|
|
|
@ -32,7 +32,7 @@ const SectionSettings: React.FC<Props> = ({ path }) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Tooltip title={t<string>('builder.common.columns.tooltip')}>
|
||||
<Tooltip title={t('builder.common.columns.tooltip') as string}>
|
||||
<ButtonBase onClick={handleClick} sx={{ padding: 1, borderRadius: 1 }} className="opacity-50 hover:opacity-75">
|
||||
<ViewWeek /> <span className="ml-1.5 text-xs">{columns}</span>
|
||||
</ButtonBase>
|
||||
|
|
|
@ -62,7 +62,7 @@ const Layout = () => {
|
|||
path="metadata.layout"
|
||||
name={t('builder.rightSidebar.sections.layout.heading')}
|
||||
action={
|
||||
<Tooltip title={t<string>('builder.rightSidebar.sections.layout.tooltip.reset-layout')}>
|
||||
<Tooltip title={t('builder.rightSidebar.sections.layout.tooltip.reset-layout') as string}>
|
||||
<IconButton onClick={handleResetLayout}>
|
||||
<Restore />
|
||||
</IconButton>
|
||||
|
@ -81,7 +81,7 @@ const Layout = () => {
|
|||
|
||||
<div className={clsx(styles.delete, { hidden: pageIndex === 0 })}>
|
||||
<Tooltip
|
||||
title={t<string>('builder.common.actions.delete', { token: t('builder.common.glossary.page') })}
|
||||
title={t('builder.common.actions.delete', { token: t('builder.common.glossary.page') }) as string}
|
||||
>
|
||||
<IconButton size="small" onClick={() => handleDeletePage(pageIndex)}>
|
||||
<Close fontSize="small" />
|
||||
|
|
|
@ -20,25 +20,24 @@ import { useMutation } from 'react-query';
|
|||
|
||||
import Heading from '@/components/shared/Heading';
|
||||
import ThemeSwitch from '@/components/shared/ThemeSwitch';
|
||||
import { Language, languages } from '@/config/languages';
|
||||
import { Language, languageMap, languages } from '@/config/languages';
|
||||
import { ServerError } from '@/services/axios';
|
||||
import queryClient from '@/services/react-query';
|
||||
import { loadSampleData, LoadSampleDataParams, resetResume, ResetResumeParams } from '@/services/resume';
|
||||
import { setLanguage, setTheme, togglePageBreakLine, togglePageOrientation } from '@/store/build/buildSlice';
|
||||
import { setTheme, togglePageBreakLine, togglePageOrientation } from '@/store/build/buildSlice';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setResumeState } from '@/store/resume/resumeSlice';
|
||||
import { dateFormatOptions } from '@/utils/date';
|
||||
|
||||
const Settings = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { locale, ...router } = useRouter();
|
||||
|
||||
const resume = useAppSelector((state) => state.resume);
|
||||
const theme = useAppSelector((state) => state.build.theme);
|
||||
const language = useAppSelector((state) => state.build.language);
|
||||
const breakLine = useAppSelector((state) => state.build.page.breakLine);
|
||||
const orientation = useAppSelector((state) => state.build.page.orientation);
|
||||
|
||||
|
@ -62,14 +61,12 @@ const Settings = () => {
|
|||
dispatch(setResumeState({ path: 'metadata.date.format', value }));
|
||||
|
||||
const handleChangeLanguage = (value: Language | null) => {
|
||||
const { pathname, asPath, query } = router;
|
||||
const { pathname, asPath, query, push } = router;
|
||||
const code = value?.code || 'en';
|
||||
|
||||
dayjs.locale(code);
|
||||
dispatch(setLanguage({ language: code }));
|
||||
document.cookie = `NEXT_LOCALE=${code}; path=/; expires=2147483647`;
|
||||
|
||||
router.push({ pathname, query }, asPath, { locale: code });
|
||||
push({ pathname, query }, asPath, { locale: code });
|
||||
};
|
||||
|
||||
const handleLoadSampleData = async () => {
|
||||
|
@ -132,7 +129,7 @@ const Settings = () => {
|
|||
disableClearable
|
||||
className="my-2 w-full"
|
||||
options={languages}
|
||||
value={language}
|
||||
value={languageMap[locale ?? 'en']}
|
||||
isOptionEqualToValue={(a, b) => a.code === b.code}
|
||||
onChange={(_, value) => handleChangeLanguage(value)}
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
|
|
|
@ -63,7 +63,7 @@ const Sharing = () => {
|
|||
|
||||
<div className="mt-1 flex w-full">
|
||||
<FormControlLabel
|
||||
label={t<string>('builder.rightSidebar.sections.sharing.short-url.label')}
|
||||
label={t('builder.rightSidebar.sections.sharing.short-url.label') as string}
|
||||
control={
|
||||
<Checkbox className="mr-1" checked={showShortUrl} onChange={(_, value) => setShowShortUrl(value)} />
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
|
|||
<ListItemText>{t('dashboard.resume.menu.share-link')}</ListItemText>
|
||||
</MenuItem>
|
||||
) : (
|
||||
<Tooltip arrow placement="right" title={t<string>('dashboard.resume.menu.tooltips.share-link')}>
|
||||
<Tooltip arrow placement="right" title={t('dashboard.resume.menu.tooltips.share-link') as string}>
|
||||
<div>
|
||||
<MenuItem>
|
||||
<ListItemIcon>
|
||||
|
@ -173,7 +173,7 @@ const ResumePreview: React.FC<Props> = ({ resume }) => {
|
|||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip arrow placement="right" title={t<string>('dashboard.resume.menu.tooltips.delete')}>
|
||||
<Tooltip arrow placement="right" title={t('dashboard.resume.menu.tooltips.delete') as string}>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<ListItemIcon>
|
||||
<DeleteOutline className="scale-90" />
|
||||
|
|
|
@ -12,7 +12,7 @@ const ColorAvatar: React.FC<Props> = ({ color, size = 20, onClick }) => {
|
|||
|
||||
return (
|
||||
<IconButton onClick={handleClick}>
|
||||
<Avatar sx={{ bgcolor: color, width: size, height: size }}> </Avatar>
|
||||
<Avatar sx={{ bgcolor: color, width: size, height: size }}> </Avatar>
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -72,19 +72,19 @@ const Heading: React.FC<Props> = ({
|
|||
})}
|
||||
>
|
||||
{isEditable && (
|
||||
<Tooltip title={t<string>('builder.common.tooltip.rename-section')}>
|
||||
<Tooltip title={t('builder.common.tooltip.rename-section') as string}>
|
||||
<IconButton onClick={toggleEditMode}>{editMode ? <Check /> : <DriveFileRenameOutline />}</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isHideable && (
|
||||
<Tooltip title={t<string>('builder.common.tooltip.toggle-visibility')}>
|
||||
<Tooltip title={t('builder.common.tooltip.toggle-visibility') as string}>
|
||||
<IconButton onClick={toggleVisibility}>{visibility ? <Visibility /> : <VisibilityOff />}</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isDeletable && (
|
||||
<Tooltip title={t<string>('builder.common.tooltip.delete-section')}>
|
||||
<Tooltip title={t('builder.common.tooltip.delete-section') as string}>
|
||||
<IconButton onClick={handleDelete}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import { Language } from '@mui/icons-material';
|
||||
import { IconButton, Popover } from '@mui/material';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { MouseEvent, useState } from 'react';
|
||||
|
||||
import { languages } from '@/config/languages';
|
||||
import { setLanguage } from '@/store/build/buildSlice';
|
||||
import { useAppDispatch } from '@/store/hooks';
|
||||
|
||||
import styles from './LanguageSwitcher.module.scss';
|
||||
|
||||
const LanguageSwitcher = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
|
@ -24,10 +22,7 @@ const LanguageSwitcher = () => {
|
|||
const handleChangeLanguage = (locale: string) => {
|
||||
const { pathname, asPath, query } = router;
|
||||
|
||||
dayjs.locale(locale);
|
||||
dispatch(setLanguage({ language: locale }));
|
||||
document.cookie = `NEXT_LOCALE=${locale}; path=/; expires=2147483647`;
|
||||
|
||||
router.push({ pathname, query }, asPath, { locale });
|
||||
};
|
||||
|
||||
|
@ -59,7 +54,7 @@ const LanguageSwitcher = () => {
|
|||
))}
|
||||
|
||||
<a href="https://translate.rxresu.me" target="_blank" rel="noreferrer" className={styles.language}>
|
||||
Missing your language?
|
||||
{t('common.footer.language.missing')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Divider, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, Toolti
|
|||
import { ListItem as ListItemType } from '@reactive-resume/schema';
|
||||
import clsx from 'clsx';
|
||||
import isFunction from 'lodash/isFunction';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { DropTargetMonitor, useDrag, useDrop, XYCoord } from 'react-dnd';
|
||||
|
||||
|
@ -26,6 +27,8 @@ type Props = {
|
|||
};
|
||||
|
||||
const ListItem: React.FC<Props> = ({ item, index, title, subtitle, onMove, onEdit, onDelete, onDuplicate }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
|
||||
|
||||
|
@ -122,29 +125,25 @@ const ListItem: React.FC<Props> = ({ item, index, title, subtitle, onMove, onEdi
|
|||
<ListItemIcon>
|
||||
<DriveFileRenameOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Edit</ListItemText>
|
||||
<ListItemText>{t('builder.common.list.actions.edit')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={() => handleDuplicate(item)}>
|
||||
<ListItemIcon>
|
||||
<FileCopy className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Duplicate</ListItemText>
|
||||
<ListItemText>{t('builder.common.list.actions.duplicate')}</ListItemText>
|
||||
</MenuItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Tooltip
|
||||
arrow
|
||||
placement="right"
|
||||
title="Are you sure you want to delete this item? This is an irreversible action."
|
||||
>
|
||||
<Tooltip arrow placement="right" title={t('builder.common.tooltip.delete-item') as string}>
|
||||
<div>
|
||||
<MenuItem onClick={() => handleDelete(item)}>
|
||||
<ListItemIcon>
|
||||
<DeleteOutline className="scale-90" />
|
||||
</ListItemIcon>
|
||||
<ListItemText>Delete</ListItemText>
|
||||
<ListItemText>{t('builder.common.list.actions.delete')}</ListItemText>
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
export const FONTS_QUERY = 'fonts';
|
||||
export const RESUMES_QUERY = 'resumes';
|
||||
|
||||
// Regular Expressions
|
||||
export const VALID_URL_REGEX = /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
||||
|
||||
// Date Formats
|
||||
export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -34,9 +35,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
title: Joi.string().required(),
|
||||
awarder: Joi.string().required(),
|
||||
date: Joi.string().allow(''),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
});
|
||||
|
||||
|
@ -154,6 +153,7 @@ const AwardModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -34,9 +35,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
name: Joi.string().required(),
|
||||
issuer: Joi.string().required(),
|
||||
date: Joi.string().allow(''),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
});
|
||||
|
||||
|
@ -154,6 +153,7 @@ const CertificateModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
import ArrayInput from '@/components/shared/ArrayInput';
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -47,9 +48,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
start: Joi.string().allow(''),
|
||||
end: Joi.string().allow(''),
|
||||
}),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
level: Joi.string().allow(''),
|
||||
levelNum: Joi.number().min(0).max(10),
|
||||
summary: Joi.string().allow(''),
|
||||
|
@ -194,6 +193,7 @@ const CustomModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
import ArrayInput from '@/components/shared/ArrayInput';
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -46,9 +47,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
start: Joi.string().allow(''),
|
||||
end: Joi.string().allow(''),
|
||||
}),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
courses: Joi.array().items(Joi.string().optional()),
|
||||
});
|
||||
|
@ -217,6 +216,7 @@ const EducationModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { useEffect, useMemo } from 'react';
|
|||
import { Controller, useForm } from 'react-hook-form';
|
||||
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -21,17 +22,14 @@ const path = 'sections.profile';
|
|||
const defaultState: FormData = {
|
||||
network: '',
|
||||
username: '',
|
||||
url: 'https://',
|
||||
url: '',
|
||||
};
|
||||
|
||||
const schema = Joi.object<FormData>({
|
||||
id: Joi.string(),
|
||||
network: Joi.string().required(),
|
||||
username: Joi.string().required(),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.default('https://')
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
});
|
||||
|
||||
const ProfileModal: React.FC = () => {
|
||||
|
@ -131,6 +129,7 @@ const ProfileModal: React.FC = () => {
|
|||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
className="col-span-2"
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
import ArrayInput from '@/components/shared/ArrayInput';
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -42,9 +43,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
start: Joi.string().allow(''),
|
||||
end: Joi.string().allow(''),
|
||||
}),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
keywords: Joi.array().items(Joi.string().optional()),
|
||||
});
|
||||
|
@ -187,6 +186,7 @@ const ProjectModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -34,9 +35,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
name: Joi.string().required(),
|
||||
publisher: Joi.string().required(),
|
||||
date: Joi.string().allow(''),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
});
|
||||
|
||||
|
@ -154,6 +153,7 @@ const PublicationModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
{...field}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -40,9 +41,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
start: Joi.string().allow(''),
|
||||
end: Joi.string().allow(''),
|
||||
}),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
});
|
||||
|
||||
|
@ -184,6 +183,7 @@ const VolunteerModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Controller, useForm } from 'react-hook-form';
|
|||
|
||||
import BaseModal from '@/components/shared/BaseModal';
|
||||
import MarkdownSupported from '@/components/shared/MarkdownSupported';
|
||||
import { VALID_URL_REGEX } from '@/constants/index';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
import { addItem, editItem } from '@/store/resume/resumeSlice';
|
||||
|
@ -40,9 +41,7 @@ const schema = Joi.object<FormData>().keys({
|
|||
start: Joi.string().allow(''),
|
||||
end: Joi.string().allow(''),
|
||||
}),
|
||||
url: Joi.string()
|
||||
.pattern(/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, { name: 'valid URL' })
|
||||
.allow(''),
|
||||
url: Joi.string().pattern(VALID_URL_REGEX, { name: 'valid URL' }).allow(''),
|
||||
summary: Joi.string().allow(''),
|
||||
});
|
||||
|
||||
|
@ -184,6 +183,7 @@ const WorkModal: React.FC = () => {
|
|||
render={({ field, fieldState }) => (
|
||||
<TextField
|
||||
label={t('builder.common.form.url.label')}
|
||||
placeholder="https://"
|
||||
className="col-span-2"
|
||||
error={!!fieldState.error}
|
||||
helperText={fieldState.error?.message}
|
||||
|
|
|
@ -125,7 +125,7 @@ const CreateResumeModal: React.FC = () => {
|
|||
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
label={t<string>('modals.dashboard.create-resume.form.public.label')}
|
||||
label={t('modals.dashboard.create-resume.form.public.label') as string}
|
||||
control={
|
||||
<Controller
|
||||
name="isPublic"
|
||||
|
|
|
@ -84,7 +84,7 @@ const ImportExternalModal: React.FC = () => {
|
|||
<p className="mb-2">
|
||||
<Trans t={t} i18nKey="modals.dashboard.import-external.linkedin.body">
|
||||
You can save time by exporting your data from LinkedIn and using it to auto-fill fields on Reactive Resume.
|
||||
Head on over to the
|
||||
Head over to the
|
||||
<a
|
||||
href="https://www.linkedin.com/psettings/member-data"
|
||||
className="underline"
|
||||
|
@ -93,7 +93,7 @@ const ImportExternalModal: React.FC = () => {
|
|||
>
|
||||
Data Privacy
|
||||
</a>
|
||||
section on LinkedIn and request an archive of your data. Once it is available, upload the ZIP archive below.
|
||||
section on LinkedIn and request an archive of your data. Once it is available, upload the ZIP file below.
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -61,12 +61,18 @@
|
|||
"page": "Page"
|
||||
},
|
||||
"list": {
|
||||
"empty-text": "This list is empty."
|
||||
"empty-text": "This list is empty.",
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"duplicate": "Duplicate",
|
||||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"tooltip": {
|
||||
"delete-section": "Delete Section",
|
||||
"rename-section": "Rename Section",
|
||||
"toggle-visibility": "Toggle Visibility"
|
||||
"toggle-visibility": "Toggle Visibility",
|
||||
"delete-item": "Are you sure you want to delete this item? This is an irreversible action."
|
||||
}
|
||||
},
|
||||
"controller": {
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
"logout": "Logout"
|
||||
}
|
||||
},
|
||||
"description": "Reactive Resume is a free and open source resume builder that's built to make the mundane tasks of creating, updating and sharing your resume as easy as 1, 2, 3.",
|
||||
"footer": {
|
||||
"credit": "A passion project by <1>Amruth Pillai</1>",
|
||||
"language": {
|
||||
"missing": "Missing your language?"
|
||||
},
|
||||
"license": "By the community, for the community."
|
||||
},
|
||||
"markdown": {
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
"actions": {
|
||||
"upload-archive": "Upload ZIP Archive"
|
||||
},
|
||||
"body": "You can save time by exporting your data from LinkedIn and using it to auto-fill fields on Reactive Resume. Head on over to the <1>Data Privacy</1> section on LinkedIn and request an archive of your data. Once it is available, upload the ZIP archive below.",
|
||||
"body": "You can save time by exporting your data from LinkedIn and using it to auto-fill fields on Reactive Resume. Head over to the <1>Data Privacy</1> section on LinkedIn and request an archive of your data. Once it is available, upload the ZIP file below.",
|
||||
"heading": "Import From LinkedIn"
|
||||
},
|
||||
"reactive-resume": {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
"logout": "ಲಾಗ್ ಔಟ್"
|
||||
}
|
||||
},
|
||||
"description": "ಪ್ರತಿಕ್ರಿಯಾತ್ಮಕ ರೇಸುಮೆವು ಉಚಿತ ಮತ್ತು ಮುಕ್ತ ಮೂಲ ರೇಸುಮೆ ಬಿಲ್ಡರ್ ಆಗಿದ್ದು, ನಿಮ್ಮ ರೇಸುಮೆ ಅನ್ನು 1, 2, 3 ರಂತೆ ಸುಲಭವಾಗಿ ರಚಿಸುವ, ನವೀಕರಿಸುವ ಮತ್ತು ಹಂಚಿಕೊಳ್ಳುವ ಪ್ರಾಪಂಚಿಕ ಕಾರ್ಯಗಳನ್ನು ಮಾಡಲು ನಿರ್ಮಿಸಲಾಗಿದೆ.",
|
||||
"footer": {
|
||||
"credit": "<1>ಅಮೃತ್ ಪಿಳ್ಳೈ</1> ಅವರು ಉತ್ಸಾಹದಿಂದ ಮಾಡಿರುವ ಪ್ರಾಜೆಕ್ಟ್",
|
||||
"license": "ಸಮುದಾಯದಿಂದ, ಸಮುದಾಯಕ್ಕಾಗಿ."
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
},
|
||||
"heading": "ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಮರೆತಿರುವಿರಾ?",
|
||||
"help-text": "%1 ರ ಜೊತೆ ಜೋಡಣೆಯಾಗಿರುವ ಖಾತೆ ಇದ್ದಲ್ಲಿ, ನೀವು ನಿಮ್ಮ ಗುಪ್ತಪದ ಮರುಹೊಂದಿಕೆ ಕೊಂಡಿಯನ್ನು ಹೊಂದಿರುವ ಮಿಂಚೆಯನ್ನು ಪಡೆಯುವಿರಿ."
|
||||
"help-text": "ಖಾತೆಯು ಅಸ್ತಿತ್ವದಲ್ಲಿದ್ದರೆ, ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು ಲಿಂಕ್ನೊಂದಿಗೆ ಇಮೇಲ್ ಅನ್ನು ನೀವು ಸ್ವೀಕರಿಸುತ್ತೀರಿ."
|
||||
},
|
||||
"login": {
|
||||
"actions": {
|
||||
|
@ -104,16 +104,16 @@
|
|||
},
|
||||
"linkedin": {
|
||||
"actions": {
|
||||
"upload-archive": "ಜಿಪ್ (ZIP) ಆರ್ಕೈವ್ ಅನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ"
|
||||
"upload-archive": "ಜಿಪ್ ಆರ್ಕೈವ್ ಅನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ"
|
||||
},
|
||||
"body": "ಲಿಂಕ್ಡ್ಇನ್ನಿಂದ ನಿಮ್ಮ ಡೇಟಾವನ್ನು ರಫ್ತು ಮಾಡುವ ಮೂಲಕ ಮತ್ತು ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ನಲ್ಲಿ ಕ್ಷೇತ್ರಗಳನ್ನು ಸ್ವಯಂ ತುಂಬಲು ಅದನ್ನು ಬಳಸುವ ಮೂಲಕ ನೀವು ಸಮಯವನ್ನು ಉಳಿಸಬಹುದು. <1>ಡೇಟಾ ಗೌಪ್ಯತೆಗೆ ಹೋಗಿ</1> ಲಿಂಕ್ಡ್ಇನ್ನಲ್ಲಿ ವಿಭಾಗ ಮತ್ತು ನಿಮ್ಮ ಡೇಟಾದ ಆರ್ಕೈವ್ ಅನ್ನು ವಿನಂತಿಸಿ. ಒಮ್ಮೆ ಅದು ಲಭ್ಯವಾದ ನಂತರ, ಕೆಳಗಿನ ಜಿಪ್ (ZIP) ಆರ್ಕೈವ್ ಅನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ.",
|
||||
"heading": "ಲಿಂಕ್ಡಿನ್(LinkedIn) ನಿಂದ ಆಮದು ಮಾಡಿಕೊಳ್ಳಿ"
|
||||
"body": "ಲಿಂಕ್ಡ್ಇನ್ನಿಂದ ನಿಮ್ಮ ಡೇಟಾವನ್ನು ರಫ್ತು ಮಾಡುವ ಮೂಲಕ ಮತ್ತು ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ನಲ್ಲಿ ಕ್ಷೇತ್ರಗಳನ್ನು ಸ್ವಯಂ ತುಂಬಲು ಅದನ್ನು ಬಳಸುವ ಮೂಲಕ ನೀವು ಸಮಯವನ್ನು ಉಳಿಸಬಹುದು. <1>ಡೇಟಾ ಗೌಪ್ಯತೆಗೆ ಹೋಗಿ</1> ಲಿಂಕ್ಡ್ಇನ್ನಲ್ಲಿ ವಿಭಾಗ ಮತ್ತು ನಿಮ್ಮ ಡೇಟಾದ ಆರ್ಕೈವ್ ಅನ್ನು ವಿನಂತಿಸಿ. ಒಮ್ಮೆ ಅದು ಲಭ್ಯವಾದ ನಂತರ, ಕೆಳಗಿನ ಜಿಪ್ ಆರ್ಕೈವ್ ಅನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ.",
|
||||
"heading": "ಲಿಂಕ್ಡಿನ್ ನಿಂದ ಆಮದು ಮಾಡಿಕೊಳ್ಳಿ"
|
||||
},
|
||||
"reactive-resume": {
|
||||
"actions": {
|
||||
"upload-json": "ಜೆಸನ್ ಅನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ"
|
||||
},
|
||||
"body": "ನೀವು ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ನ ಪ್ರಸ್ತುತ ಆವೃತ್ತಿಯೊಂದಿಗೆ ರಫ್ತು ಮಾಡಲಾದ ಜೆಸನ್(JSON) ಅನ್ನು ಹೊಂದಿದ್ದರೆ, ಮತ್ತೆ ಸಂಪಾದಿಸಬಹುದಾದ ಆವೃತ್ತಿಯನ್ನು ಪಡೆಯಲು ನೀವು ಅದನ್ನು ಇಲ್ಲಿಗೆ ಆಮದು ಮಾಡಿಕೊಳ್ಳಬಹುದು. ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ನ ಹಿಂದಿನ ಆವೃತ್ತಿಗಳು ದುರದೃಷ್ಟವಶಾತ್ ಸದ್ಯಕ್ಕೆ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ.",
|
||||
"body": "ನೀವು ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ನ ಪ್ರಸ್ತುತ ಆವೃತ್ತಿಯೊಂದಿಗೆ ರಫ್ತು ಮಾಡಲಾದ ಜೆಸನ್ ಅನ್ನು ಹೊಂದಿದ್ದರೆ, ಮತ್ತೆ ಸಂಪಾದಿಸಬಹುದಾದ ಆವೃತ್ತಿಯನ್ನು ಪಡೆಯಲು ನೀವು ಅದನ್ನು ಇಲ್ಲಿಗೆ ಆಮದು ಮಾಡಿಕೊಳ್ಳಬಹುದು. ರಿಯಾಕ್ಟಿವ್ ರೆಸ್ಯೂಮ್ನ ಹಿಂದಿನ ಆವೃತ್ತಿಗಳು ದುರದೃಷ್ಟವಶಾತ್ ಸದ್ಯಕ್ಕೆ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ.",
|
||||
"heading": "ಜೆಸನ್ ರೆಸ್ಯೂಮ್ನಿಂದ ಆಮದು ಮಾಡಿಕೊಳ್ಳಿ"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
"logout": "வெளியேறு"
|
||||
}
|
||||
},
|
||||
"description": "ரியாக்டிவ் ரெஸ்யூம் என்பது ஒரு இலவச மற்றும் ஓப்பன் சோர்ஸ் ரெஸ்யூம் பில்டராகும், இது உங்கள் விண்ணப்பத்தை 1, 2, 3 என எளிதாக உருவாக்குவது, புதுப்பித்தல் மற்றும் பகிர்வது போன்ற சர்வ சாதாரணமான பணிகளைச் செய்ய உருவாக்கப்பட்டுள்ளது.",
|
||||
"footer": {
|
||||
"credit": "<1>அம்ருத் பிள்ளை</1>யின் திட்டம்",
|
||||
"license": "சமூகத்தால், சமூகத்திற்காக."
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import merge from 'lodash/merge';
|
||||
|
||||
import { Language, languageMap } from '@/config/languages';
|
||||
|
||||
export type Theme = 'light' | 'dark';
|
||||
|
||||
export type Sidebar = 'left' | 'right';
|
||||
|
@ -13,7 +11,6 @@ export type Orientation = 'horizontal' | 'vertical';
|
|||
|
||||
export type BuildState = {
|
||||
theme?: Theme;
|
||||
language: Language;
|
||||
sidebar: Record<Sidebar, SidebarState>;
|
||||
page: {
|
||||
breakLine: boolean;
|
||||
|
@ -22,7 +19,6 @@ export type BuildState = {
|
|||
};
|
||||
|
||||
const initialState: BuildState = {
|
||||
language: languageMap['en'],
|
||||
sidebar: {
|
||||
left: { open: false },
|
||||
right: { open: false },
|
||||
|
@ -35,8 +31,6 @@ const initialState: BuildState = {
|
|||
|
||||
type SetThemePayload = { theme: Theme };
|
||||
|
||||
type SetLanguagePayload = { language: string };
|
||||
|
||||
type ToggleSidebarPayload = { sidebar: Sidebar };
|
||||
|
||||
type SetSidebarStatePayload = { sidebar: Sidebar; state: SidebarState };
|
||||
|
@ -50,11 +44,6 @@ export const buildSlice = createSlice({
|
|||
|
||||
state.theme = theme;
|
||||
},
|
||||
setLanguage: (state, action: PayloadAction<SetLanguagePayload>) => {
|
||||
const { language } = action.payload;
|
||||
|
||||
state.language = languageMap[language];
|
||||
},
|
||||
toggleSidebar: (state, action: PayloadAction<ToggleSidebarPayload>) => {
|
||||
const { sidebar } = action.payload;
|
||||
|
||||
|
@ -76,7 +65,7 @@ export const buildSlice = createSlice({
|
|||
},
|
||||
});
|
||||
|
||||
export const { setTheme, setLanguage, toggleSidebar, setSidebarState, togglePageBreakLine, togglePageOrientation } =
|
||||
export const { setTheme, toggleSidebar, setSidebarState, togglePageBreakLine, togglePageOrientation } =
|
||||
buildSlice.actions;
|
||||
|
||||
export default buildSlice.reducer;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export const getCookie = (name: string): string | undefined => {
|
||||
const value = `; ${document.cookie}`;
|
||||
const parts = value.split(`; ${name}=`);
|
||||
|
||||
if (parts.length === 2) {
|
||||
return parts.at(-1);
|
||||
}
|
||||
};
|
|
@ -1,14 +1,19 @@
|
|||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const DateWrapper: React.FC = ({ children }) => {
|
||||
const { locale } = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
// Locales
|
||||
require('dayjs/locale/kn');
|
||||
}, []);
|
||||
|
||||
locale && dayjs.locale(locale);
|
||||
}, [locale]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue