From d43e9611845ffa3f3449379054ae5e12045891b4 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sun, 12 Jan 2025 00:17:19 +0100 Subject: [PATCH] Implement localization --- app.go | 12 + frontend/package.json | 23 +- frontend/package.json.md5 | 2 +- frontend/pnpm-lock.yaml | 470 ++++++++++++++++++ frontend/src/lib/components/AddonCard.svelte | 22 +- frontend/src/lib/components/Header.svelte | 8 +- .../lib/components/LanguageSwitcher.svelte | 26 + frontend/src/lib/i18n/i18n.ts | 16 + frontend/src/lib/i18n/translations/en.json | 19 + frontend/src/lib/i18n/translations/ru.json | 19 + frontend/src/lib/router/routes/Home.svelte | 3 +- frontend/src/main.ts | 13 +- frontend/wailsjs/go/main/App.d.ts | 4 + frontend/wailsjs/go/main/App.js | 8 + main.go | 5 +- settings.json | 2 +- 16 files changed, 621 insertions(+), 31 deletions(-) create mode 100644 frontend/src/lib/components/LanguageSwitcher.svelte create mode 100644 frontend/src/lib/i18n/i18n.ts create mode 100644 frontend/src/lib/i18n/translations/en.json create mode 100644 frontend/src/lib/i18n/translations/ru.json diff --git a/app.go b/app.go index fab3445..f7ba452 100644 --- a/app.go +++ b/app.go @@ -125,3 +125,15 @@ func (a *App) SelectDirectory() (res StringResponse) { res.Data = path return } + +func (a *App) GetLocale() string { + if settings.Locale == "" { + return "en" + } + return settings.Locale +} + +func (a *App) SetLocale(locale string) error { + settings.Locale = locale + return SaveSettings(*settings) +} diff --git a/frontend/package.json b/frontend/package.json index a20fa70..2845d71 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,14 +1,14 @@ { - "name": "frontend", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-check --tsconfig ./tsconfig.json" - }, + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.json" + }, "devDependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.2", @@ -31,6 +31,7 @@ "autoprefixer": "^10.4.20", "chart.js": "^4.4.3", "regression": "^2.0.1", - "svelte-chartjs": "^3.1.5" + "svelte-chartjs": "^3.1.5", + "svelte-i18n": "^4.0.1" } } \ No newline at end of file diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index 665691e..82ad9ce 100644 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -49014f28d86d4ca6427e970cf696170b \ No newline at end of file +26349183f2bc5b05555ae3d7ca598d6d \ No newline at end of file diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f05276d..27e955d 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: svelte-chartjs: specifier: ^3.1.5 version: 3.1.5(chart.js@4.4.3)(svelte@3.59.2) + svelte-i18n: + specifier: ^4.0.1 + version: 4.0.1(svelte@3.59.2) devDependencies: '@fortawesome/fontawesome-svg-core': specifier: ^6.5.2 @@ -76,18 +79,171 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.15.18': resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} engines: {node: '>=12'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.15.18': resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@formatjs/ecma402-abstract@2.3.2': + resolution: {integrity: sha512-6sE5nyvDloULiyOMbOTJEEgWL32w+VHkZQs8S02Lnn8Y/O5aQhjOEXwWzvR7SsBE/exxlSpY2EsWZgqHbtLatg==} + + '@formatjs/fast-memoize@2.2.6': + resolution: {integrity: sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw==} + + '@formatjs/icu-messageformat-parser@2.9.8': + resolution: {integrity: sha512-hZlLNI3+Lev8IAXuwehLoN7QTKqbx3XXwFW1jh0AdIA9XJdzn9Uzr+2LLBspPm/PX0+NLIfykj/8IKxQqHUcUQ==} + + '@formatjs/icu-skeleton-parser@1.8.12': + resolution: {integrity: sha512-QRAY2jC1BomFQHYDMcZtClqHR55EEnB96V7Xbk/UiBodsuFc5kujybzt87+qj1KqmJozFhk6n4KiT1HKwAkcfg==} + + '@formatjs/intl-localematcher@0.5.10': + resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==} + '@fortawesome/fontawesome-common-types@6.6.0': resolution: {integrity: sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==} engines: {node: '>=6'} @@ -243,6 +399,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + cli-color@2.0.4: + resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==} + engines: {node: '>=0.10'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -266,6 +426,10 @@ packages: engines: {node: '>=4'} hasBin: true + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + debug@4.3.6: resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} @@ -275,6 +439,9 @@ packages: supports-color: optional: true + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -301,9 +468,23 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + es6-weak-map@2.0.3: + resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + esbuild-android-64@0.15.18: resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} engines: {node: '>=12'} @@ -429,10 +610,28 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -478,6 +677,12 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -499,6 +704,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + intl-messageformat@10.7.11: + resolution: {integrity: sha512-IB2N1tmI24k2EFH3PWjU7ivJsnWyLwOWOva0jnXFa29WzB6fb0JZ5EMQGu+XN5lDtjHYFo0/UooP67zBwUg7rQ==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -523,6 +731,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -551,6 +762,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-queue@0.1.0: + resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} + magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} @@ -558,6 +772,10 @@ packages: resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} engines: {node: '>=12'} + memoizee@0.4.17: + resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} + engines: {node: '>=0.12'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -603,6 +821,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -833,6 +1054,13 @@ packages: peerDependencies: svelte: ^3.19.0 || ^4.0.0 + svelte-i18n@4.0.1: + resolution: {integrity: sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==} + engines: {node: '>= 16'} + hasBin: true + peerDependencies: + svelte: ^3 || ^4 || ^5 + svelte-preprocess@4.10.7: resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} engines: {node: '>= 9.11.2'} @@ -897,6 +1125,13 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + timers-ext@0.1.8: + resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} + engines: {node: '>=0.12'} + + tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -907,6 +1142,9 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} @@ -979,12 +1217,107 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@esbuild/aix-ppc64@0.19.12': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + '@esbuild/android-arm@0.15.18': optional: true + '@esbuild/android-arm@0.19.12': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + '@esbuild/linux-loong64@0.15.18': optional: true + '@esbuild/linux-loong64@0.19.12': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + + '@formatjs/ecma402-abstract@2.3.2': + dependencies: + '@formatjs/fast-memoize': 2.2.6 + '@formatjs/intl-localematcher': 0.5.10 + decimal.js: 10.4.3 + tslib: 2.6.3 + + '@formatjs/fast-memoize@2.2.6': + dependencies: + tslib: 2.6.3 + + '@formatjs/icu-messageformat-parser@2.9.8': + dependencies: + '@formatjs/ecma402-abstract': 2.3.2 + '@formatjs/icu-skeleton-parser': 1.8.12 + tslib: 2.6.3 + + '@formatjs/icu-skeleton-parser@1.8.12': + dependencies: + '@formatjs/ecma402-abstract': 2.3.2 + tslib: 2.6.3 + + '@formatjs/intl-localematcher@0.5.10': + dependencies: + tslib: 2.6.3 + '@fortawesome/fontawesome-common-types@6.6.0': {} '@fortawesome/fontawesome-svg-core@6.6.0': @@ -1144,6 +1477,14 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + cli-color@2.0.4: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-iterator: 2.0.3 + memoizee: 0.4.17 + timers-ext: 0.1.8 + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -1162,10 +1503,17 @@ snapshots: cssesc@3.0.0: {} + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + debug@4.3.6: dependencies: ms: 2.1.2 + decimal.js@10.4.3: {} + deepmerge@4.3.1: {} detect-indent@6.1.0: {} @@ -1182,8 +1530,33 @@ snapshots: emoji-regex@9.2.2: {} + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + es6-promise@3.3.1: {} + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + es6-weak-map@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esbuild-android-64@0.15.18: optional: true @@ -1269,8 +1642,52 @@ snapshots: esbuild-windows-64: 0.15.18 esbuild-windows-arm64: 0.15.18 + esbuild@0.19.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + escalade@3.1.2: {} + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + estree-walker@2.0.2: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + ext@1.7.0: + dependencies: + type: 2.7.3 + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1327,6 +1744,10 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + globalyzer@0.1.0: {} + + globrex@0.1.2: {} + graceful-fs@4.2.11: {} hasown@2.0.2: @@ -1347,6 +1768,13 @@ snapshots: inherits@2.0.4: {} + intl-messageformat@10.7.11: + dependencies: + '@formatjs/ecma402-abstract': 2.3.2 + '@formatjs/fast-memoize': 2.2.6 + '@formatjs/icu-messageformat-parser': 2.9.8 + tslib: 2.6.3 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -1365,6 +1793,8 @@ snapshots: is-number@7.0.0: {} + is-promise@2.2.2: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -1385,6 +1815,10 @@ snapshots: lru-cache@10.4.3: {} + lru-queue@0.1.0: + dependencies: + es5-ext: 0.10.64 + magic-string@0.25.9: dependencies: sourcemap-codec: 1.4.8 @@ -1393,6 +1827,17 @@ snapshots: dependencies: sourcemap-codec: 1.4.8 + memoizee@0.4.17: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-weak-map: 2.0.3 + event-emitter: 0.3.5 + is-promise: 2.2.2 + lru-queue: 0.1.0 + next-tick: 1.1.0 + timers-ext: 0.1.8 + merge2@1.4.1: {} micromatch@4.0.7: @@ -1430,6 +1875,8 @@ snapshots: nanoid@3.3.7: {} + next-tick@1.1.0: {} + node-releases@2.0.18: {} normalize-path@3.0.0: {} @@ -1650,6 +2097,17 @@ snapshots: dependencies: svelte: 3.59.2 + svelte-i18n@4.0.1(svelte@3.59.2): + dependencies: + cli-color: 2.0.4 + deepmerge: 4.3.1 + esbuild: 0.19.12 + estree-walker: 2.0.2 + intl-messageformat: 10.7.11 + sade: 1.8.1 + svelte: 3.59.2 + tiny-glob: 0.2.9 + svelte-preprocess@4.10.7(postcss-load-config@4.0.2(postcss@8.4.41))(postcss@8.4.41)(sass@1.77.8)(svelte@3.59.2)(typescript@4.9.5): dependencies: '@types/pug': 2.0.10 @@ -1710,6 +2168,16 @@ snapshots: dependencies: any-promise: 1.3.0 + timers-ext@0.1.8: + dependencies: + es5-ext: 0.10.64 + next-tick: 1.1.0 + + tiny-glob@0.2.9: + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -1718,6 +2186,8 @@ snapshots: tslib@2.6.3: {} + type@2.7.3: {} + typescript@4.9.5: {} update-browserslist-db@1.1.0(browserslist@4.23.3): diff --git a/frontend/src/lib/components/AddonCard.svelte b/frontend/src/lib/components/AddonCard.svelte index 596aceb..cefc909 100644 --- a/frontend/src/lib/components/AddonCard.svelte +++ b/frontend/src/lib/components/AddonCard.svelte @@ -2,6 +2,8 @@ import { GetAddonLocalVersion, GetAddonRemoteVersion, UpdateAddon } from "$wails/main/App"; import { type main } from "$wails/models"; import { toast } from "svelte-sonner"; + import { _ } from 'svelte-i18n'; + export let addon: main.Addon; let localVersion = "0.0.0"; let remoteVersion = "0.0.0"; @@ -21,25 +23,25 @@ const res = await UpdateAddon(addon.name); if (res.error) { console.error(res.error); - toast.error(`Failed to update ${addon.name}`, { + toast.error($_('toasts.updateFailed', { values: { name: addon.name } }), { classes: { toast: 'bg-red-600' } }); } else { if (res.data) { - toast.error(`${addon.name} updated with breaking changes!`, { + toast.error($_('toasts.updateBreakingChanges', { values: { name: addon.name } }), { classes: { toast: 'bg-red-600' }, - description: 'Please restart your game to apply the update.' + description: $_('toasts.restartRequired') }); } else { - toast.success(`${addon.name} updated successfully!`, { + toast.success($_('toasts.updateSuccessful', { values: { name: addon.name } }), { classes: { toast: 'bg-green-600' }, - description: 'You may /reload ingame to apply the update.' + description: $_('toasts.reloadRequired') }); } } @@ -55,11 +57,11 @@

{addon.name}

-

Local Version: {localVersion}

-

Remote Version: {remoteVersion}

+

{$_('common.localVersion')}: {localVersion}

+

{$_('common.remoteVersion')}: {remoteVersion}

{#if upToDate} {:else} {/if} diff --git a/frontend/src/lib/components/Header.svelte b/frontend/src/lib/components/Header.svelte index 2102502..766d899 100644 --- a/frontend/src/lib/components/Header.svelte +++ b/frontend/src/lib/components/Header.svelte @@ -1,5 +1,7 @@ + +
+ {#each languages as lang} + + {/each} +
\ No newline at end of file diff --git a/frontend/src/lib/i18n/i18n.ts b/frontend/src/lib/i18n/i18n.ts new file mode 100644 index 0000000..ec15eb0 --- /dev/null +++ b/frontend/src/lib/i18n/i18n.ts @@ -0,0 +1,16 @@ +import { addMessages, init, waitLocale } from 'svelte-i18n'; +import { GetLocale } from '$wails/main/App'; +import en from './translations/en.json'; +import ru from './translations/ru.json'; + +addMessages('en', en); +addMessages('ru', ru); + +// Initialize with stored locale or fallback to browser locale +export const i18nInit = GetLocale().then(storedLocale => { + init({ + fallbackLocale: 'en', + initialLocale: storedLocale || navigator.language.split('-')[0] + }); + return waitLocale(); +}); \ No newline at end of file diff --git a/frontend/src/lib/i18n/translations/en.json b/frontend/src/lib/i18n/translations/en.json new file mode 100644 index 0000000..578c28c --- /dev/null +++ b/frontend/src/lib/i18n/translations/en.json @@ -0,0 +1,19 @@ +{ + "common": { + "upToDate": "Up to date", + "update": "Update", + "updating": "Updating...", + "localVersion": "Local Version", + "remoteVersion": "Remote Version", + "changeGamePath": "Change Game Path", + "setGamePath": "Set Game Path", + "gamePathNotValid": "Game path is not valid" + }, + "toasts": { + "updateFailed": "Failed to update {name}", + "updateSuccessful": "{name} updated successfully!", + "updateBreakingChanges": "{name} updated with breaking changes!", + "restartRequired": "Please restart your game to apply the update.", + "reloadRequired": "You may /reload ingame to apply the update." + } +} \ No newline at end of file diff --git a/frontend/src/lib/i18n/translations/ru.json b/frontend/src/lib/i18n/translations/ru.json new file mode 100644 index 0000000..70d58a7 --- /dev/null +++ b/frontend/src/lib/i18n/translations/ru.json @@ -0,0 +1,19 @@ +{ + "common": { + "upToDate": "Актуально", + "update": "Обновить", + "updating": "Обновление...", + "localVersion": "Локальная версия", + "remoteVersion": "Удаленная версия", + "changeGamePath": "Изменить путь к игре", + "setGamePath": "Указать путь к игре", + "gamePathNotValid": "Путь к игре недействителен" + }, + "toasts": { + "updateFailed": "Не удалось обновить {name}", + "updateSuccessful": "{name} успешно обновлен!", + "updateBreakingChanges": "{name} обновлен с критическими изменениями!", + "restartRequired": "Пожалуйста, перезапустите игру, чтобы применить обновление.", + "reloadRequired": "Вы можете использовать /reload в игре, чтобы применить обновление." + } +} \ No newline at end of file diff --git a/frontend/src/lib/router/routes/Home.svelte b/frontend/src/lib/router/routes/Home.svelte index 28cbe30..fa522c6 100644 --- a/frontend/src/lib/router/routes/Home.svelte +++ b/frontend/src/lib/router/routes/Home.svelte @@ -2,6 +2,7 @@ import AddonCard from "$lib/components/AddonCard.svelte"; import { GetAddons, IsGamePathValid } from "$wails/main/App"; import { type main } from "$wails/models"; + import { _ } from 'svelte-i18n'; let addons: { [key: string]: main.Addon } = {}; GetAddons().then((res) => { @@ -31,7 +32,7 @@
{:else}
-

Game path is not valid

+

{$_('common.gamePathNotValid')}

{/if} diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 95c41a5..012694d 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,8 +1,15 @@ import './style.css' import App from './App.svelte' +import { i18nInit } from './lib/i18n/i18n' -const app = new App({ - target: document.getElementById('app') +const element = document.getElementById('app') +if (!element) throw new Error('Could not find app element') + +// Initialize app after i18n is ready +const appPromise = i18nInit.then(() => { + return new App({ + target: element + }) }) -export default app +export default appPromise diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index a9270b5..6b7f969 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -14,10 +14,14 @@ export function GetAddons():Promise; export function GetGamePath():Promise; +export function GetLocale():Promise; + export function IsGamePathValid():Promise; export function SelectDirectory():Promise; export function SetGamePath(arg1:string):Promise; +export function SetLocale(arg1:string):Promise; + export function UpdateAddon(arg1:string):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index a72b1e2..66fddb9 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -26,6 +26,10 @@ export function GetGamePath() { return window['go']['main']['App']['GetGamePath'](); } +export function GetLocale() { + return window['go']['main']['App']['GetLocale'](); +} + export function IsGamePathValid() { return window['go']['main']['App']['IsGamePathValid'](); } @@ -38,6 +42,10 @@ export function SetGamePath(arg1) { return window['go']['main']['App']['SetGamePath'](arg1); } +export function SetLocale(arg1) { + return window['go']['main']['App']['SetLocale'](arg1); +} + export function UpdateAddon(arg1) { return window['go']['main']['App']['UpdateAddon'](arg1); } diff --git a/main.go b/main.go index 30fb95e..adbb42b 100644 --- a/main.go +++ b/main.go @@ -36,6 +36,7 @@ var addonService *AddonService type Settings struct { GamePath string `json:"gamePath"` Addons map[string]Addon `json:"addons"` + Locale string `json:"locale"` } func SaveSettings(settings Settings) error { @@ -81,8 +82,8 @@ func main() { err = wails.Run(&options.App{ Title: "wails-template", - Width: 700, - Height: 700, + Width: 800, + Height: 600, AssetServer: &assetserver.Options{ Assets: assets, }, diff --git a/settings.json b/settings.json index cc97bdd..e86ea01 100644 --- a/settings.json +++ b/settings.json @@ -1 +1 @@ -{"gamePath":"C:\\Games\\WoWRuski","addons":{"Channeler":{"name":"Channeler","url":"https://git.site.quack-lab.dev/dave/wow_channeler"},"Dechickenator":{"name":"Dechickenator","url":"https://git.site.quack-lab.dev/dave/wow_dechickenator"},"Heimdall":{"name":"Heimdall","url":"https://git.site.quack-lab.dev/dave/wow-Heimdall"}}} +{"gamePath":"C:\\Games\\WoWRuski","addons":{"Channeler":{"name":"Channeler","url":"https://git.site.quack-lab.dev/dave/wow_channeler"},"Dechickenator":{"name":"Dechickenator","url":"https://git.site.quack-lab.dev/dave/wow_dechickenator"},"Heimdall":{"name":"Heimdall","url":"https://git.site.quack-lab.dev/dave/wow-Heimdall"}},"locale":"en"}