From 78a8ce9479271b59d7773eec9ff25b452d081950 Mon Sep 17 00:00:00 2001 From: sundeep-co-in Date: Tue, 18 Oct 2016 17:56:40 +0530 Subject: [PATCH] django i18n --- .gitignore | 1 + Makefile | 21 +++++- awx/api/metadata.py | 19 ++--- awx/api/serializers.py | 32 ++++----- awx/api/views.py | 47 ++++++------ awx/main/access.py | 29 ++++---- awx/main/views.py | 19 ++--- awx/settings/defaults.py | 99 ++++++++++++++------------ awx/sso/pipeline.py | 7 +- awx/templates/rest_framework/api.html | 6 +- awx/templates/rest_framework/base.html | 24 +++---- awx/ui/templates/ui/index.html | 23 +++--- 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//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 %}
  • Logged in as {{ user }}{% if user.get_full_name %} ({{ user.get_full_name }}){% endif %}
  • {% endif %} -
  • Ansible Tower API Guide
  • -
  • Back to Ansible Tower
  • - +
  • {% trans 'Ansible Tower API Guide' %}
  • +
  • {% trans 'Back to Ansible Tower' %}
  • + 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 @@ + {# Copy of base.html from rest_framework with minor Ansible Tower change. #} {% load staticfiles %} {% load rest_framework %} {% load i18n %} - {% block head %} @@ -75,21 +75,21 @@
    {% if api_settings.URL_FORMAT_OVERRIDE %}
    - GET + GET -
    {% else %} - GET + GET {% endif %}
    @@ -97,13 +97,13 @@ {% if options_form %}
    - +
    {% endif %} {% if delete_form %}
    - +
    {% endif %} @@ -169,7 +169,7 @@ {% csrf_token %} {{ post_form }}
    - +
    @@ -183,7 +183,7 @@
    {% include "rest_framework/raw_data_form.html" %}
    - +
    @@ -213,7 +213,7 @@
    {{ put_form }}
    - +
    @@ -227,10 +227,10 @@ {% include "rest_framework/raw_data_form.html" %}
    {% if raw_data_put_form %} - + {% endif %} {% if raw_data_patch_form %} - + {% endif %}
    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 @@ +{% load i18n %} - Ansible Tower + {% trans 'Ansible Tower' %} @@ -49,7 +50,7 @@ - + @@ -155,27 +156,27 @@