diff --git a/package.json b/package.json index 23c6fcba..05c2c795 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "process": "^0.11.10", "quasar": "^2.0.0", "random-word-slugs": "^0.1.6", + "vue-i18n": "9", "vue-router": "^4.0.0", "vue3-apexcharts": "^1.4.1" }, diff --git a/quasar.conf.js b/quasar.conf.js index 8a56fded..fe47cab3 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -21,7 +21,8 @@ module.exports = configure(function (ctx) { // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli/boot-files boot: [ - 'init.ts' + 'init.ts', + '../i18n' ], // https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9217bb0b..7cec569a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -66,7 +66,7 @@ features = [ winreg = "0.10.1" [dependencies.tauri] -features = ["clipboard-all", "dialog-all", "fs-all", "global-shortcut-all", "gtk-tray", "notification-all", "os-all", "path-all", "process-exit", "process-relaunch", "shell-all", "system-tray", "window-all", "updater"] +features = ["clipboard-all", "dialog-all", "fs-all", "global-shortcut-all", "gtk-tray", "notification-all", "os-all", "path-all", "process-exit", "process-relaunch", "shell-all", "system-tray", "updater", "window-all"] version = "1.0.0-rc.11" [features] diff --git a/src/boot/init.ts b/src/boot/init.ts index 78393ef9..fc07cff2 100644 --- a/src/boot/init.ts +++ b/src/boot/init.ts @@ -1,5 +1,7 @@ import { boot } from 'quasar/wrappers' import VueApexCharts from "vue3-apexcharts"; +import { createI18n } from 'vue-i18n' +import messages from '../i18n' import { globalState } from "../lib/global" import { Client } from "../lib/client" import { createApi } from '../lib/util'; @@ -19,7 +21,6 @@ export default boot(async ({ app }) => { await appConfig.init() const { nodeName } = (await appConfig.read()); globalState.setNodeName(nodeName); - await globalState.loadLangData() const api = createApi(LOCAL_RPC); const client = new Client(api); const autoLauncher = new AutoLauncher(); @@ -27,4 +28,11 @@ export default boot(async ({ app }) => { app.config.globalProperties.$client = client; app.config.globalProperties.$autoLauncher = autoLauncher; app.use(VueApexCharts) + + const i18n = createI18n({ + locale: 'en-US', + messages + }); + + app.use(i18n); }) diff --git a/src/components/SaveKeys.vue b/src/components/SaveKeys.vue index 2f0eaac0..8cf9caba 100644 --- a/src/components/SaveKeys.vue +++ b/src/components/SaveKeys.vue @@ -3,7 +3,7 @@ .row.justify-center.q-gutter-lg .col-auto q-icon(color="blue-3" name="vpn_key" size="80px") - p.q-ml-sm {{ lang.seedPhrase }} + p.q-ml-sm {{ $t('saveKeys.seedPhrase') }} .col .row.justify-center .col-8 @@ -21,30 +21,32 @@ ) .row.justify-center.q-mb-lg.full-width.bg-grey-2(v-if="!revealKey") q-btn.full-width.full-width( - :label="lang.reveal" + :label="$t('saveKeys.reveal')" @click="revealKey = true" flat size="lg" ) .row.justify-center.q-mt-md(v-if="revealKey") q-btn( - :label="lang.copy" + :label="$t('saveKeys.copy')" @click="copyMnemonic" color="primary" style="max-width: 200px" ) .row - p Seed phrase is your password for your subspace farmer and wallet, this cannot be changed, guessed, or reset if lost. It is imperative that this is stored in a secure, safe location. Without the seed phrase, you will not have access to your funds. Furthermore, anyone who steals your seed phrase will be able to do as they please with your funds. + p {{ $t('saveKeys.seedPhraseText')}} .row.q-pt-md - q-checkbox(:label="lang.userConfirm" size="lg" v-model="userConfirm" :disable="!revealKey") + q-checkbox( + :label="$t('saveKeys.userConfirm')" + size="lg" + v-model="userConfirm" + :disable="!revealKey" + ) diff --git a/src/components/plotCard.vue b/src/components/plotCard.vue index 8b525ed8..8ab85642 100644 --- a/src/components/plotCard.vue +++ b/src/components/plotCard.vue @@ -3,7 +3,7 @@ q-card(bordered flat) .q-pa-sm .row.items-center q-icon.q-mr-sm(color="grey" name="downloading" size="40px") - h6.text-weight-light {{ lang.plot }} + h6.text-weight-light {{ $t('dashboard.plot') }} q-separator.q-mt-xs .row.items-center.q-mt-sm .col-auto.q-mr-md(v-if="plot.state == 'finished'") @@ -13,29 +13,26 @@ q-card(bordered flat) .col-auto.q-mr-md(v-if="plot.state == 'verifying'") q-spinner-box(color="grey" size="40px") .col - .text-weight-light {{ lang.status }} + .text-weight-light {{ $t('dashboard.status') }} p {{ plot.message }} .row.items-center.q-mt-sm .col-auto.q-mr-md q-icon(color="black" name="storage" size="40px") .col - .text-weight-light {{ lang.allocated }} + .text-weight-light {{ $t('dashboard.allocated') }} p {{ plot.plotSizeGB }} GB diff --git a/src/loc/en.json b/src/i18n/en.json similarity index 58% rename from src/loc/en.json rename to src/i18n/en.json index 315252a0..03235f6d 100644 --- a/src/loc/en.json +++ b/src/i18n/en.json @@ -13,7 +13,8 @@ "saved": "Seed phrase copied to clipboard. Backup key in a secure location.", "seedPhrase": "Seed phrase", "reveal": "reveal", - "copy": "copy to clipboard" + "copy": "copy to clipboard", + "seedPhraseText": "Seed phrase is your password for your subspace farmer and wallet, this cannot be changed, guessed, or reset if lost. It is imperative that this is stored in a secure, safe location. Without the seed phrase, you will not have access to your funds. Furthermore, anyone who steals your seed phrase will be able to do as they please with your funds." }, "setupPlot": { "pageTitle": "Setup Plots", @@ -30,7 +31,9 @@ "utilizedSpace": "How much space is currently being used.", "allocatedSpace": "Space to be shared with Subspace Protocol.", "estimatingSpace": "Requesting the blockchain size from the network.", - "plotsDirectory": "Plots Directory" + "plotsDirectory": "Plots Directory", + "start": "Start Plotting", + "allocatedErrorMsg": "Value should be a positive number" }, "plottingProgress": { "startingFarmer": "Starting the farmer ...", @@ -67,26 +70,62 @@ "reward": "Reward", "farmedBlocks": "Farmed Blocks", "farmedBy": "Farmed By", - "blockNum": "Block #", "time": "Time", "transactions": "# Transactions", - "rewards": "Reward + Fees" + "rewards": "Reward + Fees", + "totalEarned": "Total Earned", + "tokenName": "testSSC", + "rewardAddress": "Reward Address", + "loadingMsg": "Connecting to client...", + "loadingStatus": "loading...", + "syncingMsg": "Syncing node {currentBlock} of {highestBlock} Blocks", + "nodeIsSynced": "Node is synced at block: {currentBlock}", + "syncedAt": "Synced at Block # {blockNumber}", + "blockNum": "Block #" }, "importKey": { "tooltip": "Please provide a valid Reward Address to proceed.", "pageTitle": "Import Reward Address", "rewardAddress": "Please enter your Reward Address in SS58 format (it should start with the letters \"st\")", - "continue": "continue" + "continue": "continue", + "cancel": "Cancel", + "addressErrorMsg": "Invalid address" }, - "mainMenu": { + "header": { "IncentivizedLabel": "Incentivized Testnet", "IncentivizedTooltip": "Rewards (testSSC) you earn during incentivized testnet, will contribute to your mainnet rewards!", + "nodeName": "Node Name:" + }, + "menu": { "reset": "Reset Node!", "autoStart": "Start on Boot", + "export_log": "Show Logs", + "autoLaunchNotSupported": "Launch on Boot is not supported on this system.", "willAutoLaunch": "Subspace will launch automatically during boot", "willNotAutoLaunch": "Subspace will not launch automatically during boot", "reset_heading": "Are you sure you want to reset and start from scratch?", - "reset_explanation": " This will erase everything, and you will have to start from the beginning. Only do this if there is something wrong with the network, or you imported a wrong reward address.", - "export_log": "Show Logs" + "reset_explanation": " This will erase everything, and you will have to start from the beginning. Only do this if there is something wrong with the network, or you imported a wrong reward address." + }, + "disclaimer": { + "title": "TESTNET DISCLAIMER", + "text": "Tokens/credits generated on the Subspace Network testnet (\"testSSCs\") do not equate to Subspace Network mainnet tokens, have no monetary value, and cannot be exchanged for cash, cash equivalent, or other tokens or cryptocurrencies.", + "confirm": "I understand" + }, + "introModal": { + "next": "next", + "hints": "Hints", + "title": "What is Plotting, Farming, & Rewards", + "slide1": { + "text1": "Plotting is the process of assigning various \"plots\" aka tiny spots across the allocated storage of your hard drive. You can think of this as planting the seeds in a tilled field. Every plot is cryptographically independent, and verifiable, all of the plots are stored in a single file on your system.", + "text2": "Farming is the act of checking with the current \"challenge\" of the blockchain and seeing if any of your plots will win the challenge, you can think of this as checking to see if your various crops are ripe for the picking.", + "text3": "If your \"fruit is ripe\" aka if you have a plot that wins the current challenge then you are rewarded with subspace credits. aka \"Winning the block\"." + }, + "slide2": { + "text1": "Did you know Subspace is the first Proof of Capacity blockchain on Substrate?", + "text2": "Did you know that Subspace stores all data On-Chain?", + "talkWithUs": "You can talk with us on", + "visitUs": "Visit us on", + "readUp": "Read up about Subspace on" + } } } diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 00000000..8a0d83cb --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,5 @@ +import enUS from './en.json'; + +export default { + 'en-US': enUS, +}; diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index d61662f6..24a30312 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -9,10 +9,10 @@ q-layout(view="hHh lpr fFf") p {{ appVersion }} .col-auto.q-mr-md.relative-position q-badge(color="grey" text-color="white") - .q-pa-xs(style="font-size: 14px") {{ lang.IncentivizedLabel }} + .q-pa-xs(style="font-size: 14px") {{ $t('header.IncentivizedLabel') }} q-tooltip .col - p.no-margin(style="font-size: 12px") {{ lang.IncentivizedTooltip }} + p.no-margin(style="font-size: 12px") {{ $t('header.IncentivizedTooltip') }} .col-auto.q-mr-md.relative-position(v-if="global.nodeName || oldNodeName") q-badge.cursor-pointer( v-if="!isEdittingName" @@ -20,7 +20,7 @@ q-layout(view="hHh lpr fFf") color="blue-8" text-color="white" ) - .q-ma-xs(style="font-size: 14px") {{ "Node Name:" }} + .q-ma-xs(style="font-size: 14px") {{ $t('header.nodeName') }} .q-mr-xs( class="text-italic" style="font-size: 14px" @@ -56,14 +56,11 @@ import { globalState as global } from "../lib/global" import { appConfig } from "../lib/appConfig" import { relaunch } from "@tauri-apps/api/process" -const lang = global.data.loc.text.mainMenu - export default defineComponent({ name: "MainLayout", components: { MainMenu }, data() { return { - lang, global: global.data, appVersion: "", util, diff --git a/src/lib/global.ts b/src/lib/global.ts index d5d50780..048be642 100644 --- a/src/lib/global.ts +++ b/src/lib/global.ts @@ -1,7 +1,4 @@ import { reactive } from "vue"; -import { getLang, LangType } from "../loc/lang" - -const text: LangType = {} // TODO use dependency injection to ensure methods and properties can't be accessed unless they are initialized and valid // TODO: refactor according to https://vuejs.org/guide/scaling-up/state-management.html#simple-state-management-with-reactivity-api export class Global { @@ -10,19 +7,8 @@ export class Global { state: "loading", message: "loading" }, - loc: { - selected: 'en', - text - }, nodeName: '', }) - async changeLang(newLang: string): Promise { - this.data.loc.selected = newLang - await this.loadLangData() - } - async loadLangData(): Promise { - this.data.loc.text = await getLang(this.data.loc.selected) - } setNodeName(name: string) { this.data.nodeName = name; } diff --git a/src/loc/lang.ts b/src/loc/lang.ts deleted file mode 100644 index dff4eafe..00000000 --- a/src/loc/lang.ts +++ /dev/null @@ -1,11 +0,0 @@ -let loadedLangString: string -let loadedLang: LangType -export type LangType = { [index: string]: { [index: string]: string } } -export async function getLang(langString: string): Promise { - if (loadedLangString != langString || loadedLang == null) { - const langData = await import(`./${langString}.json`) as LangType - loadedLang = langData - loadedLangString = langString - } - return loadedLang -} diff --git a/src/pages/Dashboard.vue b/src/pages/Dashboard.vue index bb4e78d7..373b3199 100644 --- a/src/pages/Dashboard.vue +++ b/src/pages/Dashboard.vue @@ -20,7 +20,7 @@ q-page.q-pl-lg.q-pr-lg.q-pt-md .absolute-center .row.justify-center q-spinner-orbit(color="grey" size="120px") - h4 Connecting to client... + h4 {{ $t('dashboard.loadingMsg')}}