diff --git a/package.json b/package.json index 1bc2ce4..51ebc9a 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,14 @@ "autoprefixer": "^10.4.5", "axios": "^0.27.2", "fs": "^0.0.1-security", + "i18next": "^21.6.16", + "i18next-browser-languagedetector": "^6.1.4", + "i18next-http-backend": "^1.4.0", "lodash": "^4.17.21", "postcss": "^8.4.12", "react": "^18.1.0", "react-dom": "^18.1.0", + "react-i18next": "^11.16.8", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "react-select-search": "^3.0.9", diff --git a/public/index.html b/public/index.html index aa069f2..77405d1 100644 --- a/public/index.html +++ b/public/index.html @@ -5,9 +5,7 @@ - - React App + Grasscutter tools diff --git a/public/locales/en-US/artifact.json b/public/locales/en-US/artifact.json new file mode 100644 index 0000000..f1b1fcd --- /dev/null +++ b/public/locales/en-US/artifact.json @@ -0,0 +1,44 @@ +{ + "navigation": "Artifact", + "title": "Artifact Command Generator", + "fill_details": "Please fill in the details", + "uid": "UID", + "name": "Artifact Name", + "main_stats": "Main Stats", + "sub_stats": "Sub Stats", + "enhancement_level": "Enhancement Level", + + "FIGHT_PROP_HP": "HP", + "FIGHT_PROP_HP_PERCENT": "HP %", + "FIGHT_PROP_ATTACK": "ATK", + "FIGHT_PROP_ATTACK_PERCENT": "ATK %", + "FIGHT_PROP_DEFENSE": "DEF", + "FIGHT_PROP_DEFENSE_PERCENT": "DEF %", + "FIGHT_PROP_CHARGE_EFFICIENCY": "Energy Recharge", + "FIGHT_PROP_ELEMENT_MASTERY": "Elemental Mastery", + "FIGHT_PROP_FIRE_SUB_HURT": "Pyro RES", + "FIGHT_PROP_ELEC_SUB_HURT": "Electro RES", + "FIGHT_PROP_WATER_SUB_HURT": "Hydro RES", + "FIGHT_PROP_ICE_SUB_HURT": "Cryo RES", + "FIGHT_PROP_WIND_SUB_HURT": "Anemo RES", + "FIGHT_PROP_ROCK_SUB_HURT": "Geo RES", + "FIGHT_PROP_GRASS_SUB_HURT": "Dendro RES", + "FIGHT_PROP_PHYSICAL_SUB_HURT": "Phys RES", + "FIGHT_PROP_CRITICAL": "CRIT Rate", + "FIGHT_PROP_CRITICAL_HURT": "CRIT DMG", + "FIGHT_PROP_HEAL_ADD": "Healing Bonus", + "FIGHT_PROP_FIRE_ADD_HURT": "Pyro DMG Bonus", + "FIGHT_PROP_ELEC_ADD_HURT": "Electro DMG Bonus", + "FIGHT_PROP_WATER_ADD_HURT": "Hydro DMG Bonus", + "FIGHT_PROP_ICE_ADD_HURT": "Cryo DMG Bonus", + "FIGHT_PROP_WIND_ADD_HURT": "Anemo DMG Bonus", + "FIGHT_PROP_ROCK_ADD_HURT": "Geo DMG Bonus", + "FIGHT_PROP_GRASS_ADD_HURT": "Dendro DMG Bonus", + "FIGHT_PROP_PHYSICAL_ADD_HURT": "Physical DMG Bonus", + "FIGHT_PROP_SHIELD_COST_MINUS_RATIO": "Shield Strength", + "FIGHT_PROP_HEALED_ADD": "Incoming Healing Bonus", + "FIGHT_PROP_SKILL_CD_MINUS_RATIO": "CD Reduction", + "FIGHT_PROP_SPEED_PERCENT": "Movement Speed", + "FIGHT_PROP_ADD_HURT": "DMG Bonus", + "FIGHT_PROP_SUB_HURT": "DMG Reduction" +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index f6eb9eb..2e51b56 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import Main from "./components/Main"; import {BrowserRouter} from "react-router-dom"; import Navbar from "./components/Navbar"; import Header from "./components/Header"; +import "./i18n"; interface IState{ headerTitle: string; @@ -14,7 +15,7 @@ export default class App extends Component<{},IState>{ constructor(props:{}) { super(props); this.state = { - headerTitle: 'Hello World' + headerTitle: 'Hello World', } } @@ -36,6 +37,7 @@ export default class App extends Component<{},IState>{ const MainProps = { handleHeaderTitleChange: this.handleHeaderTitleChange } + return( diff --git a/src/components/LanguageChange.tsx b/src/components/LanguageChange.tsx new file mode 100644 index 0000000..3b1eb17 --- /dev/null +++ b/src/components/LanguageChange.tsx @@ -0,0 +1,16 @@ +import {useTranslation} from "react-i18next"; + +export default function LanguageChange() { + const {t, i18n} = useTranslation(); + const changeLanguage = async (lang: string) => { + await i18n.changeLanguage(lang); + } + + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 4674911..def1893 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,6 +1,10 @@ import React from "react"; import MenuDesktop from "./MenuDesktop"; import MenuMobile from "./MenuMobile"; +import {Menu} from "@mui/material"; +import i18n from "i18next"; +import {useTranslation} from "react-i18next"; +import LanguageChange from "./LanguageChange"; class Navbar extends React.Component { handleHeaderTitleChange; @@ -59,6 +63,7 @@ class Navbar extends React.Component {
+
@@ -68,6 +73,7 @@ class Navbar extends React.Component {
+
diff --git a/src/components/pages/Artifacts.tsx b/src/components/pages/Artifacts.tsx index 10de012..09c559f 100644 --- a/src/components/pages/Artifacts.tsx +++ b/src/components/pages/Artifacts.tsx @@ -1,8 +1,10 @@ import {ChangeEvent, useEffect, useState} from "react"; import ReliquaryDataProvider, {IReliquaryAffix, IReliquaryMain} from "../providers/ReliquaryDataProvider"; import {Autocomplete, Chip, createFilterOptions, TextField} from "@mui/material"; + //@ts-ignore import _ from "lodash"; +import {useTranslation} from "react-i18next"; interface IArtifact { id: number; @@ -19,6 +21,7 @@ export default function Artifacts() { const [selectedAffixesAmount, setSelectedAffixesAmount] = useState>({}); const [artifactEnhancements, setArtifactEnhancements] = useState(0); + const {t} = useTranslation("artifact"); const [artifactData, setArtifactData] = useState([]); const [generatedArtifact, setGeneratedArtifact] = useState("/giveart "); @@ -86,7 +89,7 @@ export default function Artifacts() { }; const getPercent = (affix:IReliquaryAffix)=>{ - if (affix.propType.indexOf("PERCENT") !== -1 || affix.propType.indexOf("CRITICAL") !== -1 || affix.propType.indexOf("EFFICIENCY") !== -1 || affix.propType.indexOf("HURT") !== -1) { + if (affix.propType.indexOf("PERCENT") !== -1 || affix.propType.indexOf("CRITICAL") !== -1 || affix.propType.indexOf("EFFICIENCY") !== -1 || affix.propType.indexOf("HURT") !== -1 || affix.propType.indexOf("RATIO") !== -1 || affix.propType.indexOf("ADD") !== -1) { return parseFloat(String(affix.propValue*100)).toPrecision(3) + "%"; } return parseInt(String(affix.propValue)); @@ -110,14 +113,14 @@ export default function Artifacts() {
-

Please fill artifact details

+

{t('fill_details')}

setUid(parseInt(event.target.value))}/> @@ -127,14 +130,14 @@ export default function Artifacts() { className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
option.name} + getOptionLabel={(option) => t(option.name)} onChange={handleArtifactChange} renderInput={(params) => } /> @@ -144,7 +147,7 @@ export default function Artifacts() { className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
option.propType} + getOptionLabel={(option) => t(option.propType)} onChange={handleMainStatChange} renderInput={(params) => } /> @@ -164,14 +167,14 @@ export default function Artifacts() { className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
{reliquaryAffixes.map((affix, index) => { return (
handleAffixSelected(e,false,0)}/> - + handleAffixSelected(e,true, affix.id)}/>
); @@ -182,7 +185,7 @@ export default function Artifacts() { className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
setArtifactEnhancements(Number(e.currentTarget.value))}/> diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..3c5a4cd --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,21 @@ +import i18n from 'i18next'; +import Backend from 'i18next-http-backend'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import { initReactI18next } from 'react-i18next'; + +i18n + .use(Backend) + .use(LanguageDetector) + .use(initReactI18next) // bind react-i18next to the instance + .init({ + fallbackLng: 'en-US', + debug: false, + interpolation: { + escapeValue: false, // not needed for react!! + }, + ns: ["artifact"], + defaultNS: "", + }); + + +export default i18n; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4af40bc..ca3b6d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1018,7 +1018,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.17.9" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz" integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== @@ -3362,6 +3362,13 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cross-fetch@3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" @@ -4843,7 +4850,7 @@ html-entities@^2.1.0, html-entities@^2.3.2: resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz" integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== -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== @@ -4861,6 +4868,13 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + 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" @@ -4955,6 +4969,27 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +i18next-browser-languagedetector@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.4.tgz#7b087c5edb6f6acd38ef54ede2160ab9cde0108f" + integrity sha512-wukWnFeU7rKIWT66VU5i8I+3Zc4wReGcuDK2+kuFhtoxBRGWGdvYI9UQmqNL/yQH1KogWwh+xGEaIPH8V/i2Zg== + dependencies: + "@babel/runtime" "^7.14.6" + +i18next-http-backend@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-1.4.0.tgz#e6abef0615316e00837798d2385c13395096f963" + integrity sha512-wsvx7E/CT1pHmBM99Vu57YLJpsrHbVjxGxf25EIJ/6oTjsvCkZZ6c3SA4TejcK5jIHfv9oLxQX8l+DFKZHZ0Gg== + dependencies: + cross-fetch "3.1.5" + +i18next@^21.6.16: + version "21.6.16" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.6.16.tgz#8cff8c3ba2ffaf8438a8c83fe284083f15cf3941" + integrity sha512-xJlzrVxG9CyAGsbMP1aKuiNr1Ed2m36KiTB7hjGMG2Zo4idfw3p9THUEu+GjBwIgEZ7F11ZbCzJcfv4uyfKNuw== + dependencies: + "@babel/runtime" "^7.17.2" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -6261,6 +6296,13 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1: version "1.3.1" resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" @@ -7374,6 +7416,15 @@ react-error-overlay@^6.0.11: resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== +react-i18next@^11.16.8: + version "11.16.8" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.16.8.tgz#e71621c16f8f77152e85795a88ba6a371e46687d" + integrity sha512-uuhSuwY0iMOPV9HUsN1OC4cGCrATPvzZ1O8UfvLR+imz/XjPmfDgZM1e4dPS0Suc2+aCQqJ0FmWP891lUkB2Cg== + dependencies: + "@babel/runtime" "^7.14.5" + html-escaper "^2.0.2" + html-parse-stringify "^3.0.1" + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" @@ -8445,6 +8496,11 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + tryer@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz" @@ -8652,6 +8708,11 @@ vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +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" @@ -8693,6 +8754,11 @@ web-vitals@^2.1.4: resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz" integrity sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" @@ -8844,6 +8910,14 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz"