diff --git a/src/js/login.js b/src/js/login.js index d067d00be..9343f8867 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -3,7 +3,7 @@ /* global angular, $, showdown */ // create main application module -var app = angular.module('Application', []); +var app = angular.module('Application', ['pascalprecht.translate', 'ngCookies']); app.filter('markdown2html', function () { var converter = new showdown.Converter({ @@ -23,7 +23,45 @@ app.config(function ($sceProvider) { $sceProvider.enabled(false); }); -app.controller('LoginController', ['$scope', '$http', function ($scope, $http) { +app.config(['$translateProvider', function ($translateProvider) { + $translateProvider.useStaticFilesLoader({ + prefix: 'translation/', + suffix: '.json' + }); + $translateProvider.useLocalStorage(); + $translateProvider.preferredLanguage('en'); + $translateProvider.fallbackLanguage('en'); +}]); + +// Add shorthand "tr" filter to avoid having ot use "translate" +// This is a copy of the code at https://github.com/angular-translate/angular-translate/blob/master/src/filter/translate.js +// If we find out how to get that function handle somehow dynamically we can use that, otherwise the copy is required +function translateFilterFactory($parse, $translate) { + + 'use strict'; + + var translateFilter = function (translationId, interpolateParams, interpolation, forceLanguage) { + if (!angular.isObject(interpolateParams)) { + var ctx = this || { + '__SCOPE_IS_NOT_AVAILABLE': 'More info at https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f' + }; + interpolateParams = $parse(interpolateParams)(ctx); + } + + return $translate.instant(translationId, interpolateParams, interpolation, forceLanguage); + }; + + if ($translate.statefulFilter()) { + translateFilter.$stateful = true; + } + + return translateFilter; +} +translateFilterFactory.displayName = 'translateFilterFactory'; +app.filter('tr', translateFilterFactory); + + +app.controller('LoginController', ['$scope', '$translate', '$http', function ($scope, $translate, $http) { // Stupid angular location provider either wants html5 location mode or not, do the query parsing on my own var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.indexOf('=') === -1 ? [item, true] : [item.slice(0, item.indexOf('=')), item.slice(item.indexOf('=')+1)]; }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {}); @@ -136,6 +174,8 @@ app.controller('LoginController', ['$scope', '$http', function ($scope, $http) { if (status !== 200) return; + if (data.language) $translate.use(data.language); + if ($scope.mode === 'login') window.document.title = data.cloudronName + ' Login'; $scope.status = data; }).error(function () { diff --git a/src/login.html b/src/login.html index 045a716d3..5399f3ea5 100644 --- a/src/login.html +++ b/src/login.html @@ -14,7 +14,7 @@ - + @@ -33,11 +33,18 @@ + + + + + + + @@ -53,33 +60,33 @@

-

Login to {{ status.cloudronName || 'Cloudron' }}

+

{{ 'login.loginTo' | tr }} {{ status.cloudronName || 'Cloudron' }}


-

Incorrect username or password

+

{{ 'login.errorIncorrectCredentials' | tr }}

- +
- +
- +
- +
- Reset password + {{ 'login.resetPasswordAction' | tr }}
@@ -91,7 +98,7 @@

-

Password reset

+

{{ 'passwordReset.title' | tr }}


@@ -99,13 +106,13 @@
- +

- +
- Back to login + {{ 'passwordReset.backToLoginAction' | tr }}
@@ -117,9 +124,9 @@

-

Password reset email sent

+

{{ 'passwordReset.emailSent.title' | tr }}


- +
@@ -131,7 +138,7 @@

-

Set new password

+

{{ 'passwordReset.newPassword.title' | tr }}


@@ -146,23 +153,23 @@
- +
- Password must be at least 8 and at most 265 characters + {{ 'passwordReset.newPassword.errorLength' | tr }}
- +
- Passwords don't match + {{ 'passwordReset.newPassword.errorMismatch' | tr }}

- +
- Back to login + {{ 'passwordReset.backToLoginAction' | tr }} @@ -174,9 +181,9 @@

-

Password changed

+

{{ 'passwordReset.success.title' | tr }}


- Open Dashboard + {{ 'passwordReset.success.openDashboardAction' | tr }}
diff --git a/src/translation/de.json b/src/translation/de.json index ce7c91ecb..6fa3f36af 100644 --- a/src/translation/de.json +++ b/src/translation/de.json @@ -17,7 +17,10 @@ "title": "Netzwerk" }, "settings": { - "title": "Einstellungen" + "title": "Einstellungen", + "language": { + "title": "Sprache" + } }, "users": { "title": "Benutzer" @@ -69,5 +72,8 @@ }, "branding": { "title": "Design- & Textanpassungen" + }, + "login": { + "password": "Passwort" } } diff --git a/src/translation/en.json b/src/translation/en.json index d1f2230c8..bbd6a5974 100644 --- a/src/translation/en.json +++ b/src/translation/en.json @@ -1261,5 +1261,37 @@ "location": "Location", "cloneAction": "Clone" } + }, + "login": { + "loginTo": "Login to", + "errorIncorrectCredentials": "Incorrect username or password", + "username": "Username", + "password": "Password", + "2faToken": "2FA Token (if enabled)", + "signInAction": "Sign in", + "resetPasswordAction": "Reset password" + }, + "passwordReset": { + "title": "Password reset", + "usernameOrEmail": "Username or Email", + "resetAction": "Reset", + "backToLoginAction": "Back to login", + "emailSent": { + "title": "Password reset email sent" + }, + "newPassword": { + "title": "Password reset email sentSet new password", + "password": "New Password", + "errorLength": "Password must be at least 8 and at most 265 characters", + "passwordRepeat": "Repeat Password", + "errorMismatch": "Passwords don't match" + }, + "passwordChanged": { + "submitAction": "Submit" + }, + "success": { + "title": "Password changed", + "openDashboardAction": "Open Dashboard" + } } }