diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index 7b1accdc3..3ad01e87a 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -1,3 +1,4 @@ +from django.utils.translation import gettext_lazy as _ import django_tables2 as tables from circuits.models import * @@ -53,19 +54,19 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): ) provider_account = tables.Column( linkify=True, - verbose_name='Account' + verbose_name=_('Account') ) status = columns.ChoiceFieldColumn() termination_a = tables.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, - verbose_name='Side A' + verbose_name=_('Side A') ) termination_z = tables.TemplateColumn( template_code=CIRCUITTERMINATION_LINK, - verbose_name='Side Z' + verbose_name=_('Side Z') ) commit_rate = CommitRateColumn( - verbose_name='Commit Rate' + verbose_name=_('Commit Rate') ) comments = columns.MarkdownColumn() tags = columns.TagColumn( diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index 1df5e3305..f3440c25d 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel from tenancy.choices import * @@ -51,24 +52,30 @@ class Contact(PrimaryModel): null=True ) name = models.CharField( + verbose_name=_('name'), max_length=100 ) title = models.CharField( + verbose_name=_('title'), max_length=100, blank=True ) phone = models.CharField( + verbose_name=_('phone'), max_length=50, blank=True ) email = models.EmailField( + verbose_name=_('email'), blank=True ) address = models.CharField( + verbose_name=_('address'), max_length=200, blank=True ) link = models.URLField( + verbose_name=_('link'), blank=True ) @@ -113,6 +120,7 @@ class ContactAssignment(ChangeLoggedModel): related_name='assignments' ) priority = models.CharField( + verbose_name=_('priority'), max_length=50, choices=ContactPriorityChoices, blank=True diff --git a/netbox/tenancy/models/tenants.py b/netbox/tenancy/models/tenants.py index a41b8bf99..bcd0a4452 100644 --- a/netbox/tenancy/models/tenants.py +++ b/netbox/tenancy/models/tenants.py @@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.db.models import Q from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from netbox.models import NestedGroupModel, PrimaryModel @@ -16,10 +17,12 @@ class TenantGroup(NestedGroupModel): An arbitrary collection of Tenants. """ name = models.CharField( + verbose_name=_('name'), max_length=100, unique=True ) slug = models.SlugField( + verbose_name=_('slug'), max_length=100, unique=True ) @@ -37,9 +40,11 @@ class Tenant(PrimaryModel): department. """ name = models.CharField( + verbose_name=_('name'), max_length=100 ) slug = models.SlugField( + verbose_name=_('slug'), max_length=100 ) group = models.ForeignKey( @@ -65,7 +70,7 @@ class Tenant(PrimaryModel): models.UniqueConstraint( fields=('group', 'name'), name='%(app_label)s_%(class)s_unique_group_name', - violation_error_message="Tenant name must be unique per group." + violation_error_message=_("Tenant name must be unique per group.") ), models.UniqueConstraint( fields=('name',), @@ -75,7 +80,7 @@ class Tenant(PrimaryModel): models.UniqueConstraint( fields=('group', 'slug'), name='%(app_label)s_%(class)s_unique_group_slug', - violation_error_message="Tenant slug must be unique per group." + violation_error_message=_("Tenant slug must be unique per group.") ), models.UniqueConstraint( fields=('slug',), diff --git a/netbox/users/models.py b/netbox/users/models.py index 0c95559ff..a3f1fa1f7 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -11,7 +11,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.urls import reverse from django.utils import timezone -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from netaddr import IPNetwork from ipam.fields import IPNetworkField @@ -40,7 +40,7 @@ class AdminGroup(Group): Proxy contrib.auth.models.Group for the admin UI """ class Meta: - verbose_name = 'Group' + verbose_name = _('Group') proxy = True @@ -49,7 +49,7 @@ class AdminUser(User): Proxy contrib.auth.models.User for the admin UI """ class Meta: - verbose_name = 'User' + verbose_name = _('User') proxy = True @@ -105,12 +105,13 @@ class UserConfig(models.Model): related_name='config' ) data = models.JSONField( + verbose_name=_('data'), default=dict ) class Meta: ordering = ['user'] - verbose_name = verbose_name_plural = 'User Preferences' + verbose_name = verbose_name_plural = _('User Preferences') def get(self, path, default=None): """ @@ -176,7 +177,7 @@ class UserConfig(models.Model): d = d[key] elif key in d: err_path = '.'.join(path.split('.')[:i + 1]) - raise TypeError(f"Key '{err_path}' is a leaf node; cannot assign new keys") + raise TypeError(_("Key '{err_path}' is a leaf node; cannot assign new keys").format(err_path=err_path)) else: d = d.setdefault(key, {}) @@ -186,7 +187,7 @@ class UserConfig(models.Model): if type(value) is dict: d[key].update(value) else: - raise TypeError(f"Key '{path}' is a dictionary; cannot assign a non-dictionary value") + raise TypeError(_("Key '{path}' is a dictionary; cannot assign a non-dictionary value").format(path=path)) else: d[key] = value @@ -246,26 +247,32 @@ class Token(models.Model): related_name='tokens' ) created = models.DateTimeField( + verbose_name=_('created'), auto_now_add=True ) expires = models.DateTimeField( + verbose_name=_('expires'), blank=True, null=True ) last_used = models.DateTimeField( + verbose_name=_('last used'), blank=True, null=True ) key = models.CharField( + verbose_name=_('key'), max_length=40, unique=True, validators=[MinLengthValidator(40)] ) write_enabled = models.BooleanField( + verbose_name=_('write enabled'), default=True, help_text=_('Permit create/update/delete operations using this key') ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) @@ -273,7 +280,7 @@ class Token(models.Model): base_field=IPNetworkField(), blank=True, null=True, - verbose_name='Allowed IPs', + verbose_name=_('Allowed IPs'), help_text=_( 'Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for no restrictions. ' 'Ex: "10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64"' @@ -344,13 +351,16 @@ class ObjectPermission(models.Model): identified by ORM query parameters. """ name = models.CharField( + verbose_name=_('name'), max_length=100 ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) enabled = models.BooleanField( + verbose_name=_('enabled'), default=True ) object_types = models.ManyToManyField( @@ -382,7 +392,7 @@ class ObjectPermission(models.Model): class Meta: ordering = ['name'] - verbose_name = "permission" + verbose_name = _("permission") def __str__(self): return self.name diff --git a/netbox/virtualization/models/clusters.py b/netbox/virtualization/models/clusters.py index 517b92ef2..0f48c50f0 100644 --- a/netbox/virtualization/models/clusters.py +++ b/netbox/virtualization/models/clusters.py @@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from dcim.models import Device from netbox.models import OrganizationalModel, PrimaryModel @@ -46,9 +47,11 @@ class Cluster(PrimaryModel): A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices. """ name = models.CharField( + verbose_name=_('name'), max_length=100 ) type = models.ForeignKey( + verbose_name=_('type'), to=ClusterType, on_delete=models.PROTECT, related_name='clusters' @@ -61,6 +64,7 @@ class Cluster(PrimaryModel): null=True ) status = models.CharField( + verbose_name=_('status'), max_length=50, choices=ClusterStatusChoices, default=ClusterStatusChoices.STATUS_ACTIVE @@ -128,7 +132,7 @@ class Cluster(PrimaryModel): nonsite_devices = Device.objects.filter(cluster=self).exclude(site=self.site).count() if nonsite_devices: raise ValidationError({ - 'site': "{} devices are assigned as hosts for this cluster but are not in site {}".format( + 'site': _("{} devices are assigned as hosts for this cluster but are not in site {}").format( nonsite_devices, self.site ) }) diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index dbbfe49ed..123d72f5e 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/netbox/virtualization/models/virtualmachines.py @@ -5,6 +5,7 @@ from django.db import models from django.db.models import Q from django.db.models.functions import Lower from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from dcim.models import BaseInterface from extras.models import ConfigContextModel @@ -63,6 +64,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): null=True ) name = models.CharField( + verbose_name=_('name'), max_length=64 ) _name = NaturalOrderingField( @@ -74,7 +76,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): max_length=50, choices=VirtualMachineStatusChoices, default=VirtualMachineStatusChoices.STATUS_ACTIVE, - verbose_name='Status' + verbose_name=_('Status') ) role = models.ForeignKey( to='dcim.DeviceRole', @@ -113,12 +115,12 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): memory = models.PositiveIntegerField( blank=True, null=True, - verbose_name='Memory (MB)' + verbose_name=_('Memory (MB)') ) disk = models.PositiveIntegerField( blank=True, null=True, - verbose_name='Disk (GB)' + verbose_name=_('Disk (GB)') ) # Counter fields @@ -152,7 +154,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): Lower('name'), 'cluster', name='%(app_label)s_%(class)s_unique_name_cluster', condition=Q(tenant__isnull=True), - violation_error_message="Virtual machine name must be unique per cluster." + violation_error_message=_("Virtual machine name must be unique per cluster.") ), ) @@ -168,23 +170,23 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): # Must be assigned to a site and/or cluster if not self.site and not self.cluster: raise ValidationError({ - 'cluster': f'A virtual machine must be assigned to a site and/or cluster.' + 'cluster': _('A virtual machine must be assigned to a site and/or cluster.') }) # Validate site for cluster & device if self.cluster and self.site and self.cluster.site != self.site: raise ValidationError({ - 'cluster': f'The selected cluster ({self.cluster}) is not assigned to this site ({self.site}).' + 'cluster': _('The selected cluster ({cluster}) is not assigned to this site ({site}).').format(cluster=self.cluster, site=self.site) }) # Validate assigned cluster device if self.device and not self.cluster: raise ValidationError({ - 'device': f'Must specify a cluster when assigning a host device.' + 'device': _('Must specify a cluster when assigning a host device.') }) if self.device and self.device not in self.cluster.devices.all(): raise ValidationError({ - 'device': f'The selected device ({self.device}) is not assigned to this cluster ({self.cluster}).' + 'device': _('The selected device ({device}) is not assigned to this cluster ({cluster}).').format(device=self.device, cluster=self.cluster) }) # Validate primary IP addresses @@ -195,7 +197,8 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): if ip is not None: if ip.address.version != family: raise ValidationError({ - field: f"Must be an IPv{family} address. ({ip} is an IPv{ip.address.version} address.)", + field: _("Must be an IPv{family} address. ({ip} is an IPv{version} address.)").format( + family=family, ip=ip, version=ip.address.version), }) if ip.assigned_object in interfaces: pass @@ -203,7 +206,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): pass else: raise ValidationError({ - field: f"The specified IP address ({ip}) is not assigned to this VM.", + field: _("The specified IP address ({ip}) is not assigned to this VM.").format(ip=ip), }) def save(self, *args, **kwargs): @@ -236,6 +239,7 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): related_name='interfaces' ) name = models.CharField( + verbose_name=_('name'), max_length=64 ) _name = NaturalOrderingField( @@ -245,6 +249,7 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): blank=True ) description = models.CharField( + verbose_name=_('description'), max_length=200, blank=True ) @@ -254,13 +259,13 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): related_name='vminterfaces_as_untagged', null=True, blank=True, - verbose_name='Untagged VLAN' + verbose_name=_('Untagged VLAN') ) tagged_vlans = models.ManyToManyField( to='ipam.VLAN', related_name='vminterfaces_as_tagged', blank=True, - verbose_name='Tagged VLANs' + verbose_name=_('Tagged VLANs') ) ip_addresses = GenericRelation( to='ipam.IPAddress', @@ -274,7 +279,7 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): related_name='vminterfaces', null=True, blank=True, - verbose_name='VRF' + verbose_name=_('VRF') ) fhrp_group_assignments = GenericRelation( to='ipam.FHRPGroupAssignment', @@ -312,26 +317,26 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): # An interface cannot be its own parent if self.pk and self.parent_id == self.pk: - raise ValidationError({'parent': "An interface cannot be its own parent."}) + raise ValidationError({'parent': _("An interface cannot be its own parent.")}) # An interface's parent must belong to the same virtual machine if self.parent and self.parent.virtual_machine != self.virtual_machine: raise ValidationError({ - 'parent': f"The selected parent interface ({self.parent}) belongs to a different virtual machine " - f"({self.parent.virtual_machine})." + 'parent': _("The selected parent interface ({parent}) belongs to a different virtual machine " + "({virtual_machine}).").format(parent=self.parent, virtual_machine=self.parent.virtual_machine) }) # Bridge validation # An interface cannot be bridged to itself if self.pk and self.bridge_id == self.pk: - raise ValidationError({'bridge': "An interface cannot be bridged to itself."}) + raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")}) # A bridged interface belong to the same virtual machine if self.bridge and self.bridge.virtual_machine != self.virtual_machine: raise ValidationError({ - 'bridge': f"The selected bridge interface ({self.bridge}) belongs to a different virtual machine " - f"({self.bridge.virtual_machine})." + 'bridge': _("The selected bridge interface ({bridge}) belongs to a different virtual machine " + "({virtual_machine}).").format(bridge=self.bridge, virtual_machine=self.bridge.virtual_machine) }) # VLAN validation @@ -339,8 +344,8 @@ class VMInterface(NetBoxModel, BaseInterface, TrackingModelMixin): # Validate untagged VLAN if self.untagged_vlan and self.untagged_vlan.site not in [self.virtual_machine.site, None]: raise ValidationError({ - 'untagged_vlan': f"The untagged VLAN ({self.untagged_vlan}) must belong to the same site as the " - f"interface's parent virtual machine, or it must be global." + 'untagged_vlan': _("The untagged VLAN ({untagged_vlan}) must belong to the same site as the " + "interface's parent virtual machine, or it must be global.").format(untagged_vlan=self.untagged_vlan) }) def to_objectchange(self, action): diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py index 1f7d76961..b7f62485e 100644 --- a/netbox/wireless/models.py +++ b/netbox/wireless/models.py @@ -1,6 +1,7 @@ from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from mptt.models import MPTTModel from dcim.choices import LinkStatusChoices @@ -24,9 +25,10 @@ class WirelessAuthenticationBase(models.Model): max_length=50, choices=WirelessAuthTypeChoices, blank=True, - verbose_name="Auth Type", + verbose_name=_("Auth Type"), ) auth_cipher = models.CharField( + verbose_name=_('auth cipher'), max_length=50, choices=WirelessAuthCipherChoices, blank=True @@ -34,7 +36,7 @@ class WirelessAuthenticationBase(models.Model): auth_psk = models.CharField( max_length=PSK_MAX_LENGTH, blank=True, - verbose_name='Pre-shared key' + verbose_name=_('Pre-shared key') ) class Meta: @@ -46,10 +48,12 @@ class WirelessLANGroup(NestedGroupModel): A nested grouping of WirelessLANs """ name = models.CharField( + verbose_name=_('name'), max_length=100, unique=True ) slug = models.SlugField( + verbose_name=_('slug'), max_length=100, unique=True ) @@ -74,7 +78,7 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel): """ ssid = models.CharField( max_length=SSID_MAX_LENGTH, - verbose_name='SSID' + verbose_name=_('SSID') ) group = models.ForeignKey( to='wireless.WirelessLANGroup', @@ -93,7 +97,7 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel): on_delete=models.PROTECT, blank=True, null=True, - verbose_name='VLAN' + verbose_name=_('VLAN') ) tenant = models.ForeignKey( to='tenancy.Tenant', @@ -134,21 +138,22 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel): limit_choices_to=get_wireless_interface_types, on_delete=models.PROTECT, related_name='+', - verbose_name="Interface A", + verbose_name=_('Interface A'), ) interface_b = models.ForeignKey( to='dcim.Interface', limit_choices_to=get_wireless_interface_types, on_delete=models.PROTECT, related_name='+', - verbose_name="Interface B", + verbose_name=_('Interface B'), ) ssid = models.CharField( max_length=SSID_MAX_LENGTH, blank=True, - verbose_name='SSID' + verbose_name=_('SSID') ) status = models.CharField( + verbose_name=_('status'), max_length=50, choices=LinkStatusChoices, default=LinkStatusChoices.STATUS_CONNECTED @@ -203,11 +208,11 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel): # Validate interface types if self.interface_a.type not in WIRELESS_IFACE_TYPES: raise ValidationError({ - 'interface_a': f"{self.interface_a.get_type_display()} is not a wireless interface." + 'interface_a': _("{type_display} is not a wireless interface.").format(type_display=self.interface_a.get_type_display()) }) if self.interface_b.type not in WIRELESS_IFACE_TYPES: raise ValidationError({ - 'interface_a': f"{self.interface_b.get_type_display()} is not a wireless interface." + 'interface_a': _("{type_display} is not a wireless interface.").format(type_display=self.interface_b.get_type_display()) }) def save(self, *args, **kwargs):