mirror of
https://github.com/sern-handler/gui
synced 2026-06-06 01:16:54 +00:00
Merge pull request #3 from sern-handler/localization
feat: localization support and spanish translation
This commit is contained in:
@@ -14,6 +14,7 @@ module.exports = {
|
|||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
|
project: '.swcrc'
|
||||||
},
|
},
|
||||||
plugins: ['react-refresh'],
|
plugins: ['react-refresh'],
|
||||||
rules: {
|
rules: {
|
||||||
@@ -21,6 +22,6 @@ module.exports = {
|
|||||||
'warn',
|
'warn',
|
||||||
{ allowConstantExport: true },
|
{ 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 { 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 colorette from 'colorette';
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
import * as os from 'node:os'
|
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')
|
const fileName = createRandomFileName('txt')
|
||||||
// Process the submitted data here
|
// Process the submitted data here
|
||||||
writeLineToLogAndConsole(`${colorette.green('✓')} Received sern init submit form data:`, fileName);
|
writeLineToLogAndConsole(`${colorette.green('✓')} Received sern init submit form data:`, fileName);
|
||||||
@@ -192,4 +192,12 @@ function randomstring(length: number) {
|
|||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
return result;
|
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",
|
"@mui/material": "^5.13.4",
|
||||||
"colorette": "^2.0.20",
|
"colorette": "^2.0.20",
|
||||||
"electron-is-dev": "^2.0.0",
|
"electron-is-dev": "^2.0.0",
|
||||||
|
"i18next": "^23.4.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"react-i18next": "^13.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swc/cli": "^0.1.62",
|
"@swc/cli": "^0.1.62",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Footer from './Footer.js';
|
|||||||
import './FunctionalityCard.js';
|
import './FunctionalityCard.js';
|
||||||
import FunctionalityCard from './FunctionalityCard.js';
|
import FunctionalityCard from './FunctionalityCard.js';
|
||||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
|
import LanguageSelector from './LanguageSelector';
|
||||||
|
|
||||||
const darkTheme = createTheme({
|
const darkTheme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
@@ -17,15 +18,14 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<ThemeProvider theme={darkTheme}>
|
<ThemeProvider theme={darkTheme}>
|
||||||
|
<LanguageSelector />
|
||||||
<h1 className="titleHeader">~$ sern</h1>
|
<h1 className="titleHeader">~$ sern</h1>
|
||||||
<div className="functionalityCards">
|
<div className="functionalityCards">
|
||||||
<FunctionalityCard
|
<FunctionalityCard
|
||||||
command="init"
|
command="init"
|
||||||
description="Scaffold a new project"
|
|
||||||
/>
|
/>
|
||||||
<FunctionalityCard
|
<FunctionalityCard
|
||||||
command="plugins"
|
command="plugins"
|
||||||
description="Install plugins on your existing project"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -5,34 +5,38 @@ import GitHubIcon from '@mui/icons-material/GitHub';
|
|||||||
import { faDiscord } from '@fortawesome/free-brands-svg-icons';
|
import { faDiscord } from '@fortawesome/free-brands-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import './Footer.css'
|
import './Footer.css'
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
const { shell } = window.require('electron');
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
|
const { t } = useTranslation('translation', { keyPrefix: 'footer' });
|
||||||
return (
|
return (
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
<Typography color="primary">
|
<Typography color="primary" sx={{ cursor: 'pointer' }}>
|
||||||
<Link href="https://sern.dev">
|
{/* this is such a hacky way to do this but it works(tm) */}
|
||||||
<PublicIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
<Link onClick={() => shell.openExternal('https://sern.dev')}>
|
||||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
<PublicIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||||
front page
|
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||||
</Typography>
|
{t('web')}
|
||||||
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<span style={{ margin: '0 4px' }}>•</span>
|
<span style={{ margin: '0 4px', cursor: 'default' }}>•</span>
|
||||||
|
|
||||||
<Link href="https://github.com/sern-handler">
|
<Link onClick={() => shell.openExternal('https://github.com/sern-handler')}>
|
||||||
<GitHubIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
<GitHubIcon color="primary" sx={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||||
github
|
github
|
||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<span style={{ margin: '0 4px' }}>•</span>
|
<span style={{ margin: '0 4px', cursor: 'default' }}>•</span>
|
||||||
|
|
||||||
<Link href="https://sern.dev/discord">
|
<Link onClick={() => shell.openExternal('https://discord.gg/sern')}>
|
||||||
<FontAwesomeIcon icon={faDiscord} style={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
<FontAwesomeIcon icon={faDiscord} style={{ fontSize: 'inherit', verticalAlign: 'middle', marginRight: '4px' }} />
|
||||||
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
<Typography variant="body1" component="span" sx={{ display: 'inline-block', verticalAlign: 'middle' }}>
|
||||||
discord
|
discord
|
||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,39 +4,49 @@ import CardContent from '@mui/material/CardContent';
|
|||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import InitModal from './InitModal.js';
|
import InitModal from './InitModal.js';
|
||||||
import PluginsModal from './PluginsModal.js';
|
import PluginsModal from './PluginsModal.js';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function cardChooser(command: string) {
|
function cardChooser(command: Props) {
|
||||||
switch (command) {
|
const cmd = command.command
|
||||||
|
switch (cmd) {
|
||||||
case 'init':
|
case 'init':
|
||||||
return <InitModal />
|
return <InitModal />
|
||||||
case 'plugins':
|
case 'plugins':
|
||||||
return <PluginsModal />
|
return <PluginsModal />
|
||||||
default:
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FunctionalityCard(props: Props) {
|
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 (
|
return (
|
||||||
<Card sx={{ width: window.innerWidth / 2 }} variant='outlined'>
|
<Card sx={{ width: window.innerWidth / 2 }} variant='outlined'>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
|
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
|
||||||
{description}
|
{resolveDescription(props)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color="text.primary">
|
<Typography color="text.primary">
|
||||||
<code>~$ sern {command}</code>
|
<code>~$ sern {props.command}</code>
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions>
|
<CardActions>
|
||||||
{/*<Button size="small">Get started</Button>*/}
|
{/*<Button size="small">Get started</Button>*/}
|
||||||
{cardChooser(command)}
|
{cardChooser(props)}
|
||||||
</CardActions>
|
</CardActions>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
command: string
|
command: 'init' | 'plugins'
|
||||||
description: string
|
|
||||||
}
|
}
|
||||||
@@ -17,22 +17,12 @@ import Alert from '@mui/material/Alert';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import './InitModal.css';
|
import './InitModal.css';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
const { ipcRenderer } = window.require('electron');
|
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() {
|
export default function InitModal() {
|
||||||
|
const { t } = useTranslation('translation', { keyPrefix: 'initModal' });
|
||||||
|
|
||||||
const [loadingBecauseItsSettingUp, setLoadingBecauseItsSettingUp] = React.useState(false);
|
const [loadingBecauseItsSettingUp, setLoadingBecauseItsSettingUp] = React.useState(false);
|
||||||
|
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
@@ -53,13 +43,13 @@ export default function InitModal() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const [chosenPackageManager, setChosenPackageManager] = React.useState('');
|
const [chosenPackageManager, setChosenPackageManager] = React.useState('');
|
||||||
const handlePackageManagerChange = (event: SelectChangeEvent<string>) => {
|
const handlePackageManagerChange = (event: SelectChangeEvent) => {
|
||||||
setChosenPackageManager(event.target.value);
|
setChosenPackageManager(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [templates, setTemplates] = React.useState<Array<TemplateList>>([]);
|
const [templates, setTemplates] = React.useState<Array<TemplateList>>([]);
|
||||||
React.useEffect(() => {
|
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((res) => res.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setTemplates(data as TemplateList[]);
|
setTemplates(data as TemplateList[]);
|
||||||
@@ -70,7 +60,7 @@ export default function InitModal() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (templates.length === 0) {
|
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('');
|
const [selectedPath, setSelectedPath] = React.useState('');
|
||||||
@@ -116,7 +106,7 @@ export default function InitModal() {
|
|||||||
const snackbarAction = (
|
const snackbarAction = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Button color="secondary" size="small" onClick={handleOpenLogFile}>
|
<Button color="secondary" size="small" onClick={handleOpenLogFile}>
|
||||||
OPEN LOG FILE
|
{t('openLogFile')}
|
||||||
</Button>
|
</Button>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
@@ -153,7 +143,7 @@ export default function InitModal() {
|
|||||||
|
|
||||||
ipcRenderer.send('submitForm', data);
|
ipcRenderer.send('submitForm', data);
|
||||||
|
|
||||||
ipcRenderer.on('submitForm', (_event, args) => {
|
ipcRenderer.on('submitForm', (_event, args: IPCCommandExitEvent) => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setLoadingBecauseItsSettingUp(false);
|
setLoadingBecauseItsSettingUp(false);
|
||||||
handleClose();
|
handleClose();
|
||||||
@@ -169,7 +159,7 @@ export default function InitModal() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleOpen}>Open modal</Button>
|
<Button onClick={handleOpen}>{t('openModalButton')}</Button>
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
@@ -183,7 +173,7 @@ export default function InitModal() {
|
|||||||
<div className="formRow">
|
<div className="formRow">
|
||||||
<TextField
|
<TextField
|
||||||
id="modal-form-projectName"
|
id="modal-form-projectName"
|
||||||
label="Project name"
|
label={t('projectName')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleProjectNameChange}
|
onChange={handleProjectNameChange}
|
||||||
required
|
required
|
||||||
@@ -191,19 +181,19 @@ export default function InitModal() {
|
|||||||
/>
|
/>
|
||||||
<FormControl fullWidth className="chooseTemplateForm">
|
<FormControl fullWidth className="chooseTemplateForm">
|
||||||
<InputLabel id="modal-form-templateLabel">
|
<InputLabel id="modal-form-templateLabel">
|
||||||
Select template
|
{t('selectTemplate')}
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
labelId="modal-form-templateSelect"
|
labelId="modal-form-templateSelect"
|
||||||
id="modal-form-templateSelect"
|
id="modal-form-templateSelect"
|
||||||
value={chosenTemplate}
|
value={chosenTemplate}
|
||||||
label="Select template"
|
label={t('selectTemplate')}
|
||||||
onChange={handleTemplateChange}
|
onChange={handleTemplateChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{templates.map((template) => (
|
{templates.map((template) => (
|
||||||
<MenuItem key={template.value} value={template.value}>
|
<MenuItem key={template.value} value={template.value}>
|
||||||
{template.title}
|
{template.title.replace('with', t('with'))}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -218,12 +208,12 @@ export default function InitModal() {
|
|||||||
onChange={handlePackagesChange}
|
onChange={handlePackagesChange}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Install packages while you're at it"
|
label={t('installPackagesCheckbox')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormControl className="choosePkgManagerForm" fullWidth>
|
<FormControl className="choosePkgManagerForm" fullWidth>
|
||||||
<InputLabel id="modal-form-packageManagerLabel">
|
<InputLabel id="modal-form-packageManagerLabel">
|
||||||
Select package manager
|
{t('selectPackageManager')}
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
labelId="modal-form-packageManagerLabel"
|
labelId="modal-form-packageManagerLabel"
|
||||||
@@ -247,7 +237,7 @@ export default function InitModal() {
|
|||||||
onClick={handleChooseDirButton}
|
onClick={handleChooseDirButton}
|
||||||
sx={{ display: 'block', margin: '0 auto', marginTop: '5px' }}
|
sx={{ display: 'block', margin: '0 auto', marginTop: '5px' }}
|
||||||
>
|
>
|
||||||
Select directory
|
{t('chooseDirectoryButton')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="formRow">
|
<div className="formRow">
|
||||||
@@ -256,7 +246,7 @@ export default function InitModal() {
|
|||||||
component="div"
|
component="div"
|
||||||
sx={{ display: 'block', margin: '0 auto', marginTop: '5px' }}
|
sx={{ display: 'block', margin: '0 auto', marginTop: '5px' }}
|
||||||
>
|
>
|
||||||
{selectedPath ? `Selected directory: ${selectedPath}` : ''}
|
{selectedPath ? `${t('selectedDirectory')} ${selectedPath}` : ''}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className="bottomRight">
|
<div className="bottomRight">
|
||||||
@@ -266,19 +256,20 @@ export default function InitModal() {
|
|||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={loading || !isFormValid()}
|
disabled={loading || !isFormValid()}
|
||||||
>
|
>
|
||||||
{loading ? 'Go!' : 'Go!'}
|
{/*{ loading ? 'Go!' : 'Go!' }*/}
|
||||||
|
{t('goButton')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Snackbar open={successSnackbarOpen} autoHideDuration={5000} onClose={handleSuccessSnackbarClose} action={snackbarAction}>
|
<Snackbar open={successSnackbarOpen} autoHideDuration={5000} onClose={handleSuccessSnackbarClose} action={snackbarAction}>
|
||||||
<Alert onClose={handleSuccessSnackbarClose} severity="success" sx={{ width: '100%' }} action={snackbarAction}>
|
<Alert onClose={handleSuccessSnackbarClose} severity="success" sx={{ width: '100%' }} action={snackbarAction}>
|
||||||
The command was successful!
|
{t('commandSuccessful')}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
<Snackbar open={errorSnackbarOpen} autoHideDuration={5000} onClose={handleErrorSnackbarClose} action={snackbarAction}>
|
<Snackbar open={errorSnackbarOpen} autoHideDuration={5000} onClose={handleErrorSnackbarClose} action={snackbarAction}>
|
||||||
<Alert onClose={handleErrorSnackbarClose} severity="error" sx={{ width: '100%' }} action={snackbarAction}>
|
<Alert onClose={handleErrorSnackbarClose} severity="error" sx={{ width: '100%' }} action={snackbarAction}>
|
||||||
The command was not successful
|
{t('commandFailed')}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
</div>
|
</div>
|
||||||
@@ -288,4 +279,9 @@ export default function InitModal() {
|
|||||||
interface TemplateList {
|
interface TemplateList {
|
||||||
title: string
|
title: string
|
||||||
value: 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 * as ReactDOM from 'react-dom/client';
|
||||||
import './main.css';
|
import './main.css';
|
||||||
import App from './App';
|
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')!);
|
const root = ReactDOM.createRoot(document.getElementById('root')!);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": ["src", "src/vite-env.d.ts"],
|
"include": ["src", "src/vite-env.d.ts"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
|||||||
27
yarn.lock
27
yarn.lock
@@ -2497,6 +2497,13 @@ hosted-git-info@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lru-cache "^6.0.0"
|
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:
|
http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
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:
|
dependencies:
|
||||||
ms "^2.0.0"
|
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:
|
iconv-corefoundation@^1.1.7:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz#31065e6ab2c9272154c8b0821151e2c88f1b002a"
|
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"
|
loose-envify "^1.1.0"
|
||||||
scheduler "^0.23.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:
|
react-is@^16.13.1, react-is@^16.7.0:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
@@ -4075,6 +4097,11 @@ vite@^4.4.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
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:
|
wcwidth@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||||
|
|||||||
Reference in New Issue
Block a user