";
- $content .= '' . lang("open app", false) . ' ';
- $APPS["taskfloor_tasks"]["content"] = $content;
-}
-?>
\ No newline at end of file
diff --git a/langs/en/2fa.json b/langs/en/2fa.json
new file mode 100644
index 0000000..4241e35
--- /dev/null
+++ b/langs/en/2fa.json
@@ -0,0 +1,16 @@
+{
+ "setup 2fa": "Setup 2-factor authentication",
+ "2fa removed": "2-factor authentication disabled.",
+ "2fa enabled": "2-factor authentication activated.",
+ "remove 2fa": "Disable 2-factor authentication",
+ "2fa explained": "2-factor authentication adds more security to your account. You can use the Auth Keys (key icon) feature of the Netsyms Business Mobile app, or another TOTP-enabled app (Authy, FreeOTP, etc) on your smartphone. When you have the app installed, you can enable 2-factor authentication by clicking the button below and scanning a QR code with the app. Whenever you sign in in the future, you'll need to input a six-digit code from your phone into the login page when prompted. You can disable 2-factor authentication from this page if you change your mind.",
+ "2fa active": "2-factor authentication is active on your account. To remove 2fa, reset your authentication secret, or change to a new security device, click the button below.",
+ "enable 2fa": "Enable 2-factor authentication",
+ "scan 2fa qrcode": "Scan the QR Code with the authenticator app, or enter the information manually. Then type in the six-digit code the app gives you and press Finish Setup.",
+ "confirm 2fa": "Finish setup",
+ "enter otp code": "Enter 6-digit code",
+ "secret key": "Secret key",
+ "label": "Label",
+ "issuer": "Issuer",
+ "no such code or code expired": "That code is incorrect or expired."
+}
diff --git a/langs/en/api.json b/langs/en/api.json
new file mode 100644
index 0000000..cd7f081
--- /dev/null
+++ b/langs/en/api.json
@@ -0,0 +1,3 @@
+{
+ "user does not exist": "User does not exist."
+}
diff --git a/langs/en/core.json b/langs/en/core.json
new file mode 100644
index 0000000..28afcee
--- /dev/null
+++ b/langs/en/core.json
@@ -0,0 +1,26 @@
+{
+ "sign in": "Sign In",
+ "username": "Username",
+ "password": "Password",
+ "continue": "Continue",
+ "authcode": "Authentication code",
+ "2fa prompt": "Enter the six-digit code from your mobile authenticator app.",
+ "2fa incorrect": "Authentication code incorrect.",
+ "login incorrect": "Login incorrect.",
+ "login server unavailable": "Login server unavailable. Try again later or contact technical support.",
+ "account locked": "This account has been disabled. Contact technical support.",
+ "password expired": "You must change your password before continuing.",
+ "account terminated": "Account terminated. Access denied.",
+ "account state error": "Your account state is not stable. Log out, restart your browser, and try again.",
+ "welcome user": "Welcome, {user}!",
+ "sign out": "Sign out",
+ "404 error": "404 Error",
+ "page not found": "Page not found.",
+ "invalid parameters": "Invalid request parameters.",
+ "login server error": "The login server returned an error: {arg}",
+ "login server user data error": "The login server refused to provide account information. Try again or contact technical support.",
+ "captcha error": "There was a problem with the CAPTCHA (robot test). Try again.",
+ "no access permission": "You do not have permission to access this system.",
+ "generic op error": "An unknown error occurred. Try again later.",
+ "home": "Home"
+}
diff --git a/langs/en/ldap.json b/langs/en/ldap.json
new file mode 100644
index 0000000..ed7eabd
--- /dev/null
+++ b/langs/en/ldap.json
@@ -0,0 +1,4 @@
+{
+ "ldap server error": "The LDAP server returned an error: {arg}",
+ "ldap error": "LDAP error: {error}"
+}
diff --git a/langs/en/password.json b/langs/en/password.json
new file mode 100644
index 0000000..3a42198
--- /dev/null
+++ b/langs/en/password.json
@@ -0,0 +1,13 @@
+{
+ "password on 500 list": "The given password is ranked number {arg} out of the 500 most common passwords. Try a different one.",
+ "change password": "Change password",
+ "current password incorrect": "The current password is incorrect. Try again.",
+ "new password mismatch": "The new passwords did not match. Try again.",
+ "weak password": "Password does not meet requirements.",
+ "password updated": "Password updated successfully.",
+ "current password": "Current password",
+ "new password": "New password",
+ "confirm password": "New password (again)",
+ "password complexity insufficent": "The new password does not meet the minumum requirements defined by your system administrator.",
+ "old and new passwords match": "Your current and new passwords are the same."
+}
diff --git a/langs/en/pin.json b/langs/en/pin.json
new file mode 100644
index 0000000..29496ce
--- /dev/null
+++ b/langs/en/pin.json
@@ -0,0 +1,9 @@
+{
+ "pin explanation": "Change or set a login PIN for the Station kiosk Quick Access. PIN codes must be between one and eight digits.",
+ "change pin": "Change PIN",
+ "new pin": "New PIN",
+ "confirm pin": "New PIN (again)",
+ "pin updated": "PIN updated.",
+ "new pin mismatch": "The new PINs don't match each other.",
+ "invalid pin format": "PIN codes must be numeric and between one and eight digits in length."
+}
diff --git a/langs/en/security.json b/langs/en/security.json
new file mode 100644
index 0000000..cdde52d
--- /dev/null
+++ b/langs/en/security.json
@@ -0,0 +1,7 @@
+{
+ "sign in again": "Please sign in again to continue.",
+ "login failed try on web": "There is a problem with your account. Visit AccountHub via a web browser for more information.",
+ "mobile login disabled": "Mobile login has been disabled by your system administrator. Contact technical support for more information.",
+ "admin alert email subject": "Alert: User login notification",
+ "admin alert email message": "You (or another administrator) requested to be notified when user \"{username}\" logged in, an event which happened just now.\r\n\r\nUsername: \t{username}\r\nApplication: \t{appname}\r\nDate\/Time: \t{datetime}\r\nIP address: \t{ipaddr}\r\n\r\nThese notifications can be disabled by editing the user in ManagePanel."
+}
diff --git a/langs/en/sync.json b/langs/en/sync.json
new file mode 100644
index 0000000..be71be0
--- /dev/null
+++ b/langs/en/sync.json
@@ -0,0 +1,12 @@
+{
+ "sync mobile": "Sync Mobile App",
+ "scan sync qrcode": "Scan this code with the mobile app or enter the code manually.",
+ "sync explained": "Access your account and apps on the go. Use a sync code to securely connect your phone or tablet to AccountHub with the Netsyms Business mobile app.",
+ "generate sync": "Create new sync code",
+ "active sync codes": "Active codes",
+ "no active codes": "No active codes.",
+ "done adding sync code": "Done adding code",
+ "manual setup": "Manual Setup:",
+ "sync key": "Sync key:",
+ "url": "URL:"
+}
diff --git a/langs/en/titles.json b/langs/en/titles.json
new file mode 100644
index 0000000..ea261ca
--- /dev/null
+++ b/langs/en/titles.json
@@ -0,0 +1,8 @@
+{
+ "account security": "Account security",
+ "security options": "Security options",
+ "account options": "Account options",
+ "sync": "Sync settings",
+ "settings": "Settings",
+ "account": "Account"
+}
diff --git a/lib/Strings.php b/lib/Strings.php
new file mode 100644
index 0000000..c99a665
--- /dev/null
+++ b/lib/Strings.php
@@ -0,0 +1,118 @@
+load("en");
+
+ if (file_exists(__DIR__ . "/../langs/$language/")) {
+ $this->language = $language;
+ $this->load($language);
+ } else {
+ trigger_error("Language $language could not be found.", E_USER_WARNING);
+ }
+ }
+
+ /**
+ * Load all JSON files for the specified language.
+ * @param string $language
+ */
+ private function load(string $language) {
+ $files = glob(__DIR__ . "/../langs/$language/*.json");
+ foreach ($files as $file) {
+ $strings = json_decode(file_get_contents($file), true);
+ foreach ($strings as $key => $val) {
+ if (array_key_exists($key, $this->strings)) {
+ trigger_error("Language key \"$key\" is defined more than once.", E_USER_WARNING);
+ }
+ $this->strings[$key] = $val;
+ }
+ }
+ }
+
+ /**
+ * Add language strings dynamically.
+ * @param array $strings ["key" => "value", ...]
+ */
+ function addStrings(array $strings) {
+ foreach ($strings as $key => $val) {
+ $this->strings[$key] = $val;
+ }
+ }
+
+ /**
+ * I18N string getter. If the key isn't found, it outputs the key itself.
+ * @param string $key
+ * @param bool $echo True to echo the result, false to return it. Default is true.
+ * @return string
+ */
+ function get(string $key, bool $echo = true): string {
+ $str = $key;
+ if (array_key_exists($key, $this->strings)) {
+ $str = $this->strings[$key];
+ } else {
+ trigger_error("Language key \"$key\" does not exist in " . $this->language, E_USER_WARNING);
+ }
+
+ if ($echo) {
+ echo $str;
+ }
+ return $str;
+ }
+
+ /**
+ * I18N string getter (with builder). If the key doesn't exist, outputs the key itself.
+ * @param string $key
+ * @param array $replace key-value array of replacements.
+ * If the string value is "hello {abc}" and you give ["abc" => "123"], the
+ * result will be "hello 123".
+ * @param bool $echo True to echo the result, false to return it. Default is true.
+ * @return string
+ */
+ function build(string $key, array $replace, bool $echo = true): string {
+ $str = $key;
+ if (array_key_exists($key, $this->strings)) {
+ $str = $this->strings[$key];
+ } else {
+ trigger_error("Language key \"$key\" does not exist in " . $this->language, E_USER_WARNING);
+ }
+
+ foreach ($replace as $find => $repl) {
+ $str = str_replace("{" . $find . "}", $repl, $str);
+ }
+
+ if ($echo) {
+ echo $str;
+ }
+ return $str;
+ }
+
+ /**
+ * Builds and returns a JSON key:value string for the supplied array of keys.
+ * @param array $keys ["key1", "key2", ...]
+ */
+ function getJSON(array $keys): string {
+ $strings = [];
+ foreach ($keys as $k) {
+ $strings[$k] = $this->get($k, false);
+ }
+ return json_encode($strings);
+ }
+
+}
diff --git a/pages.php b/pages.php
index a3fb11b..4a63a8a 100644
--- a/pages.php
+++ b/pages.php
@@ -30,13 +30,7 @@ define("PAGES", [
// Which apps to load on a given page
define("APPS", [
- "home" => [
- "taskfloor_tasks",
- "qwikclock_inout",
- "taskfloor_messages",
- "inventory_link",
- "account_security"
- ],
+ "home" => [],
"security" => [
"change_password",
"change_pin",
diff --git a/required.php b/required.php
index 1801afb..e79d7c7 100644
--- a/required.php
+++ b/required.php
@@ -62,7 +62,8 @@ if ($_SESSION['mobile'] === TRUE) {
// List of alert messages
require __DIR__ . '/lang/messages.php';
// text strings (i18n)
-require __DIR__ . '/lang/' . LANGUAGE . ".php";
+require __DIR__ . '/lib/Strings.php';
+$Strings = new Strings(LANGUAGE);
function sendError($error) {
global $SECURE_NONCE;
@@ -136,18 +137,8 @@ function is_empty($str) {
* @param boolean $echo whether to echo the result or return it (default echo)
*/
function lang($key, $echo = true) {
- if (array_key_exists($key, $GLOBALS['STRINGS'])) {
- $str = $GLOBALS['STRINGS'][$key];
- } else {
- trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING);
- $str = $key;
- }
-
- if ($echo) {
- echo $str;
- } else {
- return $str;
- }
+ global $Strings;
+ return $Strings->get($key, $echo);
}
/**
@@ -159,22 +150,8 @@ function lang($key, $echo = true) {
* @param boolean $echo whether to echo the result or return it (default echo)
*/
function lang2($key, $replace, $echo = true) {
- if (array_key_exists($key, $GLOBALS['STRINGS'])) {
- $str = $GLOBALS['STRINGS'][$key];
- } else {
- trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING);
- $str = $key;
- }
-
- foreach ($replace as $find => $repl) {
- $str = str_replace("{" . $find . "}", $repl, $str);
- }
-
- if ($echo) {
- echo $str;
- } else {
- return $str;
- }
+ global $Strings;
+ return $Strings->build($key, $replace, $echo);
}
/**
@@ -191,9 +168,7 @@ function addLangStrings($strings) {
* @param array $strings ['en_us' => ['key' => 'value']]
*/
function addMultiLangStrings($strings) {
- if (!is_empty($strings[LANGUAGE])) {
- $GLOBALS['STRINGS'] = array_merge($GLOBALS['STRINGS'], $strings[LANGUAGE]);
- }
+ throw new Exception("Calling broken function addMultiLangStrings()");
}
/**