diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 000000000..875c03fc8 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,9 @@ +module.exports = { + // https://www.i18next.com/overview/configuration-options#logging + debug: process.env.NODE_ENV === 'development', + i18n: { + defaultLocale: 'en', + locales: ['en', 'de'], + }, + reloadOnPrerender: process.env.NODE_ENV === 'development', +}; diff --git a/next.config.js b/next.config.js index b91c26880..17decd099 100644 --- a/next.config.js +++ b/next.config.js @@ -1,5 +1,7 @@ const { env } = require('process'); +const { i18n } = require('./next-i18next.config'); + const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); @@ -13,4 +15,5 @@ module.exports = withBundleAnalyzer({ outputStandalone: true, }, output: 'standalone', + i18n, }); diff --git a/package.json b/package.json index c1f504a8d..a0052a60a 100644 --- a/package.json +++ b/package.json @@ -58,10 +58,10 @@ "i18next-http-backend": "^1.4.1", "js-file-download": "^0.4.12", "next": "12.1.6", + "next-i18next": "^11.3.0", "prism-react-renderer": "^1.3.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-i18next": "^11.18.4", "sharp": "^0.30.7", "systeminformation": "^5.12.1", "uuid": "^8.3.2", diff --git a/public/locales/de-de.json b/public/locales/de/common.json similarity index 85% rename from public/locales/de-de.json rename to public/locales/de/common.json index 331c956e6..aca69de4a 100644 --- a/public/locales/de-de.json +++ b/public/locales/de/common.json @@ -108,14 +108,14 @@ "header": { "search": { "input": { - "placeholder": "Search the web..." + "placeholder": "Das Internet durchsuchen..." } }, "docker": { "errors": { "integrationFailed": { - "title": "Docker integration failed", - "message": "Did you forget to mount the docker socket ?" + "title": "Docker Integration ist gescheitert", + "message": "Hast du vergessen, den Docker Socket zu verbinden?" } }, "actionIcon": { @@ -124,22 +124,22 @@ }, "addService": { "actionIcon": { - "tooltip": "Add a service" + "tooltip": "Einen Service hinzufügen" }, "modal": { - "title": "Add service", + "title": "Service hinzufügen", "form": { "validation": { - "invalidUrl": "Please enter a valid URL", - "noStatusCodeSelected": "Please select a status code" + "invalidUrl": "Bitte gebe eine gültige Addresse ein", + "noStatusCodeSelected": "Bitte wähle einen gültigen Status Code aus" } }, "tabs": { "options": { - "title": "Options", + "title": "Optionen", "form": { "serviceName": { - "label": "Service name", + "label": "Service Namen", "placeholder": "Plex" }, "iconUrl": { @@ -149,59 +149,59 @@ "label": "Service URL" }, "onClickUrl": { - "label": "On Click URL" + "label": "URL beim Klicken" }, "serviceType": { - "label": "Service type", - "defaultValue": "Other", - "placeholder": "Pick one" + "label": "Service Typ", + "defaultValue": "Andere", + "placeholder": "Wähle einen aus..." }, "category": { - "label": "Category", - "placeholder": "Select a category or create a new one", - "nothingFound": "Nothing found", - "createLabel": "+ Create {{query}}" + "label": "Kategorie", + "placeholder": "Wähle eine Kategorie oder erstelle eine neue...", + "nothingFound": "Keine Übereinstimmungen gefunden", + "createLabel": "+ Erstellen {{query}}" }, "integrations": { "apiKey": { - "label": "API key", - "placeholder": "Your API key", + "label": "API Schlüssel", + "placeholder": "Dein API Schlüssel", "validation": { - "noKey": "Invalid Key" + "noKey": "Invalider Schlüssel" }, "tip": { - "text": "Get your API key", - "link": "here." + "text": "Hole deinen API Schlüssel", + "link": "hier." } }, "qBittorrent": { "username": { - "label": "Username", + "label": "Benutzername", "placeholder": "admin", "validation": { - "invalidUsername": "Invalid username" + "invalidUsername": "Invalider Benutzername" } }, "password": { - "label": "Password", + "label": "Passwort", "placeholder": "adminadmin", "validation": { - "invalidPassword": "Invalid password" + "invalidPassword": "Invalides Passwort" } } }, "deluge": { "password": { - "label": "Password", + "label": "Passwort", "placeholder": "password", "validation": { - "invalidPassword": "Invalid password" + "invalidPassword": "Invalides PassworT" } } }, "transmission": { "username": { - "label": "Username", + "label": "Benutzername", "placeholder": "admin", "validation": { "invalidUsername": "Invalid username" diff --git a/public/locales/en-us.json b/public/locales/en-us.json deleted file mode 100644 index 142283853..000000000 --- a/public/locales/en-us.json +++ /dev/null @@ -1,385 +0,0 @@ -{ - "settings": { - "title": "Settings", - "tooltip": "Settings", - "tabs": { - "common": { - "title": "Common", - "settings": { - "searchEngine": { - "title": "Search engine", - "tips": { -"generalTip": "Use the prefixes !yt and !t in front of your query to search on YouTube or for a Torrent respectively.", - "placeholderTip": "%s can be used as a placeholder for the query." - }, - "customEngine": { - "label": "Query URL", - "placeholder": "Custom query URL" - } - }, - "colorScheme": { - "label": "Switch to {{scheme}} mode" - }, - "widgetsPositionSwitch": { - "label": "Position widgets on left" - }, - "moduleEnabler": { - "title": "Module enabler" - }, - "language": { - "title": "Language" - }, - "configChanger": { - "configSelect": { - "label": "Config loader" - }, - "modal": { - "title": "Choose the name of your new config", - "form": { - "configName": { - "label": "Config name", - "placeholder": "Your new config name" - }, - "buttons:": { - "submit": "Confirm" - } - } - }, - "buttons": { - "download": "Download config", - "delete": { - "text": "Delete config", - "notifications": { - "deleted": { - "title": "Config deleted", - "message": "Config deleted" - }, - "deleteFailed": { - "title": "Config delete failed", - "message": "Config delete failed" - } - } - }, - "saveCopy": "Save a copy" - } - }, - "configTip": "Upload your config file by dragging and dropping it onto the page!" - } - }, - "customizations": { - "title": "Customizations", - "settings": { - "opacitySelector": { - "label": "App Opacity" - }, - "colorSelector": { - "suffix": "{{color}} color" - }, - "shadeSelector": { - "label": "Shade" - }, - "pageTitle": { - "label": "Page Title", - "placeholder": "Homarr 🦞" - }, - "logo": { - "label": "Logo", - "placeholder": "/img/logo.png" - }, - "favicon": { - "label": "Favicon", - "placeholder": "/favicon.png" - }, - "background": { - "label": "Background", - "placeholder": "/img/background.png" - }, - "buttons": { - "submit": "Save" - } - } - } - }, - "credits": { - "madeWithLove": "Made with ❤️ by @" - } - }, - "layout": { - "header": { - "search": { - "input": { - "placeholder": "Search the web..." - } - }, - "docker": { - "errors": { - "integrationFailed": { - "title": "Docker integration failed", - "message": "Did you forget to mount the docker socket ?" - } - }, - "actionIcon": { - "tooltip": "Docker" - } - }, - "addService": { - "actionIcon": { - "tooltip": "Add a service" - }, - "modal": { - "title": "Add service", - "form": { - "validation": { - "invalidUrl": "Please enter a valid URL", - "noStatusCodeSelected": "Please select a status code" - } - }, - "tabs": { - "options": { - "title": "Options", - "form": { - "serviceName": { - "label": "Service name", - "placeholder": "Plex" - }, - "iconUrl": { - "label": "Icon URL" - }, - "serviceUrl": { - "label": "Service URL" - }, - "onClickUrl": { - "label": "On Click URL" - }, - "serviceType": { - "label": "Service type", - "defaultValue": "Other", - "placeholder": "Pick one" - }, - "category": { - "label": "Category", - "placeholder": "Select a category or create a new one", - "nothingFound": "Nothing found", - "createLabel": "+ Create {{query}}" - }, - "integrations": { - "apiKey": { - "label": "API key", - "placeholder": "Your API key", - "validation": { - "noKey": "Invalid Key" - }, - "tip": { - "text": "Get your API key", - "link": "here." - } - }, - "qBittorrent": { - "username": { - "label": "Username", - "placeholder": "admin", - "validation": { - "invalidUsername": "Invalid username" - } - }, - "password": { - "label": "Password", - "placeholder": "adminadmin", - "validation": { - "invalidPassword": "Invalid password" - } - } - }, - "deluge": { - "password": { - "label": "Password", - "placeholder": "password", - "validation": { - "invalidPassword": "Invalid password" - } - } - }, - "transmission": { - "username": { - "label": "Username", - "placeholder": "admin", - "validation": { - "invalidUsername": "Invalid username" - } - }, - "password": { - "label": "Password", - "placeholder": "adminadmin", - "validation": { - "invalidPassword": "Invalid password" - } - } - } - } - } - }, - "advancedOptions": { - "title": "Advanced options", - "form": { - "httpStatusCodes": { - "label": "HTTP Status Codes", - "placeholder": "Select valid status codes", - "clearButtonLabel": "Clear selection", - "nothingFound": "Nothing found" - }, - "openServiceInNewTab": { - "label": "Open service in new tab" - }, - "buttons": { - "submit": { - "content": "Add service" - } - } - } - } - } - } - } - } - }, - "modules": { - "common": { - "mediaCard": { - "buttons": { - "play": "Play", - "request": "Request" - } - } - }, - "calendar": { - "title": "Calendar", - "description": "A calendar module for displaying upcoming releases. It interacts with the Sonarr and Radarr API.", - "options": { - "sundayStart": "Start the week on Sunday" - } - }, - "dashDot": { - "card": { - "title": "Dash.", - "errors": { - "noService": "No dash. service found. Please add one to your Homarr dashboard or set a dashdot URL in the module options", - "noInformation": "Cannot acquire information from dash. - are you running the latest version?" - }, - "graphs": { - "storage": { - "title": "Storage", - "label": "Storage:" - }, - "network": { - "title": "Network", - "label": "Network:", - "metrics": { - "download": "Down", - "upload": "Up" - } - }, - "cpu": { - "title": "CPU" - }, - "memory": { - "title": "RAM" - }, - "gpu": { - "title": "GPU" - } - } - } - }, - "torrent": { - "card": { - "title": "Your Downloads" - } - }, - "downloads": { - "card": { - "table": { - "header": { - "name": "Name", - "size": "Size", - "download": "Down", - "upload": "Up", - "estimatedTimeOfArrival": "ETA", - "progress": "Progress" - }, - "body": { - "nothingFound": "No torrents found" - } - } - } - }, - "weather": { - "card": { - "weatherDescriptions": { - "clear": "Clear", - "mainlyClear": "Mainly clear", - "fog": "Fog", - "drizzle": "Drizzle", - "freezingDrizzle": "Freezing drizzle", - "rain": "Rain", - "freezingRain": "Freezing rain", - "snowFall": "Snow fall", - "snowGrains": "Snow grains", - "rainShowers": "Rain showers", - "snowShowers": "Snow showers", - "thunderstorm": "Thunderstorm", - "thunderstormWithHail": "Thunderstorm with hail", - "unknown": "Unknown" - } - } - }, - "overseerr": { - "popup": { - "item": { - "buttons": { - "askFor": "Ask for {{title}}", - "cancel": "Cancel", - "request": "Request" - }, - "alerts": { - "automaticApproval": { - "title": "Using API key", - "text": "This request will be automatically approved" - } - } - }, - "seasonSelector": { - "caption": "Tick the seasons that you want to be downloaded", - "table": { - "header": { - "season": "Season", - "numberOfEpisodes": "Number of episodes" - } - } - } - } - }, - "ping": { - "states": { - "online": "Online {{response}}", - "offline": "Offline {{response}}", - "loading": "Loading..." - } - }, - "docker": { - "search": { - "placeholder": "Search by container or image name" - }, - "table": { - "header": { - "name": "Name", - "image": "Image", - "ports": "Ports", - "state": "State" - }, - "body": { - "portCollapse": "{{ports}} more" - } - } - } - } -} \ No newline at end of file diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 000000000..1df83e4d8 --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1,5 @@ +{ + "actions": { + "save": "Save" + } +} \ No newline at end of file diff --git a/public/locales/en/layout/add-service-app-shelf.json b/public/locales/en/layout/add-service-app-shelf.json new file mode 100644 index 000000000..c6417a161 --- /dev/null +++ b/public/locales/en/layout/add-service-app-shelf.json @@ -0,0 +1,118 @@ +{ + "actionIcon": { + "tooltip": "Add a service" + }, + "modal": { + "title": "Add service", + "form": { + "validation": { + "invalidUrl": "Please enter a valid URL", + "noStatusCodeSelected": "Please select a status code" + } + }, + "tabs": { + "options": { + "title": "Options", + "form": { + "serviceName": { + "label": "Service name", + "placeholder": "Plex" + }, + "iconUrl": { + "label": "Icon URL" + }, + "serviceUrl": { + "label": "Service URL" + }, + "onClickUrl": { + "label": "On Click URL" + }, + "serviceType": { + "label": "Service type", + "defaultValue": "Other", + "placeholder": "Pick one" + }, + "category": { + "label": "Category", + "placeholder": "Select a category or create a new one", + "nothingFound": "Nothing found", + "createLabel": "+ Create {{query}}" + }, + "integrations": { + "apiKey": { + "label": "API key", + "placeholder": "Your API key", + "validation": { + "noKey": "Invalid Key" + }, + "tip": { + "text": "Get your API key", + "link": "here." + } + }, + "qBittorrent": { + "username": { + "label": "Username", + "placeholder": "admin", + "validation": { + "invalidUsername": "Invalid username" + } + }, + "password": { + "label": "Password", + "placeholder": "adminadmin", + "validation": { + "invalidPassword": "Invalid password" + } + } + }, + "deluge": { + "password": { + "label": "Password", + "placeholder": "password", + "validation": { + "invalidPassword": "Invalid password" + } + } + }, + "transmission": { + "username": { + "label": "Username", + "placeholder": "admin", + "validation": { + "invalidUsername": "Invalid username" + } + }, + "password": { + "label": "Password", + "placeholder": "adminadmin", + "validation": { + "invalidPassword": "Invalid password" + } + } + } + } + } + }, + "advancedOptions": { + "title": "Advanced options", + "form": { + "httpStatusCodes": { + "label": "HTTP Status Codes", + "placeholder": "Select valid status codes", + "clearButtonLabel": "Clear selection", + "nothingFound": "Nothing found" + }, + "openServiceInNewTab": { + "label": "Open service in new tab" + }, + "buttons": { + "submit": { + "content": "Add service" + } + } + } + } + } + } + } \ No newline at end of file diff --git a/public/locales/en/layout/app-shelf.json b/public/locales/en/layout/app-shelf.json new file mode 100644 index 000000000..3297ffe7d --- /dev/null +++ b/public/locales/en/layout/app-shelf.json @@ -0,0 +1,10 @@ +{ + "accordions": { + "downloads": { + "text": "Your downloads" + }, + "others": { + "text": "Others" + } + } +} \ No newline at end of file diff --git a/public/locales/en/modules/common-media-cards-module.json b/public/locales/en/modules/common-media-cards-module.json new file mode 100644 index 000000000..af13d5d34 --- /dev/null +++ b/public/locales/en/modules/common-media-cards-module.json @@ -0,0 +1,6 @@ +{ + "buttons": { + "play": "Play", + "request": "Request" + } +} \ No newline at end of file diff --git a/public/locales/en/modules/dashdot-module.json b/public/locales/en/modules/dashdot-module.json new file mode 100644 index 000000000..ceb6b305a --- /dev/null +++ b/public/locales/en/modules/dashdot-module.json @@ -0,0 +1,32 @@ +{ + "card": { + "title": "Dash.", + "errors": { + "noService": "No dash. service found. Please add one to your Homarr dashboard or set a dashdot URL in the module options", + "noInformation": "Cannot acquire information from dash. - are you running the latest version?" + }, + "graphs": { + "storage": { + "title": "Storage", + "label": "Storage:" + }, + "network": { + "title": "Network", + "label": "Network:", + "metrics": { + "download": "Down", + "upload": "Up" + } + }, + "cpu": { + "title": "CPU" + }, + "memory": { + "title": "RAM" + }, + "gpu": { + "title": "GPU" + } + } + } +} \ No newline at end of file diff --git a/public/locales/en/modules/docker-module.json b/public/locales/en/modules/docker-module.json new file mode 100644 index 000000000..44fe76e17 --- /dev/null +++ b/public/locales/en/modules/docker-module.json @@ -0,0 +1,65 @@ +{ + "search": { + "placeholder": "Search by container or image name" + }, + "table": { + "header": { + "name": "Name", + "image": "Image", + "ports": "Ports", + "state": "State" + }, + "body": { + "portCollapse": "{{ports}} more" + }, + "states": { + "running": "Running", + "created": "Created", + "stopped": "Stopped", + "unknown": "Unknown" + } + }, + "actionBar": { + "addService": { + "title": "Add service", + "message": "Add service to Homarr" + }, + "restart": { + "title": "Restart" + }, + "stop": { + "title": "Stop" + }, + "start": { + "title": "Start" + }, + "refreshData": "Refresh data", + "addToHomarr": { + "title": "Add to Homarr" + }, + "remove": { + "title": "Remove" + } + }, + "messages": { + "successfullyExecuted": { + "title": "Container {{containerName}} {{action}}ed", + "message": "Your container was successfully {{action}}ed" + } + }, + "errors": { + "integrationFailed": { + "title": "Docker integration failed", + "message": "Did you forget to mount the docker socket ?" + }, + "unknownError": { + "title": "There was an error" + }, + "oneServiceAtATime": { + "title": "Please only add one service at a time!" + } + }, + "actionIcon": { + "tooltip": "Docker" + } +} \ No newline at end of file diff --git a/public/locales/en/modules/downloads-module.json b/public/locales/en/modules/downloads-module.json new file mode 100644 index 000000000..349e3b522 --- /dev/null +++ b/public/locales/en/modules/downloads-module.json @@ -0,0 +1,17 @@ +{ + "card": { + "table": { + "header": { + "name": "Name", + "size": "Size", + "download": "Down", + "upload": "Up", + "estimatedTimeOfArrival": "ETA", + "progress": "Progress" + }, + "body": { + "nothingFound": "No torrents found" + } + } + } +} \ No newline at end of file diff --git a/public/locales/en/modules/overseerr-module.json b/public/locales/en/modules/overseerr-module.json new file mode 100644 index 000000000..9d259613f --- /dev/null +++ b/public/locales/en/modules/overseerr-module.json @@ -0,0 +1,26 @@ +{ + "popup": { + "item": { + "buttons": { + "askFor": "Ask for {{title}}", + "cancel": "Cancel", + "request": "Request" + }, + "alerts": { + "automaticApproval": { + "title": "Using API key", + "text": "This request will be automatically approved" + } + } + }, + "seasonSelector": { + "caption": "Tick the seasons that you want to be downloaded", + "table": { + "header": { + "season": "Season", + "numberOfEpisodes": "Number of episodes" + } + } + } + } +} \ No newline at end of file diff --git a/public/locales/en/modules/ping-module.json b/public/locales/en/modules/ping-module.json new file mode 100644 index 000000000..305985605 --- /dev/null +++ b/public/locales/en/modules/ping-module.json @@ -0,0 +1,7 @@ +{ + "states": { + "online": "Online {{response}}", + "offline": "Offline {{response}}", + "loading": "Loading..." + } +} \ No newline at end of file diff --git a/public/locales/en/modules/search-module.json b/public/locales/en/modules/search-module.json new file mode 100644 index 000000000..209dad2fc --- /dev/null +++ b/public/locales/en/modules/search-module.json @@ -0,0 +1,5 @@ +{ + "input": { + "placeholder": "Search the web..." + } +} \ No newline at end of file diff --git a/public/locales/en/modules/weather-module.json b/public/locales/en/modules/weather-module.json new file mode 100644 index 000000000..81c82d7cc --- /dev/null +++ b/public/locales/en/modules/weather-module.json @@ -0,0 +1,20 @@ +{ + "card": { + "weatherDescriptions": { + "clear": "Clear", + "mainlyClear": "Mainly clear", + "fog": "Fog", + "drizzle": "Drizzle", + "freezingDrizzle": "Freezing drizzle", + "rain": "Rain", + "freezingRain": "Freezing rain", + "snowFall": "Snow fall", + "snowGrains": "Snow grains", + "rainShowers": "Rain showers", + "snowShowers": "Snow showers", + "thunderstorm": "Thunderstorm", + "thunderstormWithHail": "Thunderstorm with hail", + "unknown": "Unknown" + } + } +} \ No newline at end of file diff --git a/public/locales/en/settings/common.json b/public/locales/en/settings/common.json new file mode 100644 index 000000000..92b078dce --- /dev/null +++ b/public/locales/en/settings/common.json @@ -0,0 +1,11 @@ +{ + "title": "Settings", + "tooltip": "Settings", + "tabs": { + "common": "Common", + "customizations": "Customizations" + }, + "credits": { + "madeWithLove": "Made with ❤️ by @" + } +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/color-selector.json b/public/locales/en/settings/customization/color-selector.json new file mode 100644 index 000000000..d66bbfe6e --- /dev/null +++ b/public/locales/en/settings/customization/color-selector.json @@ -0,0 +1,3 @@ +{ + "suffix": "{{color}} color" +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/opacity-selector.json b/public/locales/en/settings/customization/opacity-selector.json new file mode 100644 index 000000000..edd46daea --- /dev/null +++ b/public/locales/en/settings/customization/opacity-selector.json @@ -0,0 +1,3 @@ +{ + "label": "App Opacity" +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/page-appearance.json b/public/locales/en/settings/customization/page-appearance.json new file mode 100644 index 000000000..1b513da3e --- /dev/null +++ b/public/locales/en/settings/customization/page-appearance.json @@ -0,0 +1,18 @@ +{ + "pageTitle": { + "label": "Page Title", + "placeholder": "Homarr 🦞" + }, + "logo": { + "label": "Logo", + "placeholder": "/img/logo.png" + }, + "favicon": { + "label": "Favicon", + "placeholder": "/favicon.png" + }, + "background": { + "label": "Background", + "placeholder": "/img/background.png" + } +} \ No newline at end of file diff --git a/public/locales/en/settings/customization/shade-selector.json b/public/locales/en/settings/customization/shade-selector.json new file mode 100644 index 000000000..076aee080 --- /dev/null +++ b/public/locales/en/settings/customization/shade-selector.json @@ -0,0 +1,3 @@ +{ + "label": "Shade" +} \ No newline at end of file diff --git a/public/locales/en/settings/general/color-schema.json b/public/locales/en/settings/general/color-schema.json new file mode 100644 index 000000000..16672bf7e --- /dev/null +++ b/public/locales/en/settings/general/color-schema.json @@ -0,0 +1,3 @@ +{ + "label": "Switch to {{scheme}} mode" +} \ No newline at end of file diff --git a/public/locales/en/settings/general/config-changer.json b/public/locales/en/settings/general/config-changer.json new file mode 100644 index 000000000..ea11d466c --- /dev/null +++ b/public/locales/en/settings/general/config-changer.json @@ -0,0 +1,35 @@ +{ + "configSelect": { + "label": "Config loader" + }, + "modal": { + "title": "Choose the name of your new config", + "form": { + "configName": { + "label": "Config name", + "placeholder": "Your new config name" + }, + "buttons:": { + "submit": "Confirm" + } + } + }, + "buttons": { + "download": "Download config", + "delete": { + "text": "Delete config", + "notifications": { + "deleted": { + "title": "Config deleted", + "message": "Config deleted" + }, + "deleteFailed": { + "title": "Config delete failed", + "message": "Config delete failed" + } + } + }, + "saveCopy": "Save a copy" + }, + "configTip": "Upload your config file by dragging and dropping it onto the page!" +} \ No newline at end of file diff --git a/public/locales/en/settings/general/internationalization.json b/public/locales/en/settings/general/internationalization.json new file mode 100644 index 000000000..17f0a13bf --- /dev/null +++ b/public/locales/en/settings/general/internationalization.json @@ -0,0 +1,3 @@ +{ + "label": "Language" +} \ No newline at end of file diff --git a/public/locales/en/settings/general/module-enabler.json b/public/locales/en/settings/general/module-enabler.json new file mode 100644 index 000000000..179753b6f --- /dev/null +++ b/public/locales/en/settings/general/module-enabler.json @@ -0,0 +1,3 @@ +{ + "title": "Module enabler" +} \ No newline at end of file diff --git a/public/locales/en/settings/general/search-engine.json b/public/locales/en/settings/general/search-engine.json new file mode 100644 index 000000000..789b1715b --- /dev/null +++ b/public/locales/en/settings/general/search-engine.json @@ -0,0 +1,11 @@ +{ + "title": "Search engine", + "tips": { +"generalTip": "Use the prefixes !yt and !t in front of your query to search on YouTube or for a Torrent respectively.", + "placeholderTip": "%s can be used as a placeholder for the query." + }, + "customEngine": { + "label": "Query URL", + "placeholder": "Custom query URL" + } +} \ No newline at end of file diff --git a/public/locales/en/settings/general/theme-selector.json b/public/locales/en/settings/general/theme-selector.json new file mode 100644 index 000000000..4e04d5e54 --- /dev/null +++ b/public/locales/en/settings/general/theme-selector.json @@ -0,0 +1,3 @@ +{ + "label": "Switch to {{theme}} mode" +} \ No newline at end of file diff --git a/public/locales/en/settings/general/widget-positions.json b/public/locales/en/settings/general/widget-positions.json new file mode 100644 index 000000000..746578cce --- /dev/null +++ b/public/locales/en/settings/general/widget-positions.json @@ -0,0 +1,3 @@ +{ + "label": "Position widgets on left" +} \ No newline at end of file diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx index db163f4bd..efeba042a 100644 --- a/src/components/AppShelf/AddAppShelfItem.tsx +++ b/src/components/AppShelf/AddAppShelfItem.tsx @@ -22,25 +22,26 @@ import { IconApps } from '@tabler/icons'; import { useEffect, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; import { useDebouncedValue } from '@mantine/hooks'; -import { t } from 'i18next'; +import { useTranslation } from 'next-i18next'; import { useConfig } from '../../tools/state'; import { tryMatchPort, ServiceTypeList, StatusCodes } from '../../tools/types'; import Tip from '../layout/Tip'; export function AddItemShelfButton(props: any) { const [opened, setOpened] = useState(false); + const { t } = useTranslation('layout/add-service-app-shelf'); return ( <> {t('layout.header.addService.modal.title')}} + title={{t('modal.title')}} opened={props.opened || opened} onClose={() => setOpened(false)} > - + void } & const { setOpened } = props; const { config, setConfig } = useConfig(); const [isLoading, setLoading] = useState(false); + const { t } = useTranslation('layout/add-service-app-shelf'); // Extract all the categories from the services in config const InitialCategories = config.services.reduce((acc, cur) => { @@ -121,13 +123,13 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & try { const _isValid = new URL(value); } catch (e) { - return t('layout.header.addService.modal.form.validation.invalidUrl'); + return t('modal.form.validation.invalidUrl'); } return null; }, status: (value: string[]) => { if (!value.length) { - return t('layout.header.addService.modal.form.validation.noStatusCodeSelected'); + return t('modal.form.validation.noStatusCodeSelected'); } return null; }, @@ -204,62 +206,48 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & > - - {t('layout.header.addService.modal.tabs.options.title')} - - - {t('layout.header.addService.modal.tabs.advancedOptions.title')} - + {t('modal.tabs.options.title')} + {t('modal.tabs.advancedOptions.title')} void } & return item; }} getCreateLabel={(query) => - t('layout.header.addService.modal.tabs.options.form.category.createLabel', { + t('modal.tabs.options.form.category.createLabel', { query, }) } @@ -285,36 +273,26 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & <> { form.setFieldValue('apiKey', event.currentTarget.value); }} error={ form.errors.apiKey && - t( - 'layout.header.addService.modal.tabs.options.form.integrations.apiKey.validation.noKey' - ) + t('modal.tabs.options.form.integrations.apiKey.validation.noKey') } /> - {t( - 'layout.header.addService.modal.tabs.options.form.integrations.apiKey.tip.text' - )}{' '} + {t('modal.tabs.options.form.integrations.apiKey.tip.text')}{' '} - {t( - 'layout.header.addService.modal.tabs.options.form.integrations.apiKey.tip.link' - )} + {t('modal.tabs.options.form.integrations.apiKey.tip.link')} @@ -323,11 +301,9 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & <> { @@ -336,17 +312,15 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & error={ form.errors.username && t( - 'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.username.validation.invalidUsername' + 'modal.tabs.options.form.integrations.qBittorrent.username.validation.invalidUsername' ) } /> { @@ -355,7 +329,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & error={ form.errors.password && t( - 'layout.header.addService.modal.tabs.options.form.integrations.qBittorrent.password.validation.invalidPassword' + 'modal.tabs.options.form.integrations.qBittorrent.password.validation.invalidPassword' ) } /> @@ -364,11 +338,9 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & {form.values.type === 'Deluge' && ( <> { @@ -377,7 +349,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & error={ form.errors.password && t( - 'layout.header.addService.modal.tabs.options.form.integrations.deluge.password.validation.invalidPassword' + 'modal.tabs.options.form.integrations.deluge.password.validation.invalidPassword' ) } /> @@ -386,11 +358,9 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & {form.values.type === 'Transmission' && ( <> { @@ -399,16 +369,14 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & error={ form.errors.username && t( - 'layout.header.addService.modal.tabs.options.form.integrations.transmission.username.validation.invalidUsername' + 'modal.tabs.options.form.integrations.transmission.username.validation.invalidUsername' ) } /> { @@ -417,7 +385,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & error={ form.errors.password && t( - 'layout.header.addService.modal.tabs.options.form.integrations.transmission.password.validation.invalidPassword' + 'modal.tabs.options.form.integrations.transmission.password.validation.invalidPassword' ) } /> @@ -425,32 +393,24 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & )} - + @@ -459,8 +419,7 @@ export function AddAppShelfItemForm(props: { setOpened: (b: boolean) => void } & diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index 2da1274e6..34b560860 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -11,6 +11,7 @@ import { } from '@dnd-kit/core'; import { arrayMove, SortableContext } from '@dnd-kit/sortable'; import { useLocalStorage } from '@mantine/hooks'; +import { useTranslation } from 'next-i18next'; import { useConfig } from '../../tools/state'; import { SortableAppShelfItem, AppShelfItem } from './AppShelfItem'; @@ -36,6 +37,8 @@ const AppShelf = (props: any) => { const [activeId, setActiveId] = useState(null); const { colorScheme } = useMantineColorScheme(); + const { t } = useTranslation('layout/app-shelf'); + const sensors = useSensors( useSensor(TouchSensor, { activationConstraint: { @@ -147,13 +150,13 @@ const AppShelf = (props: any) => { {/* Return the item for all services without category */} {noCategory && noCategory.length > 0 ? ( - Other + {t('accordions.others.text')} {getItems()} ) : null} {downloadEnabled ? ( - Your downloads + {t('accordions.downloads.text')} ({ root: { @@ -34,6 +34,7 @@ export function ColorSchemeSwitch() { const { config } = useConfig(); const { colorScheme, toggleColorScheme } = useMantineColorScheme(); const { classes, cx } = useStyles(); + const { t } = useTranslation('settings/general/theme-selector'); return ( @@ -42,8 +43,8 @@ export function ColorSchemeSwitch() { toggleColorScheme()} size="md" /> - {t('settings.tabs.common.settings.colorScheme.label', { - scheme: colorScheme === 'dark' ? 'light' : 'dark', + {t('label', { + theme: colorScheme === 'dark' ? 'light' : 'dark', })} Ctrl+J diff --git a/src/components/Config/ConfigChanger.tsx b/src/components/Config/ConfigChanger.tsx index 7025c79e8..0e129bd2c 100644 --- a/src/components/Config/ConfigChanger.tsx +++ b/src/components/Config/ConfigChanger.tsx @@ -1,6 +1,6 @@ import { Center, Loader, Select, Tooltip } from '@mantine/core'; import { setCookie } from 'cookies-next'; -import { t } from 'i18next'; +import { useTranslation } from 'next-i18next'; import { useEffect, useState } from 'react'; import { useConfig } from '../../tools/state'; @@ -8,6 +8,8 @@ export default function ConfigChanger() { const { config, loadConfig, setConfig, getConfigs } = useConfig(); const [configList, setConfigList] = useState([]); const [value, setValue] = useState(config.name); + const { t } = useTranslation('settings/general/config-changer'); + useEffect(() => { getConfigs().then((configs) => setConfigList(configs)); }, [config]); @@ -24,7 +26,7 @@ export default function ConfigChanger() { // return { diff --git a/src/components/Config/SaveConfig.tsx b/src/components/Config/SaveConfig.tsx index fc7f6a266..2f0c2c21b 100644 --- a/src/components/Config/SaveConfig.tsx +++ b/src/components/Config/SaveConfig.tsx @@ -4,6 +4,7 @@ import { showNotification } from '@mantine/notifications'; import axios from 'axios'; import fileDownload from 'js-file-download'; import { useState } from 'react'; +import { useTranslation } from 'next-i18next'; import { IconCheck as Check, IconDownload as Download, @@ -12,11 +13,11 @@ import { IconX as X, } from '@tabler/icons'; import { useConfig } from '../../tools/state'; -import { t } from 'i18next'; export default function SaveConfigComponent(props: any) { const [opened, setOpened] = useState(false); const { config, setConfig } = useConfig(); + const { t } = useTranslation('settings/general/config-changer'); const form = useForm({ initialValues: { configName: config.name, @@ -29,12 +30,7 @@ export default function SaveConfigComponent(props: any) { } return ( - setOpened(false)} - title={t('settings.tabs.common.settings.configChanger.modal.title')} - > + setOpened(false)} title={t('modal.title')}>
{ setConfig({ ...config, name: values.configName }); @@ -51,21 +47,17 @@ export default function SaveConfigComponent(props: any) { > - +
); diff --git a/src/components/Settings/AdvancedSettings.tsx b/src/components/Settings/AdvancedSettings.tsx index ef27a2f45..552e5b483 100644 --- a/src/components/Settings/AdvancedSettings.tsx +++ b/src/components/Settings/AdvancedSettings.tsx @@ -1,14 +1,15 @@ import { TextInput, Button, Stack } from '@mantine/core'; import { useForm } from '@mantine/form'; +import { useTranslation } from 'next-i18next'; import { useConfig } from '../../tools/state'; import { ColorSelector } from './ColorSelector'; import { OpacitySelector } from './OpacitySelector'; import { AppCardWidthSelector } from './AppCardWidthSelector'; import { ShadeSelector } from './ShadeSelector'; -import { t } from 'i18next'; export default function TitleChanger() { const { config, setConfig } = useConfig(); + const { t } = useTranslation('settings/customization/page-appearance'); const form = useForm({ initialValues: { @@ -42,26 +43,26 @@ export default function TitleChanger() {
saveChanges(values))}> - +
diff --git a/src/components/Settings/ColorSelector.tsx b/src/components/Settings/ColorSelector.tsx index 084d6de1b..817378e7e 100644 --- a/src/components/Settings/ColorSelector.tsx +++ b/src/components/Settings/ColorSelector.tsx @@ -1,8 +1,8 @@ import React, { useState } from 'react'; import { ColorSwatch, Grid, Group, Popover, Text, useMantineTheme } from '@mantine/core'; +import { useTranslation } from 'next-i18next'; import { useConfig } from '../../tools/state'; import { useColorTheme } from '../../tools/color'; -import { t } from 'i18next'; interface ColorControlProps { type: string; @@ -11,8 +11,8 @@ interface ColorControlProps { export function ColorSelector({ type }: ColorControlProps) { const { config, setConfig } = useConfig(); const [opened, setOpened] = useState(false); - const { primaryColor, secondaryColor, setPrimaryColor, setSecondaryColor } = useColorTheme(); + const { t } = useTranslation('settings/customization/color-selector'); const theme = useMantineTheme(); const colors = Object.keys(theme.colors).map((color) => ({ @@ -84,7 +84,7 @@ export function ColorSelector({ type }: ColorControlProps) { - {t('settings.tabs.customizations.settings.colorSelector.suffix', { + {t('suffix', { color: type[0].toUpperCase() + type.slice(1), })} diff --git a/src/components/Settings/CommonSettings.tsx b/src/components/Settings/CommonSettings.tsx index 0258ae95e..73dc3b63e 100644 --- a/src/components/Settings/CommonSettings.tsx +++ b/src/components/Settings/CommonSettings.tsx @@ -1,5 +1,6 @@ import { Text, SegmentedControl, TextInput, Stack } from '@mantine/core'; import { useState } from 'react'; +import { useTranslation } from 'next-i18next'; import { useConfig } from '../../tools/state'; import { ColorSchemeSwitch } from '../ColorSchemeToggle/ColorSchemeSwitch'; import { WidgetsPositionSwitch } from '../WidgetsPositionSwitch/WidgetsPositionSwitch'; @@ -7,11 +8,14 @@ import ConfigChanger from '../Config/ConfigChanger'; import SaveConfigComponent from '../Config/SaveConfig'; import ModuleEnabler from './ModuleEnabler'; import Tip from '../layout/Tip'; -import { t } from 'i18next'; import LanguageSwitch from './LanguageSwitch'; export default function CommonSettings(args: any) { const { config, setConfig } = useConfig(); + const { t } = useTranslation([ + 'settings/general/search-engine', + 'settings/general/config-changer', + ]); const matches = [ { label: 'Google', value: 'https://google.com/search?q=' }, @@ -28,12 +32,12 @@ export default function CommonSettings(args: any) { return ( - {t('settings.tabs.common.settings.searchEngine.title')} - {t('settings.tabs.common.settings.searchEngine.tips.generalTip')} + {t('title')} + {t('tips.generalTip')} {searchUrl === 'Custom' && ( <> - {t('settings.tabs.common.settings.searchEngine.tips.placeholderTip')} + {t('tips.placeholderTip')} { setCustomSearchUrl(event.currentTarget.value); @@ -80,7 +84,7 @@ export default function CommonSettings(args: any) { - {t('settings.tabs.common.settings.configTip')} + {t('configTip')} ); } diff --git a/src/components/Settings/Credits.tsx b/src/components/Settings/Credits.tsx index 32cb9f001..641889879 100644 --- a/src/components/Settings/Credits.tsx +++ b/src/components/Settings/Credits.tsx @@ -1,10 +1,12 @@ import { Group, ActionIcon, Anchor, Text } from '@mantine/core'; import { IconBrandDiscord, IconBrandGithub } from '@tabler/icons'; -import { t } from 'i18next'; +import { useTranslation } from 'next-i18next'; import { CURRENT_VERSION } from '../../../data/constants'; export default function Credits(props: any) { + const { t } = useTranslation('settings/common'); + return ( @@ -29,7 +31,7 @@ export default function Credits(props: any) { color: 'gray', }} > - {t('settings.credits.madeWithLove')} + {t('credits.madeWithLove')} (language); const data = languages.map((language) => ({ image: `https://countryflagsapi.com/png/${language.split('-').pop()}`, - label: convertCodeToName(language), + label: 'JA', value: language, - })); + }));*/ const onChangeSelect = (value: string) => { - setSelectedLanguage(value); + //setSelectedLanguage(value); - const languageName = convertCodeToName(value); + const languageName = 'JA IS HALZ SCHEISSE NE'; - changeLanguage(value) + /*changeLanguage(value) .then(() => { showNotification({ title: 'Language changed', @@ -39,20 +38,27 @@ export default function LanguageSwitch() { color: 'red', autoClose: 5000, }); - }); + });*/ }; return (