mirror of
https://github.com/upscayl/upscayl.git
synced 2024-11-27 17:00:52 +01:00
Add initial language switcher support
This commit is contained in:
parent
de34063565
commit
bbdf9259f8
@ -3,7 +3,7 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: 'export',
|
output: "export",
|
||||||
images: {
|
images: {
|
||||||
unoptimized: true,
|
unoptimized: true,
|
||||||
},
|
},
|
||||||
|
2873
package-lock.json
generated
2873
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -213,12 +213,12 @@
|
|||||||
"daisyui": "^4.10.2",
|
"daisyui": "^4.10.2",
|
||||||
"electron": "^27.3.10",
|
"electron": "^27.3.10",
|
||||||
"electron-builder": "^24.13.3",
|
"electron-builder": "^24.13.3",
|
||||||
"next": "^14.1.1",
|
"next": "^14.2.7",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-tailwindcss": "^0.4.1",
|
"prettier-plugin-tailwindcss": "^0.4.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"tailwindcss": "^3.3.5",
|
"tailwindcss": "^3.3.5",
|
||||||
"typescript": "^4.8.4"
|
"typescript": "^4.8.4"
|
||||||
},
|
},
|
||||||
@ -236,6 +236,7 @@
|
|||||||
"electron-next": "^3.1.5",
|
"electron-next": "^3.1.5",
|
||||||
"electron-settings": "^4.0.3",
|
"electron-settings": "^4.0.3",
|
||||||
"electron-updater": "^6.3.0",
|
"electron-updater": "^6.3.0",
|
||||||
|
"eslint-config-next": "^14.2.7",
|
||||||
"firebase": "^10.11.0",
|
"firebase": "^10.11.0",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"jotai": "^2.2.2",
|
"jotai": "^2.2.2",
|
||||||
|
34
renderer/atoms/translations-atom.ts
Normal file
34
renderer/atoms/translations-atom.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
import en from "../locales/en.json";
|
||||||
|
import ru from "../locales/ru.json";
|
||||||
|
import { atomWithStorage } from "jotai/utils";
|
||||||
|
|
||||||
|
// Define the shape of the translations
|
||||||
|
type Translations = typeof en;
|
||||||
|
type Locales = "en" | "ru";
|
||||||
|
|
||||||
|
// Utility function to access nested translation keys
|
||||||
|
const getNestedTranslation = (obj: Translations, key: string): string => {
|
||||||
|
return (
|
||||||
|
key.split(".").reduce((acc, part) => acc && (acc as any)[part], obj) || key
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Atom to store the current locale
|
||||||
|
export const localeAtom = atomWithStorage<Locales>("language", "en");
|
||||||
|
|
||||||
|
// Atom to get the translation function based on the current locale
|
||||||
|
export const translationAtom = atom((get) => {
|
||||||
|
const locale = get(localeAtom);
|
||||||
|
const translations: Record<Locales, Translations> = { en, ru };
|
||||||
|
|
||||||
|
return (key: string, params: Record<string, string> = {}): string => {
|
||||||
|
const template = getNestedTranslation(translations[locale], key);
|
||||||
|
|
||||||
|
// Replace placeholders with parameters, e.g., {name} => John
|
||||||
|
return Object.keys(params).reduce(
|
||||||
|
(str, paramKey) => str.replace(`{${paramKey}}`, params[paramKey]),
|
||||||
|
template,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
});
|
@ -21,6 +21,7 @@ import TurnOffNotificationsToggle from "./TurnOffNotificationsToggle";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { CustomResolutionInput } from "./CustomResolutionInput";
|
import { CustomResolutionInput } from "./CustomResolutionInput";
|
||||||
import { TileSizeInput } from "./TileSizeInput";
|
import { TileSizeInput } from "./TileSizeInput";
|
||||||
|
import LanguageSwitcher from "./language-switcher";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
batchMode: boolean;
|
batchMode: boolean;
|
||||||
@ -212,6 +213,8 @@ function SettingsTab({
|
|||||||
{/* THEME SELECTOR */}
|
{/* THEME SELECTOR */}
|
||||||
<ThemeSelect />
|
<ThemeSelect />
|
||||||
|
|
||||||
|
<LanguageSwitcher />
|
||||||
|
|
||||||
{/* IMAGE FORMAT BUTTONS */}
|
{/* IMAGE FORMAT BUTTONS */}
|
||||||
<ImageFormatSelect
|
<ImageFormatSelect
|
||||||
batchMode={batchMode}
|
batchMode={batchMode}
|
||||||
|
36
renderer/components/settings-tab/language-switcher.tsx
Normal file
36
renderer/components/settings-tab/language-switcher.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { localeAtom, translationAtom } from "@/atoms/translations-atom";
|
||||||
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
|
|
||||||
|
const locales = {
|
||||||
|
en: "English",
|
||||||
|
ru: "Русский",
|
||||||
|
};
|
||||||
|
|
||||||
|
const LanguageSwitcher = () => {
|
||||||
|
const setLocale = useSetAtom(localeAtom);
|
||||||
|
const t = useAtomValue(translationAtom);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<p className="text-sm font-medium">{t("settings.language")}</p>
|
||||||
|
<select
|
||||||
|
data-choose-theme
|
||||||
|
className="select select-primary"
|
||||||
|
onChange={(e) => setLocale(e.target.value as any)}
|
||||||
|
>
|
||||||
|
{Object.entries(locales).map((entry) => {
|
||||||
|
const [locale, label] = entry;
|
||||||
|
return (
|
||||||
|
<option value={locale} key={locale}>
|
||||||
|
{label.toLocaleUpperCase()}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LanguageSwitcher;
|
5
renderer/locales/en.json
Normal file
5
renderer/locales/en.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"language": "Change Language"
|
||||||
|
}
|
||||||
|
}
|
5
renderer/locales/ru.json
Normal file
5
renderer/locales/ru.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"language": "Меняй язык"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user