mirror of
https://github.com/sern-handler/gui
synced 2026-06-05 17:06:52 +00:00
feat: localization support and spanish translation
This commit is contained in:
@@ -14,6 +14,7 @@ module.exports = {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
tsconfigRootDir: __dirname,
|
||||
project: '.swcrc'
|
||||
},
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
@@ -21,6 +22,6 @@ module.exports = {
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
},
|
||||
'@typescript-eslint/no-non-null-assertion': 'off'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'node:path'
|
||||
import * as path from 'node:path'
|
||||
import { app, BrowserWindow, dialog, ipcMain } from 'electron';
|
||||
import isDev from 'electron-is-dev';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
import * as colorette from 'colorette';
|
||||
import * as fs from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
@@ -47,7 +47,7 @@ function createWindow() {
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('submitForm', async (event, data) => {
|
||||
ipcMain.on('submitForm', async (event, data: InitModalData) => {
|
||||
const fileName = createRandomFileName('txt')
|
||||
// Process the submitted data here
|
||||
writeLineToLogAndConsole(`${colorette.green('✓')} Received sern init submit form data:`, fileName);
|
||||
@@ -192,4 +192,12 @@ function randomstring(length: number) {
|
||||
counter += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
interface InitModalData {
|
||||
projectName: string,
|
||||
chosenTemplate: string,
|
||||
installPackages: boolean,
|
||||
chosenPackageManager: string,
|
||||
selectedPath: string
|
||||
}
|
||||
40
locales/en.json
Normal file
40
locales/en.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"credits": {
|
||||
"propsTo": "@SrIzan10 (original language for this project)",
|
||||
"btw": "All COMMENT_FOR_TRANSLATOR keys are comments that help you how to write translations. Follow these so you get approved faster."
|
||||
},
|
||||
"translation": {
|
||||
"functionalityCard": {
|
||||
"init": {
|
||||
"description": "Scaffold a new project"
|
||||
},
|
||||
"plugins": {
|
||||
"description": "Manage the plugins of an existing project"
|
||||
}
|
||||
},
|
||||
"initModal": {
|
||||
"openModalButton": "Get started",
|
||||
"projectName": "Project name",
|
||||
"selectTemplate": "Select a template",
|
||||
"couldntFetchTemplates": "Couldn't fetch templates! Please do CTRL+R (u online?)",
|
||||
"installPackagesCheckbox": "Install packages while you're at it",
|
||||
"selectPackageManager": "Select a package manager",
|
||||
"chooseDirectoryButton": "Select directory",
|
||||
"selectedDirectory": "Selected directory:",
|
||||
"goButton": "Go!",
|
||||
"commandSuccessful": "The command was successful!",
|
||||
"commandFailed": "The command failed!",
|
||||
"COMMENT_FOR_TRANSLATOR": "keep this uppercase",
|
||||
"openLogFile": "OPEN LOG FILE",
|
||||
"COMMENT_FOR_TRANSLATOR_2": "the next string is used to change the text 'with' in the sentence 'with <package manager>'",
|
||||
"with": "with"
|
||||
},
|
||||
"footer": {
|
||||
"COMMENT_FOR_TRANSLATOR": "Keep all the strings here as lowercase. It kinda gives a good vibe to the text :D",
|
||||
"web": "front page",
|
||||
"COMMENT_FOR_TRANSLATOR_2": "Keep these at their current state. They are brands after all, so you shouldn't change these unless they are called differently in other languages",
|
||||
"github": "github",
|
||||
"discord": "discord"
|
||||
}
|
||||
}
|
||||
}
|
||||
40
locales/es.json
Normal file
40
locales/es.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"credits": {
|
||||
"propsTo": "@SrIzan10 (spanish me)",
|
||||
"btw": "Todos los COMMENT_FOR_TRANSLATOR son entradas que te ayudan a traducir los textos siguiendo unas reglas. Sigue estas para ser aprobado más fácilmente."
|
||||
},
|
||||
"translation": {
|
||||
"functionalityCard": {
|
||||
"init": {
|
||||
"description": "Inicia un nuevo proyecto"
|
||||
},
|
||||
"plugins": {
|
||||
"description": "Administra los plugins de un proyecto existente"
|
||||
}
|
||||
},
|
||||
"initModal": {
|
||||
"openModalButton": "Empezar",
|
||||
"projectName": "Nombre del proyecto",
|
||||
"selectTemplate": "Selecciona una plantilla",
|
||||
"couldntFetchTemplatesError": "No se pudieron obtener las plantillas. Haz CTRL+R. (¿Estás conectado a internet?)",
|
||||
"installPackagesCheckbox": "De paso instala las depencencias",
|
||||
"selectPackageManager": "Selecciona un gestor de paquetes",
|
||||
"chooseDirectoryButton": "Selecciona un directorio",
|
||||
"selectedDirectory": "Directorio seleccionado:",
|
||||
"goButton": "¡Vamos!",
|
||||
"commandSuccessful": "El comando se ejecutó con éxito!",
|
||||
"commandFailed": "El comando falló.",
|
||||
"COMMENT_FOR_TRANSLATOR": "Mantén esto en mayúsculas",
|
||||
"openLogFile": "Abrir archivo de registro",
|
||||
"COMMENT_FOR_TRANSLATOR_2": "El texto de abajo sirve para reemplazar 'with' en las opciones de plantilla con lo de abajo. No cambiar.",
|
||||
"with": "con"
|
||||
},
|
||||
"footer": {
|
||||
"COMMENT_FOR_TRANSLATOR": "Mantén todos los textos aquí en minúsculas. Le dan un buen vibe al texto :D",
|
||||
"web": "web",
|
||||
"COMMENT_FOR_TRANSLATOR_2": "Mantén estos textos así. Al fin y al cabo son marcas, así que no deberías cambiarlas excepto si se llaman de otra forma en el idioma.",
|
||||
"github": "github",
|
||||
"discord": "discord"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,10 @@
|
||||
"@mui/material": "^5.13.4",
|
||||
"colorette": "^2.0.20",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"i18next": "^23.4.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^13.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@swc/cli": "^0.1.62",
|
||||
|
||||
@@ -3,6 +3,7 @@ import Footer from './Footer.js';
|
||||
import './FunctionalityCard.js';
|
||||
import FunctionalityCard from './FunctionalityCard.js';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import LanguageSelector from './LanguageSelector';
|
||||
|
||||
const darkTheme = createTheme({
|
||||
palette: {
|
||||
@@ -17,15 +18,14 @@ function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<LanguageSelector />
|
||||
<h1 className="titleHeader">~$ sern</h1>
|
||||
<div className="functionalityCards">
|
||||
<FunctionalityCard
|
||||
command="init"
|
||||
description="Scaffold a new project"
|
||||
/>
|
||||
<FunctionalityCard
|
||||
command="plugins"
|
||||
description="Install plugins on your existing project"
|
||||
/>
|
||||
</div>
|
||||
<Footer />
|
||||
|
||||
@@ -5,34 +5,38 @@ import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import { faDiscord } from '@fortawesome/free-brands-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import './Footer.css'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const { shell } = window.require('electron');
|
||||
|
||||
export default function Footer() {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'footer' });
|
||||
return (
|
||||
<div className="footer">
|
||||
<Typography color="primary">
|
||||
<Link href="https://sern.dev">
|
||||
<PublicIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||
front page
|
||||
</Typography>
|
||||
<Typography color="primary" sx={{ cursor: 'pointer' }}>
|
||||
{/* this is such a hacky way to do this but it works(tm) */}
|
||||
<Link onClick={() => shell.openExternal('https://sern.dev')}>
|
||||
<PublicIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||
{t('web')}
|
||||
</Typography>
|
||||
</Link>
|
||||
|
||||
<span style={{ margin: '0 4px' }}>•</span>
|
||||
<span style={{ margin: '0 4px', cursor: 'default' }}>•</span>
|
||||
|
||||
<Link href="https://github.com/sern-handler">
|
||||
<GitHubIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||
github
|
||||
</Typography>
|
||||
<Link onClick={() => shell.openExternal('https://github.com/sern-handler')}>
|
||||
<GitHubIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||
github
|
||||
</Typography>
|
||||
</Link>
|
||||
|
||||
<span style={{ margin: '0 4px' }}>•</span>
|
||||
<span style={{ margin: '0 4px', cursor: 'default' }}>•</span>
|
||||
|
||||
<Link href="https://sern.dev/discord">
|
||||
<FontAwesomeIcon icon={faDiscord} style={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||
discord
|
||||
</Typography>
|
||||
<Link onClick={() => shell.openExternal('https://discord.gg/sern')}>
|
||||
<FontAwesomeIcon icon={faDiscord} style={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||
discord
|
||||
</Typography>
|
||||
</Link>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
@@ -4,39 +4,49 @@ import CardContent from '@mui/material/CardContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import InitModal from './InitModal.js';
|
||||
import PluginsModal from './PluginsModal.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function cardChooser(command: string) {
|
||||
switch (command) {
|
||||
function cardChooser(command: Props) {
|
||||
const cmd = command.command
|
||||
switch (cmd) {
|
||||
case 'init':
|
||||
return <InitModal />
|
||||
case 'plugins':
|
||||
return <PluginsModal />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export default function FunctionalityCard(props: Props) {
|
||||
const { command, description } = props
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'functionalityCard' });
|
||||
|
||||
const resolveDescription = (command: Props) => {
|
||||
const cmd = command.command
|
||||
switch (cmd) {
|
||||
case 'init':
|
||||
return t('init.description')
|
||||
case 'plugins':
|
||||
return t('plugins.description')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card sx={{ width: window.innerWidth / 2 }} variant='outlined'>
|
||||
<CardContent>
|
||||
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
|
||||
{description}
|
||||
{resolveDescription(props)}
|
||||
</Typography>
|
||||
<Typography color="text.primary">
|
||||
<code>~$ sern {command}</code>
|
||||
<code>~$ sern {props.command}</code>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
{/*<Button size="small">Get started</Button>*/}
|
||||
{cardChooser(command)}
|
||||
{cardChooser(props)}
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
command: string
|
||||
description: string
|
||||
command: 'init' | 'plugins'
|
||||
}
|
||||
@@ -17,22 +17,12 @@ import Alert from '@mui/material/Alert';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import './InitModal.css';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const { ipcRenderer } = window.require('electron');
|
||||
|
||||
/* const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #FFF',
|
||||
boxShadow: 24,
|
||||
padding: '20px',
|
||||
color: 'white',
|
||||
}; */
|
||||
|
||||
export default function InitModal() {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'initModal' });
|
||||
|
||||
const [loadingBecauseItsSettingUp, setLoadingBecauseItsSettingUp] = React.useState(false);
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
@@ -53,13 +43,13 @@ export default function InitModal() {
|
||||
};
|
||||
|
||||
const [chosenPackageManager, setChosenPackageManager] = React.useState('');
|
||||
const handlePackageManagerChange = (event: SelectChangeEvent<string>) => {
|
||||
const handlePackageManagerChange = (event: SelectChangeEvent) => {
|
||||
setChosenPackageManager(event.target.value);
|
||||
};
|
||||
|
||||
const [templates, setTemplates] = React.useState<Array<TemplateList>>([]);
|
||||
React.useEffect(() => {
|
||||
fetch('https://raw.githubusercontent.com/sern-handler/create-bot/main/metadata/templateChoices.json')
|
||||
fetch('https://raw.githubusercontent.com/sern-handler/create-bot/main/metadata/templateChoices.jso')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setTemplates(data as TemplateList[]);
|
||||
@@ -70,7 +60,7 @@ export default function InitModal() {
|
||||
}, []);
|
||||
|
||||
if (templates.length === 0) {
|
||||
setTemplates([{ title: "Couldn't fetch templates! Please do CTRL+R", value: 'error' }]);
|
||||
setTemplates([{ title: t('couldntFetchTemplates'), value: 'error' }]);
|
||||
}
|
||||
|
||||
const [selectedPath, setSelectedPath] = React.useState('');
|
||||
@@ -116,7 +106,7 @@ export default function InitModal() {
|
||||
const snackbarAction = (
|
||||
<React.Fragment>
|
||||
<Button color="secondary" size="small" onClick={handleOpenLogFile}>
|
||||
OPEN LOG FILE
|
||||
{t('openLogFile')}
|
||||
</Button>
|
||||
<IconButton
|
||||
size="small"
|
||||
@@ -153,7 +143,7 @@ export default function InitModal() {
|
||||
|
||||
ipcRenderer.send('submitForm', data);
|
||||
|
||||
ipcRenderer.on('submitForm', (_event, args) => {
|
||||
ipcRenderer.on('submitForm', (_event, args: IPCCommandExitEvent) => {
|
||||
setLoading(false);
|
||||
setLoadingBecauseItsSettingUp(false);
|
||||
handleClose();
|
||||
@@ -169,7 +159,7 @@ export default function InitModal() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={handleOpen}>Open modal</Button>
|
||||
<Button onClick={handleOpen}>{t('openModalButton')}</Button>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
@@ -183,7 +173,7 @@ export default function InitModal() {
|
||||
<div className="formRow">
|
||||
<TextField
|
||||
id="modal-form-projectName"
|
||||
label="Project name"
|
||||
label={t('projectName')}
|
||||
variant="outlined"
|
||||
onChange={handleProjectNameChange}
|
||||
required
|
||||
@@ -191,19 +181,19 @@ export default function InitModal() {
|
||||
/>
|
||||
<FormControl fullWidth className="chooseTemplateForm">
|
||||
<InputLabel id="modal-form-templateLabel">
|
||||
Select template
|
||||
{t('selectTemplate')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="modal-form-templateSelect"
|
||||
id="modal-form-templateSelect"
|
||||
value={chosenTemplate}
|
||||
label="Select template"
|
||||
label={t('selectTemplate')}
|
||||
onChange={handleTemplateChange}
|
||||
fullWidth
|
||||
>
|
||||
{templates.map((template) => (
|
||||
<MenuItem key={template.value} value={template.value}>
|
||||
{template.title}
|
||||
{template.title.replace('with', t('with'))}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
@@ -218,12 +208,12 @@ export default function InitModal() {
|
||||
onChange={handlePackagesChange}
|
||||
/>
|
||||
}
|
||||
label="Install packages while you're at it"
|
||||
label={t('installPackagesCheckbox')}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormControl className="choosePkgManagerForm" fullWidth>
|
||||
<InputLabel id="modal-form-packageManagerLabel">
|
||||
Select package manager
|
||||
{t('selectPackageManager')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="modal-form-packageManagerLabel"
|
||||
@@ -247,7 +237,7 @@ export default function InitModal() {
|
||||
onClick={handleChooseDirButton}
|
||||
sx={{ display: 'block', margin: '0 auto', marginTop: '5px' }}
|
||||
>
|
||||
Select directory
|
||||
{t('chooseDirectoryButton')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="formRow">
|
||||
@@ -256,7 +246,7 @@ export default function InitModal() {
|
||||
component="div"
|
||||
sx={{ display: 'block', margin: '0 auto', marginTop: '5px' }}
|
||||
>
|
||||
{selectedPath ? `Selected directory: ${selectedPath}` : ''}
|
||||
{selectedPath ? `${t('selectedDirectory')} ${selectedPath}` : ''}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="bottomRight">
|
||||
@@ -266,19 +256,20 @@ export default function InitModal() {
|
||||
onClick={handleSubmit}
|
||||
disabled={loading || !isFormValid()}
|
||||
>
|
||||
{loading ? 'Go!' : 'Go!'}
|
||||
{/*{ loading ? 'Go!' : 'Go!' }*/}
|
||||
{t('goButton')}
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
</Modal>
|
||||
<Snackbar open={successSnackbarOpen} autoHideDuration={5000} onClose={handleSuccessSnackbarClose} action={snackbarAction}>
|
||||
<Alert onClose={handleSuccessSnackbarClose} severity="success" sx={{ width: '100%' }} action={snackbarAction}>
|
||||
The command was successful!
|
||||
{t('commandSuccessful')}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<Snackbar open={errorSnackbarOpen} autoHideDuration={5000} onClose={handleErrorSnackbarClose} action={snackbarAction}>
|
||||
<Alert onClose={handleErrorSnackbarClose} severity="error" sx={{ width: '100%' }} action={snackbarAction}>
|
||||
The command was not successful
|
||||
{t('commandFailed')}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</div>
|
||||
@@ -288,4 +279,9 @@ export default function InitModal() {
|
||||
interface TemplateList {
|
||||
title: string
|
||||
value: string
|
||||
}
|
||||
|
||||
interface IPCCommandExitEvent {
|
||||
exitCode: number | null
|
||||
logFileName: string
|
||||
}
|
||||
14
src/LanguageSelector.css
Normal file
14
src/LanguageSelector.css
Normal file
@@ -0,0 +1,14 @@
|
||||
.languageSelector {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.menuItems {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
38
src/LanguageSelector.tsx
Normal file
38
src/LanguageSelector.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import './LanguageSelector.css'
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import Select from '@mui/material/Select';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function LanguageSelector() {
|
||||
const { i18n } = useTranslation();
|
||||
return (
|
||||
<div className="languageSelector">
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="lang-select-label" />
|
||||
<Select
|
||||
labelId="lang-select-label"
|
||||
id="lang-select"
|
||||
defaultValue={i18n.language}
|
||||
label=""
|
||||
onChange={(event) => {
|
||||
i18n.changeLanguage(event.target.value);
|
||||
window.localStorage.setItem('lang', event.target.value);
|
||||
}}
|
||||
inputProps={{ IconComponent: () => null, sx: { padding: '0 !important' } }}
|
||||
sx={{
|
||||
height: '40px',
|
||||
textAlign: 'center',
|
||||
// this fixes a little text selection gap that appears in a
|
||||
// few pixels outside the outer part of the selection "square"
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<MenuItem value={'en'} className={'menuItems'}>🇺🇸</MenuItem>
|
||||
<MenuItem value={'es'} className={'menuItems'}>🇪🇸</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
21
src/main.tsx
21
src/main.tsx
@@ -2,11 +2,30 @@ import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
import './main.css';
|
||||
import App from './App';
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import enLocale from '../locales/en.json' assert { type: "json" };
|
||||
import esLocale from '../locales/es.json' assert { type: "json" };
|
||||
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources: {
|
||||
en: enLocale,
|
||||
es: esLocale
|
||||
},
|
||||
lng: window.localStorage.getItem('lang') || 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
}
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root')!);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src", "src/vite-env.d.ts"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
27
yarn.lock
27
yarn.lock
@@ -2497,6 +2497,13 @@ hosted-git-info@^4.1.0:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
html-parse-stringify@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
|
||||
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
|
||||
dependencies:
|
||||
void-elements "3.1.0"
|
||||
|
||||
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
@@ -2539,6 +2546,13 @@ humanize-ms@^1.2.1:
|
||||
dependencies:
|
||||
ms "^2.0.0"
|
||||
|
||||
i18next@^23.4.4:
|
||||
version "23.4.4"
|
||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.4.4.tgz#ec8fb2b5f3c5d8e3bf3f8ab1b19e743be91300e0"
|
||||
integrity sha512-+c9B0txp/x1m5zn+QlwHaCS9vyFtmIAEXbVSFzwCX7vupm5V7va8F9cJGNJZ46X9ZtoGzhIiRC7eTIIh93TxPA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.22.5"
|
||||
|
||||
iconv-corefoundation@^1.1.7:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz#31065e6ab2c9272154c8b0821151e2c88f1b002a"
|
||||
@@ -3425,6 +3439,14 @@ react-dom@^18.2.0:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-i18next@^13.1.2:
|
||||
version "13.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-13.1.2.tgz#dbb1b18c364295af2a9072333ee4e0b43cbc2da8"
|
||||
integrity sha512-D/OJ/8ZQYscabsvbCAiOgvJq8W3feQF/VIV0to1w7V7UvrUE1IZ3hcalOckUYvKBd7BP3b8EPm+hop3J8sS+Mw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.22.5"
|
||||
html-parse-stringify "^3.0.1"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
@@ -4075,6 +4097,11 @@ vite@^4.4.0:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
void-elements@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
|
||||
|
||||
wcwidth@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||
|
||||
Reference in New Issue
Block a user