diff --git a/Makefile b/Makefile index d74468a9e7..a9df006bc4 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ SETUP_TAR_CHECKSUM=$(NAME)-setup-CHECKSUM # DEB build parameters DEBUILD_BIN ?= debuild -DEBUILD_OPTS = +DEBUILD_OPTS = DPUT_BIN ?= dput DPUT_OPTS ?= -c .dput.cf -u REPREPRO_BIN ?= reprepro @@ -506,6 +506,43 @@ test_jenkins : test_coverage # UI TASKS # -------------------------------------- +HAVE_PO := $(shell ls awx/ui/po/*.po 2>/dev/null) +check-po: +ifdef HAVE_PO + # Should be 'Language: zh-CN' but not 'Language: zh_CN' in zh_CN.po + for po in awx/ui/po/*.po ; do \ + echo $$po; \ + mo="awx/ui/po/`basename $$po .po`.mo"; \ + msgfmt --check --verbose $$po -o $$mo; \ + if test "$$?" -ne 0 ; then \ + exit -1; \ + fi; \ + rm $$mo; \ + name=`echo "$$po" | grep '-'`; \ + if test "x$$name" != x ; then \ + right_name=`echo $$language | sed -e 's/-/_/'`; \ + echo "ERROR: WRONG $$name CORRECTION: $$right_name"; \ + exit -1; \ + fi; \ + language=`grep '^"Language:' "$$po" | grep '_'`; \ + if test "x$$language" != x ; then \ + right_language=`echo $$language | sed -e 's/_/-/'`; \ + echo "ERROR: WRONG $$language CORRECTION: $$right_language in $$po"; \ + exit -1; \ + fi; \ + done; +else + @echo No PO files +endif + +# generate l10n .json +languages: $(UI_DEPS_FLAG_FILE) check-po + $(NPM_BIN) --prefix awx/ui run languages + +# generate .pot +pot: $(UI_DEPS_FLAG_FILE) + $(NPM_BIN) --prefix awx/ui run pot + ui-deps: $(UI_DEPS_FLAG_FILE) $(UI_DEPS_FLAG_FILE): awx/ui/package.json @@ -520,6 +557,7 @@ ui-docker: $(UI_DEPS_FLAG_FILE) ui-release: $(UI_RELEASE_FLAG_FILE) +# todo: include languages target when .po deliverables are added to source control $(UI_RELEASE_FLAG_FILE): $(UI_DEPS_FLAG_FILE) $(NPM_BIN) --prefix awx/ui run build-release touch $(UI_RELEASE_FLAG_FILE) diff --git a/awx/ui/Gruntfile.js b/awx/ui/Gruntfile.js index 57fddad6ed..16b61f0405 100644 --- a/awx/ui/Gruntfile.js +++ b/awx/ui/Gruntfile.js @@ -16,6 +16,7 @@ module.exports = function(grunt) { // Project configuration. grunt.initConfig(configs); grunt.loadNpmTasks('grunt-newer'); + grunt.loadNpmTasks('grunt-angular-gettext'); // writes environment variables for development. current manages: // browser-sync + websocket proxy diff --git a/awx/ui/client/src/about/about.controller.js b/awx/ui/client/src/about/about.controller.js index 90824532db..2c821b9e09 100644 --- a/awx/ui/client/src/about/about.controller.js +++ b/awx/ui/client/src/about/about.controller.js @@ -1,5 +1,6 @@ export default - ['$scope', '$state', 'ConfigService', function($scope, $state, ConfigService){ + ['$scope', '$state', 'ConfigService', 'i18n', + function($scope, $state, ConfigService, i18n){ var processVersion = function(version){ // prettify version & calculate padding // e,g 3.0.0-0.git201602191743/ -> 3.0.0 @@ -20,6 +21,7 @@ export default .then(function(config){ $scope.subscription = config.license_info.subscription_name; $scope.version = processVersion(config.version); + $scope.version_str = i18n._("Version"); $('#about-modal').modal('show'); }); }; diff --git a/awx/ui/client/src/about/about.partial.html b/awx/ui/client/src/about/about.partial.html index 65cfd11f03..bcb2a5cd33 100644 --- a/awx/ui/client/src/about/about.partial.html +++ b/awx/ui/client/src/about/about.partial.html @@ -11,7 +11,7 @@
________________
-/ Tower Version \\
+/ Tower {{version_str}} \\
\\{{version}}/
----------------
\\ ^__^
diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js
index 6df9567b34..b7a3c25d43 100644
--- a/awx/ui/client/src/app.js
+++ b/awx/ui/client/src/app.js
@@ -7,6 +7,7 @@
// Vendor dependencies
import 'jquery';
import 'angular';
+import 'angular-gettext';
import 'bootstrap';
import 'jquery-ui';
import 'bootstrap-datepicker';
@@ -79,6 +80,7 @@ import config from './shared/config/main';
import './login/authenticationServices/pendo/ng-pendo';
import footer from './footer/main';
import scheduler from './scheduler/main';
+import {N_} from './i18n';
var tower = angular.module('Tower', [
// how to add CommonJS / AMD third-party dependencies:
@@ -203,6 +205,8 @@ var tower = angular.module('Tower', [
scheduler.name,
'ApiModelHelper',
'ActivityStreamHelper',
+ 'gettext',
+ 'I18N',
])
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
@@ -237,6 +241,10 @@ var tower = angular.module('Tower', [
$state.go('dashboard');
});
+ /* Mark translatable strings with N_() and
+ * extract them by 'grunt nggettext_extract'
+ * but angular.config() cannot get gettextCatalog.
+ */
$stateProvider.
state('teams', {
url: '/teams',
@@ -248,7 +256,7 @@ var tower = angular.module('Tower', [
},
ncyBreadcrumb: {
parent: 'setup',
- label: 'TEAMS'
+ label: N_("TEAMS")
}
}).
@@ -258,7 +266,7 @@ var tower = angular.module('Tower', [
controller: TeamsAdd,
ncyBreadcrumb: {
parent: "teams",
- label: "CREATE TEAM"
+ label: N_("CREATE TEAM")
}
}).
@@ -333,7 +341,7 @@ var tower = angular.module('Tower', [
},
ncyBreadcrumb: {
parent: 'setup',
- label: 'CREDENTIALS'
+ label: N_("CREDENTIALS")
}
}).
@@ -343,7 +351,7 @@ var tower = angular.module('Tower', [
controller: CredentialsAdd,
ncyBreadcrumb: {
parent: "credentials",
- label: "CREATE CREDENTIAL"
+ label: N_("CREATE CREDENTIAL")
}
}).
@@ -370,7 +378,7 @@ var tower = angular.module('Tower', [
},
ncyBreadcrumb: {
parent: 'setup',
- label: 'USERS'
+ label: N_("USERS")
}
}).
@@ -380,7 +388,7 @@ var tower = angular.module('Tower', [
controller: UsersAdd,
ncyBreadcrumb: {
parent: "users",
- label: "CREATE USER"
+ label: N_("CREATE USER")
}
}).
@@ -420,7 +428,7 @@ var tower = angular.module('Tower', [
templateUrl: urlPrefix + 'partials/sockets.html',
controller: SocketsController,
ncyBreadcrumb: {
- label: 'SOCKETS'
+ label: N_("SOCKETS")
}
});
}
@@ -443,13 +451,14 @@ var tower = angular.module('Tower', [
'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer',
'ClearScope', 'LoadConfig', 'Store', 'pendoService', 'Prompt', 'Rest',
'Wait', 'ProcessErrors', '$state', 'GetBasePath', 'ConfigService',
- 'FeaturesService', '$filter', 'SocketService',
+ 'FeaturesService', '$filter', 'SocketService', 'I18NInit',
function($stateExtender, $q, $compile, $cookieStore, $rootScope, $log,
CheckLicense, $location, Authorization, LoadBasePaths, Timer,
ClearScope, LoadConfig, Store, pendoService, Prompt, Rest, Wait,
ProcessErrors, $state, GetBasePath, ConfigService, FeaturesService,
- $filter, SocketService) {
+ $filter, SocketService, I18NInit) {
+ I18NInit();
$stateExtender.addState({
name: 'dashboard',
url: '/home',
@@ -466,7 +475,7 @@ var tower = angular.module('Tower', [
refreshButton: true
},
ncyBreadcrumb: {
- label: "DASHBOARD"
+ label: N_("DASHBOARD")
},
resolve: {
graphData: ['$q', 'jobStatusGraphData', '$rootScope',
@@ -487,7 +496,7 @@ var tower = angular.module('Tower', [
templateUrl: urlPrefix + 'partials/jobs.html',
controller: JobsListController,
ncyBreadcrumb: {
- label: "JOBS"
+ label: N_("JOBS")
},
params: {
search: {
@@ -512,7 +521,7 @@ var tower = angular.module('Tower', [
activityStreamTarget: 'project'
},
ncyBreadcrumb: {
- label: "PROJECTS"
+ label: N_("PROJECTS")
},
socket: {
"groups":{
@@ -528,7 +537,7 @@ var tower = angular.module('Tower', [
controller: ProjectsAdd,
ncyBreadcrumb: {
parent: "projects",
- label: "CREATE PROJECT"
+ label: N_("CREATE PROJECT")
},
socket: {
"groups":{
diff --git a/awx/ui/client/src/bread-crumb/bread-crumb.partial.html b/awx/ui/client/src/bread-crumb/bread-crumb.partial.html
index 8e661904ba..4500556e96 100644
--- a/awx/ui/client/src/bread-crumb/bread-crumb.partial.html
+++ b/awx/ui/client/src/bread-crumb/bread-crumb.partial.html
@@ -3,7 +3,7 @@
Are you sure you want to delete the project below?' + $filter('sanitize')(name) + '',
+ hdr: i18n._('Delete'),
+ body: i18n._('Are you sure you want to delete the project below?') + '' + $filter('sanitize')(name) + '',
action: action,
actionText: 'DELETE'
});
@@ -276,11 +277,11 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
Rest.setUrl(url);
Rest.post()
.success(function () {
- Alert('SCM Update Cancel', 'Your request to cancel the update was submitted to the task manager.', 'alert-info');
+ Alert(i18n._('SCM Update Cancel'), i18n._('Your request to cancel the update was submitted to the task manager.'), 'alert-info');
$scope.refresh();
})
.error(function (data, status) {
- ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status });
+ ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.format(i18n._('Call to %s failed. POST status: '), url) + status });
});
});
@@ -296,12 +297,12 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
if (data.can_cancel) {
$scope.$emit('Cancel_Update', url);
} else {
- Alert('Cancel Not Allowed', 'Either you do not have access or the SCM update process completed. ' +
- 'Click the Refresh button to view the latest status.', 'alert-info', null, null, null, null, true);
+ Alert(i18n._('Cancel Not Allowed'), i18n._('Either you do not have access or the SCM update process completed. ' +
+ 'Click the Refresh button to view the latest status.'), 'alert-info', null, null, null, null, true);
}
})
.error(function (data, status) {
- ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status });
+ ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.format(i18n._('Call to %s failed. GET status: '), url) + status });
});
});
@@ -316,17 +317,17 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
$scope.$emit('Check_Cancel', data);
})
.error(function (data, status) {
- ProcessErrors($scope, data, status, null, { hdr: 'Error!',
- msg: 'Call to ' + data.related.current_update + ' failed. GET status: ' + status });
+ ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
+ msg: i18n.format(i18n._('Call to %s failed. GET status: '), data.related.current_update) + status });
});
} else {
- Alert('Update Not Found', 'An SCM update does not appear to be running for project: ' + $filter('sanitize')(name) + '. Click the Refresh ' +
- 'button to view the latest status.', 'alert-info',undefined,undefined,undefined,undefined,true);
+ Alert(i18n._('Update Not Found'), i18n.format(i18n._('An SCM update does not appear to be running for project: %s. Click the Refresh ' +
+ 'button to view the latest status.'), $filter('sanitize')(name)), 'alert-info',undefined,undefined,undefined,undefined,true);
}
})
.error(function (data, status) {
- ProcessErrors($scope, data, status, null, { hdr: 'Error!',
- msg: 'Call to get project failed. GET status: ' + status });
+ ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
+ msg: i18n._('Call to get project failed. GET status: ') + status });
});
};
@@ -371,7 +372,8 @@ ProjectsList.$inject = ['$scope', '$rootScope', '$location', '$log',
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'ProjectUpdate',
'Refresh', 'Wait', 'GetChoices', 'Empty', 'Find',
- 'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService'
+ 'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService',
+ 'i18n'
];
@@ -379,7 +381,7 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
$stateParams, ProjectsForm, GenerateForm, Rest, Alert, ProcessErrors,
ClearScope, GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit,
OrganizationList, CredentialList, GetChoices, DebugForm, Wait, $state,
- CreateSelect2) {
+ CreateSelect2, i18n) {
Rest.setUrl(GetBasePath('projects'));
Rest.options()
@@ -504,8 +506,8 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
})
.error(function (data, status) {
Wait('stop');
- ProcessErrors($scope, data, status, form, { hdr: 'Error!',
- msg: 'Failed to create new project. POST returned status: ' + status });
+ ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'),
+ msg: i18n._('Failed to create new project. POST returned status: ') + status });
});
};
@@ -521,27 +523,27 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
if($scope.scm_type.value) {
switch ($scope.scm_type.value) {
case 'git':
- $scope.urlPopover = 'Example URLs for GIT SCM include:
- https://github.com/ansible/ansible.git
' +
+ $scope.urlPopover = i18n._('Example URLs for GIT SCM include:
- https://github.com/ansible/ansible.git
' +
'- git@github.com:ansible/ansible.git
- git://servername.example.com/ansible.git
' +
'Note: When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
- 'SSH. GIT read only protocol (git://) does not use username or password information.';
+ 'SSH. GIT read only protocol (git://) does not use username or password information.');
break;
case 'svn':
- $scope.urlPopover = '
Example URLs for Subversion SCM include:
' +
+ $scope.urlPopover = i18n._('Example URLs for Subversion SCM include:
' +
'- https://github.com/ansible/ansible
- svn://servername.example.com/path
' +
- '- svn+ssh://servername.example.com/path
';
+ '- svn+ssh://servername.example.com/path
');
break;
case 'hg':
- $scope.urlPopover = 'Example URLs for Mercurial SCM include:
' +
+ $scope.urlPopover = i18n._('Example URLs for Mercurial SCM include:
' +
'- https://bitbucket.org/username/project
- ssh://hg@bitbucket.org/username/project
' +
'- ssh://server.example.com/path
' +
'Note: Mercurial does not support password authentication for SSH. ' +
'Do not put the username and key in the URL. ' +
- 'If using Bitbucket and SSH, do not supply your Bitbucket username.';
+ 'If using Bitbucket and SSH, do not supply your Bitbucket username.');
break;
default:
- $scope.urlPopover = '
URL popover text';
+ $scope.urlPopover = i18n._('
URL popover text');
}
}
@@ -556,7 +558,7 @@ ProjectsAdd.$inject = ['Refresh', '$scope', '$rootScope', '$compile', '$location
'$stateParams', 'ProjectsForm', 'GenerateForm', 'Rest', 'Alert',
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ReturnToCaller',
'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList',
- 'GetChoices', 'DebugForm', 'Wait', '$state', 'CreateSelect2'
+ 'GetChoices', 'DebugForm', 'Wait', '$state', 'CreateSelect2', 'i18n'
];
@@ -566,7 +568,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
ReturnToCaller, GetProjectPath, Authorization, CredentialList, LookUpInit,
GetChoices, Empty, DebugForm, Wait, SchedulesControllerInit,
SchedulesListInit, SchedulesList, ProjectUpdate, $state, CreateSelect2,
- OrganizationList, NotificationsListInit, ToggleNotification) {
+ OrganizationList, NotificationsListInit, ToggleNotification, i18n) {
ClearScope('htmlTemplate');
@@ -769,8 +771,8 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
$scope.$emit('projectLoaded');
})
.error(function (data, status) {
- ProcessErrors($scope, data, status, form, { hdr: 'Error!',
- msg: 'Failed to retrieve project: ' + id + '. GET status: ' + status
+ ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'),
+ msg: i18n._('Failed to retrieve project: ') + id + i18n._('. GET status: ') + status
});
});
});
@@ -871,8 +873,8 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
};
Prompt({
- hdr: 'Delete',
- body: '
Are you sure you want to remove the ' + title + ' below from ' + $scope.name + '?' + name + '',
+ hdr: i18n._('Delete'),
+ body: i18n.format(i18n._('Are you sure you want to remove the %s below from %s?'), title, $scope.name) + '' + name + '',
action: action,
actionText: 'DELETE'
});
@@ -889,27 +891,27 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
if($scope.scm_type.value) {
switch ($scope.scm_type.value) {
case 'git':
- $scope.urlPopover = 'Example URLs for GIT SCM include:
- https://github.com/ansible/ansible.git
' +
+ $scope.urlPopover = i18n._('Example URLs for GIT SCM include:
- https://github.com/ansible/ansible.git
' +
'- git@github.com:ansible/ansible.git
- git://servername.example.com/ansible.git
' +
'Note: When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
- 'SSH. GIT read only protocol (git://) does not use username or password information.';
+ 'SSH. GIT read only protocol (git://) does not use username or password information.');
break;
case 'svn':
- $scope.urlPopover = '
Example URLs for Subversion SCM include:
' +
+ $scope.urlPopover = i18n._('Example URLs for Subversion SCM include:
' +
'- https://github.com/ansible/ansible
- svn://servername.example.com/path
' +
- '- svn+ssh://servername.example.com/path
';
+ '- svn+ssh://servername.example.com/path
');
break;
case 'hg':
- $scope.urlPopover = 'Example URLs for Mercurial SCM include:
' +
+ $scope.urlPopover = i18n._('Example URLs for Mercurial SCM include:
' +
'- https://bitbucket.org/username/project
- ssh://hg@bitbucket.org/username/project
' +
'- ssh://server.example.com/path
' +
'Note: Mercurial does not support password authentication for SSH. ' +
'Do not put the username and key in the URL. ' +
- 'If using Bitbucket and SSH, do not supply your Bitbucket username.';
+ 'If using Bitbucket and SSH, do not supply your Bitbucket username.');
break;
default:
- $scope.urlPopover = '
URL popover text';
+ $scope.urlPopover = i18n._('
URL popover text');
}
}
};
@@ -918,7 +920,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
if ($scope.project_obj.scm_type === "Manual" || Empty($scope.project_obj.scm_type)) {
// ignore
} else if ($scope.project_obj.status === 'updating' || $scope.project_obj.status === 'running' || $scope.project_obj.status === 'pending') {
- Alert('Update in Progress', 'The SCM update process is running.', 'alert-info');
+ Alert('Update in Progress', i18n._('The SCM update process is running.'), 'alert-info');
} else {
ProjectUpdate({ scope: $scope, project_id: $scope.project_obj.id });
}
@@ -936,5 +938,5 @@ ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
'DebugForm', 'Wait', 'SchedulesControllerInit', 'SchedulesListInit',
'SchedulesList', 'ProjectUpdate', '$state', 'CreateSelect2',
- 'OrganizationList', 'NotificationsListInit', 'ToggleNotification'
+ 'OrganizationList', 'NotificationsListInit', 'ToggleNotification', 'i18n'
];
diff --git a/awx/ui/client/src/controllers/Users.js b/awx/ui/client/src/controllers/Users.js
index eb55a1bbb2..ee7ed6f431 100644
--- a/awx/ui/client/src/controllers/Users.js
+++ b/awx/ui/client/src/controllers/Users.js
@@ -10,10 +10,12 @@
* @description This controller's the Users page
*/
+import {N_} from "../i18n";
+
const user_type_options = [
- {type: 'normal' , label: 'Normal User' },
- {type: 'system_auditor' , label: 'System Auditor' },
- {type: 'system_administrator', label: 'System Administrator' },
+ {type: 'normal' , label: N_('Normal User') },
+ {type: 'system_auditor' , label: N_('System Auditor') },
+ {type: 'system_administrator', label: N_('System Administrator') },
];
function user_type_sync($scope) {
@@ -34,7 +36,12 @@ function user_type_sync($scope) {
export function UsersList($scope, $rootScope, $location, $log, $stateParams,
Rest, Alert, UserList, GenerateList, Prompt, SearchInit, PaginateInit,
ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit,
- Wait, $state, Refresh, $filter, rbacUiControlService) {
+ Wait, $state, Refresh, $filter, rbacUiControlService, i18n) {
+
+ for (var i = 0; i < user_type_options.length; i++) {
+ user_type_options[i].label = i18n._(user_type_options[i].label);
+ }
+
ClearScope();
$scope.canAdd = false;
@@ -142,7 +149,7 @@ UsersList.$inject = ['$scope', '$rootScope', '$location', '$log',
'$stateParams', 'Rest', 'Alert', 'UserList', 'generateList', 'Prompt',
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'Wait', '$state',
- 'Refresh', '$filter', 'rbacUiControlService'
+ 'Refresh', '$filter', 'rbacUiControlService', 'i18n'
];
@@ -152,7 +159,11 @@ UsersList.$inject = ['$scope', '$rootScope', '$location', '$log',
export function UsersAdd($scope, $rootScope, $compile, $location, $log,
$stateParams, UserForm, GenerateForm, Rest, Alert, ProcessErrors,
ReturnToCaller, ClearScope, GetBasePath, LookUpInit, OrganizationList,
- ResetForm, Wait, CreateSelect2, $state) {
+ ResetForm, Wait, CreateSelect2, $state, i18n) {
+
+ for (var i = 0; i < user_type_options.length; i++) {
+ user_type_options[i].label = i18n._(user_type_options[i].label);
+ }
Rest.setUrl(GetBasePath('users'));
Rest.options()
@@ -268,14 +279,19 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
UsersAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert',
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
- 'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', 'CreateSelect2', '$state'
+ 'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', 'CreateSelect2', '$state',
+ 'i18n'
];
export function UsersEdit($scope, $rootScope, $location,
$stateParams, UserForm, GenerateForm, Rest, ProcessErrors,
RelatedSearchInit, RelatedPaginateInit, ClearScope,
- GetBasePath, ResetForm, Wait, CreateSelect2 ,$state) {
+ GetBasePath, ResetForm, Wait, CreateSelect2 ,$state, i18n) {
+
+ for (var i = 0; i < user_type_options.length; i++) {
+ user_type_options[i].label = i18n._(user_type_options[i].label);
+ }
ClearScope();
@@ -439,5 +455,5 @@ export function UsersEdit($scope, $rootScope, $location,
UsersEdit.$inject = ['$scope', '$rootScope', '$location',
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'ProcessErrors',
'RelatedSearchInit', 'RelatedPaginateInit', 'ClearScope', 'GetBasePath',
- 'ResetForm', 'Wait', 'CreateSelect2', '$state'
+ 'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n'
];
diff --git a/awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js b/awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js
index f3e4a0ad20..246ccb3a68 100644
--- a/awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js
+++ b/awx/ui/client/src/dashboard/counts/dashboard-counts.directive.js
@@ -1,7 +1,8 @@
/* jshint unused: vars */
export default
[ 'templateUrl',
- function(templateUrl) {
+ 'i18n',
+ function(templateUrl, i18n) {
return {
restrict: 'E',
scope: {
@@ -35,34 +36,34 @@ export default
{
url: "/#/home/hosts",
number: scope.data.hosts.total,
- label: "Hosts"
+ label: i18n._("Hosts")
},
{
url: "/#/home/hosts?active-failures=true",
number: scope.data.hosts.failed,
- label: "Failed Hosts",
+ label: i18n._("Failed Hosts"),
isFailureCount: true
},
{
url: "/#/inventories",
number: scope.data.inventories.total,
- label: "Inventories",
+ label: i18n._("Inventories"),
},
{
url: "/#/inventories?status=sync-failed",
number: scope.data.inventories.inventory_failed,
- label: "Inventory Sync Failures",
+ label: i18n._("Inventory Sync Failures"),
isFailureCount: true
},
{
url: "/#/projects",
number: scope.data.projects.total,
- label: "Projects"
+ label: i18n._("Projects")
},
{
url: "/#/projects?status=failed,canceled",
number: scope.data.projects.failed,
- label: "Project Sync Failures",
+ label: i18n._("Project Sync Failures"),
isFailureCount: true
}
], function(val) { return addFailureToCount(val); });
diff --git a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.partial.html b/awx/ui/client/src/dashboard/graphs/dashboard-graphs.partial.html
index 3cbbf34c49..7a818c704d 100644
--- a/awx/ui/client/src/dashboard/graphs/dashboard-graphs.partial.html
+++ b/awx/ui/client/src/dashboard/graphs/dashboard-graphs.partial.html
@@ -1,68 +1,68 @@
- JOB STATUS
+ JOB STATUS
- Period
+ Period
- Job Type
+ Job Type
- All
+ All
-
- All
+ All
-
- Inventory Sync
+ Inventory Sync
-
- SCM Update
+ SCM Update
-
- Playbook Run
+ Playbook Run
- View
+ View
diff --git a/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js b/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js
index 76176cf708..8202fbc21d 100644
--- a/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js
+++ b/awx/ui/client/src/dashboard/graphs/job-status/job-status-graph.directive.js
@@ -13,10 +13,11 @@
'adjustGraphSize',
'jobStatusGraphData',
'templateUrl',
+ 'i18n',
JobStatusGraph
];
-function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustGraphSize, graphDataService, templateUrl) {
+function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustGraphSize, graphDataService, templateUrl, i18n) {
return {
restrict: 'E',
scope: {
@@ -60,10 +61,10 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
var timeFormat, graphData = [
{ "color": "#5CB85C",
- "key": "SUCCESSFUL",
+ "key": i18n._("SUCCESSFUL"),
"values": data.jobs.successful
},
- { "key" : "FAILED" ,
+ { "key" : i18n._("FAILED") ,
"color" : "#D9534F",
"values": data.jobs.failed
}
@@ -102,14 +103,14 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
job_status_chart.interactiveLayer.tooltip.distance(-1); //distance from interactive line to tooltip
job_status_chart.xAxis
- .axisLabel("TIME")//.showMaxMin(true)
+ .axisLabel(i18n._("TIME"))//.showMaxMin(true)
.tickFormat(function(d) {
var dx = graphData[0].values[d] && graphData[0].values[d].x || 0;
return dx ? d3.time.format(timeFormat)(new Date(Number(dx+'000'))) : '';
});
job_status_chart.yAxis //Chart y-axis settings
- .axisLabel('JOBS')
+ .axisLabel(i18n._('JOBS'))
.tickFormat(d3.format('.f'));
d3.select(element.find('svg')[0])
diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js b/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js
index e34a051588..eb312af291 100644
--- a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js
+++ b/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js
@@ -5,17 +5,17 @@
*************************************************/
-export default function(){
+export default [ 'i18n', function(i18n){
return {
name: 'hosts',
iterator: 'host',
- selectTitle: 'Add Existing Hosts',
+ selectTitle: i18n._('Add Existing Hosts'),
editTitle: 'Hosts',
listTitle: 'Hosts',
index: false,
hover: true,
well: true,
- emptyListText: 'NO HOSTS FOUND',
+ emptyListText: i18n._('NO HOSTS FOUND'),
fields: {
status: {
basePath: 'unified_jobs',
@@ -91,4 +91,4 @@ export default function(){
}
};
-}
+}];
diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html
index 9215dc8332..8114f037d4 100644
--- a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html
+++ b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html
@@ -1,23 +1,23 @@
- RECENTLY USED JOB TEMPLATES
+ RECENTLY USED JOB TEMPLATES
- VIEW ALL
+ VIEW ALL
- Name
+ Name
- Activity
+ Activity
- Actions
+ Actions
- RECENTLY USED JOB TEMPLATES
+ RECENTLY USED JOB TEMPLATES
- No job templates were recently used.
+
No job templates were recently used.
You can create a job template here.
diff --git a/awx/ui/client/src/dashboard/lists/jobs/jobs-list.partial.html b/awx/ui/client/src/dashboard/lists/jobs/jobs-list.partial.html
index 8d6811a5ec..725675377c 100644
--- a/awx/ui/client/src/dashboard/lists/jobs/jobs-list.partial.html
+++ b/awx/ui/client/src/dashboard/lists/jobs/jobs-list.partial.html
@@ -1,17 +1,17 @@
- RECENT JOB RUNS
+ RECENT JOB RUNS
- VIEW ALL
+ VIEW ALL
- Name
- Time
+ Name
+ Time
- RECENTLY RUN JOBS
+ RECENTLY RUN JOBS
- No jobs were recently run.
+ No jobs were recently run.
diff --git a/awx/ui/client/src/forms/Credentials.js b/awx/ui/client/src/forms/Credentials.js
index abcbbd476d..eb4a61b923 100644
--- a/awx/ui/client/src/forms/Credentials.js
+++ b/awx/ui/client/src/forms/Credentials.js
@@ -12,14 +12,15 @@
export default
angular.module('CredentialFormDefinition', [])
- .value('CredentialForm', {
+ .factory('CredentialForm', ['i18n', function(i18n) {
+ return {
- addTitle: 'Create Credential', //Legend in add mode
+ addTitle: i18n._('Create Credential'), //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'credential',
forceListeners: true,
subFormTitles: {
- credentialSubForm: 'Type Details',
+ credentialSubForm: i18n._('Type Details'),
},
actions: {
@@ -28,7 +29,7 @@ export default
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -36,7 +37,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
@@ -46,26 +47,26 @@ export default
addRequired: false,
editRequired: false,
ngShow: 'canShareCredential',
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
ngClick: 'lookUpOrganization()',
- awPopOver: "If no organization is given, the credential can only be used by the user that creates the credential. Organization admins and system administrators can assign an organization so that roles for the credential can be assigned to users and teams in that organization.
",
- dataTitle: 'Organization ',
+ awPopOver: i18n._("If no organization is given, the credential can only be used by the user that creates the credential. Organization admins and system administrators can assign an organization so that roles for the credential can be assigned to users and teams in that organization.
"),
+ dataTitle: i18n._('Organization') + ' ',
dataPlacement: 'bottom',
dataContainer: "body",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
kind: {
- label: 'Type',
+ label: i18n._('Type'),
excludeModal: true,
type: 'select',
ngOptions: 'kind.label for kind in credential_kind_options track by kind.value', // select as label for value in array 'kind.label for kind in credential_kind_options',
ngChange: 'kindChange()',
addRequired: true,
editRequired: true,
- awPopOver:'\n' +
+ awPopOver: i18n._('\n' +
'- Machine
\n' +
'- Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
@@ -82,15 +83,15 @@ export default
'
- Usernames, passwords, and access keys for authenticating to the specified cloud or infrastructure ' +
'provider. These are used for dynamic inventory sources and for cloud provisioning and deployment ' +
'in playbook runs.
\n' +
- '
\n',
- dataTitle: 'Type',
+ '
\n'),
+ dataTitle: i18n._('Type'),
dataPlacement: 'right',
dataContainer: "body",
hasSubForm: true,
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
access_key: {
- label: 'Access Key',
+ label: i18n._('Access Key'),
type: 'text',
ngShow: "kind.value == 'aws'",
awRequiredWhen: {
@@ -103,7 +104,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
secret_key: {
- label: 'Secret Key',
+ label: i18n._('Secret Key'),
type: 'sensitive',
ngShow: "kind.value == 'aws'",
ngDisabled: "secret_key_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
@@ -118,14 +119,14 @@ export default
subForm: 'credentialSubForm'
},
security_token: {
- label: 'STS Token',
+ label: i18n._('STS Token'),
type: 'sensitive',
ngShow: "kind.value == 'aws'",
autocomplete: false,
apiField: 'security_token',
- awPopOver: "Security Token Service (STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users.To learn more about the IAM STS Token, refer to the Amazon documentation.",
+ awPopOver: i18n._("Security Token Service (STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users.To learn more about the IAM STS Token, refer to the Amazon documentation."),
hasShowInputButton: true,
- dataTitle: 'STS Token',
+ dataTitle: i18n._('STS Token'),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
@@ -136,8 +137,8 @@ export default
type: 'text',
ngShow: "kind.value == 'vmware' || kind.value == 'openstack' || kind.value === 'satellite6' || kind.value === 'cloudforms'",
awPopOverWatch: "hostPopOver",
- awPopOver: "set in helpers/credentials",
- dataTitle: 'Host',
+ awPopOver: i18n._("set in helpers/credentials"),
+ dataTitle: i18n._('Host'),
dataPlacement: 'right',
dataContainer: "body",
autocomplete: false,
@@ -149,7 +150,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"subscription": {
- label: "Subscription ID",
+ label: i18n._("Subscription ID"),
type: 'text',
ngShow: "kind.value == 'azure' || kind.value == 'azure_rm'",
awRequiredWhen: {
@@ -159,8 +160,8 @@ export default
addRequired: false,
editRequired: false,
autocomplete: false,
- awPopOver: 'Subscription ID is an Azure construct, which is mapped to a username.
',
- dataTitle: 'Subscription ID',
+ awPopOver: i18n._('Subscription ID is an Azure construct, which is mapped to a username.
'),
+ dataTitle: i18n._('Subscription ID'),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
@@ -188,15 +189,15 @@ export default
init: false
},
autocomplete: false,
- awPopOver: 'The email address assigned to the Google Compute Engine service account.
',
- dataTitle: 'Email',
+ awPopOver: i18n._('The email address assigned to the Google Compute Engine service account.
'),
+ dataTitle: i18n._('Email'),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"api_key": {
- label: 'API Key',
+ label: i18n._('API Key'),
type: 'sensitive',
ngShow: "kind.value == 'rax'",
awRequiredWhen: {
@@ -224,7 +225,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"ssh_password": {
- label: 'Password',
+ label: i18n._('Password'),
type: 'sensitive',
ngShow: "kind.value == 'ssh'",
ngDisabled: "ssh_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
@@ -232,7 +233,7 @@ export default
editRequired: false,
subCheckbox: {
variable: 'ssh_password_ask',
- text: 'Ask at runtime?',
+ text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'ssh_password\', \'undefined\')'
},
hasShowInputButton: true,
@@ -254,16 +255,16 @@ export default
editRequired: false,
awDropFile: true,
rows: 10,
- awPopOver: "SSH key description",
+ awPopOver: i18n._("SSH key description"),
awPopOverWatch: "key_description",
- dataTitle: 'Private Key',
+ dataTitle: i18n._('Private Key'),
dataPlacement: 'right',
dataContainer: "body",
subForm: "credentialSubForm",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"ssh_key_unlock": {
- label: 'Private Key Passphrase',
+ label: i18n._('Private Key Passphrase'),
type: 'sensitive',
ngShow: "kind.value == 'ssh' || kind.value == 'scm'",
addRequired: false,
@@ -272,7 +273,7 @@ export default
subCheckbox: {
variable: 'ssh_key_unlock_ask',
ngShow: "kind.value == 'ssh'",
- text: 'Ask at runtime?',
+ text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'ssh_key_unlock\', \'undefined\')',
ngDisabled: "keyEntered === false"
},
@@ -280,15 +281,15 @@ export default
subForm: 'credentialSubForm'
},
"become_method": {
- label: "Privilege Escalation",
+ label: i18n._("Privilege Escalation"),
// hintText: "If your playbooks use privilege escalation (\"sudo: true\", \"su: true\", etc), you can specify the username to become, and the password to use here.",
type: 'select',
ngShow: "kind.value == 'ssh'",
- dataTitle: 'Privilege Escalation',
+ dataTitle: i18n._('Privilege Escalation'),
ngOptions: 'become.label for become in become_options track by become.value',
- awPopOver: "Specify a method for 'become' operations. " +
+ awPopOver: i18n._("
Specify a method for 'become' operations. " +
"This is equivalent to specifying the --become-method=BECOME_METHOD parameter, where BECOME_METHOD could be "+
- "sudo | su | pbrun | pfexec | runas
(defaults to sudo)
",
+ "sudo | su | pbrun | pfexec | runas
(defaults to sudo)"),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
@@ -313,7 +314,7 @@ export default
editRequired: false,
subCheckbox: {
variable: 'become_password_ask',
- text: 'Ask at runtime?',
+ text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'become_password\', \'undefined\')'
},
hasShowInputButton: true,
@@ -322,7 +323,7 @@ export default
},
client:{
type: 'text',
- label: 'Client ID',
+ label: i18n._('Client ID'),
subForm: 'credentialSubForm',
ngShow: "kind.value === 'azure_rm'",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
@@ -331,20 +332,20 @@ export default
type: 'sensitive',
hasShowInputButton: true,
autocomplete: false,
- label: 'Client Secret',
+ label: i18n._('Client Secret'),
subForm: 'credentialSubForm',
ngShow: "kind.value === 'azure_rm'",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
tenant: {
type: 'text',
- label: 'Tenant ID',
+ label: i18n._('Tenant ID'),
subForm: 'credentialSubForm',
ngShow: "kind.value === 'azure_rm'",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
authorize: {
- label: 'Authorize',
+ label: i18n._('Authorize'),
type: 'checkbox',
ngChange: "toggleCallback('host_config_key')",
subForm: 'credentialSubForm',
@@ -352,7 +353,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
authorize_password: {
- label: 'Authorize Password',
+ label: i18n._('Authorize Password'),
type: 'sensitive',
hasShowInputButton: true,
autocomplete: false,
@@ -365,8 +366,8 @@ export default
type: 'text',
ngShow: "kind.value == 'gce' || kind.value == 'openstack'",
awPopOverWatch: "projectPopOver",
- awPopOver: "set in helpers/credentials",
- dataTitle: 'Project Name',
+ awPopOver: i18n._("set in helpers/credentials"),
+ dataTitle: i18n._('Project Name'),
dataPlacement: 'right',
dataContainer: "body",
addRequired: false,
@@ -382,12 +383,12 @@ export default
labelBind: 'domainLabel',
type: 'text',
ngShow: "kind.value == 'openstack'",
- awPopOver: "OpenStack domains define administrative " +
+ awPopOver: i18n._("
OpenStack domains define administrative " +
"boundaries. It is only needed for Keystone v3 authentication URLs. " +
"Common scenarios include:
- v2 URLs - leave blank
" +
"- v3 default - set to 'default'
" +
- "- v3 multi-domain - your domain name
",
- dataTitle: 'Domain Name',
+ "v3 multi-domain - your domain name "),
+ dataTitle: i18n._('Domain Name'),
dataPlacement: 'right',
dataContainer: "body",
addRequired: false,
@@ -396,7 +397,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"vault_password": {
- label: "Vault Password",
+ label: i18n._("Vault Password"),
type: 'sensitive',
ngShow: "kind.value == 'ssh'",
ngDisabled: "vault_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
@@ -404,7 +405,7 @@ export default
editRequired: false,
subCheckbox: {
variable: 'vault_password_ask',
- text: 'Ask at runtime?',
+ text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'vault_password\', \'undefined\')'
},
hasShowInputButton: true,
@@ -438,7 +439,7 @@ export default
dataPlacement: 'top',
basePath: 'credentials/:id/access_list/',
type: 'collection',
- title: 'Permissions',
+ title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@@ -447,9 +448,9 @@ export default
add: {
ngClick: "addPermission",
label: 'Add',
- awToolTip: 'Add a permission',
+ awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@@ -457,19 +458,19 @@ export default
fields: {
username: {
key: true,
- label: 'User',
+ label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
- label: 'Role',
+ label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
searchable: false
},
team_roles: {
- label: 'Team Roles',
+ label: i18n._('Team Roles'),
type: 'team_roles',
noSort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
@@ -487,4 +488,4 @@ export default
}
};
}
- });
+ };}]);
diff --git a/awx/ui/client/src/forms/Inventories.js b/awx/ui/client/src/forms/Inventories.js
index 72852079c3..0207124476 100644
--- a/awx/ui/client/src/forms/Inventories.js
+++ b/awx/ui/client/src/forms/Inventories.js
@@ -12,9 +12,10 @@
export default
angular.module('InventoryFormDefinition', ['ScanJobsListDefinition'])
- .value('InventoryFormObject', {
+ .factory('InventoryFormObject', ['i18n', function(i18n) {
+ return {
- addTitle: 'New Inventory',
+ addTitle: i18n._('New Inventory'),
editTitle: '{{ inventory_name }}',
name: 'inventory',
tabs: true,
@@ -22,7 +23,7 @@ export default
fields: {
inventory_name: {
realName: 'name',
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -31,14 +32,14 @@ export default
},
inventory_description: {
realName: 'description',
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@@ -50,21 +51,21 @@ export default
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
},
variables: {
- label: 'Variables',
+ label: i18n._('Variables'),
type: 'textarea',
class: 'Form-formGroup--fullWidth',
addRequired: false,
editRequird: false,
rows: 6,
"default": "---",
- awPopOver: "Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.
" +
+ awPopOver: i18n._("Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.
" +
"JSON:
\n" +
"{
\"somevar\": \"somevalue\",
\"password\": \"magic\"
}
\n" +
"YAML:
\n" +
"---
somevar: somevalue
password: magic
\n" +
'View JSON examples at www.json.org
' +
- 'View YAML examples at docs.ansible.com
',
- dataTitle: 'Inventory Variables',
+ 'View YAML examples at docs.ansible.com
'),
+ dataTitle: i18n._('Inventory Variables'),
dataPlacement: 'right',
dataContainer: 'body',
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
@@ -89,11 +90,11 @@ export default
related: {
permissions: {
- awToolTip: 'Please save before assigning permissions',
+ awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
basePath: 'inventories/:id/access_list/',
type: 'collection',
- title: 'Permissions',
+ title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@@ -102,9 +103,9 @@ export default
add: {
ngClick: "addPermission",
label: 'Add',
- awToolTip: 'Add a permission',
+ awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@@ -112,19 +113,19 @@ export default
fields: {
username: {
key: true,
- label: 'User',
+ label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
- label: 'Role',
+ label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
noSearch: true
},
team_roles: {
- label: 'Team Roles',
+ label: i18n._('Team Roles'),
type: 'team_roles',
noSort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
@@ -143,7 +144,7 @@ export default
};
}
- })
+ };}])
.factory('InventoryForm', ['InventoryFormObject', 'ScanJobsList',
function(InventoryFormObject, ScanJobsList) {
return function() {
diff --git a/awx/ui/client/src/forms/JobTemplates.js b/awx/ui/client/src/forms/JobTemplates.js
index 3ec35b2288..10d5119407 100644
--- a/awx/ui/client/src/forms/JobTemplates.js
+++ b/awx/ui/client/src/forms/JobTemplates.js
@@ -10,12 +10,14 @@
* @description This form is for adding/editing a Job Template
*/
+
export default
angular.module('JobTemplateFormDefinition', [ 'CompletedJobsDefinition'])
- .value ('JobTemplateFormObject', {
+ .factory('JobTemplateFormObject', ['i18n', function(i18n) {
+ return {
- addTitle: 'New Job Template',
+ addTitle: i18n._('New Job Template'),
editTitle: '{{ name }}',
name: 'job_templates',
base: 'job_templates',
@@ -23,7 +25,7 @@ export default
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -31,7 +33,7 @@ export default
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
@@ -39,7 +41,7 @@ export default
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
job_type: {
- label: 'Job Type',
+ label: i18n._('Job Type'),
type: 'select',
ngOptions: 'type.label for type in job_type_options track by type.value',
ngChange: 'jobTypeChange()',
@@ -47,22 +49,22 @@ export default
addRequired: true,
editRequired: true,
column: 1,
- awPopOver: "When this template is submitted as a job, setting the type to run will execute the playbook, running tasks " +
+ awPopOver: i18n._("
When this template is submitted as a job, setting the type to run will execute the playbook, running tasks " +
" on the selected hosts.
Setting the type to check will not execute the playbook. Instead, ansible will check playbook " +
" syntax, test environment setup and report problems.
Setting the type to scan will execute the playbook and store any " +
- " scanned facts for use with Tower's System Tracking feature.
",
- dataTitle: 'Job Type',
+ " scanned facts for use with Tower's System Tracking feature."),
+ dataTitle: i18n._('Job Type'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_job_type_on_launch',
ngShow: "!job_type.value || job_type.value !== 'scan'",
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
inventory: {
- label: 'Inventory',
+ label: i18n._('Inventory'),
type: 'lookup',
sourceModel: 'inventory',
sourceField: 'name',
@@ -73,19 +75,19 @@ export default
},
requiredErrorMsg: "Please select an Inventory or check the Prompt on launch option.",
column: 1,
- awPopOver: "Select the inventory containing the hosts you want this job to manage.
",
- dataTitle: 'Inventory',
+ awPopOver: i18n._("Select the inventory containing the hosts you want this job to manage.
"),
+ dataTitle: i18n._('Inventory'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_inventory_on_launch',
ngShow: "!job_type.value || job_type.value !== 'scan'",
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
project: {
- label: 'Project',
+ label: i18n._('Project'),
labelAction: {
label: 'RESET',
ngClick: 'resetProjectToDefault()',
@@ -100,14 +102,14 @@ export default
init: "true"
},
column: 1,
- awPopOver: "Select the project containing the playbook you want this job to execute.
",
- dataTitle: 'Project',
+ awPopOver: i18n._("Select the project containing the playbook you want this job to execute.
"),
+ dataTitle: i18n._('Project'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
playbook: {
- label: 'Playbook',
+ label: i18n._('Playbook'),
type:'select',
ngOptions: 'book for book in playbook_options track by book',
ngDisabled: "(job_type.value === 'scan' && project_name === 'Default') || !(job_template_obj.summary_fields.user_capabilities.edit || canAdd)",
@@ -117,14 +119,14 @@ export default
init: "true"
},
column: 1,
- awPopOver: "Select the playbook to be executed by this job.
",
- dataTitle: 'Playbook',
+ awPopOver: i18n._("Select the playbook to be executed by this job.
"),
+ dataTitle: i18n._('Playbook'),
dataPlacement: 'right',
dataContainer: "body",
includePlaybookNotFoundError: true
},
credential: {
- label: 'Machine Credential',
+ label: i18n._('Machine Credential'),
type: 'lookup',
sourceModel: 'credential',
sourceField: 'name',
@@ -135,19 +137,19 @@ export default
},
requiredErrorMsg: "Please select a Machine Credential or check the Prompt on launch option.",
column: 1,
- awPopOver: "Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
- " the username and SSH key or password that Ansible will need to log into the remote hosts.
",
- dataTitle: 'Credential',
+ awPopOver: i18n._("Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
+ " the username and SSH key or password that Ansible will need to log into the remote hosts.
"),
+ dataTitle: i18n._('Credential'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_credential_on_launch',
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
cloud_credential: {
- label: 'Cloud Credential',
+ label: i18n._('Cloud Credential'),
type: 'lookup',
sourceModel: 'cloud_credential',
sourceField: 'name',
@@ -155,15 +157,15 @@ export default
addRequired: false,
editRequired: false,
column: 1,
- awPopOver: "Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
- "running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.
",
- dataTitle: 'Cloud Credential',
+ awPopOver: i18n._("Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
+ "running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.
"),
+ dataTitle: i18n._('Cloud Credential'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
network_credential: {
- label: 'Network Credential',
+ label: i18n._('Network Credential'),
type: 'lookup',
sourceModel: 'network_credential',
sourceField: 'name',
@@ -171,14 +173,14 @@ export default
addRequired: false,
editRequired: false,
column: 1,
- awPopOver: "Network credentials are used by Ansible networking modules to connect to and manage networking devices.
",
- dataTitle: 'Network Credential',
+ awPopOver: i18n._("Network credentials are used by Ansible networking modules to connect to and manage networking devices.
"),
+ dataTitle: i18n._('Network Credential'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
forks: {
- label: 'Forks',
+ label: i18n._('Forks'),
id: 'forks-number',
type: 'number',
integer: true,
@@ -189,121 +191,121 @@ export default
editRequired: false,
'class': "input-small",
column: 1,
- awPopOver: 'The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
+ awPopOver: i18n._('
The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
'the default value from the ansible configuration file.
',
- dataTitle: 'Forks',
+ ' target=\"_blank\">ansible configuration file.'),
+ dataTitle: i18n._('Forks'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
},
limit: {
- label: 'Limit',
+ label: i18n._('Limit'),
type: 'text',
addRequired: false,
editRequired: false,
column: 1,
- awPopOver: "Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
+ awPopOver: i18n._("
Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
"Multiple patterns can be separated by ; : or ,
For more information and examples see " +
- "the Patterns topic at docs.ansible.com.
",
- dataTitle: 'Limit',
+ "the Patterns topic at docs.ansible.com."),
+ dataTitle: i18n._('Limit'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_limit_on_launch',
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
verbosity: {
- label: 'Verbosity',
+ label: i18n._('Verbosity'),
type: 'select',
ngOptions: 'v.label for v in verbosity_options track by v.value',
"default": 1,
addRequired: true,
editRequired: true,
column: 1,
- awPopOver: "Control the level of output ansible will produce as the playbook executes.
",
- dataTitle: 'Verbosity',
+ awPopOver: i18n._("Control the level of output ansible will produce as the playbook executes.
"),
+ dataTitle: i18n._('Verbosity'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
job_tags: {
- label: 'Job Tags',
+ label: i18n._('Job Tags'),
type: 'textarea',
rows: 5,
addRequired: false,
editRequired: false,
'elementClass': 'Form-textInput',
column: 2,
- awPopOver: "Provide a comma separated list of tags.
\n" +
+ awPopOver: i18n._("Provide a comma separated list of tags.
\n" +
"Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.
" +
- "Consult the Ansible documentation for further details on the usage of tags.
",
- dataTitle: "Job Tags",
+ "Consult the Ansible documentation for further details on the usage of tags.
"),
+ dataTitle: i18n._("Job Tags"),
dataPlacement: "right",
dataContainer: "body",
subCheckbox: {
variable: 'ask_tags_on_launch',
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
skip_tags: {
- label: 'Skip Tags',
+ label: i18n._('Skip Tags'),
type: 'textarea',
rows: 5,
addRequired: false,
editRequired: false,
'elementClass': 'Form-textInput',
column: 2,
- awPopOver: "Provide a comma separated list of tags.
\n" +
+ awPopOver: i18n._("Provide a comma separated list of tags.
\n" +
"Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.
" +
- "Consult the Ansible documentation for further details on the usage of tags.
",
- dataTitle: "Skip Tags",
+ "Consult the Ansible documentation for further details on the usage of tags.
"),
+ dataTitle: i18n._("Skip Tags"),
dataPlacement: "right",
dataContainer: "body",
subCheckbox: {
variable: 'ask_skip_tags_on_launch',
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
checkbox_group: {
- label: 'Options',
+ label: i18n._('Options'),
type: 'checkbox_group',
fields: [{
name: 'become_enabled',
- label: 'Enable Privilege Escalation',
+ label: i18n._('Enable Privilege Escalation'),
type: 'checkbox',
addRequired: false,
editRequird: false,
column: 2,
- awPopOver: "If enabled, run this playbook as an administrator. This is the equivalent of passing the --become option to the ansible-playbook command.
",
+ awPopOver: i18n._("If enabled, run this playbook as an administrator. This is the equivalent of passing the --become option to the ansible-playbook command.
"),
dataPlacement: 'right',
- dataTitle: 'Become Privilege Escalation',
+ dataTitle: i18n._('Become Privilege Escalation'),
dataContainer: "body",
labelClass: 'stack-inline',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'allow_callbacks',
- label: 'Allow Provisioning Callbacks',
+ label: i18n._('Allow Provisioning Callbacks'),
type: 'checkbox',
addRequired: false,
editRequird: false,
ngChange: "toggleCallback('host_config_key')",
column: 2,
- awPopOver: "Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update " +
- "using this job template.
",
+ awPopOver: i18n._("Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update " +
+ "using this job template.
"),
dataPlacement: 'right',
- dataTitle: 'Allow Provisioning Callbacks',
+ dataTitle: i18n._('Allow Provisioning Callbacks'),
dataContainer: "body",
labelClass: 'stack-inline',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
}]
},
callback_url: {
- label: 'Provisioning Callback URL',
+ label: i18n._('Provisioning Callback URL'),
type: 'text',
addRequired: false,
editRequired: false,
@@ -313,12 +315,12 @@ export default
awPopOver: "callback_help",
awPopOverWatch: "callback_help",
dataPlacement: 'top',
- dataTitle: 'Provisioning Callback URL',
+ dataTitle: i18n._('Provisioning Callback URL'),
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
host_config_key: {
- label: 'Host Config Key',
+ label: i18n._('Host Config Key'),
type: 'text',
ngShow: "allow_callbacks && allow_callbacks !== 'false'",
ngChange: "configKeyChange()",
@@ -327,26 +329,26 @@ export default
awPopOver: "callback_help",
awPopOverWatch: "callback_help",
dataPlacement: 'right',
- dataTitle: "Host Config Key",
+ dataTitle: i18n._("Host Config Key"),
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
labels: {
- label: 'Labels',
+ label: i18n._('Labels'),
type: 'select',
class: 'Form-formGroup--fullWidth',
ngOptions: 'label.label for label in labelOptions track by label.value',
multiSelect: true,
addRequired: false,
editRequired: false,
- dataTitle: 'Labels',
+ dataTitle: i18n._('Labels'),
dataPlacement: 'right',
- awPopOver: "Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.
",
+ awPopOver: i18n._("Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.
"),
dataContainer: 'body',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
variables: {
- label: 'Extra Variables',
+ label: i18n._('Extra Variables'),
type: 'textarea',
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
rows: 6,
@@ -354,18 +356,18 @@ export default
editRequired: false,
"default": "---",
column: 2,
- awPopOver: "Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter " +
+ awPopOver: i18n._("
Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter " +
"for ansible-playbook. Provide key/value pairs using either YAML or JSON.
" +
"JSON:
\n" +
"{
\"somevar\": \"somevalue\",
\"password\": \"magic\"
}
\n" +
"YAML:
\n" +
- "---
somevar: somevalue
password: magic
\n",
- dataTitle: 'Extra Variables',
+ "---
somevar: somevalue
password: magic
\n"),
+ dataTitle: i18n._('Extra Variables'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_variables_on_launch',
- text: 'Prompt on launch'
+ text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
}
@@ -409,11 +411,11 @@ export default
include: "CompletedJobsList"
},
permissions: {
- awToolTip: 'Please save before assigning permissions',
+ awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
basePath: 'job_templates/:id/access_list/',
type: 'collection',
- title: 'Permissions',
+ title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@@ -473,7 +475,7 @@ export default
}
};
}
- })
+ };}])
.factory('JobTemplateForm', ['JobTemplateFormObject', 'NotificationsList', 'CompletedJobsList',
function(JobTemplateFormObject, NotificationsList, CompletedJobsList) {
diff --git a/awx/ui/client/src/forms/Organizations.js b/awx/ui/client/src/forms/Organizations.js
index 377d5a911e..d5607f26d9 100644
--- a/awx/ui/client/src/forms/Organizations.js
+++ b/awx/ui/client/src/forms/Organizations.js
@@ -12,16 +12,17 @@
export default
angular.module('OrganizationFormDefinition', [])
- .value('OrganizationFormObject', {
+ .factory('OrganizationFormObject', ['i18n', function(i18n) {
+ return {
- addTitle: 'New Organization', //Title in add mode
+ addTitle: i18n._('New Organization'), //Title in add mode
editTitle: '{{ name }}', //Title in edit mode
name: 'organization', //entity or model name in singular form
tabs: true,
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -29,7 +30,7 @@ export default
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
@@ -56,10 +57,10 @@ export default
related: {
permissions: {
basePath: 'organizations/:id/access_list/',
- awToolTip: 'Please save before assigning permissions',
+ awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
type: 'collection',
- title: 'Permissions',
+ title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@@ -68,9 +69,9 @@ export default
add: {
ngClick: "addPermission",
label: 'Add',
- awToolTip: 'Add a permission',
+ awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@@ -78,19 +79,19 @@ export default
fields: {
username: {
key: true,
- label: 'User',
+ label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
- label: 'Role',
+ label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
searchable: false
},
team_roles: {
- label: 'Team Roles',
+ label: i18n._('Team Roles'),
type: 'team_roles',
noSort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
@@ -116,7 +117,7 @@ export default
}
};
}
- })
+ };}])
.factory('OrganizationForm', ['OrganizationFormObject', 'NotificationsList',
function(OrganizationFormObject, NotificationsList) {
diff --git a/awx/ui/client/src/forms/Projects.js b/awx/ui/client/src/forms/Projects.js
index 409386b348..309b55a84f 100644
--- a/awx/ui/client/src/forms/Projects.js
+++ b/awx/ui/client/src/forms/Projects.js
@@ -12,21 +12,22 @@
export default
angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
- .value('ProjectsFormObject', {
+ .factory('ProjectsFormObject', ['i18n', function(i18n) {
+ return {
- addTitle: 'New Project',
+ addTitle: i18n._('New Project'),
editTitle: '{{ name }}',
name: 'project',
forceListeners: true,
tabs: true,
subFormTitles: {
- sourceSubForm: 'Source Details',
+ sourceSubForm: i18n._('Source Details'),
},
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -34,14 +35,14 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@@ -50,13 +51,13 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
reqExpression: "organizationrequired",
init: "true"
},
- dataTitle: 'Organization',
+ dataTitle: i18n._('Organization'),
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
scm_type: {
- label: 'SCM Type',
+ label: i18n._('SCM Type'),
type: 'select',
class: 'Form-dropDown--scmType',
ngOptions: 'type.label for type in scm_type_options track by type.value',
@@ -76,21 +77,21 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
closeable: false
},
base_dir: {
- label: 'Project Base Path',
+ label: i18n._('Project Base Path'),
type: 'text',
class: 'Form-textUneditable',
showonly: true,
ngShow: "scm_type.value == 'manual' " ,
- awPopOver: 'Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
+ awPopOver: i18n._('
Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
'Together the base path and selected playbook directory provide the full path used to locate playbooks.
' +
- 'Use PROJECTS_ROOT in your environment settings file to determine the base path value.
',
- dataTitle: 'Project Base Path',
+ 'Use PROJECTS_ROOT in your environment settings file to determine the base path value.
'),
+ dataTitle: i18n._('Project Base Path'),
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
local_path: {
- label: 'Playbook Directory',
+ label: i18n._('Playbook Directory'),
type: 'select',
id: 'local-path-select',
ngOptions: 'path.label for path in project_local_paths',
@@ -99,10 +100,10 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
init: false
},
ngShow: "scm_type.value == 'manual' && !showMissingPlaybooksAlert",
- awPopOver: 'Select from the list of directories found in the base path.' +
+ awPopOver: i18n._('
Select from the list of directories found in the base path.' +
'Together the base path and the playbook directory provide the full path used to locate playbooks.
' +
- 'Use PROJECTS_ROOT in your environment settings file to determine the base path value.
',
- dataTitle: 'Project Path',
+ 'Use PROJECTS_ROOT in your environment settings file to determine the base path value.
'),
+ dataTitle: i18n._('Project Path'),
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
@@ -134,7 +135,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
credential: {
- label: 'SCM Credential',
+ label: i18n._('SCM Credential'),
type: 'lookup',
ngShow: "scm_type && scm_type.value !== 'manual'",
sourceModel: 'credential',
@@ -146,43 +147,43 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
checkbox_group: {
- label: 'SCM Update Options',
+ label: i18n._('SCM Update Options'),
type: 'checkbox_group',
ngShow: "scm_type && scm_type.value !== 'manual'",
subForm: 'sourceSubForm',
fields: [{
name: 'scm_clean',
- label: 'Clean',
+ label: i18n._('Clean'),
type: 'checkbox',
addRequired: false,
editRequired: false,
- awPopOver: 'Remove any local modifications prior to performing an update.
',
- dataTitle: 'SCM Clean',
+ awPopOver: i18n._('Remove any local modifications prior to performing an update.
'),
+ dataTitle: i18n._('SCM Clean'),
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options stack-inline',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'scm_delete_on_update',
- label: 'Delete on Update',
+ label: i18n._('Delete on Update'),
type: 'checkbox',
addRequired: false,
editRequired: false,
- awPopOver: 'Delete the local repository in its entirety prior to performing an update.
Depending on the size of the ' +
- 'repository this may significantly increase the amount of time required to complete an update.
',
- dataTitle: 'SCM Delete',
+ awPopOver: i18n._('Delete the local repository in its entirety prior to performing an update.
Depending on the size of the ' +
+ 'repository this may significantly increase the amount of time required to complete an update.
'),
+ dataTitle: i18n._('SCM Delete'),
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options stack-inline',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'scm_update_on_launch',
- label: 'Update on Launch',
+ label: i18n._('Update on Launch'),
type: 'checkbox',
addRequired: false,
editRequired: false,
- awPopOver: 'Each time a job runs using this project, perform an update to the local repository prior to starting the job.
',
- dataTitle: 'SCM Update',
+ awPopOver: i18n._('Each time a job runs using this project, perform an update to the local repository prior to starting the job.
'),
+ dataTitle: i18n._('SCM Update'),
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options stack-inline',
@@ -190,7 +191,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
}]
},
scm_update_cache_timeout: {
- label: 'Cache Timeout (seconds)',
+ label: i18n._('Cache Timeout (seconds)'),
id: 'scm-cache-timeout',
type: 'number',
integer: true,
@@ -200,10 +201,10 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
"default": '0',
addRequired: false,
editRequired: false,
- awPopOver: 'Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
+ awPopOver: i18n._('
Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
- 'and a new project update will be performed.
',
- dataTitle: 'Cache Timeout',
+ 'and a new project update will be performed.'),
+ dataTitle: i18n._('Cache Timeout'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
@@ -228,11 +229,11 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
related: {
permissions: {
- awToolTip: 'Please save before assigning permissions',
+ awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
basePath: 'projects/:id/access_list/',
type: 'collection',
- title: 'Permissions',
+ title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@@ -241,9 +242,9 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
add: {
ngClick: "addPermission",
label: 'Add',
- awToolTip: 'Add a permission',
+ awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@@ -289,9 +290,10 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
};
}
- })
+ };}])
- .factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList', function(ProjectsFormObject, NotificationsList) {
+ .factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList',
+ function(ProjectsFormObject, NotificationsList) {
return function() {
var itm;
for (itm in ProjectsFormObject.related) {
diff --git a/awx/ui/client/src/forms/Teams.js b/awx/ui/client/src/forms/Teams.js
index 6023650156..0d4991e59c 100644
--- a/awx/ui/client/src/forms/Teams.js
+++ b/awx/ui/client/src/forms/Teams.js
@@ -12,16 +12,17 @@
export default
angular.module('TeamFormDefinition', [])
- .value('TeamForm', {
+ .factory('TeamForm', ['i18n', function(i18n) {
+ return {
- addTitle: 'New Team', //Legend in add mode
+ addTitle: i18n._('New Team'), //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'team',
tabs: true,
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -29,14 +30,14 @@ export default
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@@ -70,10 +71,10 @@ export default
related: {
access_list: {
dataPlacement: 'top',
- awToolTip: 'Please save before adding users',
+ awToolTip: i18n._('Please save before adding users'),
basePath: 'teams/:id/access_list/',
type: 'collection',
- title: 'Users',
+ title: i18n._('Users'),
iterator: 'permission',
index: false,
open: false,
@@ -82,9 +83,9 @@ export default
add: {
ngClick: "addPermissionWithoutTeamTab",
label: 'Add',
- awToolTip: 'Add user to team',
+ awToolTip: i18n._('Add user to team'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: '(team_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@@ -92,12 +93,12 @@ export default
fields: {
username: {
key: true,
- label: 'User',
+ label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
- label: 'Role',
+ label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
@@ -108,40 +109,40 @@ export default
roles: {
hideSearchAndActions: true,
dataPlacement: 'top',
- awToolTip: 'Please save before assigning permissions',
+ awToolTip: i18n._('Please save before assigning permissions'),
basePath: 'teams/:id/roles/',
type: 'collection',
- title: 'Granted Permissions',
+ title: i18n._('Granted Permissions'),
iterator: 'role',
open: false,
index: false,
actions: {},
- emptyListText: 'No permissions have been granted',
+ emptyListText: i18n._('No permissions have been granted'),
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
ngBind: 'role.summary_fields.resource_name',
linkTo: '{{convertApiUrl(role.related[role.summary_fields.resource_type])}}',
noSort: true
},
type: {
- label: 'Type',
+ label: i18n._('Type'),
ngBind: 'role.summary_fields.resource_type_display_name',
noSort: true
},
role: {
- label: 'Role',
+ label: i18n._('Role'),
ngBind: 'role.name',
noSort: true
}
},
fieldActions: {
"delete": {
- label: 'Remove',
+ label: i18n._('Remove'),
ngClick: 'deletePermissionFromTeam(team_id, team_obj.name, role.name, role.summary_fields.resource_name, role.related.teams)',
'class': "List-actionButton--delete",
iconClass: 'fa fa-times',
- awToolTip: 'Dissasociate permission from team',
+ awToolTip: i18n._('Dissasociate permission from team'),
dataPlacement: 'top',
ngShow: 'permission.summary_fields.user_capabilities.unattach'
}
@@ -149,4 +150,4 @@ export default
hideOnSuperuser: true
}
},
- }); //InventoryForm
+ };}]); //InventoryForm
diff --git a/awx/ui/client/src/forms/Users.js b/awx/ui/client/src/forms/Users.js
index a93e549cae..73d5307471 100644
--- a/awx/ui/client/src/forms/Users.js
+++ b/awx/ui/client/src/forms/Users.js
@@ -12,9 +12,10 @@
export default
angular.module('UserFormDefinition', [])
- .value('UserForm', {
+ .factory('UserForm', ['i18n', function(i18n) {
+ return {
- addTitle: 'New User',
+ addTitle: i18n._('New User'),
editTitle: '{{ username }}',
name: 'user',
forceListeners: true,
@@ -22,7 +23,7 @@ export default
fields: {
first_name: {
- label: 'First Name',
+ label: i18n._('First Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -30,7 +31,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
last_name: {
- label: 'Last Name',
+ label: i18n._('Last Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -38,7 +39,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
email: {
- label: 'Email',
+ label: i18n._('Email'),
type: 'email',
addRequired: true,
editRequired: true,
@@ -46,7 +47,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
username: {
- label: 'Username',
+ label: i18n._('Username'),
type: 'text',
awRequiredWhen: {
reqExpression: "not_ldap_user && external_account === null",
@@ -56,7 +57,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@@ -71,7 +72,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
password: {
- label: 'Password',
+ label: i18n._('Password'),
type: 'sensitive',
hasShowInputButton: true,
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
@@ -83,7 +84,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
password_confirm: {
- label: 'Confirm Password',
+ label: i18n._('Confirm Password'),
type: 'sensitive',
hasShowInputButton: true,
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
@@ -95,7 +96,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
user_type: {
- label: 'User Type',
+ label: i18n._('User Type'),
type: 'select',
ngOptions: 'item as item.label for item in user_type_options track by item.type',
disableChooseOption: true,
@@ -124,10 +125,10 @@ export default
related: {
organizations: {
basePath: 'users/:id/organizations',
- awToolTip: 'Please save before assigning to organizations',
+ awToolTip: i18n._('Please save before assigning to organizations'),
dataPlacement: 'top',
type: 'collection',
- title: 'Organizations',
+ title: i18n._('Organizations'),
iterator: 'organization',
index: false,
open: false,
@@ -147,10 +148,10 @@ export default
},
teams: {
basePath: 'users/:id/teams',
- awToolTip: 'Please save before assigning to teams',
+ awToolTip: i18n._('Please save before assigning to teams'),
dataPlacement: 'top',
type: 'collection',
- title: 'Teams',
+ title: i18n._('Teams'),
iterator: 'team',
open: false,
index: false,
@@ -168,39 +169,39 @@ export default
hideOnSuperuser: true
},
roles: {
- awToolTip: 'Please save before assigning to organizations',
+ awToolTip: i18n._('Please save before assigning to organizations'),
dataPlacement: 'top',
hideSearchAndActions: true,
type: 'collection',
- title: 'Granted permissions',
+ title: i18n._('Granted permissions'),
iterator: 'permission',
open: false,
index: false,
- emptyListText: 'No permissions have been granted',
+ emptyListText: i18n._('No permissions have been granted'),
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
ngBind: 'permission.summary_fields.resource_name',
linkTo: '{{convertApiUrl(permission.related[permission.summary_fields.resource_type])}}',
noSort: true
},
type: {
- label: 'Type',
+ label: i18n._('Type'),
ngBind: 'permission.summary_fields.resource_type_display_name',
noSort: true
},
role: {
- label: 'Role',
+ label: i18n._('Role'),
ngBind: 'permission.name',
noSort: true
},
},
fieldActions: {
"delete": {
- label: 'Remove',
+ label: i18n._('Remove'),
ngClick: 'deletePermissionFromUser(user_id, username, permission.name, permission.summary_fields.resource_name, permission.related.users)',
iconClass: 'fa fa-times',
- awToolTip: 'Dissasociate permission from user',
+ awToolTip: i18n._('Dissasociate permission from user'),
ngShow: 'permission.summary_fields.user_capabilities.unattach'
}
},
@@ -208,4 +209,4 @@ export default
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/helpers/Credentials.js b/awx/ui/client/src/helpers/Credentials.js
index 0a81b3eee9..bf239a6595 100644
--- a/awx/ui/client/src/helpers/Credentials.js
+++ b/awx/ui/client/src/helpers/Credentials.js
@@ -13,8 +13,8 @@
export default
angular.module('CredentialsHelper', ['Utilities'])
-.factory('KindChange', ['Empty',
- function (Empty) {
+.factory('KindChange', ['Empty', 'i18n',
+ function (Empty, i18n) {
return function (params) {
var scope = params.scope,
reset = params.reset,
@@ -29,20 +29,20 @@ angular.module('CredentialsHelper', ['Utilities'])
$(this).hide();
});
// Put things in a default state
- scope.usernameLabel = 'Username';
+ scope.usernameLabel = i18n._('Username');
scope.aws_required = false;
scope.email_required = false;
scope.rackspace_required = false;
- scope.sshKeyDataLabel = 'Private Key';
+ scope.sshKeyDataLabel = i18n._('Private Key');
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
scope.key_required = false; // JT -- doing the same for key and project
scope.project_required = false;
scope.subscription_required = false;
- scope.key_description = "Paste the contents of the SSH private key file.esc or click to close";
+ scope.key_description = i18n._("Paste the contents of the SSH private key file.esc or click to close");
scope.host_required = false;
scope.password_required = false;
scope.hostLabel = '';
- scope.passwordLabel = 'Password';
+ scope.passwordLabel = i18n._('Password');
$('.popover').each(function() {
// remove lingering popover . Seems to be a bug in TB3 RC1
@@ -53,26 +53,26 @@ angular.module('CredentialsHelper', ['Utilities'])
$(this).hide();
});
// Put things in a default state
- scope.usernameLabel = 'Username';
+ scope.usernameLabel = i18n._('Username');
scope.aws_required = false;
scope.email_required = false;
scope.rackspace_required = false;
- scope.sshKeyDataLabel = 'Private Key';
+ scope.sshKeyDataLabel = i18n._('Private Key');
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
scope.key_required = false; // JT -- doing the same for key and project
scope.project_required = false;
scope.domain_required = false;
scope.subscription_required = false;
- scope.key_description = "Paste the contents of the SSH private key file.";
+ scope.key_description = i18n._("Paste the contents of the SSH private key file.");
scope.host_required = false;
scope.password_required = false;
scope.hostLabel = '';
scope.projectLabel = '';
scope.domainLabel = '';
scope.project_required = false;
- scope.passwordLabel = 'Password (API Key)';
- scope.projectPopOver = "The project value
";
- scope.hostPopOver = "The host value
";
+ scope.passwordLabel = i18n._('Password (API Key)');
+ scope.projectPopOver = i18n._("The project value
");
+ scope.hostPopOver = i18n._("The host value
");
scope.ssh_key_data_api_error = '';
if (!Empty(scope.kind)) {
// Apply kind specific settings
@@ -85,38 +85,38 @@ angular.module('CredentialsHelper', ['Utilities'])
scope.username_required = true;
break;
case 'ssh':
- scope.usernameLabel = 'Username'; //formally 'SSH Username'
- scope.becomeUsernameLabel = 'Privilege Escalation Username';
- scope.becomePasswordLabel = 'Privilege Escalation Password';
+ scope.usernameLabel = i18n._('Username'); //formally 'SSH Username'
+ scope.becomeUsernameLabel = i18n._('Privilege Escalation Username');
+ scope.becomePasswordLabel = i18n._('Privilege Escalation Password');
break;
case 'scm':
- scope.sshKeyDataLabel = 'SCM Private Key';
- scope.passwordLabel = 'Password';
+ scope.sshKeyDataLabel = i18n._('SCM Private Key');
+ scope.passwordLabel = i18n._('Password');
break;
case 'gce':
- scope.usernameLabel = 'Service Account Email Address';
- scope.sshKeyDataLabel = 'RSA Private Key';
+ scope.usernameLabel = i18n._('Service Account Email Address');
+ scope.sshKeyDataLabel = i18n._('RSA Private Key');
scope.email_required = true;
scope.key_required = true;
scope.project_required = true;
- scope.key_description = 'Paste the contents of the PEM file associated with the service account email.';
- scope.projectLabel = "Project";
+ scope.key_description = i18n._('Paste the contents of the PEM file associated with the service account email.');
+ scope.projectLabel = i18n._("Project");
scope.project_required = false;
- scope.projectPopOver = "The Project ID is the " +
+ scope.projectPopOver = i18n._("
The Project ID is the " +
"GCE assigned identification. It is constructed as " +
"two words followed by a three digit number. Such " +
- "as:
adjective-noun-000
";
+ "as: adjective-noun-000
");
break;
case 'azure':
- scope.sshKeyDataLabel = 'Management Certificate';
+ scope.sshKeyDataLabel = i18n._('Management Certificate');
scope.subscription_required = true;
scope.key_required = true;
- scope.key_description = "Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.";
+ scope.key_description = i18n._("Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.");
break;
case 'azure_rm':
- scope.usernameLabel = "Username";
+ scope.usernameLabel = i18n._("Username");
scope.subscription_required = true;
- scope.passwordLabel = 'Password';
+ scope.passwordLabel = i18n._('Password');
scope.azure_rm_required = true;
break;
case 'vmware':
@@ -124,46 +124,46 @@ angular.module('CredentialsHelper', ['Utilities'])
scope.host_required = true;
scope.password_required = true;
scope.hostLabel = "vCenter Host";
- scope.passwordLabel = 'Password';
- scope.hostPopOver = "Enter the hostname or IP address which corresponds to your VMware vCenter.";
+ scope.passwordLabel = i18n._('Password');
+ scope.hostPopOver = i18n._("Enter the hostname or IP address which corresponds to your VMware vCenter.");
break;
case 'openstack':
- scope.hostLabel = "Host (Authentication URL)";
- scope.projectLabel = "Project (Tenant Name)";
- scope.domainLabel = "Domain Name";
+ scope.hostLabel = i18n._("Host (Authentication URL)");
+ scope.projectLabel = i18n._("Project (Tenant Name)");
+ scope.domainLabel = i18n._("Domain Name");
scope.password_required = true;
scope.project_required = true;
scope.host_required = true;
scope.username_required = true;
- scope.projectPopOver = "This is the tenant name. " +
+ scope.projectPopOver = i18n._("
This is the tenant name. " +
" This value is usually the same " +
- " as the username.
";
- scope.hostPopOver = "The host to authenticate with." +
- "
For example, https://openstack.business.com/v2.0/";
+ " as the username.
");
+ scope.hostPopOver = i18n._("The host to authenticate with." +
+ "
For example, https://openstack.business.com/v2.0/");
break;
case 'satellite6':
scope.username_required = true;
scope.password_required = true;
- scope.passwordLabel = 'Password';
+ scope.passwordLabel = i18n._('Password');
scope.host_required = true;
- scope.hostLabel = "Satellite 6 Host";
- scope.hostPopOver = "Enter the hostname or IP address name which
" +
- "corresponds to your Red Hat Satellite 6 server.";
+ scope.hostLabel = i18n._("Satellite 6 Host");
+ scope.hostPopOver = i18n._("Enter the hostname or IP address name which
" +
+ "corresponds to your Red Hat Satellite 6 server.");
break;
case 'cloudforms':
scope.username_required = true;
scope.password_required = true;
- scope.passwordLabel = 'Password';
+ scope.passwordLabel = i18n._('Password');
scope.host_required = true;
- scope.hostLabel = "CloudForms Host";
- scope.hostPopOver = "Enter the hostname or IP address for the virtual
" +
- " machine which is hosting the CloudForm appliance.";
+ scope.hostLabel = i18n._("CloudForms Host");
+ scope.hostPopOver = i18n._("Enter the hostname or IP address for the virtual
" +
+ " machine which is hosting the CloudForm appliance.");
break;
case 'net':
scope.username_required = true;
scope.password_required = false;
- scope.passwordLabel = 'Password';
- scope.sshKeyDataLabel = 'SSH Key';
+ scope.passwordLabel = i18n._('Password');
+ scope.sshKeyDataLabel = i18n._('SSH Key');
break;
}
}
diff --git a/awx/ui/client/src/i18n.js b/awx/ui/client/src/i18n.js
new file mode 100644
index 0000000000..c5e5b782de
--- /dev/null
+++ b/awx/ui/client/src/i18n.js
@@ -0,0 +1,100 @@
+function isString(arg) {
+ return typeof arg === 'string';
+}
+
+function isNull(arg) {
+ return arg === null;
+}
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+
+/**
+ * @ngdoc method
+ * @name function:i18n#N_
+ * @methodOf function:N_
+ * @description this function marks the translatable string with N_
+ * for 'grunt nggettext_extract'
+ *
+*/
+export function N_(s) {
+ return s;
+}
+
+// Copied format() from util/util.js. util.js includes "require()".
+/**
+ * @ngdoc method
+ * @name function:i18n#format
+ * @methodOf function:format
+ * @description this function provides C-style's formatted sprintf().
+ *
+*/
+export function format(f) {
+ var i;
+ var formatRegExp = /%[sdj%]/g;
+ if (!isString(f)) {
+ var objects = [];
+ for (i = 0; i < arguments.length; i++) {
+ objects.push(JSON.stringify(arguments[i]));
+ }
+ return objects.join(' ');
+ }
+
+ i = 1;
+ var args = arguments;
+ var len = args.length;
+ var str = String(f).replace(formatRegExp, function(x) {
+ if (x === '%%') return '%';
+ if (i >= len) return x;
+ switch (x) {
+ case '%s': return String(args[i++]);
+ case '%d': return Number(args[i++]);
+ case '%j':
+ case '%j':
+ try {
+ return JSON.stringify(args[i++]);
+ } catch (_) {
+ return '[Circular]';
+ }
+ default:
+ return x;
+ }
+ });
+ for (var x = args[i]; i < len; x = args[++i]) {
+ if (isNull(x) || !isObject(x)) {
+ str += ' ' + x;
+ } else {
+ str += ' ' + JSON.stringify(x);
+ }
+ }
+ return str;
+}
+
+export default
+ angular.module('I18N', [])
+ .factory('I18NInit', ['$window', 'gettextCatalog',
+ function ($window, gettextCatalog) {
+ return function() {
+ var langInfo = $window.navigator.language ||
+ $window.navigator.userLanguage;
+ var langUrl = langInfo.replace('-', '_');
+ //gettextCatalog.debug = true;
+ gettextCatalog.setCurrentLanguage(langInfo);
+ // TODO: the line below is commented out temporarily until
+ // the .po files are received from the i18n team, in order to avoid
+ // 404 file not found console errors in dev
+ // gettextCatalog.loadRemote('/static/languages/' + langUrl + '.json');
+ };
+ }])
+ .factory('i18n', ['gettextCatalog',
+ function (gettextCatalog) {
+ return {
+ _: function (s) { return gettextCatalog.getString (s); },
+ N_: N_,
+ format: format,
+ hasTranslation: function () {
+ return gettextCatalog.strings[gettextCatalog.currentLanguage] !== undefined;
+ }
+ };
+ }]);
diff --git a/awx/ui/client/src/inventory-scripts/inventory-scripts.form.js b/awx/ui/client/src/inventory-scripts/inventory-scripts.form.js
index 95ebed3458..db82043c41 100644
--- a/awx/ui/client/src/inventory-scripts/inventory-scripts.form.js
+++ b/awx/ui/client/src/inventory-scripts/inventory-scripts.form.js
@@ -10,17 +10,17 @@
* @description This form is for adding/editing an organization
*/
-export default function() {
+export default ['i18n', function(i18n) {
return {
- addTitle: 'New Custom Inventory',
+ addTitle: i18n._('New Custom Inventory'),
editTitle: '{{ name }}',
name: 'custom_inventory',
showActions: true,
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -28,14 +28,14 @@ export default function() {
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
awRequiredWhen: {
reqExpression: "orgrequired",
@@ -47,7 +47,7 @@ export default function() {
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)'
},
script: {
- label: 'Custom Script',
+ label: i18n._('Custom Script'),
type: 'textarea',
class: 'Form-formGroup--fullWidth',
elementClass: 'Form-monospace',
@@ -56,9 +56,9 @@ export default function() {
awDropFile: true,
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)',
rows: 10,
- awPopOver: "
Drag and drop your custom inventory script file here or create one in the field to import your custom inventory. " +
- "
Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python
",
- dataTitle: 'Custom Script',
+ awPopOver: i18n._("Drag and drop your custom inventory script file here or create one in the field to import your custom inventory. " +
+ "
Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python
"),
+ dataTitle: i18n._('Custom Script'),
dataPlacement: 'right',
dataContainer: "body"
},
@@ -80,4 +80,4 @@ export default function() {
}
}
};
-}
+}];
diff --git a/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js b/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js
index ca48ecdecc..00ac179748 100644
--- a/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js
+++ b/awx/ui/client/src/inventory-scripts/inventory-scripts.list.js
@@ -6,10 +6,10 @@
-export default function(){
+export default ['i18n', function(i18n){
return {
name: 'inventory_scripts' ,
- listTitle: 'Inventory Scripts',
+ listTitle: i18n._('Inventory Scripts'),
iterator: 'inventory_script',
index: false,
hover: false,
@@ -17,17 +17,17 @@ export default function(){
fields: {
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-8'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
excludeModal: true,
columnClass: 'col-md-4 hidden-sm hidden-xs'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
ngBind: 'inventory_script.summary_fields.organization.name',
sourceModel: 'organization',
sourceField: 'name',
@@ -40,9 +40,9 @@ export default function(){
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addCustomInv()',
- awToolTip: 'Create a new custom inventory',
+ awToolTip: i18n._('Create a new custom inventory'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: 'canAdd'
}
},
@@ -54,29 +54,29 @@ export default function(){
edit: {
ngClick: "editCustomInv(inventory_script.id)",
icon: 'fa-edit',
- label: 'Edit',
+ label: i18n._('Edit'),
"class": 'btn-sm',
- awToolTip: 'Edit inventory script',
+ awToolTip: i18n._('Edit inventory script'),
dataPlacement: 'top',
ngShow: 'inventory_script.summary_fields.user_capabilities.edit'
},
view: {
ngClick: "editCustomInv(inventory_script.id)",
- label: 'View',
+ label: i18n._('View'),
"class": 'btn-sm',
- awToolTip: 'View inventory script',
+ awToolTip: i18n._('View inventory script'),
dataPlacement: 'top',
ngShow: '!inventory_script.summary_fields.user_capabilities.edit'
},
"delete": {
ngClick: "deleteCustomInv(inventory_script.id, inventory_script.name)",
icon: 'fa-trash',
- label: 'Delete',
+ label: i18n._('Delete'),
"class": 'btn-sm',
- awToolTip: 'Delete inventory script',
+ awToolTip: i18n._('Delete inventory script'),
dataPlacement: 'top',
ngShow: 'inventory_script.summary_fields.user_capabilities.delete'
}
}
};
-}
+}];
diff --git a/awx/ui/client/src/job-templates/add/job-templates-add.route.js b/awx/ui/client/src/job-templates/add/job-templates-add.route.js
index b4ede48910..44bc3fd2f7 100644
--- a/awx/ui/client/src/job-templates/add/job-templates-add.route.js
+++ b/awx/ui/client/src/job-templates/add/job-templates-add.route.js
@@ -5,6 +5,7 @@
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
+import {N_} from "../../i18n";
export default {
name: 'jobTemplates.add',
@@ -13,7 +14,7 @@ export default {
controller: 'JobTemplatesAdd',
ncyBreadcrumb: {
parent: "jobTemplates",
- label: "CREATE JOB TEMPLATE"
+ label: N_("CREATE JOB TEMPLATE")
},
socket:{
"groups":{
diff --git a/awx/ui/client/src/license/license.controller.js b/awx/ui/client/src/license/license.controller.js
index aac6b9446d..8b0cae9269 100644
--- a/awx/ui/client/src/license/license.controller.js
+++ b/awx/ui/client/src/license/license.controller.js
@@ -4,13 +4,15 @@
* All Rights Reserved
*************************************************/
+import {N_} from "../i18n";
+
export default
['Wait', '$state', '$scope', '$rootScope', '$location', 'GetBasePath',
'Rest', 'ProcessErrors', 'CheckLicense', 'moment','$window',
- 'ConfigService', 'FeaturesService', 'pendoService',
+ 'ConfigService', 'FeaturesService', 'pendoService', 'i18n',
function( Wait, $state, $scope, $rootScope, $location, GetBasePath, Rest,
ProcessErrors, CheckLicense, moment, $window, ConfigService,
- FeaturesService, pendoService){
+ FeaturesService, pendoService, i18n){
$scope.getKey = function(event){
// Mimic HTML5 spec, show filename
$scope.fileName = event.target.files[0].name;
@@ -90,8 +92,9 @@ export default
};
var init = function(){
- $scope.fileName = "No file selected.";
- $scope.title = $rootScope.licenseMissing ? "Tower License" : "License Management";
+ // license/license.partial.html compares fileName
+ $scope.fileName = N_("No file selected.");
+ $scope.title = $rootScope.licenseMissing ? ("Tower " + i18n._("License")) : i18n._("License Management");
Wait('start');
ConfigService.getConfig().then(function(config){
$scope.license = config;
diff --git a/awx/ui/client/src/license/license.partial.html b/awx/ui/client/src/license/license.partial.html
index fb07180d7a..5adaa8ebde 100644
--- a/awx/ui/client/src/license/license.partial.html
+++ b/awx/ui/client/src/license/license.partial.html
@@ -2,91 +2,91 @@
ng-class="{'License-container--missing': licenseMissing}">
- Details
+ Details
- License
+ License
- Valid License
- Invalid License
+ Valid License
+ Invalid License
- Version
+ Version
{{license.version}}
- License Type
+ License Type
{{license.license_info.license_type}}
- Subscription
+ Subscription
{{license.license_info.subscription_name}}
- License Key
+ License Key
{{license.license_info.license_key}}
- Expires On
+ Expires On
{{time.expiresOn}}
- Time Remaining
+ Time Remaining
{{time.remaining}}
- Hosts Available
+ Hosts Available
{{license.license_info.available_instances}}
- Hosts Used
+ Hosts Used
{{license.license_info.current_instances}}
- Hosts Remaining
+ Hosts Remaining
{{license.license_info.free_instances}}
- If you are ready to upgrade, please contact us by clicking the button below
-
+ If you are ready to upgrade, please contact us by clicking the button below
+
{{title}}
- Welcome to Ansible Tower! Please complete the steps below to acquire a license.
+ Welcome to Ansible Tower! Please complete the steps below to acquire a license.
1
- Please click the button below to visit Ansible's website to get a Tower license key.
+ Please click the button below to visit Ansible's website to get a Tower license key.
@@ -94,18 +94,18 @@
2
- Choose your license file, agree to the End User License Agreement, and click submit.
+ Choose your license file, agree to the End User License Agreement, and click submit.
diff --git a/awx/ui/client/src/lists/CompletedJobs.js b/awx/ui/client/src/lists/CompletedJobs.js
index 319ee98074..bf586613c5 100644
--- a/awx/ui/client/src/lists/CompletedJobs.js
+++ b/awx/ui/client/src/lists/CompletedJobs.js
@@ -5,22 +5,21 @@
*************************************************/
-
-
export default
angular.module('CompletedJobsDefinition', ['sanitizeFilter'])
- .value( 'CompletedJobsList', {
+ .factory('CompletedJobsList', ['i18n', function(i18n) {
+ return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
- awToolTip: 'Please save and run a job to view',
+ awToolTip: i18n._('Please save and run a job to view'),
dataPlacement: 'top',
name: 'completed_jobs',
basePath: 'job_templates/:id/jobs/?or__status=successful&or__status=failed&or__status=error&or__status=canceled',
iterator: 'completed_job',
- editTitle: 'Completed Jobs',
+ editTitle: i18n._('Completed Jobs'),
index: false,
hover: true,
well: false,
- emptyListText: 'No completed jobs',
+ emptyListText: i18n._('No completed jobs'),
fields: {
status: {
@@ -53,7 +52,7 @@ export default
dataPlacement: 'top'
},
name: {
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
searchable: false,
ngClick: "viewJobDetails(completed_job)",
@@ -61,7 +60,7 @@ export default
dataPlacement: 'top'
},
type: {
- label: 'Type',
+ label: i18n._('Type'),
ngBind: 'completed_job.type_label',
link: false,
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
@@ -70,7 +69,7 @@ export default
searchOptions: [] // populated via GetChoices() in controller
},
finished: {
- label: 'Finished',
+ label: i18n._('Finished'),
noLink: true,
searchable: false,
filter: "longDate",
@@ -79,7 +78,7 @@ export default
desc: true
},
failed: {
- label: 'Job failed?',
+ label: i18n._('Job failed?'),
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
@@ -110,4 +109,4 @@ export default
ngShow: 'completed_job.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/Credentials.js b/awx/ui/client/src/lists/Credentials.js
index d9496b1807..a46aa56d08 100644
--- a/awx/ui/client/src/lists/Credentials.js
+++ b/awx/ui/client/src/lists/Credentials.js
@@ -9,33 +9,34 @@
export default
angular.module('CredentialsListDefinition', [])
- .value('CredentialList', {
+ .factory('CredentialList', ['i18n', function(i18n) {
+ return {
name: 'credentials',
iterator: 'credential',
- selectTitle: 'Add Credentials',
- editTitle: 'Credentials',
- listTitle: 'Credentials',
+ selectTitle: i18n._('Add Credentials'),
+ editTitle: i18n._('Credentials'),
+ listTitle: i18n._('Credentials'),
selectInstructions: "Select existing credentials by clicking each credential or checking the related checkbox. When " +
"finished, click the blue Select button, located bottom right.
Create a brand new credential by clicking ",
index: false,
hover: true,
- emptyListText: 'No Credentials Have Been Created',
+ emptyListText: i18n._('No Credentials Have Been Created'),
fields: {
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-11'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
excludeModal: true,
columnClass: 'col-md-3 hidden-sm hidden-xs'
},
kind: {
- label: 'Type',
+ label: i18n._('Type'),
searchType: 'select',
searchOptions: [], // will be set by Options call to credentials resource
excludeModal: true,
@@ -43,7 +44,7 @@ export default
columnClass: 'col-md-2 hidden-sm hidden-xs'
},
owners: {
- label: 'Owners',
+ label: i18n._('Owners'),
type: 'owners',
searchable: false,
nosort: true,
@@ -56,9 +57,9 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()',
- awToolTip: 'Create a new credential',
+ awToolTip: i18n._('Create a new credential'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD'
+ buttonContent: i18n._('+ ADD')
}
},
@@ -69,18 +70,18 @@ export default
edit: {
ngClick: "editCredential(credential.id)",
icon: 'fa-edit',
- label: 'Edit',
+ label: i18n._('Edit'),
"class": 'btn-sm',
- awToolTip: 'Edit credential',
+ awToolTip: i18n._('Edit credential'),
dataPlacement: 'top',
ngShow: 'credential.summary_fields.user_capabilities.edit'
},
view: {
ngClick: "editCredential(credential.id)",
- label: 'View',
+ label: i18n._('View'),
"class": 'btn-sm',
- awToolTip: 'View credential',
+ awToolTip: i18n._('View credential'),
dataPlacement: 'top',
ngShow: '!credential.summary_fields.user_capabilities.edit'
},
@@ -88,11 +89,11 @@ export default
"delete": {
ngClick: "deleteCredential(credential.id, credential.name)",
icon: 'fa-trash',
- label: 'Delete',
+ label: i18n._('Delete'),
"class": 'btn-sm',
- awToolTip: 'Delete credential',
+ awToolTip: i18n._('Delete credential'),
dataPlacement: 'top',
ngShow: 'credential.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/Inventories.js b/awx/ui/client/src/lists/Inventories.js
index fec3b27c2d..4129079bb0 100644
--- a/awx/ui/client/src/lists/Inventories.js
+++ b/awx/ui/client/src/lists/Inventories.js
@@ -7,13 +7,14 @@
export default
angular.module('InventoriesListDefinition', [])
- .value('InventoryList', {
+ .factory('InventoryList', ['i18n', function(i18n) {
+ return {
name: 'inventories',
iterator: 'inventory',
- selectTitle: 'Add Inventories',
- editTitle: 'Inventories',
- listTitle: 'Inventories',
+ selectTitle: i18n._('Add Inventories'),
+ editTitle: i18n._('Inventories'),
+ listTitle: i18n._('Inventories'),
selectInstructions: "Click on a row to select it, and click Finished when done. Click the " +
"button to create a new inventory.",
index: false,
@@ -43,13 +44,13 @@ export default
},
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-md-5 col-sm-5 col-xs-8 List-staticColumnAdjacent',
modalColumnClass: 'col-md-11',
linkTo: '/#/inventories/{{inventory.id}}/manage'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
ngBind: 'inventory.summary_fields.organization.name',
linkTo: '/#/organizations/{{ inventory.organization }}',
sourceModel: 'organization',
@@ -58,27 +59,27 @@ export default
columnClass: 'col-md-5 col-sm-3 hidden-xs'
},
has_inventory_sources: {
- label: 'Cloud sourced?',
+ label: i18n._('Cloud sourced?'),
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
},
has_active_failures: {
- label: 'Failed hosts?',
+ label: i18n._('Failed hosts?'),
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
},
inventory_sources_with_failures: {
- label: 'Sync failures?',
+ label: i18n._('Sync failures?'),
searchType: 'select',
searchOptions: [{
- label: 'Yes',
+ label: i18n._('Yes'),
value: 'inventory_sources_with_failures__gt=0'
}, {
- label: 'No',
+ label: i18n._('No'),
value: 'inventory_sources_with_failures__lte=0'
}],
searchOnly: true
@@ -89,9 +90,9 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addInventory()',
- awToolTip: 'Create a new inventory',
+ awToolTip: i18n._('Create a new inventory'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: 'canAdd'
}
},
@@ -101,25 +102,25 @@ export default
columnClass: 'col-md-2 col-sm-4 col-xs-4',
edit: {
- label: 'Edit',
+ label: i18n._('Edit'),
ngClick: 'editInventory(inventory.id)',
- awToolTip: 'Edit inventory',
+ awToolTip: i18n._('Edit inventory'),
dataPlacement: 'top',
ngShow: 'inventory.summary_fields.user_capabilities.edit'
},
view: {
- label: 'View',
+ label: i18n._('View'),
ngClick: 'editInventory(inventory.id)',
- awToolTip: 'View inventory',
+ awToolTip: i18n._('View inventory'),
dataPlacement: 'top',
ngShow: '!inventory.summary_fields.user_capabilities.edit'
},
"delete": {
- label: 'Delete',
+ label: i18n._('Delete'),
ngClick: "deleteInventory(inventory.id, inventory.name)",
- awToolTip: 'Delete inventory',
+ awToolTip: i18n._('Delete inventory'),
dataPlacement: 'top',
ngShow: 'inventory.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/JobTemplates.js b/awx/ui/client/src/lists/JobTemplates.js
index ce75bf3dd6..9beebb1919 100644
--- a/awx/ui/client/src/lists/JobTemplates.js
+++ b/awx/ui/client/src/lists/JobTemplates.js
@@ -7,13 +7,14 @@
export default
angular.module('JobTemplatesListDefinition', [])
- .value('JobTemplateList', {
+ .factory('JobTemplateList', ['i18n', function(i18n) {
+ return {
name: 'job_templates',
iterator: 'job_template',
- selectTitle: 'Add Job Template',
- editTitle: 'Job Templates',
- listTitle: 'Job Templates',
+ selectTitle: i18n._('Add Job Template'),
+ editTitle: i18n._('Job Templates'),
+ listTitle: i18n._('Job Templates'),
selectInstructions: "Click on a row to select it, and click Finished when done. Use the " +
"button to create a new job template.",
index: false,
@@ -22,15 +23,15 @@ export default
fields: {
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-lg-2 col-md-2 col-sm-4 col-xs-9'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
columnClass: 'col-lg-2 hidden-md hidden-sm hidden-xs'
},
smart_status: {
- label: 'Activity',
+ label: i18n._('Activity'),
columnClass: 'List-tableCell col-lg-2 col-md-2 hidden-sm hidden-xs',
searchable: false,
nosort: true,
@@ -38,7 +39,7 @@ export default
type: 'template'
},
labels: {
- label: 'Labels',
+ label: i18n._('Labels'),
type: 'labels',
nosort: true,
columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
@@ -53,9 +54,9 @@ export default
mode: 'all', // One of: edit, select, all
ngClick: 'addJobTemplate()',
basePaths: ['job_templates'],
- awToolTip: 'Create a new template',
+ awToolTip: i18n._('Create a new template'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: 'canAdd'
}
},
@@ -65,52 +66,52 @@ export default
columnClass: 'col-lg-2 col-md-3 col-sm-3 col-xs-3',
submit: {
- label: 'Launch',
+ label: i18n._('Launch'),
mode: 'all',
ngClick: 'submitJob(job_template.id)',
- awToolTip: 'Start a job using this template',
+ awToolTip: i18n._('Start a job using this template'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.start'
},
schedule: {
- label: 'Schedule',
+ label: i18n._('Schedule'),
mode: 'all',
ngClick: 'scheduleJob(job_template.id)',
- awToolTip: 'Schedule future job template runs',
+ awToolTip: i18n._('Schedule future job template runs'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.schedule'
},
copy: {
- label: 'Copy',
+ label: i18n._('Copy'),
'ui-sref': 'jobTemplates.copy({id: job_template.id})',
"class": 'btn-danger btn-xs',
- awToolTip: 'Copy template',
+ awToolTip: i18n._('Copy template'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.copy'
},
edit: {
- label: 'Edit',
+ label: i18n._('Edit'),
ngClick: "editJobTemplate(job_template.id)",
- awToolTip: 'Edit template',
+ awToolTip: i18n._('Edit template'),
"class": 'btn-default btn-xs',
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.edit'
},
view: {
- label: 'View',
+ label: i18n._('View'),
ngClick: "editJobTemplate(job_template.id)",
- awToolTip: 'View template',
+ awToolTip: i18n._('View template'),
"class": 'btn-default btn-xs',
dataPlacement: 'top',
ngShow: '!job_template.summary_fields.user_capabilities.edit'
},
"delete": {
- label: 'Delete',
+ label: i18n._('Delete'),
ngClick: "deleteJobTemplate(job_template.id, job_template.name)",
"class": 'btn-danger btn-xs',
- awToolTip: 'Delete template',
+ awToolTip: i18n._('Delete template'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/PortalJobTemplates.js b/awx/ui/client/src/lists/PortalJobTemplates.js
index dac5d942f2..c1f4fbd045 100644
--- a/awx/ui/client/src/lists/PortalJobTemplates.js
+++ b/awx/ui/client/src/lists/PortalJobTemplates.js
@@ -7,26 +7,27 @@
export default
angular.module('PortalJobTemplatesListDefinition', [])
- .value('PortalJobTemplateList', {
+ .factory('PortalJobTemplateList', ['i18n', function(i18n) {
+ return {
name: 'job_templates',
iterator: 'job_template',
- editTitle: 'Job Templates',
- listTitle: 'Job Templates',
+ editTitle: i18n._('Job Templates'),
+ listTitle: i18n._('Job Templates'),
index: false,
hover: true,
well: true,
- emptyListText: 'There are no job templates to display at this time',
+ emptyListText: i18n._('There are no job templates to display at this time'),
fields: {
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-lg-5 col-md-5 col-sm-9 col-xs-8',
linkTo: '/#/job_templates/{{job_template.id}}',
searchDefault: true
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
columnClass: 'col-lg-4 col-md-4 hidden-sm hidden-xs'
}
},
@@ -36,11 +37,11 @@ export default
fieldActions: {
submit: {
- label: 'Launch',
+ label: i18n._('Launch'),
mode: 'all',
ngClick: 'submitJob(job_template.id)',
- awToolTip: 'Start a job using this template',
+ awToolTip: i18n._('Start a job using this template'),
dataPlacement: 'top'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/PortalJobs.js b/awx/ui/client/src/lists/PortalJobs.js
index bc8fd40fa8..efdf87a747 100644
--- a/awx/ui/client/src/lists/PortalJobs.js
+++ b/awx/ui/client/src/lists/PortalJobs.js
@@ -7,16 +7,17 @@
export default
angular.module('PortalJobsListDefinition', [])
- .value( 'PortalJobsList', {
+ .factory('PortalJobsList', ['i18n', function(i18n) {
+ return {
name: 'jobs',
iterator: 'job',
- editTitle: 'Jobs',
+ editTitle: i18n._('Jobs'),
index: false,
hover: true,
well: true,
- listTitle: 'Jobs',
- emptyListText: 'There are no jobs to display at this time',
+ listTitle: i18n._('Jobs'),
+ emptyListText: i18n._('There are no jobs to display at this time'),
fields: {
status: {
@@ -32,14 +33,14 @@ export default
searchLabel: 'Status'
},
name: {
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6 List-staticColumnAdjacent',
defaultSearchField: true,
linkTo: '/#/jobs/{{job.id}}',
searchDefault: true
},
finished: {
- label: 'Finished',
+ label: i18n._('Finished'),
noLink: true,
searchable: false,
filter: "longDate",
@@ -50,4 +51,4 @@ export default
},
actions: { }
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/Projects.js b/awx/ui/client/src/lists/Projects.js
index 93d940730a..600d76b29f 100644
--- a/awx/ui/client/src/lists/Projects.js
+++ b/awx/ui/client/src/lists/Projects.js
@@ -4,16 +4,16 @@
* All Rights Reserved
*************************************************/
-
export default
angular.module('ProjectsListDefinition', [])
- .value('ProjectList', {
+ .factory('ProjectList', ['i18n', function(i18n) {
+ return {
name: 'projects',
iterator: 'project',
- selectTitle: 'Add Project',
- editTitle: 'Projects',
- listTitle: 'Projects',
+ selectTitle: i18n._('Add Project'),
+ editTitle: i18n._('Projects'),
+ listTitle: i18n._('Projects'),
selectInstructions: '
Select existing projects by clicking each project or checking the related checkbox. When finished, click the blue ' +
'Select button, located bottom right.
Create a new project by clicking the button.
',
index: false,
@@ -38,19 +38,19 @@ export default
name: {
key: true,
searchDefault: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: "col-lg-4 col-md-4 col-sm-5 col-xs-7 List-staticColumnAdjacent",
modalColumnClass: 'col-md-8'
},
scm_type: {
- label: 'Type',
+ label: i18n._('Type'),
searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource
excludeModal: true,
columnClass: 'col-lg-3 col-md-2 col-sm-3 hidden-xs'
},
last_updated: {
- label: 'Last Updated',
+ label: i18n._('Last Updated'),
filter: "longDate",
columnClass: "col-lg-3 col-md-3 hidden-sm hidden-xs",
excludeModal: true,
@@ -63,18 +63,18 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addProject()',
- awToolTip: 'Create a new project',
+ awToolTip: i18n._('Create a new project'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: "canAdd"
},
refresh: {
mode: 'all',
- awToolTip: "Refresh the page",
+ awToolTip: i18n._("Refresh the page"),
ngClick: "refresh()",
ngShow: "socketStatus == 'error'",
actionClass: 'btn List-buttonDefault',
- buttonContent: 'REFRESH'
+ buttonContent: i18n._('REFRESH')
}
},
@@ -100,28 +100,28 @@ export default
},
edit: {
ngClick: "editProject(project.id)",
- awToolTip: 'Edit the project',
+ awToolTip: i18n._('Edit the project'),
dataPlacement: 'top',
ngShow: "project.summary_fields.user_capabilities.edit"
},
view: {
ngClick: "editProject(project.id)",
- awToolTip: 'View the project',
+ awToolTip: i18n._('View the project'),
dataPlacement: 'top',
ngShow: "!project.summary_fields.user_capabilities.edit",
icon: 'fa-eye',
},
"delete": {
ngClick: "deleteProject(project.id, project.name)",
- awToolTip: 'Delete the project',
+ awToolTip: i18n._('Delete the project'),
ngShow: "(project.status !== 'updating' && project.status !== 'running' && project.status !== 'pending') && project.summary_fields.user_capabilities.delete",
dataPlacement: 'top'
},
cancel: {
ngClick: "cancelUpdate(project.id, project.name)",
- awToolTip: 'Cancel the SCM update',
+ awToolTip: i18n._('Cancel the SCM update'),
ngShow: "(project.status == 'updating' || project.status == 'running' || project.status == 'pending') && project.summary_fields.user_capabilities.start",
dataPlacement: 'top'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/ScheduledJobs.js b/awx/ui/client/src/lists/ScheduledJobs.js
index 9f535c189e..b608e709b2 100644
--- a/awx/ui/client/src/lists/ScheduledJobs.js
+++ b/awx/ui/client/src/lists/ScheduledJobs.js
@@ -7,14 +7,15 @@
export default
angular.module('ScheduledJobsDefinition', ['sanitizeFilter'])
- .value( 'ScheduledJobsList', {
+ .factory('ScheduledJobsList', ['i18n', function(i18n) {
+ return {
name: 'schedules',
iterator: 'schedule',
- editTitle: 'Scheduled Jobs',
+ editTitle: i18n._('Scheduled Jobs'),
hover: true,
well: false,
- emptyListText: 'No schedules exist',
+ emptyListText: i18n._('No schedules exist'),
fields: {
enabled: {
@@ -29,7 +30,7 @@ export default
dataPlacement: 'top'
},
name: {
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-5 col-sm-5 col-xs-7 List-staticColumnAdjacent',
sourceModel: 'unified_job_template',
sourceField: 'name',
@@ -40,7 +41,7 @@ export default
defaultSearchField: true
},
type: {
- label: 'Type',
+ label: i18n._('Type'),
noLink: true,
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
sourceModel: 'unified_job_template',
@@ -52,15 +53,15 @@ export default
searchable: true,
searchType: 'select',
searchOptions: [
- { value: 'inventorysource', label: 'Inventory Sync' },
- { value: 'jobtemplate', label: 'Playbook Run' },
- { value: 'project', label: 'SCM Update' },
- { value: 'systemjobtemplate', label: 'Management Job'}
+ { value: 'inventorysource', label: i18n._('Inventory Sync') },
+ { value: 'jobtemplate', label: i18n._('Playbook Run') },
+ { value: 'project', label: i18n._('SCM Update') },
+ { value: 'systemjobtemplate', label: i18n._('Management Job')}
]
},
next_run: {
- label: 'Next Run',
+ label: i18n._('Next Run'),
noLink: true,
searchable: false,
columnClass: "col-lg-3 col-md-2 col-sm-3 hidden-xs",
@@ -77,23 +78,23 @@ export default
"edit": {
mode: "all",
ngClick: "editSchedule(schedule)",
- awToolTip: "Edit the schedule",
+ awToolTip: i18n._("Edit the schedule"),
dataPlacement: "top",
ngShow: 'schedule.summary_fields.user_capabilities.edit'
},
"view": {
mode: "all",
ngClick: "editSchedule(schedule)",
- awToolTip: "View the schedule",
+ awToolTip: i18n._("View the schedule"),
dataPlacement: "top",
ngShow: '!schedule.summary_fields.user_capabilities.edit'
},
"delete": {
mode: 'all',
ngClick: 'deleteSchedule(schedule.id)',
- awToolTip: 'Delete the schedule',
+ awToolTip: i18n._('Delete the schedule'),
dataPlacement: 'top',
ngShow: 'schedule.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/Teams.js b/awx/ui/client/src/lists/Teams.js
index 73fbfe38e2..a2ff5924d4 100644
--- a/awx/ui/client/src/lists/Teams.js
+++ b/awx/ui/client/src/lists/Teams.js
@@ -7,13 +7,14 @@
export default
angular.module('TeamsListDefinition', [])
- .value('TeamList', {
+ .factory('TeamList', ['i18n', function(i18n) {
+ return {
name: 'teams',
iterator: 'team',
- selectTitle: 'Add Team',
- editTitle: 'Teams',
- listTitle: 'Teams',
+ selectTitle: i18n._('Add Team'),
+ editTitle: i18n._('Teams'),
+ listTitle: i18n._('Teams'),
selectInstructions: "Click on a row to select it, and click Finished when done. Click the " +
"button to create a new team.",
index: false,
@@ -22,17 +23,17 @@ export default
fields: {
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-lg-3 col-md-4 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-8'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
columnClass: 'col-lg-3 col-md-3 hidden-sm hidden-xs',
excludeModal: true
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
ngBind: 'team.organization_name',
sourceModel: 'organization',
sourceField: 'name',
@@ -45,9 +46,9 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addTeam()',
- awToolTip: 'Create a new team',
+ awToolTip: i18n._('Create a new team'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: 'canAdd'
}
},
@@ -57,30 +58,30 @@ export default
columnClass: 'col-lg-3 col-md-2 col-sm-3 col-xs-3',
edit: {
- label: 'Edit',
+ label: i18n._('Edit'),
ngClick: "editTeam(team.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
- awToolTip: 'Edit team',
+ awToolTip: i18n._('Edit team'),
dataPlacement: 'top',
ngShow: 'team.summary_fields.user_capabilities.edit'
},
view: {
- label: 'View',
+ label: i18n._('View'),
ngClick: "editTeam(team.id)",
"class": 'btn-xs btn-default',
- awToolTip: 'View team',
+ awToolTip: i18n._('View team'),
dataPlacement: 'top',
ngShow: '!team.summary_fields.user_capabilities.edit'
},
"delete": {
- label: 'Delete',
+ label: i18n._('Delete'),
ngClick: "deleteTeam(team.id, team.name)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
- awToolTip: 'Delete team',
+ awToolTip: i18n._('Delete team'),
dataPlacement: 'top',
ngShow: 'team.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/lists/Users.js b/awx/ui/client/src/lists/Users.js
index ad70d1c30f..14bb0f154b 100644
--- a/awx/ui/client/src/lists/Users.js
+++ b/awx/ui/client/src/lists/Users.js
@@ -7,13 +7,14 @@
export default
angular.module('UserListDefinition', [])
- .value('UserList', {
+ .factory('UserList', ['i18n', function(i18n) {
+ return {
name: 'users',
iterator: 'user',
- selectTitle: 'Add Users',
- editTitle: 'Users',
- listTitle: 'Users',
+ selectTitle: i18n._('Add Users'),
+ editTitle: i18n._('Users'),
+ listTitle: i18n._('Users'),
selectInstructions: 'Select existing users by clicking each user or checking the related checkbox. When finished, click the blue ' +
'Select button, located bottom right.
When available, a brand new user can be created by clicking the ' +
' button.
',
@@ -23,15 +24,15 @@ export default
fields: {
username: {
key: true,
- label: 'Username',
+ label: i18n._('Username'),
columnClass: 'col-md-3 col-sm-3 col-xs-9'
},
first_name: {
- label: 'First Name',
+ label: i18n._('First Name'),
columnClass: 'col-md-3 col-sm-3 hidden-xs'
},
last_name: {
- label: 'Last Name',
+ label: i18n._('Last Name'),
columnClass: 'col-md-3 col-sm-3 hidden-xs'
}
},
@@ -42,9 +43,9 @@ export default
mode: 'all', // One of: edit, select, all
ngClick: 'addUser()',
basePaths: ['organizations', 'users'], // base path must be in list, or action not available
- awToolTip: 'Create a new user',
+ awToolTip: i18n._('Create a new user'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: 'canAdd'
}
},
@@ -54,32 +55,32 @@ export default
columnClass: 'col-md-3 col-sm-3 col-xs-3',
edit: {
- label: 'Edit',
+ label: i18n._('Edit'),
ngClick: "editUser(user.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
- awToolTip: 'Edit user',
+ awToolTip: i18n._('Edit user'),
dataPlacement: 'top',
ngShow: 'user.summary_fields.user_capabilities.edit'
},
view: {
- label: 'View',
+ label: i18n._('View'),
ngClick: "editUser(user.id)",
"class": 'btn-xs btn-default',
- awToolTip: 'View user',
+ awToolTip: i18n._('View user'),
dataPlacement: 'top',
ngShow: '!user.summary_fields.user_capabilities.edit'
},
"delete": {
- label: 'Delete',
+ label: i18n._('Delete'),
ngClick: "deleteUser(user.id, user.username)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
- awToolTip: 'Delete user',
+ awToolTip: i18n._('Delete user'),
dataPlacement: 'top',
ngShow: 'user.summary_fields.user_capabilities.delete'
}
}
- });
+ };}]);
diff --git a/awx/ui/client/src/login/loginModal/loginModal.partial.html b/awx/ui/client/src/login/loginModal/loginModal.partial.html
index 74a0c4fd43..56d811a200 100644
--- a/awx/ui/client/src/login/loginModal/loginModal.partial.html
+++ b/awx/ui/client/src/login/loginModal/loginModal.partial.html
@@ -10,7 +10,7 @@
ng-src="/static/assets/{{ customLogo }}" >
-
+
Welcome to Ansible Tower! Please sign in.
@@ -43,7 +43,7 @@
autocomplete="off" novalidate>
diff --git a/awx/ui/client/src/main-menu/main-menu.partial.html b/awx/ui/client/src/main-menu/main-menu.partial.html
index 6af41a58ad..558d733197 100644
--- a/awx/ui/client/src/main-menu/main-menu.partial.html
+++ b/awx/ui/client/src/main-menu/main-menu.partial.html
@@ -16,7 +16,7 @@
href="/#/projects"
ng-class="{'is-currentRoute' : isCurrentState('projects')}">
- PROJECTS
+ PROJECTS
- INVENTORIES
+ INVENTORIES
- JOB TEMPLATES
+ JOB TEMPLATES
- JOBS
+ JOBS
- VIEW USER PAGE FOR {{ $root.current_user.username | uppercase }}
+ VIEW USER PAGE FOR {{ $root.current_user.username | uppercase }}
- SETTINGS
+ SETTINGS
- PORTAL MODE
+ PORTAL MODE
- VIEW DOCUMENTATION
+ VIEW DOCUMENTATION
- LOG OUT
+ LOG OUT
@@ -92,7 +92,7 @@
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('projects'), 'is-loggedOut' : !current_user || !current_user.username}">
- PROJECTS
+ PROJECTS
- INVENTORIES
+ INVENTORIES
- JOB TEMPLATES
+ JOB TEMPLATES
- JOBS
+ JOBS
@@ -157,7 +157,7 @@
ng-href="/#/portal"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('portalMode'), 'is-loggedOut' : !current_user || !current_user.username}"
- aw-tool-tip="My View"
+ aw-tool-tip="{{'My View'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body">
@@ -170,7 +170,7 @@
ng-href="http://docs.ansible.com/ansible-tower/"
ng-hide="licenseMissing"
ng-class="{'is-loggedOut' : !current_user || !current_user.username}"
- aw-tool-tip="View Documentation"
+ aw-tool-tip="{{'View Documentation'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body"
@@ -185,7 +185,7 @@
ng-class="{'is-currentRoute' : isCurrentState('logout'),
'is-loggedOut' : !current_user || !current_user.username,
'MainMenu-item--licenseMissing' : licenseMissing}"
- aw-tool-tip="Log Out"
+ aw-tool-tip="{{'Log Out'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body"
diff --git a/awx/ui/client/src/management-jobs/card/card.partial.html b/awx/ui/client/src/management-jobs/card/card.partial.html
index 0b90bf8bdc..b8a370916d 100644
--- a/awx/ui/client/src/management-jobs/card/card.partial.html
+++ b/awx/ui/client/src/management-jobs/card/card.partial.html
@@ -1,7 +1,7 @@
-
+
Management Jobs
@@ -18,18 +18,18 @@
diff --git a/awx/ui/client/src/notifications/notificationTemplates.form.js b/awx/ui/client/src/notifications/notificationTemplates.form.js
index b796bb5ae2..a8c83c320c 100644
--- a/awx/ui/client/src/notifications/notificationTemplates.form.js
+++ b/awx/ui/client/src/notifications/notificationTemplates.form.js
@@ -10,20 +10,20 @@
* @description This form is for adding/editing an organization
*/
-export default function() {
+export default ['i18n', function(i18n) {
return {
- addTitle: 'New Notification Template',
+ addTitle: i18n._('New Notification Template'),
editTitle: '{{ name }}',
name: 'notification_template',
showActions: true,
subFormTitles: {
- typeSubForm: 'Type Details',
+ typeSubForm: i18n._('Type Details'),
},
fields: {
name: {
- label: 'Name',
+ label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@@ -31,14 +31,14 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
- label: 'Description',
+ label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
- label: 'Organization',
+ label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@@ -50,7 +50,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
notification_type: {
- label: 'Type',
+ label: i18n._('Type'),
type: 'select',
addRequired: true,
editRequired: true,
@@ -61,7 +61,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
username: {
- label: 'Username',
+ label: i18n._('Username'),
type: 'text',
ngShow: "notification_type.value == 'email' ",
subForm: 'typeSubForm',
@@ -69,7 +69,7 @@ export default function() {
},
host: {
- label: 'Host',
+ label: i18n._('Host'),
type: 'text',
awRequiredWhen: {
reqExpression: "email_required",
@@ -80,7 +80,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
sender: {
- label: 'Sender Email',
+ label: i18n._('Sender Email'),
type: 'text',
awRequiredWhen: {
reqExpression: "email_required",
@@ -91,12 +91,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
recipients: {
- label: 'Recipient List',
+ label: i18n._('Recipient List'),
type: 'textarea',
rows: 3,
- awPopOver: 'Type an option on each line.
'+
- 'For example:
alias1@email.com
\n alias2@email.com
\n',
- dataTitle: 'Recipient List',
+ awPopOver: i18n._('
Type an option on each line.
'+
+ 'For example:
alias1@email.com
\n alias2@email.com
\n'),
+ dataTitle: i18n._('Recipient List'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@@ -135,12 +135,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
channels: {
- label: 'Destination Channels',
+ label: i18n._('Destination Channels'),
type: 'textarea',
rows: 3,
- awPopOver: '
Type an option on each line. The pound symbol (#) is not required.
'+
- 'For example:
engineering
\n #support
\n',
- dataTitle: 'Destination Channels',
+ awPopOver: i18n._('
Type an option on each line. The pound symbol (#) is not required.
'+
+ 'For example:
engineering
\n #support
\n'),
+ dataTitle: i18n._('Destination Channels'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@@ -152,12 +152,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
rooms: {
- label: 'Destination Channels',
+ label: i18n._('Destination Channels'),
type: 'textarea',
rows: 3,
- awPopOver: '
Type an option on each line. The pound symbol (#) is not required.
'+
- 'For example:
engineering
\n #support
\n',
- dataTitle: 'Destination Channels',
+ awPopOver: i18n._('
Type an option on each line. The pound symbol (#) is not required.
'+
+ 'For example:
engineering
\n #support
\n'),
+ dataTitle: i18n._('Destination Channels'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@@ -181,7 +181,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
account_token: {
- label: 'Account Token',
+ label: i18n._('Account Token'),
type: 'sensitive',
hasShowInputButton: true,
awRequiredWhen: {
@@ -193,10 +193,10 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
from_number: {
- label: 'Source Phone Number',
+ label: i18n._('Source Phone Number'),
type: 'text',
- awPopOver: '
Number associated with the "Messaging Service" in Twilio.
'+
- 'This must be of the form +18005550199.
',
+ awPopOver: i18n._('Number associated with the "Messaging Service" in Twilio.
'+
+ 'This must be of the form +18005550199.
'),
awRequiredWhen: {
reqExpression: "twilio_required",
init: "false"
@@ -206,12 +206,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
to_numbers: {
- label: 'Destination SMS Number',
+ label: i18n._('Destination SMS Number'),
type: 'textarea',
rows: 3,
- awPopOver: 'Type an option on each line.
'+
- 'For example:
+12125552368
\n+19105556162
\n',
- dataTitle: 'Destination SMS Number',
+ awPopOver: i18n._('
Type an option on each line.
'+
+ 'For example:
+12125552368
\n+19105556162
\n'),
+ dataTitle: i18n._('Destination SMS Number'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@@ -223,7 +223,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
account_sid: {
- label: 'Account SID',
+ label: i18n._('Account SID'),
type: 'text',
awRequiredWhen: {
reqExpression: "twilio_required",
@@ -234,7 +234,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
subdomain: {
- label: 'Pagerduty subdomain',
+ label: i18n._('Pagerduty subdomain'),
type: 'text',
awRequiredWhen: {
reqExpression: "pagerduty_required",
@@ -245,7 +245,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
service_key: {
- label: 'API Service/Integration Key',
+ label: i18n._('API Service/Integration Key'),
type: 'text',
awRequiredWhen: {
reqExpression: "pagerduty_required",
@@ -256,7 +256,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
client_name: {
- label: 'Client Identifier',
+ label: i18n._('Client Identifier'),
type: 'text',
awRequiredWhen: {
reqExpression: "pagerduty_required",
@@ -267,7 +267,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
message_from: {
- label: 'Label to be shown with notification',
+ label: i18n._('Label to be shown with notification'),
type: 'text',
awRequiredWhen: {
reqExpression: "hipchat_required",
@@ -290,10 +290,10 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
color: {
- label: 'Notification Color',
+ label: i18n._('Notification Color'),
type: 'text',
- awPopOver: '
Color can be one of yellow, green, red, ' +
- 'purple, gray, or random.\n',
+ awPopOver: i18n._('
Color can be one of yellow, green, red, ' +
+ 'purple, gray, or random.\n'),
awRequiredWhen: {
reqExpression: "hipchat_required",
init: "false"
@@ -303,14 +303,14 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
notify: {
- label: 'Notify Channel',
+ label: i18n._('Notify Channel'),
type: 'checkbox',
ngShow: "notification_type.value == 'hipchat' ",
subForm: 'typeSubForm',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
url: {
- label: 'Target URL',
+ label: i18n._('Target URL'),
type: 'text',
awRequiredWhen: {
reqExpression: "webhook_required",
@@ -321,7 +321,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
headers: {
- label: 'HTTP Headers',
+ label: i18n._('HTTP Headers'),
type: 'textarea',
rows: 5,
'class': 'Form-formGroup--fullWidth',
@@ -329,20 +329,20 @@ export default function() {
reqExpression: "webhook_required",
init: "false"
},
- awPopOver: '
Specify HTTP Headers in JSON format
' +
+ awPopOver: i18n._('Specify HTTP Headers in JSON format
' +
'For example:
\n' +
'{\n' +
' "X-Auth-Token": "828jf0",\n' +
' "X-Ansible": "Is great!"\n' +
'}\n' +
- '',
+ ''),
dataPlacement: 'right',
ngShow: "notification_type.value == 'webhook' ",
subForm: 'typeSubForm',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
server: {
- label: 'IRC Server Address',
+ label: i18n._('IRC Server Address'),
type: 'text',
awRequiredWhen: {
reqExpression: "irc_required",
@@ -353,7 +353,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
nickname: {
- label: 'IRC Nick',
+ label: i18n._('IRC Nick'),
type: 'text',
awRequiredWhen: {
reqExpression: "irc_required",
@@ -364,12 +364,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
targets: {
- label: 'Destination Channels or Users',
+ label: i18n._('Destination Channels or Users'),
type: 'textarea',
rows: 3,
- awPopOver: 'Type an option on each line. The pound symbol (#) is not required.
'+
- 'For example:
#support or support
\n @username or username
\n',
- dataTitle: 'Destination Channels',
+ awPopOver: i18n._('
Type an option on each line. The pound symbol (#) is not required.
'+
+ 'For example:
#support or support
\n @username or username
\n'),
+ dataTitle: i18n._('Destination Channels'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@@ -381,27 +381,27 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
use_ssl: {
- label: 'SSL Connection',
+ label: i18n._('SSL Connection'),
type: 'checkbox',
ngShow: "notification_type.value == 'irc'",
subForm: 'typeSubForm',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
checkbox_group: {
- label: 'Options',
+ label: i18n._('Options'),
type: 'checkbox_group',
subForm: 'typeSubForm',
ngShow: "notification_type.value == 'email'",
fields: [{
name: 'use_tls',
- label: 'Use TLS',
+ label: i18n._('Use TLS'),
type: 'checkbox',
ngShow: "notification_type.value == 'email' ",
labelClass: 'checkbox-options stack-inline',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'use_ssl',
- label: 'Use SSL',
+ label: i18n._('Use SSL'),
type: 'checkbox',
ngShow: "notification_type.value == 'email'",
labelClass: 'checkbox-options stack-inline',
@@ -426,4 +426,4 @@ export default function() {
}
}
};
-}
+}];
diff --git a/awx/ui/client/src/notifications/notificationTemplates.list.js b/awx/ui/client/src/notifications/notificationTemplates.list.js
index 58d0794d4a..2c6f54f08c 100644
--- a/awx/ui/client/src/notifications/notificationTemplates.list.js
+++ b/awx/ui/client/src/notifications/notificationTemplates.list.js
@@ -8,10 +8,10 @@
* off of the settings page
*/
-export default function(){
+export default ['i18n', function(i18n){
return {
name: 'notification_templates' ,
- listTitle: 'Notification Templates',
+ listTitle: i18n._('Notification Templates'),
iterator: 'notification_template',
index: false,
hover: false,
@@ -30,12 +30,12 @@ export default function(){
},
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
linkTo: '/#/notification_templates/{{notification_template.id}}'
},
notification_type: {
- label: 'Type',
+ label: i18n._('Type'),
searchType: 'select',
searchOptions: [],
excludeModal: true,
@@ -47,9 +47,9 @@ export default function(){
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addNotification()',
- awToolTip: 'Create a new custom inventory',
+ awToolTip: i18n._('Create a new custom inventory'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD',
+ buttonContent: i18n._('+ ADD'),
ngShow: 'canAdd'
}
},
@@ -60,38 +60,38 @@ export default function(){
test: {
ngClick: "testNotification(notification_template.id)",
icon: 'fa-bell-o',
- label: 'Edit',
+ label: i18n._('Edit'),
"class": 'btn-sm',
- awToolTip: 'Test notification',
+ awToolTip: i18n._('Test notification'),
dataPlacement: 'top',
ngShow: 'notification_template.summary_fields.user_capabilities.edit'
},
edit: {
ngClick: "editNotification(notification_template.id)",
icon: 'fa-edit',
- label: 'Edit',
+ label: i18n._('Edit'),
"class": 'btn-sm',
- awToolTip: 'Edit notification',
+ awToolTip: i18n._('Edit notification'),
dataPlacement: 'top',
ngShow: 'notification_template.summary_fields.user_capabilities.edit'
},
view: {
ngClick: "editNotification(notification_template.id)",
- label: 'View',
+ label: i18n._('View'),
"class": 'btn-sm',
- awToolTip: 'View notification',
+ awToolTip: i18n._('View notification'),
dataPlacement: 'top',
ngShow: '!notification_template.summary_fields.user_capabilities.edit'
},
"delete": {
ngClick: "deleteNotification(notification_template.id, notification_template.name)",
icon: 'fa-trash',
- label: 'Delete',
+ label: i18n._('Delete'),
"class": 'btn-sm',
- awToolTip: 'Delete notification',
+ awToolTip: i18n._('Delete notification'),
dataPlacement: 'top',
ngShow: 'notification_template.summary_fields.user_capabilities.delete'
}
}
};
-}
+}];
diff --git a/awx/ui/client/src/notifications/notifications.list.js b/awx/ui/client/src/notifications/notifications.list.js
index ed74e0b12b..688047d370 100644
--- a/awx/ui/client/src/notifications/notifications.list.js
+++ b/awx/ui/client/src/notifications/notifications.list.js
@@ -8,13 +8,13 @@
* used in the related tabs
*/
-export default function(){
+export default ['i18n', function(i18n){
return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
dataPlacement: 'top',
- awToolTip: 'Please save before adding notifications',
+ awToolTip: i18n._('Please save before adding notifications'),
name: 'notifications' ,
- title: 'Notifications',
+ title: i18n._('Notifications'),
iterator: 'notification',
index: false,
hover: false,
@@ -23,19 +23,19 @@ export default function(){
fields: {
name: {
key: true,
- label: 'Name',
+ label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
linkTo: '/#/notification_templates/{{notifier.id}}',
},
notification_type: {
- label: 'Type',
+ label: i18n._('Type'),
searchType: 'select',
searchOptions: [],
excludeModal: true,
columnClass: 'col-md-4 hidden-sm hidden-xs'
},
notification_templates_success: {
- label: 'Success',
+ label: i18n._('Success'),
flag: 'notification_templates_success',
type: "toggle",
ngClick: "toggleNotification($event, notification.id, \"notification_templates_success\")",
@@ -46,7 +46,7 @@ export default function(){
nosort: true,
},
notification_templates_error: {
- label: 'Failure',
+ label: i18n._('Failure'),
columnClass: 'NotifierList-lastColumn',
flag: 'notification_templates_error',
type: "toggle",
@@ -63,12 +63,12 @@ export default function(){
label: 'Add Notification',
mode: 'all', // One of: edit, select, all
ngClick: 'addNotificationTemplate()',
- awToolTip: 'Create a new notification template',
+ awToolTip: i18n._('Create a new notification template'),
actionClass: 'btn List-buttonSubmit',
- buttonContent: '+ ADD NOTIFICATION TEMPLATE',
+ buttonContent: i18n._('+ ADD NOTIFICATION TEMPLATE'),
ngShow: 'current_user.is_superuser || (current_user_admin_orgs && current_user_admin_orgs.length > 0)'
}
}
};
-}
+}];
diff --git a/awx/ui/client/src/notifications/shared/type-change.service.js b/awx/ui/client/src/notifications/shared/type-change.service.js
index 0827e88b34..bb1b6f0bed 100644
--- a/awx/ui/client/src/notifications/shared/type-change.service.js
+++ b/awx/ui/client/src/notifications/shared/type-change.service.js
@@ -4,8 +4,8 @@
* All Rights Reserved
*************************************************/
-export default [
-function () {
+export default ['i18n',
+function (i18n) {
return{
getDetailFields: function(type) {
var obj = {};
@@ -24,20 +24,20 @@ function () {
obj.room_required = false;
switch (type) {
case 'email':
- obj.portLabel = ' Port';
- obj.passwordLabel = ' Password';
+ obj.portLabel = ' ' + i18n._('Port');
+ obj.passwordLabel = ' ' + i18n._('Password');
obj.email_required = true;
obj.port_required = true;
obj.password_required = false;
break;
case 'slack':
- obj.tokenLabel =' Token';
+ obj.tokenLabel =' ' + i18n._('Token');
obj.slack_required = true;
obj.token_required = true;
obj.channel_required = true;
break;
case 'hipchat':
- obj.tokenLabel = ' Token';
+ obj.tokenLabel = ' ' + i18n._('Token');
obj.hipchat_required = true;
obj.room_required = true;
obj.token_required = true;
@@ -49,13 +49,13 @@ function () {
obj.webhook_required = true;
break;
case 'pagerduty':
- obj.tokenLabel = ' API Token';
+ obj.tokenLabel = ' ' + i18n._('API Token');
obj.pagerduty_required = true;
obj.token_required = true;
break;
case 'irc':
- obj.portLabel = ' IRC Server Port';
- obj.passwordLabel = ' IRC Server Password';
+ obj.portLabel = ' ' + i18n._('IRC Server Port');
+ obj.passwordLabel = ' ' + i18n._('IRC Server Password');
obj.irc_required = true;
obj.password_required = true;
obj.port_required = true;
diff --git a/awx/ui/client/src/organizations/list/organizations-list.partial.html b/awx/ui/client/src/organizations/list/organizations-list.partial.html
index 9746cc5756..72da4ffa40 100644
--- a/awx/ui/client/src/organizations/list/organizations-list.partial.html
+++ b/awx/ui/client/src/organizations/list/organizations-list.partial.html
@@ -3,18 +3,19 @@
-
- organizations
+
+ ORGANIZATIONS
{{ orgCount }}
+
@@ -22,7 +23,7 @@
- PLEASE ADD ITEMS TO THIS LIST
+ PLEASE ADD ITEMS TO THIS LIST
- {{step.ncyBreadcrumbLabel}}
+ {{step.ncyBreadcrumbLabel|translate}}
- {{step.ncyBreadcrumbLabel}}
+ {{step.ncyBreadcrumbLabel|translate}}
diff --git a/awx/ui/client/src/partials/jobs.html b/awx/ui/client/src/partials/jobs.html
index a36b785d33..b7fa8d808e 100644
--- a/awx/ui/client/src/partials/jobs.html
+++ b/awx/ui/client/src/partials/jobs.html
@@ -7,17 +7,17 @@
- Jobs
+ Jobs
- Schedules
+ Schedules
-
diff --git a/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html b/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html
index 22bf88e899..cd8aec3812 100644
--- a/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html
+++ b/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html
@@ -3,13 +3,13 @@
My Jobs
+ ng-click='filterUser()' class="btn btn-xs" translate>My Jobs
All Jobs
+ 'btn-default' : activeFilter != 'all'}" ng-click='filterAll()' class="btn btn-xs btn-default" translate>All Jobs
- REFRESH
+ REFRESH
diff --git a/awx/ui/client/src/setup-menu/setup-menu.partial.html b/awx/ui/client/src/setup-menu/setup-menu.partial.html
index fc9a04d4ed..f8f6276cec 100644
--- a/awx/ui/client/src/setup-menu/setup-menu.partial.html
+++ b/awx/ui/client/src/setup-menu/setup-menu.partial.html
@@ -1,57 +1,57 @@
- Organizations
-
+
Organizations
+
Group all of your content to manage permissions across departments in your company.
- Users
-
+
Users
+
Allow others to sign into Tower and own the content they create.
- Teams
-
+
Teams
+
Split up your organization to associate content and control permissions for groups.
- Credentials
-
+
Credentials
+
Add passwords, SSH keys, etc. for Tower to use when launching jobs against machines, or when syncing inventories or projects.
- Management Jobs
-
+
Management Jobs
+
Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info.
- Inventory Scripts
-
+
Inventory Scripts
+
Create and edit scripts to dynamically load hosts from any source.
- Notifications
-
+
Notifications
+
Create templates for sending notifications with Email, HipChat, Slack, and SMS.
- View Your License
-
+
View Your License
+
View and edit your license information.
- About Tower
-
+
About Tower
+
View information about this version of Ansible Tower.
diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js
index 821857e3f6..86622485fa 100644
--- a/awx/ui/client/src/shared/form-generator.js
+++ b/awx/ui/client/src/shared/form-generator.js
@@ -142,10 +142,11 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
.factory('GenerateForm', ['$rootScope', '$location', '$compile', 'generateList',
'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column',
'NavigationLink', 'HelpCollapse', 'DropDown', 'Empty', 'SelectIcon',
- 'Store', 'ActionButton', 'getSearchHtml',
+ 'Store', 'ActionButton', 'getSearchHtml', 'i18n',
function ($rootScope, $location, $compile, GenerateList, SearchWidget,
PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse,
- DropDown, Empty, SelectIcon, Store, ActionButton, getSearchHtml) {
+ DropDown, Empty, SelectIcon, Store, ActionButton, getSearchHtml,
+ i18n) {
return {
setForm: function (form) { this.form = form; },
@@ -875,20 +876,27 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
// Add error messages
if ((options.mode === 'add' && field.addRequired) || (options.mode === 'edit' && field.editRequired) ||
field.awRequiredWhen) {
+ var error_message = i18n._("Please enter a value.");
html += "" + (field.requiredErrorMsg ? field.requiredErrorMsg : "Please enter a value.") + "\n";
+ this.form.name + '_form.' + fld + ".$error.required\">" + (field.requiredErrorMsg ? field.requiredErrorMsg : error_message) + "\n";
}
if (field.type === "email") {
+ var error_message = i18n._("Please enter a valid email address.");
html += "Please enter a valid email address.\n";
+ this.form.name + '_form.' + fld + ".$error.email\">" +
+ error_message + " \n";
}
if (field.awPassMatch) {
+ var error_message = i18n._("This value does not match the password you entered previously. Please confirm that password.");
html += "This value does not match the password you entered previously. Please confirm that password.\n";
+ ".$error.awpassmatch\">" +
+ error_message + "\n";
}
if (field.awValidUrl) {
+ var error_message = i18n._("Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character.");
html += "Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character. \n";
+ ".$error.awvalidurl\">" +
+ error_message + "\n";
}
html += "\n";
@@ -901,7 +909,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
//fields with sensitive data that needs to be obfuscated from view
if (field.type === 'sensitive') {
- field.showInputInnerHTML = "Show";
+ field.showInputInnerHTML = i18n._("Show");
field.inputType = "password";
html += "\t" + label();
@@ -918,6 +926,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
$(inputId).attr("type", "password");
}
};
+ var tooltip = i18n._("Toggle the display of plaintext.");
html += "\\n";
@@ -925,7 +934,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "\n";
html += "\n" + (field.requiredErrorMsg ? field.requiredErrorMsg : "Please enter a value.") + "\n \n";
+ this.form.name + "_form." + fld + ".$error.required'>\n" + (field.requiredErrorMsg ? field.requiredErrorMsg : error_message) + "\n\n";
}
if (field.type === "email") {
+ var error_message = i18n._("Please enter a valid email address.");
html += "\nPlease enter a valid email address.\n\n";
+ this.form.name + "_form." + fld + ".$error.email'>\n" +
+ error_message + "\n\n";
}
if (field.awPassMatch) {
+ var error_message = i18n._("This value does not match the password you entered previously. Please confirm that password.");
html += "\nThis value does not match the password you entered previously. Please confirm that password.\n\n";
+ ".$error.awpassmatch'>\n" +
+ error_message + "\n\n";
}
if (field.awValidUrl) {
+ var error_message = i18n._("Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character.");
html += "\nPlease enter a URL that begins with ssh, http or https. The URL may not contain the '@' character.\n\n";
+ ".$error.awvalidurl'>\n" +
+ error_message + "\n\n";
}
if (field.chkPass && $AnsibleConfig) {
// password strength
if ($AnsibleConfig.password_length) {
+ var error_message = i18n.format(i18n._("Your password must be %d characters long."), $AnsibleConfig.password_length);
html += "Your password must be " + $AnsibleConfig.password_length + " characters long.\n";
+ ".$error.password_length\">" +
+ error_message + "\n";
}
if ($AnsibleConfig.password_hasLowercase) {
+ var error_message = i18n._("Your password must contain a lowercase letter.");
html += "Your password must contain a lowercase letter.\n";
+ ".$error.hasLowercase\">" +
+ error_message + "\n";
}
if ($AnsibleConfig.password_hasUppercase) {
+ var error_message = i18n._("Your password must contain an uppercase letter.");
html += "Your password must contain an uppercase letter.\n";
+ ".$error.hasUppercase\">" +
+ error_message + "\n";
}
if ($AnsibleConfig.password_hasNumber) {
+ var error_message = i18n._("Your password must contain a number.");
html += "Your password must contain a number.\n";
+ ".$error.hasNumber\">" +
+ error_message + "\n";
}
if ($AnsibleConfig.password_hasSymbol) {
+ var error_message = i18n.format(i18n._("Your password must contain one of the following characters: %s"), "`~!@#$%^&*()_-+=|}\]{\[;:\"\'?\/>.<,");
html += "Your password must contain one of the following characters: `~!@#$%^&*()_-+=|}\]{\[;:\"\'?\/>.<,\n";
+ ".$error.hasSymbol\">" +
+ error_message + "\n";
}
}
@@ -1136,7 +1162,13 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
if(!field.multiSelect && !field.disableChooseOption){
html += "\n";
}
@@ -1488,10 +1520,14 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "";
html += (options.mode === 'edit') ? this.form.editTitle : this.form.addTitle;
if(this.form.name === "user"){
+ var user_str = i18n._("Admin");
html+= "Admin";
+ "ng-show='is_superuser'>" +
+ user_str + "";
+ user_str = i18n._("Auditor");
html+= "Auditor";
+ "ng-show='is_system_auditor'>" +
+ user_str + "";
html+= "LDAP";
html+= "";
if(this.mode === "edit"){
html += "Details";
+ "ng-class=\"{'is-selected': " + this.form.name + "Selected }\">" +
+ details + "";
for (itm in this.form.related) {
collection = this.form.related[itm];
@@ -1548,7 +1587,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
}
else if(this.mode === "add"){
html += "Details";
+ "class=\"Form-tab is-selected\">" +
+ details + "";
for (itm in this.form.related) {
collection = this.form.related[itm];
@@ -1693,31 +1733,31 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
// Set default color and label for Save and Reset
if (btn === 'save') {
- button.label = 'Save';
+ button.label = i18n._('Save');
button['class'] = 'Form-saveButton';
}
if (btn === 'cancel') {
- button.label = 'Cancel';
+ button.label = i18n._('Cancel');
button['class'] = 'Form-cancelButton';
}
if (btn === 'close') {
- button.label = 'Close';
+ button.label = i18n._('Close');
button['class'] = 'Form-cancelButton';
}
if (btn === 'launch') {
- button.label = 'Launch';
+ button.label = i18n._('Launch');
button['class'] = 'Form-launchButton';
}
if (btn === 'add_survey') {
- button.label = 'Add Survey';
+ button.label = i18n._('Add Survey');
button['class'] = 'Form-surveyButton';
}
if (btn === 'edit_survey') {
- button.label = 'Edit Survey';
+ button.label = i18n._('Edit Survey');
button['class'] = 'Form-surveyButton';
}
if (btn === 'view_survey') {
- button.label = 'View Survey';
+ button.label = i18n._('View Survey');
button['class'] = 'Form-surveyButton';
}
diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js
index dd7a6beb41..504df42d3b 100644
--- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js
+++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js
@@ -100,8 +100,9 @@ import {templateUrl} from '../../shared/template-url/template-url.factory';
export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'getSearchHtml',
'Column', 'DropDown', 'NavigationLink', 'SelectIcon', 'ActionButton',
+ 'i18n',
function ($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, getSearchHtml, Column, DropDown, NavigationLink,
- SelectIcon, ActionButton) {
+ SelectIcon, ActionButton, i18n) {
return {
setList: function (list) {
@@ -310,7 +311,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
if (list.listTitle) {
- html += "" + list.listTitle + "";
+ html += "" + list.listTitle + " ";
// We want to show the list title badge by default and only hide it when the list config specifically passes a false flag
list.listTitleBadge = (typeof list.listTitleBadge === 'boolean' && list.listTitleBadge === false) ? false : true;
if(list.listTitleBadge) {
@@ -411,7 +412,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
// Show the "no items" box when loading is done and the user isn't actively searching and there are no results
html += "";
- html += (list.emptyListText) ? list.emptyListText : "PLEASE ADD ITEMS TO THIS LIST";
+ html += (list.emptyListText) ? list.emptyListText : i18n._("PLEASE ADD ITEMS TO THIS LIST");
html += "";
// Add a title and optionally a close button (used on Inventory->Groups)
@@ -580,7 +581,9 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
// Message for loading
innerTable += " \n";
- innerTable += "Loading... \n";
+ var loading = i18n._("Loading...");
+ innerTable += "" +
+ loading + " \n";
innerTable += " \n";
// End List
@@ -684,7 +687,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
html += "";
- html += (list.fieldActions.label === undefined || list.fieldActions.label) ? "Actions" : "";
+ html += (list.fieldActions.label === undefined || list.fieldActions.label) ? i18n._("Actions") : "";
html += " \n";
}
html += "\n";
diff --git a/awx/ui/client/src/shared/socket/socket.service.js b/awx/ui/client/src/shared/socket/socket.service.js
index 056bd09bbf..5cd9ebfe40 100644
--- a/awx/ui/client/src/shared/socket/socket.service.js
+++ b/awx/ui/client/src/shared/socket/socket.service.js
@@ -5,8 +5,8 @@
*************************************************/
import ReconnectingWebSocket from 'reconnectingwebsocket';
export default
-['$rootScope', '$location', '$log','$state', '$q',
- function ($rootScope, $location, $log, $state, $q) {
+['$rootScope', '$location', '$log','$state', '$q', 'i18n',
+ function ($rootScope, $location, $log, $state, $q, i18n) {
var needsResubscribing = false,
socketPromise = $q.defer();
return {
@@ -169,15 +169,15 @@ export default
if(self.socket){
if (self.socket.readyState === 0 ) {
$rootScope.socketStatus = 'connecting';
- $rootScope.socketTip = "Live events: attempting to connect to the Tower server.";
+ $rootScope.socketTip = i18n._("Live events: attempting to connect to the Tower server.");
}
else if (self.socket.readyState === 1){
$rootScope.socketStatus = 'ok';
- $rootScope.socketTip = "Live events: connected. Pages containing job status information will automatically update in real-time.";
+ $rootScope.socketTip = i18n._("Live events: connected. Pages containing job status information will automatically update in real-time.");
}
else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){
$rootScope.socketStatus = 'error';
- $rootScope.socketTip = "Live events: error connecting to the Tower server.";
+ $rootScope.socketTip = i18n._("Live events: error connecting to the Tower server.");
}
return;
}
diff --git a/awx/ui/grunt-tasks/concurrent.js b/awx/ui/grunt-tasks/concurrent.js
index 1c2efab61f..f9606f0900 100644
--- a/awx/ui/grunt-tasks/concurrent.js
+++ b/awx/ui/grunt-tasks/concurrent.js
@@ -1,9 +1,9 @@
module.exports = {
dev: {
- tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:config', 'less:dev'],
+ tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:languages', 'copy:config', 'less:dev'],
},
prod: {
- tasks: ['newer:copy:vendor', 'newer:copy:assets', 'newer:copy:partials', 'newer:copy:config', 'newer:less:prod']
+ tasks: ['newer:copy:vendor', 'newer:copy:assets', 'newer:copy:partials', 'newer:copy:languages', 'newer:copy:config', 'newer:less:prod']
},
watch: {
tasks: ['watch:css', 'watch:partials', 'watch:assets', ['webpack:dev', 'watch:config']],
diff --git a/awx/ui/grunt-tasks/copy.js b/awx/ui/grunt-tasks/copy.js
index bbd68021fd..5e32be24b3 100644
--- a/awx/ui/grunt-tasks/copy.js
+++ b/awx/ui/grunt-tasks/copy.js
@@ -43,6 +43,14 @@ module.exports = {
dest: 'static/partials/'
}]
},
+ languages: {
+ files: [{
+ cwd: 'client/',
+ expand: true,
+ src: 'languages/*.json',
+ dest: 'static/'
+ }]
+ },
config: {
files: { 'static/config.js': ['client/src/config.js'] }
}
diff --git a/awx/ui/grunt-tasks/nggettext_compile.js b/awx/ui/grunt-tasks/nggettext_compile.js
new file mode 100644
index 0000000000..c69c159ef7
--- /dev/null
+++ b/awx/ui/grunt-tasks/nggettext_compile.js
@@ -0,0 +1,15 @@
+module.exports = {
+ all: {
+ options: {
+ format: 'json'
+ },
+ files: [ {
+ expand: true,
+ dot: true,
+ dest: 'client/languages',
+ cwd: 'po',
+ ext: '.json',
+ src: ['*.po']
+ } ]
+ }
+};
diff --git a/awx/ui/grunt-tasks/nggettext_extract.js b/awx/ui/grunt-tasks/nggettext_extract.js
new file mode 100644
index 0000000000..3257b384ee
--- /dev/null
+++ b/awx/ui/grunt-tasks/nggettext_extract.js
@@ -0,0 +1,11 @@
+module.exports = {
+ all: {
+ options: {
+ markerNames: ['_', 'N_']
+ },
+ files: {
+ 'po/ansible-tower.pot': ['client/src/**/*.js',
+ 'client/src/**/*.html']
+ }
+ },
+};
diff --git a/awx/ui/package.json b/awx/ui/package.json
index 2aadac00ff..9406e24173 100644
--- a/awx/ui/package.json
+++ b/awx/ui/package.json
@@ -18,6 +18,8 @@
"scripts": {
"build-docker-machine": "ip=$(docker-machine ip $DOCKER_MACHINE_NAME); npm set ansible-tower:django_host ${ip}; grunt dev",
"build-docker-cid": "ip=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' $DOCkER_CID` | npm set config ansible-tower:django_host ${ip}; grunt dev",
+ "pot": "grunt nggettext_extract",
+ "languages": "grunt nggettext_compile",
"build-release": "grunt release",
"pretest": "grunt clean:coverage",
"test": "karma start karma.conf.js",
@@ -34,6 +36,7 @@
"browser-sync": "^2.14.0",
"expose-loader": "^0.7.1",
"grunt": "^1.0.1",
+ "grunt-angular-gettext": "^2.2.3",
"grunt-browser-sync": "^2.2.0",
"grunt-cli": "^1.2.0",
"grunt-concurrent": "^2.3.0",
@@ -75,6 +78,7 @@
"angular-codemirror": "chouseknecht/angular-codemirror#1.0.4",
"angular-cookies": "^1.4.3",
"angular-drag-and-drop-lists": "leigh-johnson/angular-drag-and-drop-lists#1.4.0",
+ "angular-gettext": "^2.3.5",
"angular-md5": "^0.1.8",
"angular-moment": "^0.10.1",
"angular-resource": "^1.4.3",
diff --git a/awx/ui/po/ansible-tower.pot b/awx/ui/po/ansible-tower.pot
new file mode 100644
index 0000000000..70b35a0ba9
--- /dev/null
+++ b/awx/ui/po/ansible-tower.pot
@@ -0,0 +1,2041 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Project-Id-Version: \n"
+
+#: client/src/forms/Credentials.js:453
+#: client/src/forms/Inventories.js:108
+#: client/src/forms/Organizations.js:74
+#: client/src/forms/Projects.js:247
+#: client/src/forms/Teams.js:88
+#: client/src/inventory-scripts/inventory-scripts.list.js:45
+#: client/src/lists/Credentials.js:62
+#: client/src/lists/Inventories.js:95
+#: client/src/lists/JobTemplates.js:59
+#: client/src/lists/Projects.js:68
+#: client/src/lists/Teams.js:51
+#: client/src/lists/Users.js:48
+#: client/src/notifications/notificationTemplates.list.js:52
+msgid "+ ADD"
+msgstr ""
+
+#: client/src/notifications/notifications.list.js:68
+msgid "+ ADD NOTIFICATION TEMPLATE"
+msgstr ""
+
+#: client/src/organizations/list/organizations-list.partial.html:15
+msgid "+ ADD"
+msgstr ""
+
+#: client/src/controllers/Projects.js:779
+msgid ". GET status:"
+msgstr ""
+
+#: client/src/controllers/Projects.js:270
+msgid "Are you sure you want to delete the project below?"
+msgstr ""
+
+#: client/src/controllers/Projects.js:881
+msgid "Are you sure you want to remove the %s below from %s?"
+msgstr ""
+
+#: client/src/controllers/Projects.js:328
+msgid "An SCM update does not appear to be running for project: %s. Click the Refresh button to view the latest status."
+msgstr ""
+
+#: client/src/controllers/Projects.js:304
+msgid "Either you do not have access or the SCM update process completed. Click the Refresh button to view the latest status."
+msgstr ""
+
+#: client/src/forms/Credentials.js:127
+msgid "Security Token Service (STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users.To learn more about the IAM STS Token, refer to the Amazon documentation."
+msgstr ""
+
+#: client/src/forms/Credentials.js:69
+msgid ""
+"\n"
+"- Machine
\n"
+"- Authentication for remote machine access. This can include SSH keys, usernames, passwords, and sudo information. Machine credentials are used when submitting jobs to run playbooks against remote hosts.
- Network
\n"
+"- Authentication for network device access. This can include SSH keys, usernames, passwords, and authorize information. Network credentials are used when submitting jobs to run playbooks against network devices.
- Source Control
\n"
+"- Used to check out and synchronize playbook repositories with a remote source control management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are used by Projects.
\n"
+"- Others (Cloud Providers)
\n"
+"- Usernames, passwords, and access keys for authenticating to the specified cloud or infrastructure provider. These are used for dynamic inventory sources and for cloud provisioning and deployment in playbook runs.
\n"
+"
"
+msgstr ""
+
+#: client/src/controllers/Projects.js:550
+#: client/src/controllers/Projects.js:918
+msgid " URL popover text"
+msgstr ""
+
+#: client/src/forms/Projects.js:85
+msgid "
Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. Together the base path and selected playbook directory provide the full path used to locate playbooks.
Use PROJECTS_ROOT in your environment settings file to determine the base path value.
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:295
+msgid "Color can be one of yellow, green, red, purple, gray, or random."
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:228
+msgid "
Control the level of output ansible will produce as the playbook executes.
"
+msgstr ""
+
+#: client/src/forms/Projects.js:172
+msgid "Delete the local repository in its entirety prior to performing an update.
Depending on the size of the repository this may significantly increase the amount of time required to complete an update.
"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.form.js:59
+msgid "Drag and drop your custom inventory script file here or create one in the field to import your custom inventory.
Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python
"
+msgstr ""
+
+#: client/src/forms/Projects.js:185
+msgid "Each time a job runs using this project, perform an update to the local repository prior to starting the job.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:298
+msgid "Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update using this job template.
"
+msgstr ""
+
+#: client/src/forms/Inventories.js:61
+msgid ""
+"Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.
JSON:
\n"
+"{
\"somevar\": \"somevalue\",
\"password\": \"magic\"
}
\n"
+"YAML:
\n"
+"---
somevar: somevalue
password: magic
\n"
+"View JSON examples at www.json.org
View YAML examples at docs.ansible.com
"
+msgstr ""
+
+#: client/src/controllers/Projects.js:530
+#: client/src/controllers/Projects.js:898
+msgid "Example URLs for GIT SCM include:
- https://github.com/ansible/ansible.git
- git@github.com:ansible/ansible.git
- git://servername.example.com/ansible.git
Note: When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using SSH. GIT read only protocol (git://) does not use username or password information."
+msgstr ""
+
+#: client/src/controllers/Projects.js:542
+#: client/src/controllers/Projects.js:910
+msgid "
Example URLs for Mercurial SCM include:
- https://bitbucket.org/username/project
- ssh://hg@bitbucket.org/username/project
- ssh://server.example.com/path
Note: Mercurial does not support password authentication for SSH. Do not put the username and key in the URL. If using Bitbucket and SSH, do not supply your Bitbucket username."
+msgstr ""
+
+#: client/src/controllers/Projects.js:537
+#: client/src/controllers/Projects.js:905
+msgid "
Example URLs for Subversion SCM include:
- https://github.com/ansible/ansible
- svn://servername.example.com/path
- svn+ssh://servername.example.com/path
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:284
+msgid "If enabled, run this playbook as an administrator. This is the equivalent of passing the --become option to the ansible-playbook command.
"
+msgstr ""
+
+#: client/src/forms/Credentials.js:55
+msgid "If no organization is given, the credential can only be used by the user that creates the credential. Organization admins and system administrators can assign an organization so that roles for the credential can be assigned to users and teams in that organization.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:176
+msgid "Network credentials are used by Ansible networking modules to connect to and manage networking devices.
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:198
+msgid "Number associated with the \"Messaging Service\" in Twilio.
This must be of the form +18005550199.
"
+msgstr ""
+
+#: client/src/forms/Credentials.js:386
+msgid "OpenStack domains define administrative boundaries. It is only needed for Keystone v3 authentication URLs. Common scenarios include:
- v2 URLs - leave blank
- v3 default - set to 'default'
- v3 multi-domain - your domain name
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:346
+msgid "Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:359
+msgid ""
+"Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter for ansible-playbook. Provide key/value pairs using either YAML or JSON.
JSON:
\n"
+"{
\"somevar\": \"somevalue\",
\"password\": \"magic\"
}
\n"
+"YAML:
\n"
+"---
somevar: somevalue
password: magic
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:262
+msgid ""
+"Provide a comma separated list of tags.
\n"
+"Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.
Consult the Ansible documentation for further details on the usage of tags.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:242
+msgid ""
+"Provide a comma separated list of tags.
\n"
+"Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.
Consult the Ansible documentation for further details on the usage of tags.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:208
+msgid "Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. Multiple patterns can be separated by ; : or ,
For more information and examples see the Patterns topic at docs.ansible.com.
"
+msgstr ""
+
+#: client/src/forms/Projects.js:160
+msgid "Remove any local modifications prior to performing an update.
"
+msgstr ""
+
+#: client/src/forms/Projects.js:103
+msgid "Select from the list of directories found in the base path.Together the base path and the playbook directory provide the full path used to locate playbooks.
Use PROJECTS_ROOT in your environment settings file to determine the base path value.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:140
+msgid "Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing the username and SSH key or password that Ansible will need to log into the remote hosts.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:78
+msgid "Select the inventory containing the hosts you want this job to manage.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:122
+msgid "Select the playbook to be executed by this job.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:105
+msgid "Select the project containing the playbook you want this job to execute.
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:160
+msgid "Selecting an optional cloud credential in the job template will pass along the access credentials to the running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:332
+msgid ""
+"Specify HTTP Headers in JSON format
For example:
\n"
+"{\n"
+" \"X-Auth-Token\": \"828jf0\",\n"
+" \"X-Ansible\": \"Is great!\"\n"
+"}\n"
+""
+msgstr ""
+
+#: client/src/forms/Credentials.js:290
+msgid "Specify a method for 'become' operations. This is equivalent to specifying the --become-method=BECOME_METHOD parameter, where BECOME_METHOD could be sudo | su | pbrun | pfexec | runas
(defaults to sudo)
"
+msgstr ""
+
+#: client/src/forms/Credentials.js:163
+msgid "Subscription ID is an Azure construct, which is mapped to a username.
"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:105
+msgid "The Project ID is the GCE assigned identification. It is constructed as two words followed by a three digit number. Such as:
adjective-noun-000
"
+msgstr ""
+
+#: client/src/forms/Credentials.js:192
+msgid "The email address assigned to the Google Compute Engine service account.
"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:141
+msgid "The host to authenticate with.
For example, https://openstack.business.com/v2.0/"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:75
+msgid "
The host value
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:194
+msgid "The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies the default value from the ansible configuration file.
"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:74
+msgid "The project value
"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:138
+msgid "This is the tenant name. This value is usually the same as the username.
"
+msgstr ""
+
+#: client/src/forms/Projects.js:204
+msgid "Time in seconds to consider a project to be current. During job runs and callbacks the task system will evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, and a new project update will be performed.
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:370
+msgid ""
+"Type an option on each line. The pound symbol (#) is not required.
For example:
#support or support
\n"
+" @username or username
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:141
+#: client/src/notifications/notificationTemplates.form.js:158
+msgid ""
+"
Type an option on each line. The pound symbol (#) is not required.
For example:
engineering
\n"
+" #support
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:212
+msgid ""
+"
Type an option on each line.
For example:
+12125552368
\n"
+"+19105556162
"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:97
+msgid ""
+"
Type an option on each line.
For example:
alias1@email.com
\n"
+" alias2@email.com
"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:52
+msgid "
When this template is submitted as a job, setting the type to run will execute the playbook, running tasks on the selected hosts.
Setting the type to check will not execute the playbook. Instead, ansible will check playbook syntax, test environment setup and report problems.
Setting the type to scan will execute the playbook and store any scanned facts for use with Tower's System Tracking feature.
"
+msgstr ""
+
+#: client/src/forms/Credentials.js:200
+msgid "API Key"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:248
+msgid "API Service/Integration Key"
+msgstr ""
+
+#: client/src/notifications/shared/type-change.service.js:52
+msgid "API Token"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:53
+msgid "About Tower"
+msgstr ""
+
+#: client/src/forms/Credentials.js:94
+msgid "Access Key"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:226
+msgid "Account SID"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:184
+msgid "Account Token"
+msgstr ""
+
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:20
+#: client/src/shared/list-generator/list-generator.factory.js:690
+msgid "Actions"
+msgstr ""
+
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:17
+#: client/src/lists/JobTemplates.js:34
+msgid "Activity"
+msgstr ""
+
+#: client/src/lists/Credentials.js:17
+msgid "Add Credentials"
+msgstr ""
+
+#: client/src/dashboard/hosts/dashboard-hosts.list.js:12
+msgid "Add Existing Hosts"
+msgstr ""
+
+#: client/src/lists/Inventories.js:15
+msgid "Add Inventories"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:15
+msgid "Add Job Template"
+msgstr ""
+
+#: client/src/lists/Projects.js:14
+msgid "Add Project"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1752
+msgid "Add Survey"
+msgstr ""
+
+#: client/src/lists/Teams.js:15
+msgid "Add Team"
+msgstr ""
+
+#: client/src/lists/Users.js:15
+msgid "Add Users"
+msgstr ""
+
+#: client/src/forms/Credentials.js:451
+#: client/src/forms/Inventories.js:106
+#: client/src/forms/Organizations.js:72
+#: client/src/forms/Projects.js:245
+msgid "Add a permission"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:23
+msgid "Add passwords, SSH keys, etc. for Tower to use when launching jobs against machines, or when syncing inventories or projects."
+msgstr ""
+
+#: client/src/forms/Teams.js:86
+msgid "Add user to team"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1523
+msgid "Admin"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:37
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:43
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:65
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:74
+msgid "All"
+msgstr ""
+
+#: client/src/portal-mode/portal-mode-jobs.partial.html:7
+msgid "All Jobs"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:292
+#: client/src/forms/JobTemplates.js:301
+msgid "Allow Provisioning Callbacks"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:11
+msgid "Allow others to sign into Tower and own the content they create."
+msgstr ""
+
+#: client/src/forms/Credentials.js:236
+#: client/src/forms/Credentials.js:276
+#: client/src/forms/Credentials.js:317
+#: client/src/forms/Credentials.js:408
+msgid "Ask at runtime?"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1527
+msgid "Auditor"
+msgstr ""
+
+#: client/src/forms/Credentials.js:348
+msgid "Authorize"
+msgstr ""
+
+#: client/src/forms/Credentials.js:356
+msgid "Authorize Password"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:286
+msgid "Become Privilege Escalation"
+msgstr ""
+
+#: client/src/license/license.partial.html:104
+msgid "Browse"
+msgstr ""
+
+#: client/src/app.js:441
+msgid "CREATE CREDENTIAL"
+msgstr ""
+
+#: client/src/job-templates/add/job-templates-add.route.js:17
+msgid "CREATE JOB TEMPLATE"
+msgstr ""
+
+#: client/src/app.js:307
+msgid "CREATE PROJECT"
+msgstr ""
+
+#: client/src/app.js:356
+msgid "CREATE TEAM"
+msgstr ""
+
+#: client/src/app.js:478
+msgid "CREATE USER"
+msgstr ""
+
+#: client/src/app.js:431
+msgid "CREDENTIALS"
+msgstr ""
+
+#: client/src/forms/Projects.js:207
+msgid "Cache Timeout"
+msgstr ""
+
+#: client/src/forms/Projects.js:194
+msgid "Cache Timeout (seconds)"
+msgstr ""
+
+#: client/src/controllers/Projects.js:264
+msgid "Call to %s failed. DELETE returned status:"
+msgstr ""
+
+#: client/src/controllers/Projects.js:309
+#: client/src/controllers/Projects.js:325
+msgid "Call to %s failed. GET status:"
+msgstr ""
+
+#: client/src/controllers/Projects.js:288
+msgid "Call to %s failed. POST status:"
+msgstr ""
+
+#: client/src/controllers/Projects.js:334
+msgid "Call to get project failed. GET status:"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1740
+msgid "Cancel"
+msgstr ""
+
+#: client/src/controllers/Projects.js:304
+msgid "Cancel Not Allowed"
+msgstr ""
+
+#: client/src/lists/Projects.js:122
+msgid "Cancel the SCM update"
+msgstr ""
+
+#: client/src/controllers/Projects.js:66
+msgid "Canceled. Click for details"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1169
+msgid "Choose a %s"
+msgstr ""
+
+#: client/src/license/license.partial.html:97
+msgid "Choose your license file, agree to the End User License Agreement, and click submit."
+msgstr ""
+
+#: client/src/forms/Projects.js:156
+msgid "Clean"
+msgstr ""
+
+#: client/src/forms/Credentials.js:326
+msgid "Client ID"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:259
+msgid "Client Identifier"
+msgstr ""
+
+#: client/src/forms/Credentials.js:335
+msgid "Client Secret"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1744
+msgid "Close"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:152
+#: client/src/forms/JobTemplates.js:162
+msgid "Cloud Credential"
+msgstr ""
+
+#: client/src/lists/Inventories.js:62
+msgid "Cloud sourced?"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:158
+msgid "CloudForms Host"
+msgstr ""
+
+#: client/src/lists/CompletedJobs.js:18
+msgid "Completed Jobs"
+msgstr ""
+
+#: client/src/management-jobs/card/card.partial.html:32
+msgid "Configure Notifications"
+msgstr ""
+
+#: client/src/forms/Users.js:87
+msgid "Confirm Password"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:85
+msgid "Copy"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:88
+msgid "Copy template"
+msgstr ""
+
+#: client/src/forms/Credentials.js:18
+msgid "Create Credential"
+msgstr ""
+
+#: client/src/lists/Credentials.js:60
+msgid "Create a new credential"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.list.js:43
+#: client/src/notifications/notificationTemplates.list.js:50
+msgid "Create a new custom inventory"
+msgstr ""
+
+#: client/src/lists/Inventories.js:93
+msgid "Create a new inventory"
+msgstr ""
+
+#: client/src/notifications/notifications.list.js:66
+msgid "Create a new notification template"
+msgstr ""
+
+#: client/src/organizations/list/organizations-list.partial.html:16
+msgid "Create a new organization"
+msgstr ""
+
+#: client/src/lists/Projects.js:66
+msgid "Create a new project"
+msgstr ""
+
+#: client/src/lists/Teams.js:49
+msgid "Create a new team"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:57
+msgid "Create a new template"
+msgstr ""
+
+#: client/src/lists/Users.js:46
+msgid "Create a new user"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:35
+msgid "Create and edit scripts to dynamically load hosts from any source."
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:42
+msgid "Create templates for sending notifications with Email, HipChat, Slack, and SMS."
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:142
+msgid "Credential"
+msgstr ""
+
+#: client/src/lists/Credentials.js:18
+#: client/src/lists/Credentials.js:19
+#: client/src/setup-menu/setup-menu.partial.html:22
+msgid "Credentials"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.form.js:50
+#: client/src/inventory-scripts/inventory-scripts.form.js:61
+msgid "Custom Script"
+msgstr ""
+
+#: client/src/app.js:264
+msgid "DASHBOARD"
+msgstr ""
+
+#: client/src/controllers/Projects.js:269
+#: client/src/controllers/Projects.js:880
+#: client/src/inventory-scripts/inventory-scripts.list.js:74
+#: client/src/lists/Credentials.js:92
+#: client/src/lists/Inventories.js:119
+#: client/src/lists/JobTemplates.js:109
+#: client/src/lists/Teams.js:78
+#: client/src/lists/Users.js:77
+#: client/src/notifications/notificationTemplates.list.js:89
+msgid "Delete"
+msgstr ""
+
+#: client/src/lists/Credentials.js:94
+msgid "Delete credential"
+msgstr ""
+
+#: client/src/lists/Inventories.js:121
+msgid "Delete inventory"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.list.js:76
+msgid "Delete inventory script"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.list.js:91
+msgid "Delete notification"
+msgstr ""
+
+#: client/src/forms/Projects.js:168
+msgid "Delete on Update"
+msgstr ""
+
+#: client/src/lists/Teams.js:82
+msgid "Delete team"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:112
+msgid "Delete template"
+msgstr ""
+
+#: client/src/lists/Projects.js:116
+msgid "Delete the project"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:95
+msgid "Delete the schedule"
+msgstr ""
+
+#: client/src/lists/Users.js:81
+msgid "Delete user"
+msgstr ""
+
+#: client/src/forms/Credentials.js:40
+#: client/src/forms/Inventories.js:35
+#: client/src/forms/JobTemplates.js:36
+#: client/src/forms/Organizations.js:33
+#: client/src/forms/Projects.js:38
+#: client/src/forms/Teams.js:33
+#: client/src/inventory-scripts/inventory-scripts.form.js:31
+#: client/src/inventory-scripts/inventory-scripts.list.js:25
+#: client/src/lists/Credentials.js:34
+#: client/src/lists/JobTemplates.js:30
+#: client/src/lists/PortalJobTemplates.js:30
+#: client/src/lists/Teams.js:31
+#: client/src/notifications/notificationTemplates.form.js:34
+msgid "Description"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:138
+#: client/src/notifications/notificationTemplates.form.js:143
+#: client/src/notifications/notificationTemplates.form.js:155
+#: client/src/notifications/notificationTemplates.form.js:160
+#: client/src/notifications/notificationTemplates.form.js:372
+msgid "Destination Channels"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:367
+msgid "Destination Channels or Users"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:209
+#: client/src/notifications/notificationTemplates.form.js:214
+msgid "Destination SMS Number"
+msgstr ""
+
+#: client/src/license/license.partial.html:5
+#: client/src/shared/form-generator.js:1561
+msgid "Details"
+msgstr ""
+
+#: client/src/forms/Teams.js:145
+msgid "Dissasociate permission from team"
+msgstr ""
+
+#: client/src/forms/Users.js:204
+msgid "Dissasociate permission from user"
+msgstr ""
+
+#: client/src/forms/Credentials.js:391
+#: client/src/helpers/Credentials.js:133
+msgid "Domain Name"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.list.js:57
+#: client/src/lists/Credentials.js:73
+#: client/src/lists/Inventories.js:105
+#: client/src/lists/JobTemplates.js:93
+#: client/src/lists/Teams.js:61
+#: client/src/lists/Users.js:58
+#: client/src/notifications/notificationTemplates.list.js:63
+#: client/src/notifications/notificationTemplates.list.js:72
+msgid "Edit"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1756
+msgid "Edit Survey"
+msgstr ""
+
+#: client/src/lists/Credentials.js:75
+msgid "Edit credential"
+msgstr ""
+
+#: client/src/lists/Inventories.js:107
+msgid "Edit inventory"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.list.js:59
+msgid "Edit inventory script"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.list.js:74
+msgid "Edit notification"
+msgstr ""
+
+#: client/src/lists/Teams.js:65
+msgid "Edit team"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:95
+msgid "Edit template"
+msgstr ""
+
+#: client/src/lists/Projects.js:103
+msgid "Edit the project"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:81
+msgid "Edit the schedule"
+msgstr ""
+
+#: client/src/lists/Users.js:62
+msgid "Edit user"
+msgstr ""
+
+#: client/src/forms/Credentials.js:193
+#: client/src/forms/Users.js:42
+msgid "Email"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:279
+msgid "Enable Privilege Escalation"
+msgstr ""
+
+#: client/src/license/license.partial.html:108
+msgid "End User License Agreement"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:159
+msgid "Enter the hostname or IP address for the virtual
machine which is hosting the CloudForm appliance."
+msgstr ""
+
+#: client/src/helpers/Credentials.js:150
+msgid "Enter the hostname or IP address name which
corresponds to your Red Hat Satellite 6 server."
+msgstr ""
+
+#: client/src/helpers/Credentials.js:128
+msgid "Enter the hostname or IP address which corresponds to your VMware vCenter."
+msgstr ""
+
+#: client/src/controllers/Projects.js:242
+#: client/src/controllers/Projects.js:263
+#: client/src/controllers/Projects.js:288
+#: client/src/controllers/Projects.js:309
+#: client/src/controllers/Projects.js:324
+#: client/src/controllers/Projects.js:333
+#: client/src/controllers/Projects.js:513
+#: client/src/controllers/Projects.js:778
+msgid "Error!"
+msgstr ""
+
+#: client/src/license/license.partial.html:39
+msgid "Expires On"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:351
+#: client/src/forms/JobTemplates.js:365
+msgid "Extra Variables"
+msgstr ""
+
+#: client/src/dashboard/graphs/job-status/job-status-graph.directive.js:67
+msgid "FAILED"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:80
+msgid "Failed"
+msgstr ""
+
+#: client/src/dashboard/counts/dashboard-counts.directive.js:44
+msgid "Failed Hosts"
+msgstr ""
+
+#: client/src/lists/Inventories.js:69
+msgid "Failed hosts?"
+msgstr ""
+
+#: client/src/controllers/Projects.js:514
+msgid "Failed to create new project. POST returned status:"
+msgstr ""
+
+#: client/src/controllers/Projects.js:779
+msgid "Failed to retrieve project:"
+msgstr ""
+
+#: client/src/notifications/notifications.list.js:49
+msgid "Failure"
+msgstr ""
+
+#: client/src/lists/CompletedJobs.js:72
+#: client/src/lists/PortalJobs.js:43
+msgid "Finished"
+msgstr ""
+
+#: client/src/forms/Users.js:26
+#: client/src/lists/Users.js:31
+msgid "First Name"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:183
+#: client/src/forms/JobTemplates.js:197
+msgid "Forks"
+msgstr ""
+
+#: client/src/forms/Teams.js:115
+msgid "Granted Permissions"
+msgstr ""
+
+#: client/src/forms/Users.js:176
+msgid "Granted permissions"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:5
+msgid "Group all of your content to manage permissions across departments in your company."
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:324
+msgid "HTTP Headers"
+msgstr ""
+
+#: client/src/forms/Credentials.js:141
+#: client/src/notifications/notificationTemplates.form.js:72
+msgid "Host"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:131
+msgid "Host (Authentication URL)"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:323
+#: client/src/forms/JobTemplates.js:332
+msgid "Host Config Key"
+msgstr ""
+
+#: client/src/dashboard/counts/dashboard-counts.directive.js:39
+msgid "Hosts"
+msgstr ""
+
+#: client/src/license/license.partial.html:52
+msgid "Hosts Available"
+msgstr ""
+
+#: client/src/license/license.partial.html:64
+msgid "Hosts Remaining"
+msgstr ""
+
+#: client/src/license/license.partial.html:58
+msgid "Hosts Used"
+msgstr ""
+
+#: client/src/license/license.partial.html:116
+msgid "I agree to the End User License Agreement"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:104
+#: client/src/main-menu/main-menu.partial.html:27
+msgid "INVENTORIES"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:356
+msgid "IRC Nick"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:345
+msgid "IRC Server Address"
+msgstr ""
+
+#: client/src/notifications/shared/type-change.service.js:58
+msgid "IRC Server Password"
+msgstr ""
+
+#: client/src/notifications/shared/type-change.service.js:57
+msgid "IRC Server Port"
+msgstr ""
+
+#: client/src/license/license.partial.html:70
+msgid "If you are ready to upgrade, please contact us by clicking the button below"
+msgstr ""
+
+#: client/src/license/license.partial.html:11
+msgid "Invalid License"
+msgstr ""
+
+#: client/src/dashboard/counts/dashboard-counts.directive.js:50
+#: client/src/lists/Inventories.js:16
+#: client/src/lists/Inventories.js:17
+msgid "Inventories"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:67
+#: client/src/forms/JobTemplates.js:79
+msgid "Inventory"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.list.js:12
+#: client/src/setup-menu/setup-menu.partial.html:34
+msgid "Inventory Scripts"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:46
+#: client/src/lists/ScheduledJobs.js:56
+msgid "Inventory Sync"
+msgstr ""
+
+#: client/src/dashboard/counts/dashboard-counts.directive.js:55
+msgid "Inventory Sync Failures"
+msgstr ""
+
+#: client/src/forms/Inventories.js:68
+msgid "Inventory Variables"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:4
+msgid "JOB STATUS"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:113
+#: client/src/main-menu/main-menu.partial.html:35
+msgid "JOB TEMPLATES"
+msgstr ""
+
+#: client/src/app.js:284
+#: client/src/dashboard/graphs/job-status/job-status-graph.directive.js:113
+#: client/src/main-menu/main-menu.partial.html:122
+#: client/src/main-menu/main-menu.partial.html:43
+msgid "JOBS"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:235
+#: client/src/forms/JobTemplates.js:245
+msgid "Job Tags"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:16
+#: client/src/lists/JobTemplates.js:17
+#: client/src/lists/PortalJobTemplates.js:15
+#: client/src/lists/PortalJobTemplates.js:16
+msgid "Job Templates"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:32
+#: client/src/forms/JobTemplates.js:44
+#: client/src/forms/JobTemplates.js:56
+msgid "Job Type"
+msgstr ""
+
+#: client/src/lists/CompletedJobs.js:81
+msgid "Job failed?"
+msgstr ""
+
+#: client/src/lists/PortalJobs.js:15
+#: client/src/lists/PortalJobs.js:19
+#: client/src/partials/jobs.html:10
+msgid "Jobs"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:83
+msgid "LOG OUT"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:270
+msgid "Label to be shown with notification"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:337
+#: client/src/forms/JobTemplates.js:344
+#: client/src/lists/JobTemplates.js:42
+msgid "Labels"
+msgstr ""
+
+#: client/src/forms/Users.js:34
+#: client/src/lists/Users.js:35
+msgid "Last Name"
+msgstr ""
+
+#: client/src/lists/Projects.js:53
+msgid "Last Updated"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:69
+#: client/src/lists/PortalJobTemplates.js:40
+#: client/src/shared/form-generator.js:1748
+msgid "Launch"
+msgstr ""
+
+#: client/src/management-jobs/card/card.partial.html:21
+msgid "Launch Management Job"
+msgstr ""
+
+#: client/src/license/license.controller.js:97
+#: client/src/license/license.partial.html:8
+msgid "License"
+msgstr ""
+
+#: client/src/license/license.partial.html:102
+msgid "License File"
+msgstr ""
+
+#: client/src/license/license.partial.html:33
+msgid "License Key"
+msgstr ""
+
+#: client/src/license/license.controller.js:97
+msgid "License Management"
+msgstr ""
+
+#: client/src/license/license.partial.html:21
+msgid "License Type"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:203
+#: client/src/forms/JobTemplates.js:211
+msgid "Limit"
+msgstr ""
+
+#: client/src/shared/Socket.js:59
+msgid "Live events: attempting to connect to the Tower server."
+msgstr ""
+
+#: client/src/shared/Socket.js:62
+msgid "Live events: connected. Pages containing job status information will automatically update in real-time."
+msgstr ""
+
+#: client/src/shared/Socket.js:56
+msgid "Live events: error connecting to the Tower server."
+msgstr ""
+
+#: client/src/shared/list-generator/list-generator.factory.js:584
+msgid "Loading..."
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:188
+msgid "Log Out"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:129
+msgid "Machine Credential"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:29
+msgid "Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info."
+msgstr ""
+
+#: client/src/helpers/Credentials.js:111
+msgid "Management Certificate"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:59
+msgid "Management Job"
+msgstr ""
+
+#: client/src/management-jobs/card/card.partial.html:4
+#: client/src/setup-menu/setup-menu.partial.html:28
+msgid "Management Jobs"
+msgstr ""
+
+#: client/src/controllers/Projects.js:79
+msgid "Manual projects do not require a schedule"
+msgstr ""
+
+#: client/src/controllers/Projects.js:78
+msgid "Manual projects do not require an SCM update"
+msgstr ""
+
+#: client/src/portal-mode/portal-mode-jobs.partial.html:4
+msgid "My Jobs"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:160
+msgid "My View"
+msgstr ""
+
+#: client/src/dashboard/hosts/dashboard-hosts.list.js:18
+msgid "NO HOSTS FOUND"
+msgstr ""
+
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:14
+#: client/src/dashboard/lists/jobs/jobs-list.partial.html:13
+#: client/src/forms/Credentials.js:32
+#: client/src/forms/Inventories.js:26
+#: client/src/forms/JobTemplates.js:28
+#: client/src/forms/Organizations.js:25
+#: client/src/forms/Projects.js:30
+#: client/src/forms/Teams.js:123
+#: client/src/forms/Teams.js:25
+#: client/src/forms/Users.js:183
+#: client/src/inventory-scripts/inventory-scripts.form.js:23
+#: client/src/inventory-scripts/inventory-scripts.list.js:20
+#: client/src/lists/CompletedJobs.js:55
+#: client/src/lists/Credentials.js:29
+#: client/src/lists/Inventories.js:47
+#: client/src/lists/JobTemplates.js:26
+#: client/src/lists/PortalJobTemplates.js:24
+#: client/src/lists/PortalJobs.js:36
+#: client/src/lists/Projects.js:41
+#: client/src/lists/ScheduledJobs.js:33
+#: client/src/lists/Teams.js:26
+#: client/src/notifications/notificationTemplates.form.js:26
+#: client/src/notifications/notificationTemplates.list.js:33
+#: client/src/notifications/notifications.list.js:26
+msgid "Name"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:168
+#: client/src/forms/JobTemplates.js:177
+msgid "Network Credential"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.form.js:16
+msgid "New Custom Inventory"
+msgstr ""
+
+#: client/src/forms/Inventories.js:18
+msgid "New Inventory"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:20
+msgid "New Job Template"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:16
+msgid "New Notification Template"
+msgstr ""
+
+#: client/src/forms/Organizations.js:18
+msgid "New Organization"
+msgstr ""
+
+#: client/src/forms/Projects.js:18
+msgid "New Project"
+msgstr ""
+
+#: client/src/forms/Teams.js:18
+msgid "New Team"
+msgstr ""
+
+#: client/src/forms/Users.js:18
+msgid "New User"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:64
+msgid "Next Run"
+msgstr ""
+
+#: client/src/lists/Inventories.js:82
+msgid "No"
+msgstr ""
+
+#: client/src/lists/Credentials.js:24
+msgid "No Credentials Have Been Created"
+msgstr ""
+
+#: client/src/controllers/Projects.js:232
+msgid "No SCM Configuration"
+msgstr ""
+
+#: client/src/controllers/Projects.js:223
+msgid "No Updates Available"
+msgstr ""
+
+#: client/src/lists/CompletedJobs.js:22
+msgid "No completed jobs"
+msgstr ""
+
+#: client/src/license/license.controller.js:96
+msgid "No file selected."
+msgstr ""
+
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:56
+msgid ""
+"No job templates were recently used.
\n"
+" You can create a job template here."
+msgstr ""
+
+#: client/src/dashboard/lists/jobs/jobs-list.partial.html:46
+msgid "No jobs were recently run."
+msgstr ""
+
+#: client/src/forms/Teams.js:120
+#: client/src/forms/Users.js:180
+msgid "No permissions have been granted"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:18
+msgid "No schedules exist"
+msgstr ""
+
+#: client/src/controllers/Users.js:16
+msgid "Normal User"
+msgstr ""
+
+#: client/src/controllers/Projects.js:81
+msgid "Not configured for SCM"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:293
+msgid "Notification Color"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.list.js:14
+msgid "Notification Templates"
+msgstr ""
+
+#: client/src/notifications/notifications.list.js:17
+#: client/src/setup-menu/setup-menu.partial.html:41
+msgid "Notifications"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:306
+msgid "Notify Channel"
+msgstr ""
+
+#: client/src/organizations/list/organizations-list.partial.html:6
+msgid "ORGANIZATIONS"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:275
+#: client/src/notifications/notificationTemplates.form.js:391
+msgid "Options"
+msgstr ""
+
+#: client/src/forms/Credentials.js:50
+#: client/src/forms/Credentials.js:56
+#: client/src/forms/Inventories.js:42
+#: client/src/forms/Projects.js:45
+#: client/src/forms/Projects.js:54
+#: client/src/forms/Teams.js:40
+#: client/src/forms/Users.js:60
+#: client/src/inventory-scripts/inventory-scripts.form.js:38
+#: client/src/inventory-scripts/inventory-scripts.list.js:30
+#: client/src/lists/Inventories.js:53
+#: client/src/lists/Teams.js:36
+#: client/src/notifications/notificationTemplates.form.js:41
+msgid "Organization"
+msgstr ""
+
+#: client/src/forms/Users.js:131
+#: client/src/setup-menu/setup-menu.partial.html:4
+msgid "Organizations"
+msgstr ""
+
+#: client/src/lists/Credentials.js:47
+msgid "Owners"
+msgstr ""
+
+#: client/src/login/loginModal/loginModal.partial.html:64
+msgid "PASSWORD"
+msgstr ""
+
+#: client/src/organizations/list/organizations-list.partial.html:26
+#: client/src/shared/list-generator/list-generator.factory.js:415
+msgid "PLEASE ADD ITEMS TO THIS LIST"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:67
+msgid "PORTAL MODE"
+msgstr ""
+
+#: client/src/app.js:297
+#: client/src/main-menu/main-menu.partial.html:19
+#: client/src/main-menu/main-menu.partial.html:95
+msgid "PROJECTS"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:237
+msgid "Pagerduty subdomain"
+msgstr ""
+
+#: client/src/forms/Credentials.js:228
+#: client/src/forms/Users.js:75
+#: client/src/helpers/Credentials.js:119
+#: client/src/helpers/Credentials.js:127
+#: client/src/helpers/Credentials.js:147
+#: client/src/helpers/Credentials.js:156
+#: client/src/helpers/Credentials.js:165
+#: client/src/helpers/Credentials.js:45
+#: client/src/helpers/Credentials.js:94
+#: client/src/notifications/shared/type-change.service.js:28
+msgid "Password"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:73
+msgid "Password (API Key)"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:20
+msgid "Past 24 Hours"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:15
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:26
+msgid "Past Month"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:23
+msgid "Past Week"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:102
+msgid "Paste the contents of the PEM file associated with the service account email."
+msgstr ""
+
+#: client/src/helpers/Credentials.js:114
+msgid "Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console."
+msgstr ""
+
+#: client/src/helpers/Credentials.js:66
+msgid "Paste the contents of the SSH private key file."
+msgstr ""
+
+#: client/src/helpers/Credentials.js:41
+msgid "Paste the contents of the SSH private key file.esc or click to close"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:8
+msgid "Period"
+msgstr ""
+
+#: client/src/forms/Credentials.js:442
+#: client/src/forms/Inventories.js:97
+#: client/src/forms/JobTemplates.js:418
+#: client/src/forms/Organizations.js:63
+#: client/src/forms/Projects.js:236
+msgid "Permissions"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:112
+#: client/src/forms/JobTemplates.js:123
+msgid "Playbook"
+msgstr ""
+
+#: client/src/forms/Projects.js:94
+msgid "Playbook Directory"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:52
+#: client/src/lists/ScheduledJobs.js:57
+msgid "Playbook Run"
+msgstr ""
+
+#: client/src/license/license.partial.html:84
+msgid "Please click the button below to visit Ansible's website to get a Tower license key."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1026
+#: client/src/shared/form-generator.js:896
+msgid "Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1014
+#: client/src/shared/form-generator.js:884
+msgid "Please enter a valid email address."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1009
+#: client/src/shared/form-generator.js:879
+msgid "Please enter a value."
+msgstr ""
+
+#: client/src/lists/CompletedJobs.js:13
+msgid "Please save and run a job to view"
+msgstr ""
+
+#: client/src/notifications/notifications.list.js:15
+msgid "Please save before adding notifications"
+msgstr ""
+
+#: client/src/forms/Teams.js:74
+msgid "Please save before adding users"
+msgstr ""
+
+#: client/src/forms/Inventories.js:93
+#: client/src/forms/JobTemplates.js:414
+#: client/src/forms/Organizations.js:60
+#: client/src/forms/Projects.js:232
+#: client/src/forms/Teams.js:112
+msgid "Please save before assigning permissions"
+msgstr ""
+
+#: client/src/forms/Users.js:128
+#: client/src/forms/Users.js:172
+msgid "Please save before assigning to organizations"
+msgstr ""
+
+#: client/src/forms/Users.js:151
+msgid "Please save before assigning to teams"
+msgstr ""
+
+#: client/src/notifications/shared/type-change.service.js:27
+msgid "Port"
+msgstr ""
+
+#: client/src/forms/Credentials.js:260
+#: client/src/helpers/Credentials.js:36
+#: client/src/helpers/Credentials.js:60
+msgid "Private Key"
+msgstr ""
+
+#: client/src/forms/Credentials.js:267
+msgid "Private Key Passphrase"
+msgstr ""
+
+#: client/src/forms/Credentials.js:284
+#: client/src/forms/Credentials.js:288
+msgid "Privilege Escalation"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:90
+msgid "Privilege Escalation Password"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:89
+msgid "Privilege Escalation Username"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:106
+#: client/src/forms/JobTemplates.js:90
+#: client/src/helpers/Credentials.js:103
+msgid "Project"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:132
+msgid "Project (Tenant Name)"
+msgstr ""
+
+#: client/src/forms/Projects.js:80
+#: client/src/forms/Projects.js:88
+msgid "Project Base Path"
+msgstr ""
+
+#: client/src/forms/Credentials.js:370
+msgid "Project Name"
+msgstr ""
+
+#: client/src/forms/Projects.js:106
+msgid "Project Path"
+msgstr ""
+
+#: client/src/dashboard/counts/dashboard-counts.directive.js:66
+msgid "Project Sync Failures"
+msgstr ""
+
+#: client/src/controllers/Projects.js:243
+msgid "Project lookup failed. GET returned:"
+msgstr ""
+
+#: client/src/dashboard/counts/dashboard-counts.directive.js:61
+#: client/src/lists/Projects.js:15
+#: client/src/lists/Projects.js:16
+msgid "Projects"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:147
+#: client/src/forms/JobTemplates.js:216
+#: client/src/forms/JobTemplates.js:250
+#: client/src/forms/JobTemplates.js:270
+#: client/src/forms/JobTemplates.js:370
+msgid "Prompt on launch"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:308
+#: client/src/forms/JobTemplates.js:318
+msgid "Provisioning Callback URL"
+msgstr ""
+
+#: client/src/dashboard/lists/jobs/jobs-list.partial.html:4
+msgid "RECENT JOB RUNS"
+msgstr ""
+
+#: client/src/dashboard/lists/jobs/jobs-list.partial.html:42
+msgid "RECENTLY RUN JOBS"
+msgstr ""
+
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:4
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:52
+msgid "RECENTLY USED JOB TEMPLATES"
+msgstr ""
+
+#: client/src/lists/Projects.js:77
+#: client/src/partials/jobs.html:20
+#: client/src/portal-mode/portal-mode-jobs.partial.html:12
+msgid "REFRESH"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:98
+msgid "RSA Private Key"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:94
+#: client/src/notifications/notificationTemplates.form.js:99
+msgid "Recipient List"
+msgstr ""
+
+#: client/src/bread-crumb/bread-crumb.partial.html:6
+#: client/src/lists/Projects.js:73
+#: client/src/partials/jobs.html:19
+msgid "Refresh the page"
+msgstr ""
+
+#: client/src/forms/Teams.js:141
+#: client/src/forms/Users.js:201
+msgid "Remove"
+msgstr ""
+
+#: client/src/license/license.partial.html:89
+msgid "Request License"
+msgstr ""
+
+#: client/src/forms/Credentials.js:466
+#: client/src/forms/Inventories.js:121
+#: client/src/forms/Organizations.js:87
+#: client/src/forms/Teams.js:101
+#: client/src/forms/Teams.js:134
+#: client/src/forms/Users.js:194
+msgid "Role"
+msgstr ""
+
+#: client/src/forms/Projects.js:161
+msgid "SCM Clean"
+msgstr ""
+
+#: client/src/forms/Projects.js:138
+msgid "SCM Credential"
+msgstr ""
+
+#: client/src/forms/Projects.js:174
+msgid "SCM Delete"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:93
+msgid "SCM Private Key"
+msgstr ""
+
+#: client/src/forms/Projects.js:60
+msgid "SCM Type"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:49
+#: client/src/forms/Projects.js:186
+#: client/src/lists/ScheduledJobs.js:58
+msgid "SCM Update"
+msgstr ""
+
+#: client/src/controllers/Projects.js:284
+msgid "SCM Update Cancel"
+msgstr ""
+
+#: client/src/forms/Projects.js:150
+msgid "SCM Update Options"
+msgstr ""
+
+#: client/src/controllers/Projects.js:70
+msgid "SCM update currently running"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:59
+msgid "SETTINGS"
+msgstr ""
+
+#: client/src/login/loginModal/loginModal.partial.html:93
+msgid "SIGN IN"
+msgstr ""
+
+#: client/src/app.js:518
+msgid "SOCKETS"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:166
+msgid "SSH Key"
+msgstr ""
+
+#: client/src/forms/Credentials.js:258
+msgid "SSH key description"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:384
+msgid "SSL Connection"
+msgstr ""
+
+#: client/src/forms/Credentials.js:122
+#: client/src/forms/Credentials.js:129
+msgid "STS Token"
+msgstr ""
+
+#: client/src/dashboard/graphs/job-status/job-status-graph.directive.js:64
+msgid "SUCCESSFUL"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:149
+msgid "Satellite 6 Host"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1736
+msgid "Save"
+msgstr ""
+
+#: client/src/license/license.partial.html:122
+msgid "Save successful!"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:77
+msgid "Schedule"
+msgstr ""
+
+#: client/src/management-jobs/card/card.partial.html:26
+msgid "Schedule Management Job"
+msgstr ""
+
+#: client/src/controllers/Projects.js:62
+msgid "Schedule future SCM updates"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:80
+msgid "Schedule future job template runs"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:15
+msgid "Scheduled Jobs"
+msgstr ""
+
+#: client/src/partials/jobs.html:15
+msgid "Schedules"
+msgstr ""
+
+#: client/src/forms/Credentials.js:107
+msgid "Secret Key"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:83
+msgid "Sender Email"
+msgstr ""
+
+#: client/src/helpers/Credentials.js:97
+msgid "Service Account Email Address"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:147
+msgid "Settings"
+msgstr ""
+
+#: client/src/shared/form-generator.js:912
+msgid "Show"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:255
+#: client/src/forms/JobTemplates.js:265
+msgid "Skip Tags"
+msgstr ""
+
+#: client/src/forms/Projects.js:24
+msgid "Source Details"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:196
+msgid "Source Phone Number"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:17
+msgid "Split up your organization to associate content and control permissions for groups."
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:72
+#: client/src/lists/PortalJobTemplates.js:43
+msgid "Start a job using this template"
+msgstr ""
+
+#: client/src/controllers/Projects.js:61
+msgid "Start an SCM update"
+msgstr ""
+
+#: client/src/license/license.partial.html:121
+msgid "Submit"
+msgstr ""
+
+#: client/src/license/license.partial.html:27
+msgid "Subscription"
+msgstr ""
+
+#: client/src/forms/Credentials.js:153
+#: client/src/forms/Credentials.js:164
+msgid "Subscription ID"
+msgstr ""
+
+#: client/src/notifications/notifications.list.js:38
+msgid "Success"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:77
+msgid "Successful"
+msgstr ""
+
+#: client/src/lists/Inventories.js:76
+msgid "Sync failures?"
+msgstr ""
+
+#: client/src/controllers/Users.js:18
+msgid "System Administrator"
+msgstr ""
+
+#: client/src/controllers/Users.js:17
+msgid "System Auditor"
+msgstr ""
+
+#: client/src/app.js:346
+msgid "TEAMS"
+msgstr ""
+
+#: client/src/dashboard/graphs/job-status/job-status-graph.directive.js:106
+msgid "TIME"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:313
+msgid "Target URL"
+msgstr ""
+
+#: client/src/forms/Credentials.js:473
+#: client/src/forms/Inventories.js:128
+#: client/src/forms/Organizations.js:94
+msgid "Team Roles"
+msgstr ""
+
+#: client/src/forms/Users.js:154
+#: client/src/lists/Teams.js:16
+#: client/src/lists/Teams.js:17
+#: client/src/setup-menu/setup-menu.partial.html:16
+msgid "Teams"
+msgstr ""
+
+#: client/src/forms/Credentials.js:342
+msgid "Tenant ID"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.list.js:65
+msgid "Test notification"
+msgstr ""
+
+#: client/src/controllers/Projects.js:927
+msgid "The SCM update process is running."
+msgstr ""
+
+#: client/src/controllers/Projects.js:232
+msgid "The selected project is not configured for SCM. To configure for SCM, edit the project and provide SCM settings, and then run an update."
+msgstr ""
+
+#: client/src/lists/PortalJobTemplates.js:20
+msgid "There are no job templates to display at this time"
+msgstr ""
+
+#: client/src/lists/PortalJobs.js:20
+msgid "There are no jobs to display at this time"
+msgstr ""
+
+#: client/src/controllers/Projects.js:223
+msgid "There is no SCM update information available for this project. An update has not yet been completed. If you have not already done so, start an update for this project."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1020
+#: client/src/shared/form-generator.js:890
+msgid "This value does not match the password you entered previously. Please confirm that password."
+msgstr ""
+
+#: client/src/dashboard/lists/jobs/jobs-list.partial.html:14
+msgid "Time"
+msgstr ""
+
+#: client/src/license/license.partial.html:45
+msgid "Time Remaining"
+msgstr ""
+
+#: client/src/shared/form-generator.js:929
+msgid "Toggle the display of plaintext."
+msgstr ""
+
+#: client/src/notifications/shared/type-change.service.js:34
+#: client/src/notifications/shared/type-change.service.js:40
+msgid "Token"
+msgstr ""
+
+#: client/src/forms/Credentials.js:62
+#: client/src/forms/Credentials.js:87
+#: client/src/forms/Teams.js:129
+#: client/src/forms/Users.js:189
+#: client/src/lists/CompletedJobs.js:63
+#: client/src/lists/Credentials.js:39
+#: client/src/lists/Projects.js:46
+#: client/src/lists/ScheduledJobs.js:44
+#: client/src/notifications/notificationTemplates.form.js:53
+#: client/src/notifications/notificationTemplates.list.js:38
+#: client/src/notifications/notifications.list.js:31
+msgid "Type"
+msgstr ""
+
+#: client/src/forms/Credentials.js:23
+#: client/src/notifications/notificationTemplates.form.js:21
+msgid "Type Details"
+msgstr ""
+
+#: client/src/login/loginModal/loginModal.partial.html:45
+msgid "USERNAME"
+msgstr ""
+
+#: client/src/app.js:468
+msgid "USERS"
+msgstr ""
+
+#: client/src/controllers/Projects.js:328
+msgid "Update Not Found"
+msgstr ""
+
+#: client/src/forms/Projects.js:181
+msgid "Update on Launch"
+msgstr ""
+
+#: client/src/license/license.partial.html:71
+msgid "Upgrade"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:404
+msgid "Use SSL"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.form.js:397
+msgid "Use TLS"
+msgstr ""
+
+#: client/src/forms/Credentials.js:461
+#: client/src/forms/Inventories.js:116
+#: client/src/forms/Organizations.js:82
+#: client/src/forms/Teams.js:96
+msgid "User"
+msgstr ""
+
+#: client/src/forms/Users.js:99
+msgid "User Type"
+msgstr ""
+
+#: client/src/forms/Users.js:50
+#: client/src/helpers/Credentials.js:117
+#: client/src/helpers/Credentials.js:32
+#: client/src/helpers/Credentials.js:56
+#: client/src/helpers/Credentials.js:88
+#: client/src/lists/Users.js:27
+#: client/src/notifications/notificationTemplates.form.js:64
+msgid "Username"
+msgstr ""
+
+#: client/src/forms/Teams.js:77
+#: client/src/lists/Users.js:16
+#: client/src/lists/Users.js:17
+#: client/src/setup-menu/setup-menu.partial.html:10
+msgid "Users"
+msgstr ""
+
+#: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:7
+#: client/src/dashboard/lists/jobs/jobs-list.partial.html:7
+msgid "VIEW ALL"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:75
+msgid "VIEW DOCUMENTATION"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:51
+msgid "VIEW USER PAGE FOR {{ $root.current_user.username | uppercase }}"
+msgstr ""
+
+#: client/src/license/license.partial.html:10
+msgid "Valid License"
+msgstr ""
+
+#: client/src/forms/Inventories.js:54
+msgid "Variables"
+msgstr ""
+
+#: client/src/forms/Credentials.js:400
+msgid "Vault Password"
+msgstr ""
+
+#: client/src/forms/JobTemplates.js:221
+#: client/src/forms/JobTemplates.js:229
+msgid "Verbosity"
+msgstr ""
+
+#: client/src/about/about.controller.js:24
+#: client/src/license/license.partial.html:15
+msgid "Version"
+msgstr ""
+
+#: client/src/dashboard/graphs/dashboard-graphs.partial.html:58
+#: client/src/inventory-scripts/inventory-scripts.list.js:65
+#: client/src/lists/Credentials.js:82
+#: client/src/lists/Inventories.js:112
+#: client/src/lists/JobTemplates.js:101
+#: client/src/lists/Teams.js:70
+#: client/src/lists/Users.js:68
+#: client/src/notifications/notificationTemplates.list.js:80
+msgid "View"
+msgstr ""
+
+#: client/src/main-menu/main-menu.partial.html:173
+msgid "View Documentation"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1760
+msgid "View Survey"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:47
+msgid "View Your License"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:48
+msgid "View and edit your license information."
+msgstr ""
+
+#: client/src/lists/Credentials.js:84
+msgid "View credential"
+msgstr ""
+
+#: client/src/setup-menu/setup-menu.partial.html:54
+msgid "View information about this version of Ansible Tower."
+msgstr ""
+
+#: client/src/lists/Inventories.js:114
+msgid "View inventory"
+msgstr ""
+
+#: client/src/inventory-scripts/inventory-scripts.list.js:67
+msgid "View inventory script"
+msgstr ""
+
+#: client/src/notifications/notificationTemplates.list.js:82
+msgid "View notification"
+msgstr ""
+
+#: client/src/lists/Teams.js:73
+msgid "View team"
+msgstr ""
+
+#: client/src/lists/JobTemplates.js:103
+msgid "View template"
+msgstr ""
+
+#: client/src/lists/Projects.js:109
+msgid "View the project"
+msgstr ""
+
+#: client/src/lists/ScheduledJobs.js:88
+msgid "View the schedule"
+msgstr ""
+
+#: client/src/lists/Users.js:71
+msgid "View user"
+msgstr ""
+
+#: client/src/login/loginModal/loginModal.partial.html:13
+msgid "Welcome to Ansible Tower! Please sign in."
+msgstr ""
+
+#: client/src/license/license.partial.html:78
+msgid "Welcome to Ansible Tower! Please complete the steps below to acquire a license."
+msgstr ""
+
+#: client/src/lists/Inventories.js:79
+msgid "Yes"
+msgstr ""
+
+#: client/src/shared/form-generator.js:1034
+msgid "Your password must be %d characters long."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1040
+msgid "Your password must contain a lowercase letter."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1052
+msgid "Your password must contain a number."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1046
+msgid "Your password must contain an uppercase letter."
+msgstr ""
+
+#: client/src/shared/form-generator.js:1058
+msgid "Your password must contain one of the following characters: %s"
+msgstr ""
+
+#: client/src/controllers/Projects.js:284
+msgid "Your request to cancel the update was submitted to the task manager."
+msgstr ""
+
+#: client/src/forms/Credentials.js:140
+#: client/src/forms/Credentials.js:369
+msgid "set in helpers/credentials"
+msgstr ""
diff --git a/awx/ui/webpack.config.js b/awx/ui/webpack.config.js
index 57d8dd880c..e598f4154a 100644
--- a/awx/ui/webpack.config.js
+++ b/awx/ui/webpack.config.js
@@ -7,6 +7,7 @@ var vendorPkgs = [
'angular-codemirror',
'angular-cookies',
'angular-drag-and-drop-lists',
+ 'angular-gettext',
'angular-md5',
'angular-moment',
'angular-sanitize',