From 8cb7e930bf50ce3ccd55b54d2f61c492ed617260 Mon Sep 17 00:00:00 2001 From: Willian Rodrigues Date: Wed, 9 Mar 2022 18:09:14 -0300 Subject: [PATCH] add i18next localization --- package.json | 3 + release/app/package.json | 3 +- release/app/yarn.lock | 5 -- src/main/menu.ts | 80 ++----------------- src/renderer/App.tsx | 32 ++++---- .../components/CreatePageButton/index.tsx | 16 ++-- src/renderer/components/editor/index.tsx | 32 ++++---- .../components/header/DeletePageButton.tsx | 13 +-- .../components/header/PageSelector.tsx | 14 ++-- .../components/header/PairDeckButton.tsx | 13 +-- src/renderer/components/header/index.tsx | 4 +- src/renderer/constants/key-types.constant.ts | 70 ++++++++-------- src/renderer/i18n/index.ts | 18 +++++ src/renderer/i18n/locales/en-US/common.json | 10 +++ .../locales/en-US/create-page-button.json | 11 +++ src/renderer/i18n/locales/en-US/editor.json | 22 +++++ src/renderer/i18n/locales/en-US/header.json | 22 +++++ src/renderer/i18n/locales/en-US/index.ts | 13 +++ src/renderer/i18n/locales/en-US/keys.json | 39 +++++++++ src/renderer/i18n/locales/index.ts | 9 +++ src/renderer/i18n/locales/pt-BR/common.json | 10 +++ .../locales/pt-BR/create-page-button.json | 11 +++ src/renderer/i18n/locales/pt-BR/editor.json | 22 +++++ src/renderer/i18n/locales/pt-BR/header.json | 22 +++++ src/renderer/i18n/locales/pt-BR/index.ts | 13 +++ src/renderer/i18n/locales/pt-BR/keys.json | 39 +++++++++ src/renderer/pages/start/index.tsx | 10 ++- src/renderer/redux/ducks/devices.ts | 1 + src/renderer/redux/ducks/keys.ts | 13 ++- yarn.lock | 39 ++++++++- 30 files changed, 433 insertions(+), 176 deletions(-) create mode 100644 src/renderer/i18n/index.ts create mode 100644 src/renderer/i18n/locales/en-US/common.json create mode 100644 src/renderer/i18n/locales/en-US/create-page-button.json create mode 100644 src/renderer/i18n/locales/en-US/editor.json create mode 100644 src/renderer/i18n/locales/en-US/header.json create mode 100644 src/renderer/i18n/locales/en-US/index.ts create mode 100644 src/renderer/i18n/locales/en-US/keys.json create mode 100644 src/renderer/i18n/locales/index.ts create mode 100644 src/renderer/i18n/locales/pt-BR/common.json create mode 100644 src/renderer/i18n/locales/pt-BR/create-page-button.json create mode 100644 src/renderer/i18n/locales/pt-BR/editor.json create mode 100644 src/renderer/i18n/locales/pt-BR/header.json create mode 100644 src/renderer/i18n/locales/pt-BR/index.ts create mode 100644 src/renderer/i18n/locales/pt-BR/keys.json diff --git a/package.json b/package.json index 112b507..07f7fb5 100644 --- a/package.json +++ b/package.json @@ -228,11 +228,14 @@ "electron-updater": "^4.6.4", "framer-motion": "^5.6.0", "history": "^5.2.0", + "i18next": "^21.6.13", + "i18next-browser-languagedetector": "^6.1.3", "react": "^17.0.2", "react-dnd": "^14.0.5", "react-dnd-html5-backend": "^14.1.0", "react-dom": "^17.0.2", "react-hook-form": "^7.24.2", + "react-i18next": "^11.15.5", "react-icons": "^4.3.1", "react-input-mask": "^2.0.4", "react-qr-code": "^2.0.3", diff --git a/release/app/package.json b/release/app/package.json index ed1b5d6..3d2f0f3 100644 --- a/release/app/package.json +++ b/release/app/package.json @@ -1,6 +1,6 @@ { "name": "odeck", - "version": "0.0.1", + "version": "0.0.2", "description": "A free and open-source alternative to StreamDeck", "main": "./dist/main/main.js", "author": { @@ -18,7 +18,6 @@ "cors": "^2.8.5", "express": "^4.17.2", "node-abi": "^3.8.0", - "node-key-sender": "^1.0.11", "open": "^8.4.0", "play-sound": "^1.1.5", "robotjs": "^0.6.0", diff --git a/release/app/yarn.lock b/release/app/yarn.lock index 34fc3bd..551d041 100644 --- a/release/app/yarn.lock +++ b/release/app/yarn.lock @@ -532,11 +532,6 @@ node-abi@^3.8.0: dependencies: semver "^7.3.5" -node-key-sender@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/node-key-sender/-/node-key-sender-1.0.11.tgz#93210f07163607d8daf2874f1a29567d0acdb94c" - integrity sha512-vv2IXd8QdZBFYXaIy02uy2rK6EKj+tOTEuoTxJKS9l8zw8Cz6DeLffR8ompj7N2A3h6XK7aiy+YAcTaeOqwp2Q== - noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz" diff --git a/src/main/menu.ts b/src/main/menu.ts index 3650185..052dabe 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -5,6 +5,7 @@ import { BrowserWindow, MenuItemConstructorOptions, } from 'electron'; +import i18n from '../renderer/i18n'; interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions { selector?: string; @@ -84,6 +85,7 @@ export default class MenuBuilder { }, ], }; + const subMenuEdit: DarwinMenuItemConstructorOptions = { label: 'Edit', submenu: [ @@ -195,90 +197,24 @@ export default class MenuBuilder { buildDefaultTemplate() { const templateDefault = [ { - label: '&File', - submenu: [ - { - label: '&Open', - accelerator: 'Ctrl+O', - }, - { - label: '&Close', - accelerator: 'Ctrl+W', - click: () => { - this.mainWindow.close(); - }, - }, - ], - }, - { - label: '&View', - submenu: - process.env.NODE_ENV === 'development' || - process.env.DEBUG_PROD === 'true' - ? [ - { - label: '&Reload', - accelerator: 'Ctrl+R', - click: () => { - this.mainWindow.webContents.reload(); - }, - }, - { - label: 'Toggle &Full Screen', - accelerator: 'F11', - click: () => { - this.mainWindow.setFullScreen( - !this.mainWindow.isFullScreen() - ); - }, - }, - { - label: 'Toggle &Developer Tools', - accelerator: 'Alt+Ctrl+I', - click: () => { - this.mainWindow.webContents.toggleDevTools(); - }, - }, - ] - : [ - { - label: 'Toggle &Full Screen aa', - accelerator: 'F11', - click: () => { - this.mainWindow.setFullScreen( - !this.mainWindow.isFullScreen() - ); - }, - }, - ], - }, - { - label: 'Help', + label: i18n.t('help'), submenu: [ { - label: 'Learn More', - click() { - shell.openExternal('https://electronjs.org'); - }, - }, - { - label: 'Documentation', + label: i18n.t('documentation'), click() { - shell.openExternal( - 'https://github.com/electron/electron/tree/main/docs#readme' - ); + shell.openExternal('https://github.com/willianrod/ODeck#readme'); }, }, { - label: 'Community Discussions', + label: i18n.t('community_discussions'), click() { shell.openExternal('https://www.electronjs.org/community'); }, }, { - label: 'Search Issues', + label: i18n.t('report_bug'), click() { - shell.openExternal('https://github.com/electron/electron/issues'); + shell.openExternal('https://github.com/willianrod/ODeck/issues'); }, }, ], diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index cf31304..2815417 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -3,28 +3,32 @@ import { ChakraProvider } from '@chakra-ui/react'; import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; +import { I18nextProvider } from 'react-i18next'; import SocketProvider from './context/socket.context'; import './App.css'; import Home from './pages/home'; import theme from './theme'; import Start from './pages/start'; import store from './redux/store'; +import i18n from './i18n'; export default function App() { return ( - - - - - - - } /> - } /> - - - - - - + + + + + + + + } /> + } /> + + + + + + + ); } diff --git a/src/renderer/components/CreatePageButton/index.tsx b/src/renderer/components/CreatePageButton/index.tsx index 5e59aa2..a252f7e 100644 --- a/src/renderer/components/CreatePageButton/index.tsx +++ b/src/renderer/components/CreatePageButton/index.tsx @@ -10,6 +10,7 @@ import { ModalFooter, useDisclosure, } from '@chakra-ui/react'; +import { useTranslation } from 'react-i18next'; import { useCallback, memo } from 'react'; import { useDispatch } from 'react-redux'; import { useForm } from 'react-hook-form'; @@ -20,6 +21,7 @@ import TextInput from '../Form/TextInput'; const CreatePageButton = () => { const { isOpen, onOpen, onClose } = useDisclosure(); + const { t } = useTranslation('create-page-button'); const dispatch = useDispatch(); @@ -39,19 +41,19 @@ const CreatePageButton = () => { return ( <> - Create a new device + {t('create_new_device')}
{ @@ -75,13 +77,13 @@ const CreatePageButton = () => { diff --git a/src/renderer/components/editor/index.tsx b/src/renderer/components/editor/index.tsx index c7915d7..b22e33b 100644 --- a/src/renderer/components/editor/index.tsx +++ b/src/renderer/components/editor/index.tsx @@ -9,6 +9,7 @@ import { Text, } from '@chakra-ui/react'; import { useSelector, useDispatch } from 'react-redux'; +import { useTranslation } from 'react-i18next'; import { useForm } from 'react-hook-form'; import Form from 'renderer/components/Form'; import CheckboxInput from 'renderer/components/Form/CheckboxInput'; @@ -22,6 +23,8 @@ import SelectInput from '../Form/SelectInput'; import ColorInput from '../Form/ColorInput'; const Editor = () => { + const { t } = useTranslation('editor'); + const { currentKey, pages } = useSelector((state: any) => ({ currentKey: state.keys.currentKey, pages: state.pages.items, @@ -64,7 +67,7 @@ const Editor = () => { return ( { return ( { ); @@ -97,7 +100,7 @@ const Editor = () => { return ( ({ key: p.id, label: p.name }))} /> ); @@ -114,7 +117,7 @@ const Editor = () => { { @@ -131,7 +134,7 @@ const Editor = () => { { - + @@ -154,14 +157,14 @@ const Editor = () => {
@@ -194,11 +197,10 @@ const Editor = () => { - No Action Key Selected + {t('empty.title')} - Drag an action key from the sidebar and drop into any available key - to start editing. + {t('empty.description')} diff --git a/src/renderer/components/header/DeletePageButton.tsx b/src/renderer/components/header/DeletePageButton.tsx index 35bfe52..65b0965 100644 --- a/src/renderer/components/header/DeletePageButton.tsx +++ b/src/renderer/components/header/DeletePageButton.tsx @@ -9,13 +9,16 @@ import { AlertDialogFooter, useDisclosure, } from '@chakra-ui/react'; +import { useTranslation } from 'react-i18next'; import { memo, useCallback, useRef } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { MdDelete } from 'react-icons/md'; import { deletePage } from 'renderer/redux/ducks/pages'; const DeletePageButton = () => { + const { t } = useTranslation('header'); const { isOpen, onOpen, onClose } = useDisclosure(); + const dispatch = useDispatch(); const cancelRef = useRef(); @@ -37,7 +40,7 @@ const DeletePageButton = () => { size="sm" onClick={onOpen} > - Delete Page + {t('delete_page')} { - Are you sure you want to delete this page? + {t('dialog.delete_title')} - This action cannot be undone. + {t('dialog.delete_description')} diff --git a/src/renderer/components/header/PageSelector.tsx b/src/renderer/components/header/PageSelector.tsx index 317dbc2..2292ff4 100644 --- a/src/renderer/components/header/PageSelector.tsx +++ b/src/renderer/components/header/PageSelector.tsx @@ -14,6 +14,7 @@ import { } from '@chakra-ui/react'; import { useCallback, memo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { useTranslation } from 'react-i18next'; import { useForm } from 'react-hook-form'; import { MdAdd } from 'react-icons/md'; import { IPage } from 'interfaces'; @@ -24,6 +25,7 @@ import Form from '../Form'; import DeletePageButton from './DeletePageButton'; const PageSelector = () => { + const { t } = useTranslation('header'); const { isOpen, onOpen, onClose } = useDisclosure(); const dispatch = useDispatch(); @@ -67,7 +69,7 @@ const PageSelector = () => { <> {
diff --git a/src/renderer/components/header/index.tsx b/src/renderer/components/header/index.tsx index 3e2d8a4..8b031dc 100644 --- a/src/renderer/components/header/index.tsx +++ b/src/renderer/components/header/index.tsx @@ -1,5 +1,6 @@ import { Icon, Flex, Spacer, Button } from '@chakra-ui/react'; import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { MdNavigateBefore } from 'react-icons/md'; import { useNavigate } from 'react-router'; @@ -7,6 +8,7 @@ import PageSelector from './PageSelector'; import PairDeckButton from './PairDeckButton'; const Header = () => { + const { t } = useTranslation('header'); const navigate = useNavigate(); const goBack = useCallback(() => { @@ -20,7 +22,7 @@ const Header = () => { size="sm" onClick={goBack} > - Go Back + {t('go_back')} diff --git a/src/renderer/constants/key-types.constant.ts b/src/renderer/constants/key-types.constant.ts index 2fb58c1..4a4a5e1 100644 --- a/src/renderer/constants/key-types.constant.ts +++ b/src/renderer/constants/key-types.constant.ts @@ -19,85 +19,85 @@ import { } from 'react-icons/md'; import { IKeyType } from 'interfaces'; import MediaKeys from 'server/enums/media-keys.enum'; +import i18n from 'renderer/i18n'; const KEY_TYPES: Array = [ { key: 'DECK', - label: 'Deck', + label: i18n.t('keys:deck'), icon: MdOutlineCropLandscape, items: [ { type: KeyTypes.NAVIGATE, - label: 'Navigate', - description: 'Navigate to another page', + label: i18n.t('keys:navigate.title'), + description: i18n.t('keys:navigate.description'), icon: MdNavigateNext, defaults: { color: '#fff', - label: 'Go to...', + label: i18n.t('keys:navigate.label'), }, }, { type: KeyTypes.GO_BACK, - label: 'Go back', - description: 'Return to previous page', + label: i18n.t('keys:go_back.title'), + description: i18n.t('keys:go_back.description'), icon: MdNavigateBefore, defaults: { color: '#fff', - label: 'Go Back', + label: i18n.t('keys:go_back.label'), }, }, ], }, { key: 'SYSTEM', - label: 'System', + label: i18n.t('keys:system'), icon: MdSettings, items: [ { type: KeyTypes.EXECUTABLE, - label: 'Run executable', - description: 'Open an app installed on your computer', + label: i18n.t('keys:executable.title'), + description: i18n.t('keys:executable.description'), icon: MdMonitor, defaults: { color: '#fff', - label: 'Open app', + label: i18n.t('keys:executable.label'), }, }, { type: KeyTypes.HOTKEY, - label: 'Hotkey', - description: - 'Simulates a combination of keys to invoke a hotkey shortcut', + label: i18n.t('keys:hotkey.title'), + description: i18n.t('keys:hotkey.description'), icon: MdKeyboard, defaults: { color: '#fff', - label: 'Hotkey', + label: i18n.t('keys:hotkey.label'), }, }, { type: KeyTypes.URL, - label: 'Website', - description: 'Open an URL in your default browser', + label: i18n.t('keys:website.title'), + description: i18n.t('keys:website.description'), icon: MdLink, defaults: { color: '#fff', - label: 'Open link', + label: i18n.t('keys:website.label'), }, }, ], }, { key: 'MEDIA', - label: 'Media', + label: i18n.t('keys:media'), icon: MdMusicNote, items: [ { type: KeyTypes.PLAY, - label: 'Play', + label: i18n.t('keys:play'), icon: MdPlayArrow, defaults: { color: '#fff', - label: 'Play', + label: i18n.t('keys:play'), actionConfig: { mediaKey: MediaKeys.PLAY, }, @@ -105,11 +105,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.PAUSE, - label: 'Pause', + label: i18n.t('keys:pause'), icon: MdPause, defaults: { color: '#fff', - label: 'Pause', + label: i18n.t('keys:pause'), actionConfig: { mediaKey: MediaKeys.PAUSE, }, @@ -117,11 +117,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.NEXT, - label: 'Next', + label: i18n.t('keys:next'), icon: MdSkipNext, defaults: { color: '#fff', - label: 'Next', + label: i18n.t('keys:next'), actionConfig: { mediaKey: MediaKeys.NEXT, }, @@ -129,11 +129,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.PREVIOUS, - label: 'Previous', + label: i18n.t('keys:previous'), icon: MdSkipPrevious, defaults: { color: '#fff', - label: 'Previous', + label: i18n.t('keys:previous'), actionConfig: { mediaKey: MediaKeys.PREVIOUS, }, @@ -141,11 +141,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.STOP, - label: 'Stop', + label: i18n.t('keys:stop'), icon: MdStop, defaults: { color: '#fff', - label: 'Stop', + label: i18n.t('keys:stop'), actionConfig: { mediaKey: MediaKeys.STOP, }, @@ -153,11 +153,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.MUTE, - label: 'Mute', + label: i18n.t('keys:mute'), icon: MdVolumeOff, defaults: { color: '#fff', - label: 'Mute', + label: i18n.t('keys:mute'), actionConfig: { mediaKey: MediaKeys.MUTE, }, @@ -165,11 +165,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.VOL_DOWN, - label: 'Volume -', + label: i18n.t('keys:volume_minus'), icon: MdVolumeDown, defaults: { color: '#fff', - label: 'Volume -', + label: i18n.t('keys:volume_minus'), actionConfig: { mediaKey: MediaKeys.VOL_DOWN, }, @@ -177,11 +177,11 @@ const KEY_TYPES: Array = [ }, { type: KeyTypes.VOL_UP, - label: 'Volume +', + label: i18n.t('keys:volume_plus'), icon: MdVolumeUp, defaults: { color: '#fff', - label: 'Volume +', + label: i18n.t('keys:volume_plus'), actionConfig: { mediaKey: MediaKeys.VOL_UP, }, diff --git a/src/renderer/i18n/index.ts b/src/renderer/i18n/index.ts new file mode 100644 index 0000000..472bf00 --- /dev/null +++ b/src/renderer/i18n/index.ts @@ -0,0 +1,18 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import resources from './locales'; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources, + fallbackLng: 'en', + interpolation: { + escapeValue: false, + }, + defaultNS: 'common', + }); + +export default i18n; diff --git a/src/renderer/i18n/locales/en-US/common.json b/src/renderer/i18n/locales/en-US/common.json new file mode 100644 index 0000000..eaf9b6d --- /dev/null +++ b/src/renderer/i18n/locales/en-US/common.json @@ -0,0 +1,10 @@ +{ + "app_name": "ODeck", + "welcome_to": "Welcome to", + "select": "Select", + "select_device": "Select a device:", + "help": "Help", + "documentation": "Documentation", + "community_discussions": "Community Discussions", + "report_bug": "Report Bug" +} \ No newline at end of file diff --git a/src/renderer/i18n/locales/en-US/create-page-button.json b/src/renderer/i18n/locales/en-US/create-page-button.json new file mode 100644 index 0000000..254b7a3 --- /dev/null +++ b/src/renderer/i18n/locales/en-US/create-page-button.json @@ -0,0 +1,11 @@ +{ + "create_device": "Create Device", + "create_new_device": "Create a new device", + "label": { + "name": "Name", + "amount_vertically": "Amount of keys vertically", + "amount_horizontally": "Amount of keys horizontally" + }, + "cancel": "Cancel", + "create": "Create" +} \ No newline at end of file diff --git a/src/renderer/i18n/locales/en-US/editor.json b/src/renderer/i18n/locales/en-US/editor.json new file mode 100644 index 0000000..062d130 --- /dev/null +++ b/src/renderer/i18n/locales/en-US/editor.json @@ -0,0 +1,22 @@ +{ + "label": { + "path": "Path", + "bindings": "Bindings", + "url": "URL", + "page": "Page", + "label": "Label", + "background_color": "Background Color", + "background_url": "Background URL", + "text_color": "Text Color", + "hide_label": "Hide Label", + "remove": "Remove", + "save": "Save" + }, + "hint": { + "url": "Place here the URL you want to open when the button is pressed" + }, + "empty": { + "title": "No Action Key Selected", + "description": "Drag an action key from the sidebar and drop into any available key to start editing." + } +} diff --git a/src/renderer/i18n/locales/en-US/header.json b/src/renderer/i18n/locales/en-US/header.json new file mode 100644 index 0000000..b8634de --- /dev/null +++ b/src/renderer/i18n/locales/en-US/header.json @@ -0,0 +1,22 @@ +{ + "delete_page": "Delete Page", + "dialog": { + "delete_title": "Are you sure you want to delete this page?", + "delete_description": "This action cannot be undone.", + "add_title": "Add Page", + "add_description": "Create a new page", + "connect_title": "Connect your device:", + "connect_description": "Select your local network IP address, then open the app and scan the QR Code below with your device.", + "cancel": "Cancel", + "delete": "Delete", + "create": "Create", + "close": "Close" + }, + "label": { + "name": "Name" + }, + "select_ip_address": "Select an IP address", + "select_page": "Select a page", + "pair_deck": "Pair Deck", + "go_back": "Go Back" +} diff --git a/src/renderer/i18n/locales/en-US/index.ts b/src/renderer/i18n/locales/en-US/index.ts new file mode 100644 index 0000000..edd4421 --- /dev/null +++ b/src/renderer/i18n/locales/en-US/index.ts @@ -0,0 +1,13 @@ +import common from './common.json'; +import editor from './editor.json'; +import header from './header.json'; +import keys from './keys.json'; +import createPageButton from './create-page-button.json'; + +export default { + common, + editor, + header, + keys, + 'create-page-button': createPageButton, +}; diff --git a/src/renderer/i18n/locales/en-US/keys.json b/src/renderer/i18n/locales/en-US/keys.json new file mode 100644 index 0000000..a4eeb54 --- /dev/null +++ b/src/renderer/i18n/locales/en-US/keys.json @@ -0,0 +1,39 @@ +{ + "deck": "Deck", + "navigate": { + "title": "Navigate", + "description": "Navigate to another page", + "label": "Go to..." + }, + "go_back": { + "title": "Go back", + "description": "Return to previous page", + "label": "Go Back" + }, + "system": "System", + "executable": { + "title": "Run executable", + "description": "Open an app installed on your computer", + "label": "Open app" + }, + "hotkey": { + "title": "HotKey", + "description": "Simulates a combination of keys to invoke a hotkey shortcut", + "label": "Hotkey" + }, + "website": { + "title": "Website", + "description": "Open an URL in your default browser", + "label": "Open link" + }, + "media": "Media", + "play": "Play", + "pause": "Pause", + "next": "Next", + "previous": "Previous", + "stop": "Stop", + "mute": "Mute", + "volume_plus": "Volume +", + "volume_minus": "Volume -" +} + diff --git a/src/renderer/i18n/locales/index.ts b/src/renderer/i18n/locales/index.ts new file mode 100644 index 0000000..9140f1d --- /dev/null +++ b/src/renderer/i18n/locales/index.ts @@ -0,0 +1,9 @@ +import en from './en-US'; +import pt from './pt-BR'; + +const resources = { + en, + pt, +}; + +export default resources; diff --git a/src/renderer/i18n/locales/pt-BR/common.json b/src/renderer/i18n/locales/pt-BR/common.json new file mode 100644 index 0000000..39b1b2e --- /dev/null +++ b/src/renderer/i18n/locales/pt-BR/common.json @@ -0,0 +1,10 @@ +{ + "app_name": "ODeck", + "welcome_to": "Bem-Vindo ao", + "select": "Selecione", + "select_device": "Selecione um dispositivo:", + "help": "Ajuda", + "documentation": "Documentação", + "community_discussions": "Discussões da comunidade", + "report_bug": "Relatar um problema" +} \ No newline at end of file diff --git a/src/renderer/i18n/locales/pt-BR/create-page-button.json b/src/renderer/i18n/locales/pt-BR/create-page-button.json new file mode 100644 index 0000000..db621c3 --- /dev/null +++ b/src/renderer/i18n/locales/pt-BR/create-page-button.json @@ -0,0 +1,11 @@ +{ + "create_device": "Criar dispositivo", + "create_new_device": "Criar novo dispositivo", + "label": { + "name": "Nome", + "amount_vertically": "Quantidade de teclas verticalmente", + "amount_horizontally": "Quantidade de teclas horizontalmente" + }, + "cancel": "Cancelar", + "create": "Criar" +} \ No newline at end of file diff --git a/src/renderer/i18n/locales/pt-BR/editor.json b/src/renderer/i18n/locales/pt-BR/editor.json new file mode 100644 index 0000000..6396dfd --- /dev/null +++ b/src/renderer/i18n/locales/pt-BR/editor.json @@ -0,0 +1,22 @@ +{ + "label": { + "path": "Caminho", + "bindings": "Bindings", + "url": "URL", + "page": "Página", + "label": "Rótulo", + "background_color": "Cor de fundo", + "background_url": "URL da imagem de fundo", + "text_color": "Cor do Texto", + "hide_label": "Esconder Rótulo", + "remove": "Remover", + "save": "Salvar" + }, + "hint": { + "url": "Coloque aqui o URL que você quer que seja aberto quando pressionar o botão" + }, + "empty": { + "title": "Nenhuma tecla selecionada", + "description": "Arraste uma tecla de ação do menu lateral em uma tecla disponível para começar." + } +} diff --git a/src/renderer/i18n/locales/pt-BR/header.json b/src/renderer/i18n/locales/pt-BR/header.json new file mode 100644 index 0000000..a550759 --- /dev/null +++ b/src/renderer/i18n/locales/pt-BR/header.json @@ -0,0 +1,22 @@ +{ + "delete_page": "Excluir Página", + "dialog": { + "delete_title": "Você tem certeza que quer excluir esta página?", + "delete_description": "Esta ação não pode ser desfeita.", + "add_title": "Adicionar Página", + "add_description": "Criar uma nova página", + "connect_title": "Conectar seu dispositivo:", + "connect_description": "Selecione o endereço IP do computador na sua rede local, depois abra o aplicativo e escaneie o QR Code com seu dispositivo", + "cancel": "Cancelar", + "delete": "Excluir", + "create": "Criar", + "close": "Fechar" + }, + "label": { + "name": "Nome" + }, + "select_ip_address": "Selecione um endereço IP", + "select_page": "Selecione uma Página", + "pair_deck": "Parear Deck", + "go_back": "Voltar" +} diff --git a/src/renderer/i18n/locales/pt-BR/index.ts b/src/renderer/i18n/locales/pt-BR/index.ts new file mode 100644 index 0000000..edd4421 --- /dev/null +++ b/src/renderer/i18n/locales/pt-BR/index.ts @@ -0,0 +1,13 @@ +import common from './common.json'; +import editor from './editor.json'; +import header from './header.json'; +import keys from './keys.json'; +import createPageButton from './create-page-button.json'; + +export default { + common, + editor, + header, + keys, + 'create-page-button': createPageButton, +}; diff --git a/src/renderer/i18n/locales/pt-BR/keys.json b/src/renderer/i18n/locales/pt-BR/keys.json new file mode 100644 index 0000000..0278cb5 --- /dev/null +++ b/src/renderer/i18n/locales/pt-BR/keys.json @@ -0,0 +1,39 @@ +{ + "deck": "Deck", + "navigate": { + "title": "Navegar", + "description": "Navegar para outra página", + "label": "Vá para..." + }, + "go_back": { + "title": "Voltar", + "description": "Voltar para a página anterior", + "label": "Voltar" + }, + "system": "Sistema", + "executable": { + "title": "Abrir executável", + "description": "Abrir um aplicativo instalado em seu computador", + "label": "Abrir aplicativo" + }, + "hotkey": { + "title": "HotKey", + "description": "Simula uma combinações de teclas para invocar um atalho de teclado", + "label": "Hotkey" + }, + "website": { + "title": "Website", + "description": "Abrir um URL no seu navegador padrão", + "label": "Abrir link" + }, + "media": "Mídia", + "play": "Tocar", + "pause": "Pausar", + "next": "Próximo", + "previous": "Anterior", + "stop": "Parar", + "mute": "Silenciar", + "volume_plus": "Volume +", + "volume_minus": "Volume -" +} + diff --git a/src/renderer/pages/start/index.tsx b/src/renderer/pages/start/index.tsx index 266bb36..d196827 100644 --- a/src/renderer/pages/start/index.tsx +++ b/src/renderer/pages/start/index.tsx @@ -1,6 +1,7 @@ import { useCallback } from 'react'; import { Box, Flex, Text, Center, Button, Heading } from '@chakra-ui/react'; import { useSelector, useDispatch } from 'react-redux'; +import { useTranslation } from 'react-i18next'; import { IDevice } from 'interfaces'; import { useNavigate } from 'react-router-dom'; import { selectDevice } from 'renderer/redux/ducks/devices'; @@ -8,6 +9,7 @@ import CreatePageButton from 'renderer/components/CreatePageButton'; const Start = () => { const navigate = useNavigate(); + const { t } = useTranslation(); const devices = useSelector((state: any) => state.devices.items); const dispatch = useDispatch(); @@ -49,7 +51,7 @@ const Start = () => { colorScheme="blue" onClick={() => handleSelectDevice(device)} > - Select + {t('select')} @@ -64,8 +66,8 @@ const Start = () => { background="linear-gradient(265.97deg, #171923 0%, #18202E 28.18%, #1A2D38 43.12%, #1A3638 59.94%, #0E2829 79.05%, #171923 100.72%)" > - Welcome to - ODeck + {t('welcome_to')} + {t('app_name')} @@ -75,7 +77,7 @@ const Start = () => { alignItems="center" justifyContent="space-between" > - Select a device: + {t('select_device')} {devices.map(renderDevice)} diff --git a/src/renderer/redux/ducks/devices.ts b/src/renderer/redux/ducks/devices.ts index 95d3008..4b95a11 100644 --- a/src/renderer/redux/ducks/devices.ts +++ b/src/renderer/redux/ducks/devices.ts @@ -39,6 +39,7 @@ export const setCurrentDevice = (device: IDevice) => { // Thunks export const selectDevice = ({ device, deviceType }: any) => { return (dispatch: any, _getState: any, socket: Socket) => { + if (!device?.id) return; dispatch(setCurrentDevice(device)); socket.emit(EventTypes.DEVICES.SELECT, { deviceId: device.id, diff --git a/src/renderer/redux/ducks/keys.ts b/src/renderer/redux/ducks/keys.ts index 5ae8479..c8790d3 100644 --- a/src/renderer/redux/ducks/keys.ts +++ b/src/renderer/redux/ducks/keys.ts @@ -11,6 +11,7 @@ const initialState = { const Types = { SET_KEYS: 'DECK/KEYS/SET_KEYS', SET_CURRENT_KEY: 'DECK/KEYS/SET_CURRENT_KEY', + CLEAR_CURRENT_KEY: 'DECK/KEYS/CLEAR_CURRENT_KEY', }; // Reducer @@ -22,6 +23,9 @@ const reducer = (state = initialState, action: any) => { case Types.SET_CURRENT_KEY: { return { ...state, currentKey: action.currentKey }; } + case Types.CLEAR_CURRENT_KEY: { + return { ...state, currentKey: null }; + } default: return state; } @@ -36,6 +40,10 @@ export const setCurrentKey = (currentKey: IButtonKey) => { return { type: Types.SET_CURRENT_KEY, currentKey }; }; +export const clearCurrentKey = () => { + return { type: Types.CLEAR_CURRENT_KEY }; +}; + // Thunks export const createKey = (newKey: IButtonKey) => { return (dispatch: any, _getState: any, socket: Socket) => { @@ -52,8 +60,9 @@ export const updateKey = (updatedButton: IButtonKey) => { }; export const deleteKey = (currentKey: IButtonKey) => { - return (_dispatch: any, _getState: any, socket: Socket) => { - socket.emit('DELETE_KEY', currentKey); + return (dispatch: any, _getState: any, socket: Socket) => { + socket.emit(EventTypes.KEYS.DELETE, currentKey); + dispatch(clearCurrentKey()); }; }; diff --git a/yarn.lock b/yarn.lock index 6a45686..fe68c90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -275,7 +275,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.15.4", "@babel/runtime@^7.17.0": +"@babel/runtime@^7.12.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.0": version "7.17.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== @@ -5670,7 +5670,7 @@ html-entities@^2.1.0, html-entities@^2.3.2: resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz" integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ== -html-escaper@^2.0.0: +html-escaper@^2.0.0, html-escaper@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== @@ -5688,6 +5688,13 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.7.2" +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" + html-webpack-plugin@^5.5.0: version "5.5.0" resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz" @@ -5807,6 +5814,20 @@ husky@^7.0.4: resolved "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz" integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== +i18next-browser-languagedetector@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.3.tgz#9bc47fb4a5db86d567318895df343d2d1b9692f3" + integrity sha512-T+oGXHXtrur14CGnZZ7qQ07X38XJQEI00b/4ILrtO6xPbwTlQ1wtMZC2H+tBULixHuVUXv8LKbxfjyITJkezUg== + dependencies: + "@babel/runtime" "^7.14.6" + +i18next@^21.6.13: + version "21.6.13" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.6.13.tgz#e881b05f156ac06997e9b63379d8b2674bb4a4f2" + integrity sha512-MVjNttw+5mIuu2/fwTpSU0EeI7iU/6pnDvGQboCzkILiv0/gD+FLZaF7qSHmUHO4ZkE6xJQ9SlBgGvMHxhC82Q== + dependencies: + "@babel/runtime" "^7.12.0" + iconv-corefoundation@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz#31065e6ab2c9272154c8b0821151e2c88f1b002a" @@ -8459,6 +8480,15 @@ react-hook-form@^7.24.2: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.24.2.tgz" integrity sha512-Ora2l2A4ts8xLxP9QOKEAObTMNZNdR7gt3UpWb9alFJx/AFAQcYAi/joLNo6PoC0AE/Vyq4pnCYb9jUufWoVNw== +react-i18next@^11.15.5: + version "11.15.5" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.15.5.tgz#3de940a1c5a27956d8265663ca67494881f385e8" + integrity sha512-vBWuVEQgrhZrGKpyv8FmJ7Zs5jRQWl794Tte7yzJ0okZqqi3jd6j2pLYNg441WcREsbIOvWdiDXbY7W6E93p1A== + dependencies: + "@babel/runtime" "^7.14.5" + html-escaper "^2.0.2" + html-parse-stringify "^3.0.1" + react-icons@^4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz" @@ -10004,6 +10034,11 @@ verror@^1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk= + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz"