summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile21
-rw-r--r--awx/api/metadata.py19
-rw-r--r--awx/api/serializers.py32
-rw-r--r--awx/api/views.py47
-rw-r--r--awx/main/access.py29
-rw-r--r--awx/main/views.py19
-rw-r--r--awx/settings/defaults.py99
-rw-r--r--awx/sso/pipeline.py7
-rw-r--r--awx/templates/rest_framework/api.html6
-rw-r--r--awx/templates/rest_framework/base.html24
-rw-r--r--awx/ui/templates/ui/index.html23
12 files changed, 182 insertions, 145 deletions
diff --git a/.gitignore b/.gitignore
index afd8aa7187..ca9dd12298 100644
--- a/.gitignore
+++ b/.gitignore
@@ -106,6 +106,7 @@ reports
*.log.[0-9]
*.results
local/
+*.mo
# AWX python libs populated by requirements.txt
awx/lib/.deps_built
diff --git a/Makefile b/Makefile
index 83db7eb6ea..49e9e64d89 100644
--- a/Makefile
+++ b/Makefile
@@ -493,6 +493,25 @@ test_tox:
# Alias existing make target so old versions run against Jekins the same way
test_jenkins : test_coverage
+# l10n TASKS
+# --------------------------------------
+
+LANG = "en-us"
+messages:
+ @if [ "$(VENV_BASE)" ]; then \
+ . $(VENV_BASE)/tower/bin/activate; \
+ fi; \
+ $(PYTHON) manage.py makemessages -l $(LANG)
+
+languages:
+ @if [ "$(VENV_BASE)" ]; then \
+ . $(VENV_BASE)/tower/bin/activate; \
+ fi; \
+ $(PYTHON) manage.py compilemessages
+
+# End l10n TASKS
+# --------------------------------------
+
# UI TASKS
# --------------------------------------
@@ -506,7 +525,7 @@ ui-docker-machine: $(UI_DEPS_FLAG_FILE)
ui-docker: $(UI_DEPS_FLAG_FILE)
$(NPM_BIN) --prefix awx/ui run build-docker-cid
-ui-release: $(UI_RELEASE_FLAG_FILE)
+ui-release: languages $(UI_RELEASE_FLAG_FILE)
$(UI_RELEASE_FLAG_FILE): $(UI_DEPS_FLAG_FILE)
$(NPM_BIN) --prefix awx/ui run build-release
diff --git a/awx/api/metadata.py b/awx/api/metadata.py
index b329d83793..54850f285e 100644
--- a/awx/api/metadata.py
+++ b/awx/api/metadata.py
@@ -7,6 +7,7 @@ from collections import OrderedDict
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.utils.encoding import force_text, smart_text
+from django.utils.translation import ugettext_lazy as _
# Django REST Framework
from rest_framework import exceptions
@@ -46,15 +47,15 @@ class Metadata(metadata.SimpleMetadata):
serializer = getattr(field, 'parent', None)
if serializer:
field_help_text = {
- 'id': 'Database ID for this {}.',
- 'name': 'Name of this {}.',
- 'description': 'Optional description of this {}.',
- 'type': 'Data type for this {}.',
- 'url': 'URL for this {}.',
- 'related': 'Data structure with URLs of related resources.',
- 'summary_fields': 'Data structure with name/description for related resources.',
- 'created': 'Timestamp when this {} was created.',
- 'modified': 'Timestamp when this {} was last modified.',
+ 'id': _('Database ID for this {}.'),
+ 'name': _('Name of this {}.'),
+ 'description': _('Optional description of this {}.'),
+ 'type': _('Data type for this {}.'),
+ 'url': _('URL for this {}.'),
+ 'related': _('Data structure with URLs of related resources.'),
+ 'summary_fields': _('Data structure with name/description for related resources.'),
+ 'created': _('Timestamp when this {} was created.'),
+ 'modified': _('Timestamp when this {} was last modified.'),
}
if field.field_name in field_help_text:
if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'model'):
diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index ded5d454c3..7e507015b4 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -20,7 +20,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist, ValidationError as DjangoValidationError
from django.db import models
-# from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import force_text
from django.utils.text import capfirst
@@ -694,9 +694,9 @@ class UnifiedJobStdoutSerializer(UnifiedJobSerializer):
class UserSerializer(BaseSerializer):
password = serializers.CharField(required=False, default='', write_only=True,
- help_text='Write-only field used to change the password.')
+ help_text=_('Write-only field used to change the password.'))
ldap_dn = serializers.CharField(source='profile.ldap_dn', read_only=True)
- external_account = serializers.SerializerMethodField(help_text='Set if the account is managed by an external service')
+ external_account = serializers.SerializerMethodField(help_text=_('Set if the account is managed by an external service'))
is_system_auditor = serializers.BooleanField(default=False)
show_capabilities = ['edit', 'delete']
@@ -961,7 +961,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
class ProjectPlaybooksSerializer(ProjectSerializer):
- playbooks = serializers.ReadOnlyField(help_text='Array of playbooks available within this project.')
+ playbooks = serializers.ReadOnlyField(help_text=_('Array of playbooks available within this project.'))
class Meta:
model = Project
@@ -1717,18 +1717,18 @@ class CredentialSerializerCreate(CredentialSerializer):
user = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all(),
required=False, default=None, write_only=True, allow_null=True,
- help_text='Write-only field used to add user to owner role. If provided, '
- 'do not give either team or organization. Only valid for creation.')
+ help_text=_('Write-only field used to add user to owner role. If provided, '
+ 'do not give either team or organization. Only valid for creation.'))
team = serializers.PrimaryKeyRelatedField(
queryset=Team.objects.all(),
required=False, default=None, write_only=True, allow_null=True,
- help_text='Write-only field used to add team to owner role. If provided, '
- 'do not give either user or organization. Only valid for creation.')
+ help_text=_('Write-only field used to add team to owner role. If provided, '
+ 'do not give either user or organization. Only valid for creation.'))
organization = serializers.PrimaryKeyRelatedField(
queryset=Organization.objects.all(),
required=False, default=None, write_only=True, allow_null=True,
- help_text='Write-only field used to add organization to owner role. If provided, '
- 'do not give either team or team. Only valid for creation.')
+ help_text=_('Write-only field used to add organization to owner role. If provided, '
+ 'do not give either team or team. Only valid for creation.'))
class Meta:
model = Credential
@@ -2791,15 +2791,15 @@ class ActivityStreamSerializer(BaseSerializer):
ret = super(ActivityStreamSerializer, self).get_fields()
for key, field in ret.items():
if key == 'changes':
- field.help_text = 'A summary of the new and changed values when an object is created, updated, or deleted'
+ field.help_text = _('A summary of the new and changed values when an object is created, updated, or deleted')
if key == 'object1':
- field.help_text = ('For create, update, and delete events this is the object type that was affected. '
- 'For associate and disassociate events this is the object type associated or disassociated with object2.')
+ field.help_text = _('For create, update, and delete events this is the object type that was affected. '
+ 'For associate and disassociate events this is the object type associated or disassociated with object2.')
if key == 'object2':
- field.help_text = ('Unpopulated for create, update, and delete events. For associate and disassociate '
- 'events this is the object type that object1 is being associated with.')
+ field.help_text = _('Unpopulated for create, update, and delete events. For associate and disassociate '
+ 'events this is the object type that object1 is being associated with.')
if key == 'operation':
- field.help_text = 'The action taken with respect to the given object(s).'
+ field.help_text = _('The action taken with respect to the given object(s).')
return ret
def get_changes(self, obj):
diff --git a/awx/api/views.py b/awx/api/views.py
index b5e6c5a45a..b22dc60b53 100644
--- a/awx/api/views.py
+++ b/awx/api/views.py
@@ -29,6 +29,7 @@ from django.template.loader import render_to_string
from django.core.servers.basehttp import FileWrapper
from django.http import HttpResponse
from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext_lazy as _
# Django REST Framework
@@ -86,14 +87,14 @@ class ApiRootView(APIView):
authentication_classes = []
permission_classes = (AllowAny,)
- view_name = 'REST API'
+ view_name = _('REST API')
def get(self, request, format=None):
''' list supported API versions '''
current = reverse('api:api_v1_root_view', args=[])
data = dict(
- description = 'Ansible Tower REST API',
+ description = _('Ansible Tower REST API'),
current_version = current,
available_versions = dict(
v1 = current
@@ -105,7 +106,7 @@ class ApiV1RootView(APIView):
authentication_classes = []
permission_classes = (AllowAny,)
- view_name = 'Version 1'
+ view_name = _('Version 1')
def get(self, request, format=None):
''' list top level resources '''
@@ -156,7 +157,7 @@ class ApiV1PingView(APIView):
"""
permission_classes = (AllowAny,)
authentication_classes = ()
- view_name = 'Ping'
+ view_name = _('Ping')
new_in_210 = True
def get(self, request, format=None):
@@ -182,7 +183,7 @@ class ApiV1PingView(APIView):
class ApiV1ConfigView(APIView):
permission_classes = (IsAuthenticated,)
- view_name = 'Configuration'
+ view_name = _('Configuration')
def get(self, request, format=None):
'''Return various sitewide configuration settings.'''
@@ -278,7 +279,7 @@ class ApiV1ConfigView(APIView):
class DashboardView(APIView):
- view_name = "Dashboard"
+ view_name = _("Dashboard")
new_in_14 = True
def get(self, request, format=None):
@@ -383,7 +384,7 @@ class DashboardView(APIView):
class DashboardJobsGraphView(APIView):
- view_name = "Dashboard Jobs Graphs"
+ view_name = _("Dashboard Jobs Graphs")
new_in_200 = True
def get(self, request, format=None):
@@ -433,7 +434,7 @@ class DashboardJobsGraphView(APIView):
class ScheduleList(ListAPIView):
- view_name = "Schedules"
+ view_name = _("Schedules")
model = Schedule
serializer_class = ScheduleSerializer
new_in_148 = True
@@ -450,7 +451,7 @@ class ScheduleUnifiedJobsList(SubListAPIView):
serializer_class = UnifiedJobSerializer
parent_model = Schedule
relationship = 'unifiedjob_set'
- view_name = 'Schedule Jobs List'
+ view_name = _('Schedule Jobs List')
new_in_148 = True
class AuthView(APIView):
@@ -998,7 +999,7 @@ class ProjectTeamsList(ListAPIView):
class ProjectSchedulesList(SubListCreateAttachDetachAPIView):
- view_name = "Project Schedules"
+ view_name = _("Project Schedules")
model = Schedule
serializer_class = ScheduleSerializer
@@ -1162,7 +1163,7 @@ class UserMeList(ListAPIView):
model = User
serializer_class = UserSerializer
- view_name = 'Me'
+ view_name = _('Me')
def get_queryset(self):
return self.model.objects.filter(pk=self.request.user.pk)
@@ -2038,7 +2039,7 @@ class InventoryInventorySourcesList(SubListAPIView):
serializer_class = InventorySourceSerializer
parent_model = Inventory
relationship = None # Not defined since using get_queryset().
- view_name = 'Inventory Source List'
+ view_name = _('Inventory Source List')
new_in_14 = True
def get_queryset(self):
@@ -2071,7 +2072,7 @@ class InventorySourceDetail(RetrieveUpdateAPIView):
class InventorySourceSchedulesList(SubListCreateAttachDetachAPIView):
- view_name = "Inventory Source Schedules"
+ view_name = _("Inventory Source Schedules")
model = Schedule
serializer_class = ScheduleSerializer
@@ -2292,7 +2293,7 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
class JobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
- view_name = "Job Template Schedules"
+ view_name = _("Job Template Schedules")
model = Schedule
serializer_class = ScheduleSerializer
@@ -2823,7 +2824,7 @@ class SystemJobTemplateLaunch(GenericAPIView):
class SystemJobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
- view_name = "System Job Template Schedules"
+ view_name = _("System Job Template Schedules")
model = Schedule
serializer_class = ScheduleSerializer
@@ -2991,7 +2992,7 @@ class BaseJobHostSummariesList(SubListAPIView):
serializer_class = JobHostSummarySerializer
parent_model = None # Subclasses must define this attribute.
relationship = 'job_host_summaries'
- view_name = 'Job Host Summaries List'
+ view_name = _('Job Host Summaries List')
class HostJobHostSummariesList(BaseJobHostSummariesList):
@@ -3026,7 +3027,7 @@ class JobEventChildrenList(SubListAPIView):
serializer_class = JobEventSerializer
parent_model = JobEvent
relationship = 'children'
- view_name = 'Job Event Children List'
+ view_name = _('Job Event Children List')
class JobEventHostsList(SubListAPIView):
@@ -3034,7 +3035,7 @@ class JobEventHostsList(SubListAPIView):
serializer_class = HostSerializer
parent_model = JobEvent
relationship = 'hosts'
- view_name = 'Job Event Hosts List'
+ view_name = _('Job Event Hosts List')
class BaseJobEventsList(SubListAPIView):
@@ -3042,7 +3043,7 @@ class BaseJobEventsList(SubListAPIView):
serializer_class = JobEventSerializer
parent_model = None # Subclasses must define this attribute.
relationship = 'job_events'
- view_name = 'Job Events List'
+ view_name = _('Job Events List')
class HostJobEventsList(BaseJobEventsList):
@@ -3074,7 +3075,7 @@ class JobJobEventsList(BaseJobEventsList):
class JobJobPlaysList(BaseJobEventsList):
parent_model = Job
- view_name = 'Job Plays List'
+ view_name = _('Job Plays List')
new_in_200 = True
@paginated
@@ -3149,7 +3150,7 @@ class JobJobTasksList(BaseJobEventsList):
and their completion status.
"""
parent_model = Job
- view_name = 'Job Play Tasks List'
+ view_name = _('Job Play Tasks List')
new_in_200 = True
@paginated
@@ -3444,7 +3445,7 @@ class BaseAdHocCommandEventsList(SubListAPIView):
serializer_class = AdHocCommandEventSerializer
parent_model = None # Subclasses must define this attribute.
relationship = 'ad_hoc_command_events'
- view_name = 'Ad Hoc Command Events List'
+ view_name = _('Ad Hoc Command Events List')
new_in_220 = True
@@ -3660,7 +3661,7 @@ class NotificationTemplateDetail(RetrieveUpdateDestroyAPIView):
class NotificationTemplateTest(GenericAPIView):
- view_name = 'NotificationTemplate Test'
+ view_name = _('NotificationTemplate Test')
model = NotificationTemplate
serializer_class = EmptySerializer
new_in_300 = True
diff --git a/awx/main/access.py b/awx/main/access.py
index d70ce55af1..891a8b97e3 100644
--- a/awx/main/access.py
+++ b/awx/main/access.py
@@ -11,6 +11,7 @@ from django.conf import settings
from django.db.models import Q
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext_lazy as _
# Django REST Framework
from rest_framework.exceptions import ParseError, PermissionDenied, ValidationError
@@ -199,24 +200,24 @@ class BaseAccess(object):
validation_info['grace_period_remaining'] = 99999999
if check_expiration and validation_info.get('time_remaining', None) is None:
- raise PermissionDenied("License is missing.")
+ raise PermissionDenied(_("License is missing."))
if check_expiration and validation_info.get("grace_period_remaining") <= 0:
- raise PermissionDenied("License has expired.")
+ raise PermissionDenied(_("License has expired."))
free_instances = validation_info.get('free_instances', 0)
available_instances = validation_info.get('available_instances', 0)
if add_host and free_instances == 0:
- raise PermissionDenied("License count of %s instances has been reached." % available_instances)
+ raise PermissionDenied(_("License count of %s instances has been reached.") % available_instances)
elif add_host and free_instances < 0:
- raise PermissionDenied("License count of %s instances has been exceeded." % available_instances)
+ raise PermissionDenied(_("License count of %s instances has been exceeded.") % available_instances)
elif not add_host and free_instances < 0:
- raise PermissionDenied("Host count exceeds available instances.")
+ raise PermissionDenied(_("Host count exceeds available instances."))
if feature is not None:
if "features" in validation_info and not validation_info["features"].get(feature, False):
- raise LicenseForbids("Feature %s is not enabled in the active license." % feature)
+ raise LicenseForbids(_("Feature %s is not enabled in the active license.") % feature)
elif "features" not in validation_info:
- raise LicenseForbids("Features not found in active license.")
+ raise LicenseForbids(_("Features not found in active license."))
def get_user_capabilities(self, obj, method_list=[], parent_obj=None):
if obj is None:
@@ -535,7 +536,7 @@ class HostAccess(BaseAccess):
# Prevent moving a host to a different inventory.
inventory_pk = get_pk_from_dict(data, 'inventory')
if obj and inventory_pk and obj.inventory.pk != inventory_pk:
- raise PermissionDenied('Unable to change inventory on a host.')
+ raise PermissionDenied(_('Unable to change inventory on a host.'))
# Checks for admin or change permission on inventory, controls whether
# the user can edit variable data.
return obj and self.user in obj.inventory.admin_role
@@ -547,7 +548,7 @@ class HostAccess(BaseAccess):
return False
# Prevent assignments between different inventories.
if obj.inventory != sub_obj.inventory:
- raise ParseError('Cannot associate two items from different inventories.')
+ raise ParseError(_('Cannot associate two items from different inventories.'))
return True
def can_delete(self, obj):
@@ -581,7 +582,7 @@ class GroupAccess(BaseAccess):
# Prevent moving a group to a different inventory.
inventory_pk = get_pk_from_dict(data, 'inventory')
if obj and inventory_pk and obj.inventory.pk != inventory_pk:
- raise PermissionDenied('Unable to change inventory on a group.')
+ raise PermissionDenied(_('Unable to change inventory on a group.'))
# Checks for admin or change permission on inventory, controls whether
# the user can attach subgroups or edit variable data.
return obj and self.user in obj.inventory.admin_role
@@ -593,7 +594,7 @@ class GroupAccess(BaseAccess):
return False
# Prevent assignments between different inventories.
if obj.inventory != sub_obj.inventory:
- raise ParseError('Cannot associate two items from different inventories.')
+ raise ParseError(_('Cannot associate two items from different inventories.'))
# Prevent group from being assigned as its own (grand)child.
if type(obj) == type(sub_obj):
parent_pks = set(obj.all_parents.values_list('pk', flat=True))
@@ -805,7 +806,7 @@ class TeamAccess(BaseAccess):
# Prevent moving a team to a different organization.
org_pk = get_pk_from_dict(data, 'organization')
if obj and org_pk and obj.organization.pk != org_pk:
- raise PermissionDenied('Unable to change organization on a team.')
+ raise PermissionDenied(_('Unable to change organization on a team.'))
if self.user.is_superuser:
return True
return self.user in obj.admin_role
@@ -818,9 +819,9 @@ class TeamAccess(BaseAccess):
of a resource role to the team."""
if isinstance(sub_obj, Role):
if sub_obj.content_object is None:
- raise PermissionDenied("The {} role cannot be assigned to a team".format(sub_obj.name))
+ raise PermissionDenied(_("The {} role cannot be assigned to a team").format(sub_obj.name))
elif isinstance(sub_obj.content_object, User):
- raise PermissionDenied("The admin_role for a User cannot be assigned to a team")
+ raise PermissionDenied(_("The admin_role for a User cannot be assigned to a team"))
if isinstance(sub_obj.content_object, ResourceMixin):
role_access = RoleAccess(self.user)
diff --git a/awx/main/views.py b/awx/main/views.py
index a1036a96e6..f476f81cfd 100644
--- a/awx/main/views.py
+++ b/awx/main/views.py
@@ -4,6 +4,7 @@
# Django
from django.shortcuts import render
from django.utils.html import format_html
+from django.utils.translation import ugettext_lazy as _
# Django REST Framework
from rest_framework import exceptions, permissions, views
@@ -16,7 +17,7 @@ class ApiErrorView(views.APIView):
metadata_class = None
allowed_methods = ('GET', 'HEAD')
exception_class = exceptions.APIException
- view_name = 'API Error'
+ view_name = _('API Error')
def get_view_name(self):
return self.view_name
@@ -45,31 +46,31 @@ def handle_error(request, status=404, **kwargs):
def handle_400(request):
kwargs = {
- 'name': 'Bad Request',
- 'content': 'The request could not be understood by the server.',
+ 'name': _('Bad Request'),
+ 'content': _('The request could not be understood by the server.'),
}
return handle_error(request, 400, **kwargs)
def handle_403(request):
kwargs = {
- 'name': 'Forbidden',
- 'content': 'You don\'t have permission to access the requested resource.',
+ 'name': _('Forbidden'),
+ 'content': _('You don\'t have permission to access the requested resource.'),
}
return handle_error(request, 403, **kwargs)
def handle_404(request):
kwargs = {
- 'name': 'Not Found',
- 'content': 'The requested resource could not be found.',
+ 'name': _('Not Found'),
+ 'content': _('The requested resource could not be found.'),
}
return handle_error(request, 404, **kwargs)
def handle_500(request):
kwargs = {
- 'name': 'Server Error',
- 'content': 'A server error has occurred.',
+ 'name': _('Server Error'),
+ 'content': _('A server error has occurred.'),
}
return handle_error(request, 500, **kwargs)
diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py
index 630b5ee6ee..3d64651dea 100644
--- a/awx/settings/defaults.py
+++ b/awx/settings/defaults.py
@@ -18,6 +18,9 @@ for setting in dir(global_settings):
if setting == setting.upper():
setattr(this_module, setting, getattr(global_settings, setting))
+# gettext
+from django.utils.translation import ugettext_lazy as _
+
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@@ -118,6 +121,11 @@ LOG_ROOT = os.path.join(BASE_DIR)
# The heartbeat file for the tower scheduler
SCHEDULE_METADATA_LOCATION = os.path.join(BASE_DIR, '.tower_cycle')
+# Django gettext files path: locale/<lang-code>/LC_MESSAGES/django.po, django.mo
+LOCALE_PATHS = (
+ os.path.join(BASE_DIR, 'locale'),
+)
+
# Maximum number of the same job that can be waiting to run when launching from scheduler
# Note: This setting may be overridden by database settings.
SCHEDULE_MAX_JOBS = 10
@@ -155,8 +163,9 @@ TEMPLATE_CONTEXT_PROCESSORS = ( # NOQA
)
MIDDLEWARE_CLASSES = ( # NOQA
- 'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
@@ -560,12 +569,12 @@ AD_HOC_COMMANDS = [
# instead (based on docs from:
# http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/Service_Access_Endpoints-d1e517.html)
RAX_REGION_CHOICES = [
- ('ORD', 'Chicago'),
- ('DFW', 'Dallas/Ft. Worth'),
- ('IAD', 'Northern Virginia'),
- ('LON', 'London'),
- ('SYD', 'Sydney'),
- ('HKG', 'Hong Kong'),
+ ('ORD', _('Chicago')),
+ ('DFW', _('Dallas/Ft. Worth')),
+ ('IAD', _('Northern Virginia')),
+ ('LON', _('London')),
+ ('SYD', _('Sydney')),
+ ('HKG', _('Hong Kong')),
]
# Inventory variable name/values for determining if host is active/enabled.
@@ -592,18 +601,18 @@ INV_ENV_VARIABLE_BLACKLIST = ("HOME", "USER", "_", "TERM")
# list of names here. The available region IDs will be pulled from boto.
# http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region
EC2_REGION_NAMES = {
- 'us-east-1': 'US East (Northern Virginia)',
- 'us-west-2': 'US West (Oregon)',
- 'us-west-1': 'US West (Northern California)',
- 'eu-central-1': 'EU (Frankfurt)',
- 'eu-west-1': 'EU (Ireland)',
- 'ap-southeast-1': 'Asia Pacific (Singapore)',
- 'ap-southeast-2': 'Asia Pacific (Sydney)',
- 'ap-northeast-1': 'Asia Pacific (Tokyo)',
- 'ap-northeast-2': 'Asia Pacific (Seoul)',
- 'sa-east-1': 'South America (Sao Paulo)',
- 'us-gov-west-1': 'US West (GovCloud)',
- 'cn-north-1': 'China (Beijing)',
+ 'us-east-1': _('US East (Northern Virginia)'),
+ 'us-west-2': _('US West (Oregon)'),
+ 'us-west-1': _('US West (Northern California)'),
+ 'eu-central-1': _('EU (Frankfurt)'),
+ 'eu-west-1': _('EU (Ireland)'),
+ 'ap-southeast-1': _('Asia Pacific (Singapore)'),
+ 'ap-southeast-2': _('Asia Pacific (Sydney)'),
+ 'ap-northeast-1': _('Asia Pacific (Tokyo)'),
+ 'ap-northeast-2': _('Asia Pacific (Seoul)'),
+ 'sa-east-1': _('South America (Sao Paulo)'),
+ 'us-gov-west-1': _('US West (GovCloud)'),
+ 'cn-north-1': _('China (Beijing)'),
}
EC2_REGIONS_BLACKLIST = [
@@ -652,19 +661,19 @@ VMWARE_EXCLUDE_EMPTY_GROUPS = True
# provide a list here.
# Source: https://developers.google.com/compute/docs/zones
GCE_REGION_CHOICES = [
- ('us-east1-b', 'US East (B)'),
- ('us-east1-c', 'US East (C)'),
- ('us-east1-d', 'US East (D)'),
- ('us-central1-a', 'US Central (A)'),
- ('us-central1-b', 'US Central (B)'),
- ('us-central1-c', 'US Central (C)'),
- ('us-central1-f', 'US Central (F)'),
- ('europe-west1-b', 'Europe West (B)'),
- ('europe-west1-c', 'Europe West (C)'),
- ('europe-west1-d', 'Europe West (D)'),
- ('asia-east1-a', 'Asia East (A)'),
- ('asia-east1-b', 'Asia East (B)'),
- ('asia-east1-c', 'Asia East (C)'),
+ ('us-east1-b', _('US East (B)')),
+ ('us-east1-c', _('US East (C)')),
+ ('us-east1-d', _('US East (D)')),
+ ('us-central1-a', _('US Central (A)')),
+ ('us-central1-b', _('US Central (B)')),
+ ('us-central1-c', _('US Central (C)')),
+ ('us-central1-f', _('US Central (F)')),
+ ('europe-west1-b', _('Europe West (B)')),
+ ('europe-west1-c', _('Europe West (C)')),
+ ('europe-west1-d', _('Europe West (D)')),
+ ('asia-east1-a', _('Asia East (A)')),
+ ('asia-east1-b', _('Asia East (B)')),
+ ('asia-east1-c', _('Asia East (C)')),
]
GCE_REGIONS_BLACKLIST = []
@@ -688,19 +697,19 @@ GCE_INSTANCE_ID_VAR = None
# It's not possible to get zones in Azure without authenticating, so we
# provide a list here.
AZURE_REGION_CHOICES = [
- ('Central_US', 'US Central'),
- ('East_US_1', 'US East'),
- ('East_US_2', 'US East 2'),
- ('North_Central_US', 'US North Central'),
- ('South_Central_US', 'US South Central'),
- ('West_US', 'US West'),
- ('North_Europe', 'Europe North'),
- ('West_Europe', 'Europe West'),
- ('East_Asia_Pacific', 'Asia Pacific East'),
- ('Southest_Asia_Pacific', 'Asia Pacific Southeast'),
- ('East_Japan', 'Japan East'),
- ('West_Japan', 'Japan West'),
- ('South_Brazil', 'Brazil South'),
+ ('Central_US', _('US Central')),
+ ('East_US_1', _('US East')),
+ ('East_US_2', _('US East 2')),
+ ('North_Central_US', _('US North Central')),
+ ('South_Central_US', _('US South Central')),
+ ('West_US', _('US West')),
+ ('North_Europe', _('Europe North')),
+ ('West_Europe', _('Europe West')),
+ ('East_Asia_Pacific', _('Asia Pacific East')),
+ ('Southest_Asia_Pacific', _('Asia Pacific Southeast')),
+ ('East_Japan', _('Japan East')),
+ ('West_Japan', _('Japan West')),
+ ('South_Brazil', _('Brazil South')),
]
AZURE_REGIONS_BLACKLIST = []
diff --git a/awx/sso/pipeline.py b/awx/sso/pipeline.py
index 738a9b3b0c..2a16eb25b0 100644
--- a/awx/sso/pipeline.py
+++ b/awx/sso/pipeline.py
@@ -7,6 +7,9 @@ import re
# Python Social Auth
from social.exceptions import AuthException
+# Django
+from django.utils.translation import ugettext_lazy as _
+
# Tower
from awx.conf.license import feature_enabled
@@ -18,13 +21,13 @@ class AuthNotFound(AuthException):
super(AuthNotFound, self).__init__(backend, *args, **kwargs)
def __str__(self):
- return 'An account cannot be found for {0}'.format(self.email_or_uid)
+ return _('An account cannot be found for {0}').format(self.email_or_uid)
class AuthInactive(AuthException):
def __str__(self):
- return 'Your account is inactive'
+ return _('Your account is inactive')
def check_user_found_or_created(backend, details, user=None, *args, **kwargs):
diff --git a/awx/templates/rest_framework/api.html b/awx/templates/rest_framework/api.html
index c40d81ff63..746521f542 100644
--- a/awx/templates/rest_framework/api.html
+++ b/awx/templates/rest_framework/api.html
@@ -36,9 +36,9 @@
{% if user.is_authenticated %}
<li><a href="{% url 'api:user_me_list' %}" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="Logged in as {{ user }}{% if user.get_full_name %} ({{ user.get_full_name }}){% endif %}"><span class="glyphicon glyphicon-user"></span> <span class="visible-xs-inline">Logged in as </span>{{ user }}{% if user.get_full_name %}<span class="visible-xs-inline"> ({{ user.get_full_name }})</span>{% endif %}</a></li>
{% endif %}
- <li><a href="//docs.ansible.com/ansible-tower/{{short_tower_version}}/html/towerapi/index.html" target="_blank" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="Ansible Tower API Guide"><span class="glyphicon glyphicon-question-sign"></span><span class="visible-xs-inline"> Ansible Tower API Guide</span></a></li>
- <li><a href="/" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="Back to Ansible Tower"><span class="glyphicon glyphicon-circle-arrow-left"></span><span class="visible-xs-inline"> Back to Ansible Tower</span></a></li>
- <li class="hidden-xs"><a href="#" class="resize" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="Resize"><span class="glyphicon glyphicon-resize-full"></span></a></li>
+ <li><a href="//docs.ansible.com/ansible-tower/{{short_tower_version}}/html/towerapi/index.html" target="_blank" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="{% trans 'Ansible Tower API Guide' %}"><span class="glyphicon glyphicon-question-sign"></span><span class="visible-xs-inline">{% trans 'Ansible Tower API Guide' %}</span></a></li>
+ <li><a href="/" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="{% trans 'Back to Ansible Tower' %}"><span class="glyphicon glyphicon-circle-arrow-left"></span><span class="visible-xs-inline">{% trans 'Back to Ansible Tower' %}</span></a></li>
+ <li class="hidden-xs"><a href="#" class="resize" data-toggle="tooltip" data-placement="bottom" data-delay="1000" title="{% trans 'Resize' %}"><span class="glyphicon glyphicon-resize-full"></span></a></li>
</ul>
</div>
</div>
diff --git a/awx/templates/rest_framework/base.html b/awx/templates/rest_framework/base.html
index 6ed3cd456f..a6c4169ebd 100644
--- a/awx/templates/rest_framework/base.html
+++ b/awx/templates/rest_framework/base.html
@@ -1,8 +1,8 @@
+<!DOCTYPE html>
{# Copy of base.html from rest_framework with minor Ansible Tower change. #}
{% load staticfiles %}
{% load rest_framework %}
{% load i18n %}
-<!DOCTYPE html>
<html>
<head>
{% block head %}
@@ -75,21 +75,21 @@
<fieldset>
{% if api_settings.URL_FORMAT_OVERRIDE %}
<div class="btn-group format-selection">
- <a class="btn btn-primary js-tooltip" href="{{ request.get_full_path }}" rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a>
+ <a class="btn btn-primary js-tooltip" href="{{ request.get_full_path }}" rel="nofollow" title="{% blocktrans %}Make a GET request on the {{ name }} resource{% endblocktrans %}">GET</a>
- <button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown" title="Specify a format for the GET request">
+ <button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown" title="{% trans 'Specify a format for the GET request' %}">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% for format in available_formats %}
<li>
- <a class="js-tooltip format-option" href="{% add_query_param request api_settings.URL_FORMAT_OVERRIDE format %}" rel="nofollow" title="Make a GET request on the {{ name }} resource with the format set to `{{ format }}`">{{ format }}</a>
+ <a class="js-tooltip format-option" href="{% add_query_param request api_settings.URL_FORMAT_OVERRIDE format %}" rel="nofollow" title="{% blocktrans %}Make a GET request on the {{ name }} resource with the format set to `{{ format }}`{% endblocktrans %}">{{ format }}</a>
</li>
{% endfor %}
</ul>
</div>
{% else %}
- <a class="btn btn-primary js-tooltip" href="{{ request.get_full_path }}" rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a>
+ <a class="btn btn-primary js-tooltip" href="{{ request.get_full_path }}" rel="nofollow" title="{% blocktrans %}Make a GET request on the {{ name }} resource{% endblocktrans %}">GET</a>
{% endif %}
</fieldset>
</form>
@@ -97,13 +97,13 @@
{% if options_form %}
<form class="button-form" action="{{ request.get_full_path }}" data-method="OPTIONS">
- <button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button>
+ <button class="btn btn-primary js-tooltip" title="{% blocktrans %}Make an OPTIONS request on the {{ name }} resource{% endblocktrans %}">OPTIONS</button>
</form>
{% endif %}
{% if delete_form %}
<form class="button-form" action="{{ request.get_full_path }}" data-method="DELETE">
- <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button>
+ <button class="btn btn-danger js-tooltip" title="{% blocktrans %}Make a DELETE request on the {{ name }} resource{% endblocktrans %}">DELETE</button>
</form>
{% endif %}
@@ -169,7 +169,7 @@
{% csrf_token %}
{{ post_form }}
<div class="form-actions">
- <button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
+ <button class="btn btn-primary" title="{% blocktrans %}Make a POST request on the {{ name }} resource{% endblocktrans %}">POST</button>
</div>
</fieldset>
</form>
@@ -183,7 +183,7 @@
<fieldset>
{% include "rest_framework/raw_data_form.html" %}
<div class="form-actions">
- <button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
+ <button class="btn btn-primary" title="{% blocktrans %}Make a POST request on the {{ name }} resource{% endblocktrans %}">POST</button>
</div>
</fieldset>
</form>
@@ -213,7 +213,7 @@
<fieldset>
{{ put_form }}
<div class="form-actions">
- <button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
+ <button class="btn btn-primary js-tooltip" title="{% blocktrans %}Make a PUT request on the {{ name }} resource{% endblocktrans %}">PUT</button>
</div>
</fieldset>
</form>
@@ -227,10 +227,10 @@
{% include "rest_framework/raw_data_form.html" %}
<div class="form-actions">
{% if raw_data_put_form %}
- <button class="btn btn-primary js-tooltip" title="Make a PUT request on the {{ name }} resource">PUT</button>
+ <button class="btn btn-primary js-tooltip" title="{% blocktrans %}Make a PUT request on the {{ name }} resource{% endblocktrans %}">PUT</button>
{% endif %}
{% if raw_data_patch_form %}
- <button data-method="PATCH" class="btn btn-primary js-tooltip" title="Make a PATCH request on the {{ name }} resource">PATCH</button>
+ <button data-method="PATCH" class="btn btn-primary js-tooltip" title="{% blocktrans %}Make a PATCH request on the {{ name }} resource{% endblocktrans %}">PATCH</button>
{% endif %}
</div>
</fieldset>
diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html
index d5777c9619..2eb6c0f599 100644
--- a/awx/ui/templates/ui/index.html
+++ b/awx/ui/templates/ui/index.html
@@ -1,9 +1,10 @@
<!DOCTYPE html>
+{% load i18n %}
<html lang="en" ng-app="Tower">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <title>Ansible Tower</title>
+ <title>{% trans 'Ansible Tower' %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="{{ STATIC_URL }}assets/custom-theme/jquery-ui-1.10.3.custom.min.css" />
<link rel="stylesheet" href="{{ STATIC_URL }}assets/ansible-bootstrap.min.css" />
@@ -49,7 +50,7 @@
<!-- Password Dialog -->
<div id="password-modal" style="display: none;"></div>
- <div id="idle-modal" style="display:none">Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?</div>
+ <div id="idle-modal" style="display:none">{% blocktrans %}Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?{% endblocktrans %}</div>
<stream-detail-modal></stream-detail-modal>
@@ -155,27 +156,27 @@
<div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form" class="MgmtCards-promptText">
- Set how many days of data should be retained. <br>
+ {% trans 'Set how many days of data should be retained.' %}<br>
<input type="integer" id="days_to_keep" name="days_to_keep" ng-model="days_to_keep" ng-required="true" class="form-control Form-textInput" min=0 max=9999 style="margin-top:10px;" integer>
<div class="error" ng-show="prompt_for_days_form.days_to_keep.$dirty && (prompt_for_days_form.days_to_keep.$error.number || prompt_for_days_form.days_to_keep.$error.integer ||
prompt_for_days_form.days_to_keep.$error.required ||
prompt_for_days_form.days_to_keep.$error.min ||
- prompt_for_days_form.days_to_keep.$error.max)">Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.</div>
+ prompt_for_days_form.days_to_keep.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</form>
</div>
<div id="prompt-for-days-facts" style="display:none">
<form name="prompt_for_days_facts_form" id="prompt_for_days_facts_form" class="MgmtCards-promptText">
- <div style="padding-bottom:15px;">For facts collected older than the time period specified,
+ <div style="padding-bottom:15px;">{% blocktrans %}For facts collected older than the time period specified,
save one fact scan (snapshot) per time window (frequency).
For example, facts older than 30 days are purged, while one
weekly fact scan is kept.<br> <br>
- CAUTION: Setting both numerical variables to "0" will delete all facts.<br><br>
+ CAUTION: Setting both numerical variables to "0" will delete all facts.<br><br>{% endblocktrans %}
</div>
<div class="form-group">
<label for="description">
<span class="label-text">
- Select a time period after which to remove old facts
+ {% trans 'Select a time period after which to remove old facts' %}
</span>
</label>
<div class="row">
@@ -189,12 +190,12 @@
<div class="error" ng-show="prompt_for_days_facts_form.keep_amount.$dirty && (prompt_for_days_facts_form.keep_amount.$error.number || prompt_for_days_facts_form.keep_amount.$error.integer ||
prompt_for_days_facts_form.keep_amount.$error.required ||
prompt_for_days_facts_form.keep_amount.$error.min ||
- prompt_for_days_facts_form.keep_amount.$error.max)">Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.</div>
+ prompt_for_days_facts_form.keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</div>
<div class="form-group ">
<label for="description">
<span class="label-text">
- Select a frequency for snapshot retention
+ {% trans 'Select a frequency for snapshot retention' %}
</span>
</label>
<div class="row">
@@ -209,14 +210,14 @@
<div class="error" ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && (prompt_for_days_facts_form.granularity_keep_amount.$error.number || prompt_for_days_facts_form.granularity_keep_amount.$error.integer ||
prompt_for_days_facts_form.granularity_keep_amount.$error.required ||
prompt_for_days_facts_form.granularity_keep_amount.$error.min ||
- prompt_for_days_facts_form.granularity_keep_amount.$error.max)">Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.</div>
+ prompt_for_days_facts_form.granularity_keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
</div>
</form>
</div>
<div class="overlay"></div>
- <div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i> <p>working...</p></div>
+ <div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i> <p>{% trans 'working...' %}</p></div>
</div>
<tower-footer></tower-footer>
</body>