From c141475404d635df0451c8c1244f113ddb5b00f1 Mon Sep 17 00:00:00 2001
From: Daniel Scalzi <d_scalzi@yahoo.com>
Date: Sun, 7 Apr 2019 23:33:40 -0400
Subject: [PATCH] Initial work on localization.

---
 app/app.ejs                       |  8 ++++-
 app/assets/js/langloader.js       | 21 +++++++++++++
 app/assets/js/preloader.js        |  4 +++
 app/assets/js/scripts/landing.js  |  2 +-
 app/assets/js/scripts/login.js    | 42 +++++++++++++-------------
 app/assets/js/scripts/uibinder.js |  1 +
 app/assets/lang/en_US.json        | 49 +++++++++++++++++++++++++++++++
 dev-app-update.yml                |  2 +-
 8 files changed, 105 insertions(+), 24 deletions(-)
 create mode 100644 app/assets/js/langloader.js
 create mode 100644 app/assets/lang/en_US.json

diff --git a/app/app.ejs b/app/app.ejs
index f30df0e6..91becc32 100644
--- a/app/app.ejs
+++ b/app/app.ejs
@@ -1,6 +1,6 @@
 <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 <head>
-    <meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self'"/>
+    <meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-In6B8teKZQll5heMl9bS7CESTbGvuAt3VVV86BUQBDk='"/>
     <title>Westeroscraft Launcher</title>
     <script src="./assets/js/scripts/uicore.js"></script>
     <script src="./assets/js/scripts/uibinder.js"></script>
@@ -43,5 +43,11 @@
             </div>
         </div>
     </div>
+    <script>
+        // Load language
+        for(let key of Object.keys(Lang.query('html'))){
+            document.getElementById(key).innerHTML = Lang.query(`html.${key}`)
+        }
+    </script>
 </body>
 </html>
\ No newline at end of file
diff --git a/app/assets/js/langloader.js b/app/assets/js/langloader.js
new file mode 100644
index 00000000..4e13d93b
--- /dev/null
+++ b/app/assets/js/langloader.js
@@ -0,0 +1,21 @@
+const fs = require('fs-extra')
+const path = require('path')
+
+let lang
+
+exports.loadLanguage = function(id){
+    lang = JSON.parse(fs.readFileSync(path.resolve('app', 'assets', 'lang', `${id}.json`))) || {}
+}
+
+exports.query = function(id){
+    let query = id.split('.')
+    let res = lang
+    for(let q of query){
+        res = res[q]
+    }
+    return res === lang ? {} : res
+}
+
+exports.queryJS = function(id){
+    return exports.query(`js.${id}`)
+}
\ No newline at end of file
diff --git a/app/assets/js/preloader.js b/app/assets/js/preloader.js
index c6307b68..792c5304 100644
--- a/app/assets/js/preloader.js
+++ b/app/assets/js/preloader.js
@@ -5,6 +5,7 @@ const path          = require('path')
 
 const ConfigManager = require('./configmanager')
 const DistroManager = require('./distromanager')
+const LangLoader    = require('./langloader')
 const logger        = require('./loggerutil')('%c[Preloader]', 'color: #a02d2a; font-weight: bold')
 
 logger.log('Loading..')
@@ -12,6 +13,9 @@ logger.log('Loading..')
 // Load ConfigManager
 ConfigManager.load()
 
+// Load Strings
+LangLoader.loadLanguage('en_US')
+
 function onDistroLoad(data){
     if(data != null){
         
diff --git a/app/assets/js/scripts/landing.js b/app/assets/js/scripts/landing.js
index 5affa8b6..247f6354 100644
--- a/app/assets/js/scripts/landing.js
+++ b/app/assets/js/scripts/landing.js
@@ -92,7 +92,7 @@ document.getElementById('launch_button').addEventListener('click', function(e){
         asyncSystemScan(mcVersion)
     } else {
 
-        setLaunchDetails('Please wait..')
+        setLaunchDetails(Lang.queryJS('landing.launch.pleaseWait'))
         toggleLaunchArea(true)
         setLaunchPercentage(0, 100)
 
diff --git a/app/assets/js/scripts/login.js b/app/assets/js/scripts/login.js
index 67e7a58a..34078bd1 100644
--- a/app/assets/js/scripts/login.js
+++ b/app/assets/js/scripts/login.js
@@ -56,7 +56,7 @@ function shakeError(element){
 function validateEmail(value){
     if(value){
         if(!basicEmail.test(value) && !validUsername.test(value)){
-            showError(loginEmailError, '* Invalid Value')
+            showError(loginEmailError, Lang.queryJS('login.error.invalidValue'))
             loginDisabled(true)
             lu = false
         } else {
@@ -68,7 +68,7 @@ function validateEmail(value){
         }
     } else {
         lu = false
-        showError(loginEmailError, '* Required')
+        showError(loginEmailError, Lang.queryJS('login.error.requiredValue'))
         loginDisabled(true)
     }
 }
@@ -87,7 +87,7 @@ function validatePassword(value){
         }
     } else {
         lp = false
-        showError(loginPasswordError, '* Required')
+        showError(loginPasswordError, Lang.queryJS('login.error.invalidValue'))
         loginDisabled(true)
     }
 }
@@ -129,10 +129,10 @@ function loginDisabled(v){
 function loginLoading(v){
     if(v){
         loginButton.setAttribute('loading', v)
-        loginButton.innerHTML = loginButton.innerHTML.replace('LOGIN', 'LOGGING IN')
+        loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn'))
     } else {
         loginButton.removeAttribute('loading')
-        loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'LOGIN')
+        loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login'))
     }
 }
 
@@ -166,8 +166,8 @@ function resolveError(err){
     // Node error => err.code | err.message
     if(err.cause != null && err.cause === 'UserMigratedException') {
         return {
-            title: 'Error During Login:<br>Invalid Credentials',
-            desc: 'You\'ve attempted to login with a migrated account. Try again using the account email as the username.'
+            title: Lang.queryJS('login.error.userMigrated.title'),
+            desc: Lang.queryJS('login.error.userMigrated.desc')
         }
     } else {
         if(err.error != null){
@@ -175,13 +175,13 @@ function resolveError(err){
                 if(err.errorMessage != null){
                     if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){
                         return {
-                            title: 'Error During Login:<br>Invalid Credentials',
-                            desc: 'The email or password you\'ve entered is incorrect. Please try again.'
+                            title: Lang.queryJS('login.error.invalidCredentials.title'),
+                            desc: Lang.queryJS('login.error.invalidCredentials.desc')
                         }
                     } else if(err.errorMessage === 'Invalid credentials.'){
                         return {
-                            title: 'Error During Login:<br>Too Many Attempts',
-                            desc: 'There have been too many login attempts with this account recently. Please try again later.'
+                            title: Lang.queryJS('login.error.rateLimit.title'),
+                            desc: Lang.queryJS('login.error.rateLimit.desc')
                         }
                     }
                 }
@@ -192,14 +192,14 @@ function resolveError(err){
                 if(err.code === 'ENOENT'){
                     // No Internet.
                     return {
-                        title: 'Error During Login:<br>No Internet Connection',
-                        desc: 'You must be connected to the internet in order to login. Please connect and try again.'
+                        title: Lang.queryJS('login.error.noInternet.title'),
+                        desc: Lang.queryJS('login.error.noInternet.desc')
                     }
                 } else if(err.code === 'ENOTFOUND'){
                     // Could not reach server.
                     return {
-                        title: 'Error During Login:<br>Authentication Server Offline',
-                        desc: 'Mojang\'s authentication server is currently offline or unreachable. Please wait a bit and try again. You can check the status of the server on <a href="https://help.mojang.com/">Mojang\'s help portal</a>.'
+                        title: Lang.queryJS('login.error.authDown.title'),
+                        desc: Lang.queryJS('login.error.authDown.desc')
                     }
                 }
             }
@@ -208,13 +208,13 @@ function resolveError(err){
     if(err.message != null){
         if(err.message === 'NotPaidAccount'){
             return {
-                title: 'Error During Login:<br>Game Not Purchased',
-                desc: 'The account you are trying to login with has not purchased a copy of Minecraft.<br>You may purchase a copy on <a href="https://minecraft.net/">Minecraft.net</a>'
+                title: Lang.queryJS('login.error.notPaid.title'),
+                desc: Lang.queryJS('login.error.notPaid.desc')
             }
         } else {
             // Unknown error with request.
             return {
-                title: 'Error During Login:<br>Unknown Error',
+                title: Lang.queryJS('login.error.unknown.title'),
                 desc: err.message
             }
         }
@@ -264,7 +264,7 @@ loginButton.addEventListener('click', () => {
 
     AuthManager.addAccount(loginUsername.value, loginPassword.value).then((value) => {
         updateSelectedAccount(value)
-        loginButton.innerHTML = loginButton.innerHTML.replace('LOGGING IN', 'SUCCESS')
+        loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
         $('.circle-loader').toggleClass('load-complete')
         $('.checkmark').toggle()
         setTimeout(() => {
@@ -281,14 +281,14 @@ loginButton.addEventListener('click', () => {
                 $('.circle-loader').toggleClass('load-complete')
                 $('.checkmark').toggle()
                 loginLoading(false)
-                loginButton.innerHTML = loginButton.innerHTML.replace('SUCCESS', 'LOGIN')
+                loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
                 formDisabled(false)
             })
         }, 1000)
     }).catch((err) => {
         loginLoading(false)
         const errF = resolveError(err)
-        setOverlayContent(errF.title, errF.desc, 'Try Again')
+        setOverlayContent(errF.title, errF.desc, Lang.queryJS('login.tryAgain'))
         setOverlayHandler(() => {
             formDisabled(false)
             toggleOverlay(false)
diff --git a/app/assets/js/scripts/uibinder.js b/app/assets/js/scripts/uibinder.js
index 78adddf7..0b080d1b 100644
--- a/app/assets/js/scripts/uibinder.js
+++ b/app/assets/js/scripts/uibinder.js
@@ -8,6 +8,7 @@ const path          = require('path')
 const AuthManager   = require('./assets/js/authmanager')
 const ConfigManager = require('./assets/js/configmanager')
 const DistroManager = require('./assets/js/distromanager')
+const Lang          = require('./assets/js/langloader')
 
 let rscShouldLoad = false
 let fatalStartupError = false
diff --git a/app/assets/lang/en_US.json b/app/assets/lang/en_US.json
new file mode 100644
index 00000000..46b5a3dc
--- /dev/null
+++ b/app/assets/lang/en_US.json
@@ -0,0 +1,49 @@
+{
+    "html": {
+        "avatarOverlay": "Dankname"
+    },
+    "js": {
+        "login": {
+            "error": {
+                "invalidValue": "* Invalid Value",
+                "requiredValue": "* Required",
+                "userMigrated": {
+                    "title": "Error During Login:<br>Invalid Credentials",
+                    "desc": "You've attempted to login with a migrated account. Try again using the account email as the username."
+                },
+                "invalidCredentials": {
+                    "title": "Error During Login:<br>Invalid Credentials",
+                    "desc": "The email or password you've entered is incorrect. Please try again."
+                },
+                "rateLimit": {
+                    "title": "Error During Login:<br>Too Many Attempts",
+                    "desc": "There have been too many login attempts with this account recently. Please try again later."
+                },
+                "noInternet": {
+                    "title": "Error During Login:<br>No Internet Connection",
+                    "desc": "You must be connected to the internet in order to login. Please connect and try again."
+                },
+                "authDown": {
+                    "title": "Error During Login:<br>Authentication Server Offline",
+                    "desc": "Mojang's authentication server is currently offline or unreachable. Please wait a bit and try again. You can check the status of the server on <a href=\"https://help.mojang.com/\">Mojang's help portal</a>."
+                },
+                "notPaid": {
+                    "title": "Error During Login:<br>Game Not Purchased",
+                    "desc": "The account you are trying to login with has not purchased a copy of Minecraft.<br>You may purchase a copy on <a href=\"https://minecraft.net/\">Minecraft.net</a>"
+                },
+                "unknown": {
+                    "title": "Error During Login:<br>Unknown Error"
+                }
+            },
+            "login": "LOGIN",
+            "loggingIn": "LOGGING IN",
+            "success": "SUCCESS",
+            "tryAgain": "Try Again"
+        },
+        "landing": {
+            "launch": {
+                "pleaseWait": "Please wait.."
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/dev-app-update.yml b/dev-app-update.yml
index 60dd682c..ba0abe75 100644
--- a/dev-app-update.yml
+++ b/dev-app-update.yml
@@ -1,3 +1,3 @@
-owner: WesterosCraftCode
+owner: dscalzi
 repo: ElectronLauncher
 provider: github