diff --git a/netbox/circuits/apps.py b/netbox/circuits/apps.py
index bc0b7d87d..78b30c972 100644
--- a/netbox/circuits/apps.py
+++ b/netbox/circuits/apps.py
@@ -1,9 +1,10 @@
from django.apps import AppConfig
+from django.utils.translation import gettext as _
class CircuitsConfig(AppConfig):
name = "circuits"
- verbose_name = "Circuits"
+ verbose_name = _('Circuits')
def ready(self):
import circuits.signals
diff --git a/netbox/circuits/filters.py b/netbox/circuits/filters.py
index 206dcc305..b71f91dba 100644
--- a/netbox/circuits/filters.py
+++ b/netbox/circuits/filters.py
@@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
+from django.utils.translation import gettext as _
from dcim.models import Region, Site
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
@@ -21,31 +22,31 @@ __all__ = (
class ProviderFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site',
queryset=Site.objects.all(),
- label='Site',
+ label=_('Site'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
tag = TagFilter()
@@ -75,27 +76,27 @@ class CircuitTypeFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
- label='Provider (ID)',
+ label=_('Provider (ID)'),
)
provider = django_filters.ModelMultipleChoiceFilter(
field_name='provider__slug',
queryset=Provider.objects.all(),
to_field_name='slug',
- label='Provider (slug)',
+ label=_('Provider (slug)'),
)
type_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitType.objects.all(),
- label='Circuit type (ID)',
+ label=_('Circuit type (ID)'),
)
type = django_filters.ModelMultipleChoiceFilter(
field_name='type__slug',
queryset=CircuitType.objects.all(),
to_field_name='slug',
- label='Circuit type (slug)',
+ label=_('Circuit type (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=CircuitStatusChoices,
@@ -104,26 +105,26 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, Cr
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='terminations__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='terminations__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
tag = TagFilter()
@@ -147,21 +148,21 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, Cr
class CircuitTerminationFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
circuit_id = django_filters.ModelMultipleChoiceFilter(
queryset=Circuit.objects.all(),
- label='Circuit',
+ label=_('Circuit'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
class Meta:
diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py
index 2185d1eab..37d1b6aa8 100644
--- a/netbox/circuits/forms.py
+++ b/netbox/circuits/forms.py
@@ -1,4 +1,5 @@
from django import forms
+from django.utils.translation import gettext as _
from dcim.models import Region, Site
from extras.forms import (
@@ -64,30 +65,30 @@ class ProviderBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdi
)
asn = forms.IntegerField(
required=False,
- label='ASN'
+ label=_('ASN')
)
account = forms.CharField(
max_length=30,
required=False,
- label='Account number'
+ label=_('Account number')
)
portal_url = forms.URLField(
required=False,
- label='Portal'
+ label=_('Portal')
)
noc_contact = forms.CharField(
required=False,
widget=SmallTextarea,
- label='NOC contact'
+ label=_('NOC contact')
)
admin_contact = forms.CharField(
required=False,
widget=SmallTextarea,
- label='Admin contact'
+ label=_('Admin contact')
)
comments = CommentField(
widget=SmallTextarea,
- label='Comments'
+ label=_('Comments')
)
class Meta:
@@ -100,7 +101,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Provider
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -123,7 +124,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
)
asn = forms.IntegerField(
required=False,
- label='ASN'
+ label=_('ASN')
)
tag = TagFilterField(model)
@@ -149,7 +150,7 @@ class CircuitTypeCSVForm(CSVModelForm):
model = CircuitType
fields = CircuitType.csv_headers
help_texts = {
- 'name': 'Name of circuit type',
+ 'name': _('Name of circuit type'),
}
@@ -189,23 +190,23 @@ class CircuitCSVForm(CustomFieldModelCSVForm):
provider = CSVModelChoiceField(
queryset=Provider.objects.all(),
to_field_name='name',
- help_text='Assigned provider'
+ help_text=_('Assigned provider')
)
type = CSVModelChoiceField(
queryset=CircuitType.objects.all(),
to_field_name='name',
- help_text='Type of circuit'
+ help_text=_('Type of circuit')
)
status = CSVChoiceField(
choices=CircuitStatusChoices,
required=False,
- help_text='Operational status'
+ help_text=_('Operational status')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
class Meta:
@@ -240,7 +241,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
)
commit_rate = forms.IntegerField(
required=False,
- label='Commit rate (Kbps)'
+ label=_('Commit rate (Kbps)')
)
description = forms.CharField(
max_length=100,
@@ -248,7 +249,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
)
comments = CommentField(
widget=SmallTextarea,
- label='Comments'
+ label=_('Comments')
)
class Meta:
@@ -264,7 +265,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
]
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
type = DynamicModelMultipleChoiceField(
queryset=CircuitType.objects.all(),
@@ -309,7 +310,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
commit_rate = forms.IntegerField(
required=False,
min_value=0,
- label='Commit rate (Kbps)'
+ label=_('Commit rate (Kbps)')
)
tag = TagFilterField(model)
diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py
index 57d41a994..dac3ffde8 100644
--- a/netbox/circuits/models.py
+++ b/netbox/circuits/models.py
@@ -1,6 +1,7 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.urls import reverse
+from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.constants import CONNECTION_STATUS_CHOICES
@@ -38,25 +39,25 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
asn = ASNField(
blank=True,
null=True,
- verbose_name='ASN',
- help_text='32-bit autonomous system number'
+ verbose_name=_('ASN'),
+ help_text=_('32-bit autonomous system number')
)
account = models.CharField(
max_length=30,
blank=True,
- verbose_name='Account number'
+ verbose_name=_('Account number')
)
portal_url = models.URLField(
blank=True,
- verbose_name='Portal URL'
+ verbose_name=_('Portal URL')
)
noc_contact = models.TextField(
blank=True,
- verbose_name='NOC contact'
+ verbose_name=_('NOC contact')
)
admin_contact = models.TextField(
blank=True,
- verbose_name='Admin contact'
+ verbose_name=_('Admin contact')
)
comments = models.TextField(
blank=True
@@ -143,7 +144,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
"""
cid = models.CharField(
max_length=50,
- verbose_name='Circuit ID'
+ verbose_name=_('Circuit ID')
)
provider = models.ForeignKey(
to='circuits.Provider',
@@ -170,7 +171,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
install_date = models.DateField(
blank=True,
null=True,
- verbose_name='Date installed'
+ verbose_name=_('Date installed')
)
commit_rate = models.PositiveIntegerField(
blank=True,
@@ -258,7 +259,7 @@ class CircuitTermination(CableTermination):
term_side = models.CharField(
max_length=1,
choices=CircuitTerminationSideChoices,
- verbose_name='Termination'
+ verbose_name=_('Termination')
)
site = models.ForeignKey(
to='dcim.Site',
@@ -277,23 +278,23 @@ class CircuitTermination(CableTermination):
blank=True
)
port_speed = models.PositiveIntegerField(
- verbose_name='Port speed (Kbps)'
+ verbose_name=_('Port speed (Kbps)')
)
upstream_speed = models.PositiveIntegerField(
blank=True,
null=True,
- verbose_name='Upstream speed (Kbps)',
- help_text='Upstream speed, if different from port speed'
+ verbose_name=_('Upstream speed (Kbps)'),
+ help_text=_('Upstream speed, if different from port speed')
)
xconnect_id = models.CharField(
max_length=50,
blank=True,
- verbose_name='Cross-connect ID'
+ verbose_name=_('Cross-connect ID')
)
pp_info = models.CharField(
max_length=100,
blank=True,
- verbose_name='Patch panel/port(s)'
+ verbose_name=_('Patch panel/port(s)')
)
description = models.CharField(
max_length=200,
diff --git a/netbox/circuits/tables.py b/netbox/circuits/tables.py
index ea17031a1..ad5d5ca66 100644
--- a/netbox/circuits/tables.py
+++ b/netbox/circuits/tables.py
@@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
+from django.utils.translation import gettext as _
from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, TagColumn, ToggleColumn
@@ -29,7 +30,7 @@ class ProviderTable(BaseTable):
name = tables.LinkColumn()
circuit_count = tables.Column(
accessor=Accessor('count_circuits'),
- verbose_name='Circuits'
+ verbose_name=_('Circuits')
)
tags = TagColumn(
url_name='circuits:provider_list'
@@ -51,7 +52,7 @@ class CircuitTypeTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
circuit_count = tables.Column(
- verbose_name='Circuits'
+ verbose_name=_('Circuits')
)
actions = tables.TemplateColumn(
template_code=CIRCUITTYPE_ACTIONS,
@@ -72,7 +73,7 @@ class CircuitTypeTable(BaseTable):
class CircuitTable(BaseTable):
pk = ToggleColumn()
cid = tables.LinkColumn(
- verbose_name='ID'
+ verbose_name=_('ID')
)
provider = tables.LinkColumn(
viewname='circuits:provider',
@@ -85,10 +86,10 @@ class CircuitTable(BaseTable):
template_code=COL_TENANT
)
a_side = tables.Column(
- verbose_name='A Side'
+ verbose_name=_('A Side')
)
z_side = tables.Column(
- verbose_name='Z Side'
+ verbose_name=_('Z Side')
)
tags = TagColumn(
url_name='circuits:circuit_list'
diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py
index 709d2a726..e707d466f 100644
--- a/netbox/circuits/views.py
+++ b/netbox/circuits/views.py
@@ -5,6 +5,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction
from django.db.models import Count, OuterRef, Subquery
from django.shortcuts import get_object_or_404, redirect, render
+from django.utils.translation import gettext as _
from django.views.generic import View
from django_tables2 import RequestConfig
@@ -250,7 +251,7 @@ def circuit_terminations_swap(request, pk):
else:
termination_z.term_side = 'A'
termination_z.save()
- messages.success(request, "Swapped terminations for circuit {}.".format(circuit))
+ messages.success(request, _('Swapped terminations for circuit {}.').format(circuit))
return redirect('circuits:circuit', pk=circuit.pk)
else:
diff --git a/netbox/dcim/apps.py b/netbox/dcim/apps.py
index 78a243f84..b0dc5cc0f 100644
--- a/netbox/dcim/apps.py
+++ b/netbox/dcim/apps.py
@@ -1,9 +1,9 @@
from django.apps import AppConfig
-
+from django.utils.translation import gettext as _
class DCIMConfig(AppConfig):
name = "dcim"
- verbose_name = "DCIM"
+ verbose_name = _('DCIM')
def ready(self):
diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py
index 8c24180bb..a901a99fd 100644
--- a/netbox/dcim/filters.py
+++ b/netbox/dcim/filters.py
@@ -1,5 +1,6 @@
import django_filters
from django.contrib.auth.models import User
+from django.utils.translation import gettext as _
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
@@ -63,13 +64,13 @@ __all__ = (
class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Region.objects.all(),
- label='Parent region (ID)',
+ label=_('Parent region (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=Region.objects.all(),
to_field_name='slug',
- label='Parent region (slug)',
+ label=_('Parent region (slug)'),
)
class Meta:
@@ -80,7 +81,7 @@ class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
status = django_filters.MultipleChoiceFilter(
choices=SiteStatusChoices,
@@ -90,14 +91,14 @@ class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
queryset=Region.objects.all(),
field_name='region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
tag = TagFilter()
@@ -134,34 +135,34 @@ class RackGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
- label='Rack group (ID)',
+ label=_('Rack group (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=RackGroup.objects.all(),
to_field_name='slug',
- label='Rack group (slug)',
+ label=_('Rack group (slug)'),
)
class Meta:
@@ -179,43 +180,43 @@ class RackRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='group',
lookup_expr='in',
- label='Rack group (ID)',
+ label=_('Rack group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
- label='Rack group (slug)',
+ label=_('Rack group (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=RackStatusChoices,
@@ -223,13 +224,13 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=RackRole.objects.all(),
- label='Role (ID)',
+ label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=RackRole.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
@@ -258,45 +259,45 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
rack_id = django_filters.ModelMultipleChoiceFilter(
queryset=Rack.objects.all(),
- label='Rack (ID)',
+ label=_('Rack (ID)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='rack__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack__group',
lookup_expr='in',
- label='Rack group (ID)',
+ label=_('Rack group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack__group',
lookup_expr='in',
to_field_name='slug',
- label='Rack group (slug)',
+ label=_('Rack group (slug)'),
)
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
- label='User (ID)',
+ label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user',
queryset=User.objects.all(),
to_field_name='username',
- label='User (name)',
+ label=_('User (name)'),
)
class Meta:
@@ -324,45 +325,45 @@ class ManufacturerFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class DeviceTypeFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
- label='Manufacturer (ID)',
+ label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
- label='Manufacturer (slug)',
+ label=_('Manufacturer (slug)'),
)
console_ports = django_filters.BooleanFilter(
method='_console_ports',
- label='Has console ports',
+ label=_('Has console ports'),
)
console_server_ports = django_filters.BooleanFilter(
method='_console_server_ports',
- label='Has console server ports',
+ label=_('Has console server ports'),
)
power_ports = django_filters.BooleanFilter(
method='_power_ports',
- label='Has power ports',
+ label=_('Has power ports'),
)
power_outlets = django_filters.BooleanFilter(
method='_power_outlets',
- label='Has power outlets',
+ label=_('Has power outlets'),
)
interfaces = django_filters.BooleanFilter(
method='_interfaces',
- label='Has interfaces',
+ label=_('Has interfaces'),
)
pass_through_ports = django_filters.BooleanFilter(
method='_pass_through_ports',
- label='Has pass-through ports',
+ label=_('Has pass-through ports'),
)
device_bays = django_filters.BooleanFilter(
method='_device_bays',
- label='Has device bays',
+ label=_('Has device bays'),
)
tag = TagFilter()
@@ -411,7 +412,7 @@ class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
devicetype_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceType.objects.all(),
field_name='device_type_id',
- label='Device type (ID)',
+ label=_('Device type (ID)'),
)
@@ -482,13 +483,13 @@ class PlatformFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer',
queryset=Manufacturer.objects.all(),
- label='Manufacturer (ID)',
+ label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
- label='Manufacturer (slug)',
+ label=_('Manufacturer (slug)'),
)
class Meta:
@@ -505,87 +506,87 @@ class DeviceFilterSet(
):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_type__manufacturer',
queryset=Manufacturer.objects.all(),
- label='Manufacturer (ID)',
+ label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='device_type__manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
- label='Manufacturer (slug)',
+ label=_('Manufacturer (slug)'),
)
device_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceType.objects.all(),
- label='Device type (ID)',
+ label=_('Device type (ID)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_role_id',
queryset=DeviceRole.objects.all(),
- label='Role (ID)',
+ label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='device_role__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
- label='Platform (ID)',
+ label=_('Platform (ID)'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platform__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
- label='Platform (slug)',
+ label=_('Platform (slug)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site name (slug)',
+ label=_('Site name (slug)'),
)
rack_group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack__group',
lookup_expr='in',
- label='Rack group (ID)',
+ label=_('Rack group (ID)'),
)
rack_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack',
queryset=Rack.objects.all(),
- label='Rack (ID)',
+ label=_('Rack (ID)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cluster.objects.all(),
- label='VM cluster (ID)',
+ label=_('VM cluster (ID)'),
)
model = django_filters.ModelMultipleChoiceFilter(
field_name='device_type__slug',
queryset=DeviceType.objects.all(),
to_field_name='slug',
- label='Device model (slug)',
+ label=_('Device model (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=DeviceStatusChoices,
@@ -593,55 +594,55 @@ class DeviceFilterSet(
)
is_full_depth = django_filters.BooleanFilter(
field_name='device_type__is_full_depth',
- label='Is full depth',
+ label=_('Is full depth'),
)
mac_address = MultiValueMACAddressFilter(
field_name='interfaces__mac_address',
- label='MAC address',
+ label=_('MAC address'),
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
)
has_primary_ip = django_filters.BooleanFilter(
method='_has_primary_ip',
- label='Has a primary IP',
+ label=_('Has a primary IP'),
)
virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_chassis',
queryset=VirtualChassis.objects.all(),
- label='Virtual chassis (ID)',
+ label=_('Virtual chassis (ID)'),
)
virtual_chassis_member = django_filters.BooleanFilter(
method='_virtual_chassis_member',
- label='Is a virtual chassis member'
+ label=_('Is a virtual chassis member')
)
console_ports = django_filters.BooleanFilter(
method='_console_ports',
- label='Has console ports',
+ label=_('Has console ports'),
)
console_server_ports = django_filters.BooleanFilter(
method='_console_server_ports',
- label='Has console server ports',
+ label=_('Has console server ports'),
)
power_ports = django_filters.BooleanFilter(
method='_power_ports',
- label='Has power ports',
+ label=_('Has power ports'),
)
power_outlets = django_filters.BooleanFilter(
method='_power_outlets',
- label='Has power outlets',
+ label=_('Has power outlets'),
)
interfaces = django_filters.BooleanFilter(
method='_interfaces',
- label='Has interfaces',
+ label=_('Has interfaces'),
)
pass_through_ports = django_filters.BooleanFilter(
method='_pass_through_ports',
- label='Has pass-through ports',
+ label=_('Has pass-through ports'),
)
device_bays = django_filters.BooleanFilter(
method='_device_bays',
- label='Has device bays',
+ label=_('Has device bays'),
)
tag = TagFilter()
@@ -703,41 +704,41 @@ class DeviceFilterSet(
class DeviceComponentFilterSet(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site name (slug)',
+ label=_('Site name (slug)'),
)
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
- label='Device (ID)',
+ label=_('Device (ID)'),
)
device = django_filters.ModelMultipleChoiceFilter(
field_name='device__name',
queryset=Device.objects.all(),
to_field_name='name',
- label='Device (name)',
+ label=_('Device (name)'),
)
tag = TagFilter()
@@ -817,19 +818,19 @@ class PowerOutletFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
# Override device and device_id filters from DeviceComponentFilterSet to match against any peer virtual chassis
# members
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
- label='Device',
+ label=_('Device'),
)
device_id = MultiValueNumberFilter(
method='filter_device_id',
field_name='pk',
- label='Device (ID)',
+ label=_('Device (ID)'),
)
cabled = django_filters.BooleanFilter(
field_name='cable',
@@ -838,22 +839,22 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
)
kind = django_filters.CharFilter(
method='filter_kind',
- label='Kind of interface',
+ label=_('Kind of interface'),
)
lag_id = django_filters.ModelMultipleChoiceFilter(
field_name='lag',
queryset=Interface.objects.all(),
- label='LAG interface (ID)',
+ label=_('LAG interface (ID)'),
)
mac_address = MultiValueMACAddressFilter()
tag = TagFilter()
vlan_id = django_filters.CharFilter(
method='filter_vlan_id',
- label='Assigned VLAN'
+ label=_('Assigned VLAN')
)
vlan = django_filters.CharFilter(
method='filter_vlan',
- label='Assigned VID'
+ label=_('Assigned VID')
)
type = django_filters.MultipleChoiceFilter(
choices=InterfaceTypeChoices,
@@ -946,54 +947,54 @@ class DeviceBayFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site name (slug)',
+ label=_('Site name (slug)'),
)
device_id = django_filters.ModelChoiceFilter(
queryset=Device.objects.all(),
- label='Device (ID)',
+ label=_('Device (ID)'),
)
device = django_filters.ModelChoiceFilter(
queryset=Device.objects.all(),
to_field_name='name',
- label='Device (name)',
+ label=_('Device (name)'),
)
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=InventoryItem.objects.all(),
- label='Parent inventory item (ID)',
+ label=_('Parent inventory item (ID)'),
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
- label='Manufacturer (ID)',
+ label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
- label='Manufacturer (slug)',
+ label=_('Manufacturer (slug)'),
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
@@ -1019,42 +1020,42 @@ class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class VirtualChassisFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='master__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='master__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='master__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='master__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site name (slug)',
+ label=_('Site name (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='master__tenant',
queryset=Tenant.objects.all(),
- label='Tenant (ID)',
+ label=_('Tenant (ID)'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='master__tenant__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
- label='Tenant (slug)',
+ label=_('Tenant (slug)'),
)
tag = TagFilter()
@@ -1075,7 +1076,7 @@ class VirtualChassisFilterSet(BaseFilterSet):
class CableFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
type = django_filters.MultipleChoiceFilter(
choices=CableTypeChoices
@@ -1138,7 +1139,7 @@ class CableFilterSet(BaseFilterSet):
class ConsoleConnectionFilterSet(BaseFilterSet):
site = django_filters.CharFilter(
method='filter_site',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
device_id = MultiValueNumberFilter(
method='filter_device'
@@ -1169,7 +1170,7 @@ class ConsoleConnectionFilterSet(BaseFilterSet):
class PowerConnectionFilterSet(BaseFilterSet):
site = django_filters.CharFilter(
method='filter_site',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
device_id = MultiValueNumberFilter(
method='filter_device'
@@ -1200,7 +1201,7 @@ class PowerConnectionFilterSet(BaseFilterSet):
class InterfaceConnectionFilterSet(BaseFilterSet):
site = django_filters.CharFilter(
method='filter_site',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
device_id = MultiValueNumberFilter(
method='filter_device'
@@ -1234,36 +1235,36 @@ class InterfaceConnectionFilterSet(BaseFilterSet):
class PowerPanelFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site name (slug)',
+ label=_('Site name (slug)'),
)
rack_group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack_group',
lookup_expr='in',
- label='Rack group (ID)',
+ label=_('Rack group (ID)'),
)
class Meta:
@@ -1282,40 +1283,40 @@ class PowerPanelFilterSet(BaseFilterSet):
class PowerFeedFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site name (slug)',
+ label=_('Site name (slug)'),
)
power_panel_id = django_filters.ModelMultipleChoiceFilter(
queryset=PowerPanel.objects.all(),
- label='Power panel (ID)',
+ label=_('Power panel (ID)'),
)
rack_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack',
queryset=Rack.objects.all(),
- label='Rack (ID)',
+ label=_('Rack (ID)'),
)
tag = TagFilter()
diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py
index 4de81ac54..9c1873b3d 100644
--- a/netbox/dcim/forms.py
+++ b/netbox/dcim/forms.py
@@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.forms.array import SimpleArrayField
from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext as _
from mptt.forms import TreeNodeChoiceField
from netaddr import EUI
from netaddr.core import AddrFormatError
@@ -65,7 +66,7 @@ class DeviceComponentFilterForm(BootstrapMixin, forms.Form):
]
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -92,7 +93,7 @@ class DeviceComponentFilterForm(BootstrapMixin, forms.Form):
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device'
+ label=_('Device')
)
@@ -136,7 +137,7 @@ class BulkRenameForm(forms.Form):
use_regex = forms.BooleanField(
required=False,
initial=True,
- label='Use regular expressions'
+ label=_('Use regular expressions')
)
def clean(self):
@@ -197,7 +198,7 @@ class RegionCSVForm(CSVModelForm):
queryset=Region.objects.all(),
required=False,
to_field_name='name',
- help_text='Name of parent region'
+ help_text=_('Name of parent region')
)
class Meta:
@@ -209,7 +210,7 @@ class RegionFilterForm(BootstrapMixin, forms.Form):
model = Site
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
@@ -267,19 +268,19 @@ class SiteCSVForm(CustomFieldModelCSVForm):
status = CSVChoiceField(
choices=SiteStatusChoices,
required=False,
- help_text='Operational status'
+ help_text=_('Operational status')
)
region = CSVModelChoiceField(
queryset=Region.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned region'
+ help_text=_('Assigned region')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
class Meta:
@@ -316,7 +317,7 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
min_value=BGP_ASN_MIN,
max_value=BGP_ASN_MAX,
required=False,
- label='ASN'
+ label=_('ASN')
)
description = forms.CharField(
max_length=100,
@@ -339,7 +340,7 @@ class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
field_order = ['q', 'status', 'region', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
status = forms.MultipleChoiceField(
choices=SiteStatusChoices,
@@ -387,15 +388,15 @@ class RackGroupCSVForm(CSVModelForm):
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
parent = CSVModelChoiceField(
queryset=RackGroup.objects.all(),
required=False,
to_field_name='name',
- help_text='Parent rack group',
+ help_text=_('Parent rack group'),
error_messages={
- 'invalid_choice': 'Rack group not found.',
+ 'invalid_choice': _('Rack group not found.'),
}
)
@@ -524,32 +525,32 @@ class RackCSVForm(CustomFieldModelCSVForm):
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Name of assigned tenant'
+ help_text=_('Name of assigned tenant')
)
status = CSVChoiceField(
choices=RackStatusChoices,
required=False,
- help_text='Operational status'
+ help_text=_('Operational status')
)
role = CSVModelChoiceField(
queryset=RackRole.objects.all(),
required=False,
to_field_name='name',
- help_text='Name of assigned role'
+ help_text=_('Name of assigned role')
)
type = CSVChoiceField(
choices=RackTypeChoices,
required=False,
- help_text='Rack type'
+ help_text=_('Rack type')
)
width = forms.ChoiceField(
choices=RackWidthChoices,
- help_text='Rail-to-rail width (in inches)'
+ help_text=_('Rail-to-rail width (in inches)')
)
outer_unit = CSVChoiceField(
choices=RackDimensionUnitChoices,
required=False,
- help_text='Unit for outer dimensions'
+ help_text=_('Unit for outer dimensions')
)
class Meta:
@@ -601,7 +602,7 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
serial = forms.CharField(
max_length=50,
required=False,
- label='Serial Number'
+ label=_('Serial Number')
)
asset_tag = forms.CharField(
max_length=50,
@@ -619,12 +620,12 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
)
u_height = forms.IntegerField(
required=False,
- label='Height (U)'
+ label=_('Height (U)')
)
desc_units = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect,
- label='Descending units'
+ label=_('Descending units')
)
outer_width = forms.IntegerField(
required=False,
@@ -641,7 +642,7 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
)
comments = CommentField(
widget=SmallTextarea,
- label='Comments'
+ label=_('Comments')
)
class Meta:
@@ -655,7 +656,7 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -684,7 +685,7 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
'site'
),
required=False,
- label='Rack group',
+ label=_('Rack group'),
widget=APISelectMultiple(
null_option=True
)
@@ -714,7 +715,7 @@ class RackElevationFilterForm(RackFilterForm):
field_order = ['q', 'region', 'site', 'group_id', 'id', 'status', 'role', 'tenant_group', 'tenant']
id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(),
- label='Rack',
+ label=_('Rack'),
required=False,
widget=APISelectMultiple(
display_field='display_name',
@@ -758,7 +759,7 @@ class RackReservationForm(BootstrapMixin, TenancyForm, forms.ModelForm):
)
units = NumericArrayField(
base_field=forms.IntegerField(),
- help_text="Comma-separated list of numeric unit IDs. A range may be specified using a hyphen."
+ help_text=_('Comma-separated list of numeric unit IDs. A range may be specified using a hyphen.')
)
user = forms.ModelChoiceField(
queryset=User.objects.order_by(
@@ -778,29 +779,29 @@ class RackReservationCSVForm(CSVModelForm):
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
- help_text='Parent site'
+ help_text=_('Parent site')
)
rack_group = CSVModelChoiceField(
queryset=RackGroup.objects.all(),
to_field_name='name',
required=False,
- help_text="Rack's group (if any)"
+ help_text=_("Rack's group (if any)")
)
rack = CSVModelChoiceField(
queryset=Rack.objects.all(),
to_field_name='name',
- help_text='Rack'
+ help_text=_('Rack')
)
units = SimpleArrayField(
base_field=forms.IntegerField(),
required=True,
- help_text='Comma-separated list of individual unit numbers'
+ help_text=_('Comma-separated list of individual unit numbers')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
class Meta:
@@ -853,7 +854,7 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm):
field_order = ['q', 'site', 'group_id', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
site = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(),
@@ -866,7 +867,7 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm):
group_id = DynamicModelMultipleChoiceField(
queryset=RackGroup.objects.prefetch_related('site'),
required=False,
- label='Rack group',
+ label=_('Rack group'),
widget=APISelectMultiple(
null_option=True,
)
@@ -951,7 +952,7 @@ class DeviceTypeBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkE
is_full_depth = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect(),
- label='Is full depth'
+ label=_('Is full depth')
)
class Meta:
@@ -962,7 +963,7 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = DeviceType
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
manufacturer = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
@@ -979,42 +980,42 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
)
console_ports = forms.NullBooleanField(
required=False,
- label='Has console ports',
+ label=_('Has console ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
console_server_ports = forms.NullBooleanField(
required=False,
- label='Has console server ports',
+ label=_('Has console server ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
power_ports = forms.NullBooleanField(
required=False,
- label='Has power ports',
+ label=_('Has power ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
power_outlets = forms.NullBooleanField(
required=False,
- label='Has power outlets',
+ label=_('Has power outlets'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
interfaces = forms.NullBooleanField(
required=False,
- label='Has interfaces',
+ label=_('Has interfaces'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
pass_through_ports = forms.NullBooleanField(
required=False,
- label='Has pass-through ports',
+ label=_('Has pass-through ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@@ -1046,7 +1047,7 @@ class ComponentTemplateCreateForm(BootstrapMixin, forms.Form):
)
)
name_pattern = ExpandableNameField(
- label='Name'
+ label=_('Name')
)
@@ -1138,12 +1139,12 @@ class PowerPortTemplateCreateForm(ComponentTemplateCreateForm):
maximum_draw = forms.IntegerField(
min_value=1,
required=False,
- help_text="Maximum power draw (watts)"
+ help_text=_('Maximum power draw (watts)')
)
allocated_draw = forms.IntegerField(
min_value=1,
required=False,
- help_text="Allocated power draw (watts)"
+ help_text=_('Allocated power draw (watts)')
)
@@ -1160,12 +1161,12 @@ class PowerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
maximum_draw = forms.IntegerField(
min_value=1,
required=False,
- help_text="Maximum power draw (watts)"
+ help_text=_('Maximum power draw (watts)')
)
allocated_draw = forms.IntegerField(
min_value=1,
required=False,
- help_text="Allocated power draw (watts)"
+ help_text=_('Allocated power draw (watts)')
)
class Meta:
@@ -1282,7 +1283,7 @@ class InterfaceTemplateCreateForm(ComponentTemplateCreateForm):
)
mgmt_only = forms.BooleanField(
required=False,
- label='Management only'
+ label=_('Management only')
)
@@ -1299,7 +1300,7 @@ class InterfaceTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
mgmt_only = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect,
- label='Management only'
+ label=_('Management only')
)
class Meta:
@@ -1336,8 +1337,8 @@ class FrontPortTemplateCreateForm(ComponentTemplateCreateForm):
)
rear_port_set = forms.MultipleChoiceField(
choices=[],
- label='Rear ports',
- help_text='Select one rear port assignment for each front port being created.',
+ label=_('Rear ports'),
+ help_text=_('Select one rear port assignment for each front port being created.'),
)
def __init__(self, *args, **kwargs):
@@ -1423,7 +1424,7 @@ class RearPortTemplateCreateForm(ComponentTemplateCreateForm):
min_value=REARPORT_POSITIONS_MIN,
max_value=REARPORT_POSITIONS_MAX,
initial=1,
- help_text='The number of front ports which may be mapped to each rear port'
+ help_text=_('The number of front ports which may be mapped to each rear port')
)
@@ -1641,7 +1642,7 @@ class PlatformCSVForm(CSVModelForm):
queryset=Manufacturer.objects.all(),
required=False,
to_field_name='name',
- help_text='Limit platform assignments to this manufacturer'
+ help_text=_('Limit platform assignments to this manufacturer')
)
class Meta:
@@ -1672,7 +1673,7 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
position = forms.TypedChoiceField(
required=False,
empty_value=None,
- help_text="The lowest-numbered unit occupied by the device",
+ help_text=_('The lowest-numbered unit occupied by the device'),
widget=APISelect(
api_url='/api/dcim/racks/{{rack}}/elevation/',
disabled_indicator='device'
@@ -1851,39 +1852,39 @@ class BaseDeviceCSVForm(CustomFieldModelCSVForm):
device_role = CSVModelChoiceField(
queryset=DeviceRole.objects.all(),
to_field_name='name',
- help_text='Assigned role'
+ help_text=_('Assigned role')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
manufacturer = CSVModelChoiceField(
queryset=Manufacturer.objects.all(),
to_field_name='name',
- help_text='Device type manufacturer'
+ help_text=_('Device type manufacturer')
)
device_type = CSVModelChoiceField(
queryset=DeviceType.objects.all(),
to_field_name='model',
- help_text='Device type model'
+ help_text=_('Device type model')
)
platform = CSVModelChoiceField(
queryset=Platform.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned platform'
+ help_text=_('Assigned platform')
)
status = CSVChoiceField(
choices=DeviceStatusChoices,
- help_text='Operational status'
+ help_text=_('Operational status')
)
cluster = CSVModelChoiceField(
queryset=Cluster.objects.all(),
to_field_name='name',
required=False,
- help_text='Virtualization cluster'
+ help_text=_('Virtualization cluster')
)
class Meta:
@@ -1904,24 +1905,24 @@ class DeviceCSVForm(BaseDeviceCSVForm):
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
rack_group = CSVModelChoiceField(
queryset=RackGroup.objects.all(),
to_field_name='name',
required=False,
- help_text="Rack's group (if any)"
+ help_text=_("Rack's group (if any)")
)
rack = CSVModelChoiceField(
queryset=Rack.objects.all(),
to_field_name='name',
required=False,
- help_text="Assigned rack"
+ help_text=_('Assigned rack')
)
face = CSVChoiceField(
choices=DeviceFaceChoices,
required=False,
- help_text='Mounted rack face'
+ help_text=_('Mounted rack face')
)
class Meta(BaseDeviceCSVForm.Meta):
@@ -1951,12 +1952,12 @@ class ChildDeviceCSVForm(BaseDeviceCSVForm):
parent = CSVModelChoiceField(
queryset=Device.objects.all(),
to_field_name='name',
- help_text='Parent device'
+ help_text=_('Parent device')
)
device_bay = CSVModelChoiceField(
queryset=DeviceBay.objects.all(),
to_field_name='name',
- help_text='Device bay in which this device is installed'
+ help_text=_('Device bay in which this device is installed')
)
class Meta(BaseDeviceCSVForm.Meta):
@@ -2021,7 +2022,7 @@ class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
serial = forms.CharField(
max_length=50,
required=False,
- label='Serial Number'
+ label=_('Serial Number')
)
class Meta:
@@ -2038,7 +2039,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
]
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -2066,7 +2067,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
rack_group_id = DynamicModelMultipleChoiceField(
queryset=RackGroup.objects.all(),
required=False,
- label='Rack group',
+ label=_('Rack group'),
widget=APISelectMultiple(
filter_for={
'rack_id': 'group_id',
@@ -2076,7 +2077,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(),
required=False,
- label='Rack',
+ label=_('Rack'),
widget=APISelectMultiple(
null_option=True,
)
@@ -2092,7 +2093,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
- label='Manufacturer',
+ label=_('Manufacturer'),
widget=APISelectMultiple(
filter_for={
'device_type_id': 'manufacturer_id',
@@ -2102,7 +2103,7 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
device_type_id = DynamicModelMultipleChoiceField(
queryset=DeviceType.objects.all(),
required=False,
- label='Model',
+ label=_('Model'),
widget=APISelectMultiple(
display_field="model",
)
@@ -2123,60 +2124,60 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
)
mac_address = forms.CharField(
required=False,
- label='MAC address'
+ label=_('MAC address')
)
has_primary_ip = forms.NullBooleanField(
required=False,
- label='Has a primary IP',
+ label=_('Has a primary IP'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
virtual_chassis_member = forms.NullBooleanField(
required=False,
- label='Virtual chassis member',
+ label=_('Virtual chassis member'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
console_ports = forms.NullBooleanField(
required=False,
- label='Has console ports',
+ label=_('Has console ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
console_server_ports = forms.NullBooleanField(
required=False,
- label='Has console server ports',
+ label=_('Has console server ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
power_ports = forms.NullBooleanField(
required=False,
- label='Has power ports',
+ label=_('Has power ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
power_outlets = forms.NullBooleanField(
required=False,
- label='Has power outlets',
+ label=_('Has power outlets'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
interfaces = forms.NullBooleanField(
required=False,
- label='Has interfaces',
+ label=_('Has interfaces'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
pass_through_ports = forms.NullBooleanField(
required=False,
- label='Has pass-through ports',
+ label=_('Has pass-through ports'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@@ -2196,7 +2197,7 @@ class ComponentCreateForm(BootstrapMixin, forms.Form):
queryset=Device.objects.all()
)
name_pattern = ExpandableNameField(
- label='Name'
+ label=_('Name')
)
@@ -2206,7 +2207,7 @@ class DeviceBulkAddComponentForm(BootstrapMixin, forms.Form):
widget=forms.MultipleHiddenInput()
)
name_pattern = ExpandableNameField(
- label='Name'
+ label=_('Name')
)
def clean_tags(self):
@@ -2428,12 +2429,12 @@ class PowerPortCreateForm(ComponentCreateForm):
maximum_draw = forms.IntegerField(
min_value=1,
required=False,
- help_text="Maximum draw in watts"
+ help_text=_('Maximum draw in watts')
)
allocated_draw = forms.IntegerField(
min_value=1,
required=False,
- help_text="Allocated draw in watts"
+ help_text=_('Allocated draw in watts')
)
description = forms.CharField(
max_length=100,
@@ -2618,12 +2619,12 @@ class PowerOutletCSVForm(CSVModelForm):
queryset=PowerPort.objects.all(),
required=False,
to_field_name='name',
- help_text='Local power port which feeds this outlet'
+ help_text=_('Local power port which feeds this outlet')
)
feed_leg = CSVChoiceField(
choices=PowerOutletFeedLegChoices,
required=False,
- help_text='Electrical phase (for three-phase circuits)'
+ help_text=_('Electrical phase (for three-phase circuits)')
)
class Meta:
@@ -2673,7 +2674,7 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
)
mac_address = forms.CharField(
required=False,
- label='MAC address'
+ label=_('MAC address')
)
tag = TagFilterField(model)
@@ -2682,7 +2683,7 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
untagged_vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
- label='Untagged VLAN',
+ label=_('Untagged VLAN'),
widget=APISelect(
display_field='display_name',
full=True,
@@ -2694,7 +2695,7 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
tagged_vlans = DynamicModelMultipleChoiceField(
queryset=VLAN.objects.all(),
required=False,
- label='Tagged VLANs',
+ label=_('Tagged VLANs'),
widget=APISelectMultiple(
display_field='display_name',
full=True,
@@ -2720,7 +2721,7 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
'mode': StaticSelect2(),
}
labels = {
- 'mode': '802.1Q Mode',
+ 'mode': _('802.1Q Mode'),
}
help_texts = {
'mode': INTERFACE_MODE_HELP_TEXT,
@@ -2757,23 +2758,23 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
lag = forms.ModelChoiceField(
queryset=Interface.objects.all(),
required=False,
- label='Parent LAG',
+ label=_('Parent LAG'),
widget=StaticSelect2(),
)
mtu = forms.IntegerField(
required=False,
min_value=INTERFACE_MTU_MIN,
max_value=INTERFACE_MTU_MAX,
- label='MTU'
+ label=_('MTU')
)
mac_address = forms.CharField(
required=False,
- label='MAC Address'
+ label=_('MAC Address')
)
mgmt_only = forms.BooleanField(
required=False,
- label='Management only',
- help_text='This interface is used only for out-of-band management'
+ label=_('Management only'),
+ help_text=_('This interface is used only for out-of-band management')
)
description = forms.CharField(
max_length=100,
@@ -2938,16 +2939,16 @@ class InterfaceCSVForm(CSVModelForm):
queryset=Interface.objects.all(),
required=False,
to_field_name='name',
- help_text='Parent LAG interface'
+ help_text=_('Parent LAG interface')
)
type = CSVChoiceField(
choices=InterfaceTypeChoices,
- help_text='Physical medium'
+ help_text=_('Physical medium')
)
mode = CSVChoiceField(
choices=InterfaceModeChoices,
required=False,
- help_text='IEEE 802.1Q operational mode (for L2 interfaces)'
+ help_text=_('IEEE 802.1Q operational mode (for L2 interfaces)')
)
class Meta:
@@ -3029,8 +3030,8 @@ class FrontPortCreateForm(ComponentCreateForm):
)
rear_port_set = forms.MultipleChoiceField(
choices=[],
- label='Rear ports',
- help_text='Select one rear port assignment for each front port being created.',
+ label=_('Rear ports'),
+ help_text=_('Select one rear port assignment for each front port being created.'),
)
description = forms.CharField(
required=False
@@ -3129,18 +3130,18 @@ class FrontPortCSVForm(CSVModelForm):
rear_port = CSVModelChoiceField(
queryset=RearPort.objects.all(),
to_field_name='name',
- help_text='Corresponding rear port'
+ help_text=_('Corresponding rear port')
)
type = CSVChoiceField(
choices=PortTypeChoices,
- help_text='Physical medium classification'
+ help_text=_('Physical medium classification')
)
class Meta:
model = FrontPort
fields = FrontPort.csv_headers
help_texts = {
- 'rear_port_position': 'Mapped position on corresponding rear port',
+ 'rear_port_position': _('Mapped position on corresponding rear port'),
}
def __init__(self, *args, **kwargs):
@@ -3205,7 +3206,7 @@ class RearPortCreateForm(ComponentCreateForm):
min_value=REARPORT_POSITIONS_MIN,
max_value=REARPORT_POSITIONS_MAX,
initial=1,
- help_text='The number of front ports which may be mapped to each rear port'
+ help_text=_('The number of front ports which may be mapped to each rear port')
)
description = forms.CharField(
required=False
@@ -3256,7 +3257,7 @@ class RearPortCSVForm(CSVModelForm):
to_field_name='name'
)
type = CSVChoiceField(
- help_text='Physical medium classification',
+ help_text=_('Physical medium classification'),
choices=PortTypeChoices,
)
@@ -3264,7 +3265,7 @@ class RearPortCSVForm(CSVModelForm):
model = RearPort
fields = RearPort.csv_headers
help_texts = {
- 'positions': 'Number of front ports which may be mapped'
+ 'positions': _('Number of front ports which may be mapped')
}
@@ -3301,8 +3302,8 @@ class DeviceBayCreateForm(ComponentCreateForm):
class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
installed_device = forms.ModelChoiceField(
queryset=Device.objects.all(),
- label='Child Device',
- help_text="Child devices must first be created and assigned to the site/rack of the parent device.",
+ label=_('Child Device'),
+ help_text=_('Child devices must first be created and assigned to the site/rack of the parent device.'),
widget=StaticSelect2(),
)
@@ -3361,9 +3362,9 @@ class DeviceBayCSVForm(CSVModelForm):
queryset=Device.objects.all(),
required=False,
to_field_name='name',
- help_text='Child device installed within this bay',
+ help_text=_('Child device installed within this bay'),
error_messages={
- 'invalid_choice': 'Child device not found.',
+ 'invalid_choice': _('Child device not found.'),
}
)
@@ -3408,7 +3409,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, forms.ModelForm):
"""
termination_b_site = DynamicModelChoiceField(
queryset=Site.objects.all(),
- label='Site',
+ label=_('Site'),
required=False,
widget=APISelect(
filter_for={
@@ -3419,7 +3420,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, forms.ModelForm):
)
termination_b_rack = DynamicModelChoiceField(
queryset=Rack.objects.all(),
- label='Rack',
+ label=_('Rack'),
required=False,
widget=APISelect(
filter_for={
@@ -3432,7 +3433,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, forms.ModelForm):
)
termination_b_device = DynamicModelChoiceField(
queryset=Device.objects.all(),
- label='Device',
+ label=_('Device'),
required=False,
widget=APISelect(
display_field='display_name',
@@ -3457,7 +3458,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, forms.ModelForm):
class ConnectCableToConsolePortForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/console-ports/',
disabled_indicator='cable',
@@ -3467,7 +3468,7 @@ class ConnectCableToConsolePortForm(ConnectCableToDeviceForm):
class ConnectCableToConsoleServerPortForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/console-server-ports/',
disabled_indicator='cable',
@@ -3477,7 +3478,7 @@ class ConnectCableToConsoleServerPortForm(ConnectCableToDeviceForm):
class ConnectCableToPowerPortForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/power-ports/',
disabled_indicator='cable',
@@ -3487,7 +3488,7 @@ class ConnectCableToPowerPortForm(ConnectCableToDeviceForm):
class ConnectCableToPowerOutletForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/power-outlets/',
disabled_indicator='cable',
@@ -3497,7 +3498,7 @@ class ConnectCableToPowerOutletForm(ConnectCableToDeviceForm):
class ConnectCableToInterfaceForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/interfaces/',
disabled_indicator='cable',
@@ -3510,7 +3511,7 @@ class ConnectCableToInterfaceForm(ConnectCableToDeviceForm):
class ConnectCableToFrontPortForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/front-ports/',
disabled_indicator='cable',
@@ -3520,7 +3521,7 @@ class ConnectCableToFrontPortForm(ConnectCableToDeviceForm):
class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/rear-ports/',
disabled_indicator='cable',
@@ -3531,7 +3532,7 @@ class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm):
termination_b_provider = DynamicModelChoiceField(
queryset=Provider.objects.all(),
- label='Provider',
+ label=_('Provider'),
required=False,
widget=APISelect(
filter_for={
@@ -3541,7 +3542,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm):
)
termination_b_site = DynamicModelChoiceField(
queryset=Site.objects.all(),
- label='Site',
+ label=_('Site'),
required=False,
widget=APISelect(
filter_for={
@@ -3551,7 +3552,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm):
)
termination_b_circuit = DynamicModelChoiceField(
queryset=Circuit.objects.all(),
- label='Circuit',
+ label=_('Circuit'),
widget=APISelect(
display_field='cid',
filter_for={
@@ -3560,7 +3561,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm):
)
)
termination_b_id = forms.IntegerField(
- label='Side',
+ label=_('Side'),
widget=APISelect(
api_url='/api/circuits/circuit-terminations/',
disabled_indicator='cable',
@@ -3580,7 +3581,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm):
class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
termination_b_site = DynamicModelChoiceField(
queryset=Site.objects.all(),
- label='Site',
+ label=_('Site'),
required=False,
widget=APISelect(
display_field='cid',
@@ -3592,7 +3593,7 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
)
termination_b_rackgroup = DynamicModelChoiceField(
queryset=RackGroup.objects.all(),
- label='Rack Group',
+ label=_('Rack Group'),
required=False,
widget=APISelect(
display_field='cid',
@@ -3603,7 +3604,7 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
)
termination_b_powerpanel = DynamicModelChoiceField(
queryset=PowerPanel.objects.all(),
- label='Power Panel',
+ label=_('Power Panel'),
required=False,
widget=APISelect(
filter_for={
@@ -3612,7 +3613,7 @@ class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm):
)
)
termination_b_id = forms.IntegerField(
- label='Name',
+ label=_('Name'),
widget=APISelect(
api_url='/api/dcim/power-feeds/',
)
@@ -3640,7 +3641,7 @@ class CableForm(BootstrapMixin, forms.ModelForm):
}
error_messages = {
'length': {
- 'max_value': 'Maximum length is 32767 (any unit)'
+ 'max_value': _('Maximum length is 32767 (any unit)')
}
}
@@ -3650,49 +3651,49 @@ class CableCSVForm(CSVModelForm):
side_a_device = CSVModelChoiceField(
queryset=Device.objects.all(),
to_field_name='name',
- help_text='Side A device'
+ help_text=_('Side A device')
)
side_a_type = CSVModelChoiceField(
queryset=ContentType.objects.all(),
limit_choices_to=CABLE_TERMINATION_MODELS,
to_field_name='model',
- help_text='Side A type'
+ help_text=_('Side A type')
)
side_a_name = forms.CharField(
- help_text='Side A component name'
+ help_text=_('Side A component name')
)
# Termination B
side_b_device = CSVModelChoiceField(
queryset=Device.objects.all(),
to_field_name='name',
- help_text='Side B device'
+ help_text=_('Side B device')
)
side_b_type = CSVModelChoiceField(
queryset=ContentType.objects.all(),
limit_choices_to=CABLE_TERMINATION_MODELS,
to_field_name='model',
- help_text='Side B type'
+ help_text=_('Side B type')
)
side_b_name = forms.CharField(
- help_text='Side B component name'
+ help_text=_('Side B component name')
)
# Cable attributes
status = CSVChoiceField(
choices=CableStatusChoices,
required=False,
- help_text='Connection status'
+ help_text=_('Connection status')
)
type = CSVChoiceField(
choices=CableTypeChoices,
required=False,
- help_text='Physical medium classification'
+ help_text=_('Physical medium classification')
)
length_unit = CSVChoiceField(
choices=CableLengthUnitChoices,
required=False,
- help_text='Length unit'
+ help_text=_('Length unit')
)
class Meta:
@@ -3821,7 +3822,7 @@ class CableFilterForm(BootstrapMixin, forms.Form):
model = Cable
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
site = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(),
@@ -3849,7 +3850,7 @@ class CableFilterForm(BootstrapMixin, forms.Form):
rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(),
required=False,
- label='Rack',
+ label=_('Rack'),
widget=APISelectMultiple(
null_option=True,
filter_for={
@@ -3875,7 +3876,7 @@ class CableFilterForm(BootstrapMixin, forms.Form):
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device'
+ label=_('Device')
)
@@ -3898,7 +3899,7 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device'
+ label=_('Device')
)
@@ -3917,7 +3918,7 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device'
+ label=_('Device')
)
@@ -3936,7 +3937,7 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device'
+ label=_('Device')
)
@@ -3968,7 +3969,7 @@ class InventoryItemCreateForm(BootstrapMixin, forms.Form):
queryset=Device.objects.prefetch_related('device_type__manufacturer')
)
name_pattern = ExpandableNameField(
- label='Name'
+ label=_('Name')
)
manufacturer = DynamicModelChoiceField(
queryset=Manufacturer.objects.all(),
@@ -3977,7 +3978,7 @@ class InventoryItemCreateForm(BootstrapMixin, forms.Form):
part_id = forms.CharField(
max_length=50,
required=False,
- label='Part ID'
+ label=_('Part ID')
)
serial = forms.CharField(
max_length=50,
@@ -4025,7 +4026,7 @@ class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm):
part_id = forms.CharField(
max_length=50,
required=False,
- label='Part ID'
+ label=_('Part ID')
)
description = forms.CharField(
max_length=100,
@@ -4042,7 +4043,7 @@ class InventoryItemFilterForm(BootstrapMixin, forms.Form):
model = InventoryItem
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -4069,7 +4070,7 @@ class InventoryItemFilterForm(BootstrapMixin, forms.Form):
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device'
+ label=_('Device')
)
manufacturer = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
@@ -4138,8 +4139,8 @@ class DeviceVCMembershipForm(forms.ModelForm):
'vc_position', 'vc_priority',
]
labels = {
- 'vc_position': 'Position',
- 'vc_priority': 'Priority',
+ 'vc_position': _('Position'),
+ 'vc_priority': _('Priority'),
}
def __init__(self, validate_vc_position=False, *args, **kwargs):
@@ -4228,7 +4229,7 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = VirtualChassis
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -4302,7 +4303,7 @@ class PowerPanelCSVForm(CSVModelForm):
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
- help_text='Name of parent site'
+ help_text=_('Name of parent site')
)
rack_group = CSVModelChoiceField(
queryset=RackGroup.objects.all(),
@@ -4353,7 +4354,7 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = PowerPanel
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -4380,7 +4381,7 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldFilterForm):
rack_group_id = DynamicModelMultipleChoiceField(
queryset=RackGroup.objects.all(),
required=False,
- label='Rack group (ID)',
+ label=_('Rack group (ID)'),
widget=APISelectMultiple(
null_option=True,
)
@@ -4440,44 +4441,44 @@ class PowerFeedCSVForm(CustomFieldModelCSVForm):
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
power_panel = CSVModelChoiceField(
queryset=PowerPanel.objects.all(),
to_field_name='name',
- help_text='Upstream power panel'
+ help_text=_('Upstream power panel')
)
rack_group = CSVModelChoiceField(
queryset=RackGroup.objects.all(),
to_field_name='name',
required=False,
- help_text="Rack's group (if any)"
+ help_text=_("Rack's group (if any)")
)
rack = CSVModelChoiceField(
queryset=Rack.objects.all(),
to_field_name='name',
required=False,
- help_text='Rack'
+ help_text=_('Rack')
)
status = CSVChoiceField(
choices=PowerFeedStatusChoices,
required=False,
- help_text='Operational status'
+ help_text=_('Operational status')
)
type = CSVChoiceField(
choices=PowerFeedTypeChoices,
required=False,
- help_text='Primary or redundant'
+ help_text=_('Primary or redundant')
)
supply = CSVChoiceField(
choices=PowerFeedSupplyChoices,
required=False,
- help_text='Supply type (AC/DC)'
+ help_text=_('Supply type (AC/DC)')
)
phase = CSVChoiceField(
choices=PowerFeedPhaseChoices,
required=False,
- help_text='Single or three-phase'
+ help_text=_('Single or three-phase')
)
class Meta:
@@ -4558,7 +4559,7 @@ class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
)
comments = CommentField(
widget=SmallTextarea,
- label='Comments'
+ label=_('Comments')
)
class Meta:
@@ -4571,7 +4572,7 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = PowerFeed
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -4599,7 +4600,7 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldFilterForm):
power_panel_id = DynamicModelMultipleChoiceField(
queryset=PowerPanel.objects.all(),
required=False,
- label='Power panel',
+ label=_('Power panel'),
widget=APISelectMultiple(
null_option=True,
)
@@ -4607,7 +4608,7 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldFilterForm):
rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(),
required=False,
- label='Rack',
+ label=_('Rack'),
widget=APISelectMultiple(
null_option=True,
)
diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py
index ef5b07aca..15b528e6a 100644
--- a/netbox/dcim/models/__init__.py
+++ b/netbox/dcim/models/__init__.py
@@ -13,6 +13,7 @@ from django.db import models
from django.db.models import Count, F, ProtectedError, Sum
from django.urls import reverse
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext as _
from mptt.models import MPTTModel, TreeForeignKey
from taggit.managers import TaggableManager
from timezone_field import TimeZoneField
@@ -182,13 +183,13 @@ class Site(ChangeLoggedModel, CustomFieldModel):
facility = models.CharField(
max_length=50,
blank=True,
- help_text='Local facility ID or description'
+ help_text=_('Local facility ID or description')
)
asn = ASNField(
blank=True,
null=True,
- verbose_name='ASN',
- help_text='32-bit autonomous system number'
+ verbose_name=_('ASN'),
+ help_text=_('32-bit autonomous system number')
)
time_zone = TimeZoneField(
blank=True
@@ -210,14 +211,14 @@ class Site(ChangeLoggedModel, CustomFieldModel):
decimal_places=6,
blank=True,
null=True,
- help_text='GPS coordinate (latitude)'
+ help_text=_('GPS coordinate (latitude)')
)
longitude = models.DecimalField(
max_digits=9,
decimal_places=6,
blank=True,
null=True,
- help_text='GPS coordinate (longitude)'
+ help_text=_('GPS coordinate (longitude)')
)
contact_name = models.CharField(
max_length=50,
@@ -229,7 +230,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
)
contact_email = models.EmailField(
blank=True,
- verbose_name='Contact E-mail'
+ verbose_name=_('Contact E-mail')
)
comments = models.TextField(
blank=True
@@ -428,8 +429,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
max_length=50,
blank=True,
null=True,
- verbose_name='Facility ID',
- help_text='Locally-assigned identifier'
+ verbose_name=_('Facility ID'),
+ help_text=_('Locally-assigned identifier')
)
site = models.ForeignKey(
to='dcim.Site',
@@ -442,7 +443,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
related_name='racks',
blank=True,
null=True,
- help_text='Assigned group'
+ help_text=_('Assigned group')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@@ -462,53 +463,53 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
related_name='racks',
blank=True,
null=True,
- help_text='Functional role'
+ help_text=_('Functional role')
)
serial = models.CharField(
max_length=50,
blank=True,
- verbose_name='Serial number'
+ verbose_name=_('Serial number')
)
asset_tag = models.CharField(
max_length=50,
blank=True,
null=True,
unique=True,
- verbose_name='Asset tag',
- help_text='A unique tag used to identify this rack'
+ verbose_name=_('Asset tag'),
+ help_text=_('A unique tag used to identify this rack')
)
type = models.CharField(
choices=RackTypeChoices,
max_length=50,
blank=True,
- verbose_name='Type'
+ verbose_name=_('Type')
)
width = models.PositiveSmallIntegerField(
choices=RackWidthChoices,
default=RackWidthChoices.WIDTH_19IN,
- verbose_name='Width',
- help_text='Rail-to-rail width'
+ verbose_name=_('Width'),
+ help_text=_('Rail-to-rail width')
)
u_height = models.PositiveSmallIntegerField(
default=RACK_U_HEIGHT_DEFAULT,
- verbose_name='Height (U)',
+ verbose_name=_('Height (U)'),
validators=[MinValueValidator(1), MaxValueValidator(100)],
- help_text='Height in rack units'
+ help_text=_('Height in rack units')
)
desc_units = models.BooleanField(
default=False,
- verbose_name='Descending units',
- help_text='Units are numbered top-to-bottom'
+ verbose_name=_('Descending units'),
+ help_text=_('Units are numbered top-to-bottom')
)
outer_width = models.PositiveSmallIntegerField(
blank=True,
null=True,
- help_text='Outer dimension of rack (width)'
+ help_text=_('Outer dimension of rack (width)')
)
outer_depth = models.PositiveSmallIntegerField(
blank=True,
null=True,
- help_text='Outer dimension of rack (depth)'
+ help_text=_('Outer dimension of rack (depth)')
)
outer_unit = models.CharField(
max_length=50,
@@ -949,24 +950,24 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
part_number = models.CharField(
max_length=50,
blank=True,
- help_text='Discrete part number (optional)'
+ help_text=_('Discrete part number (optional)')
)
u_height = models.PositiveSmallIntegerField(
default=1,
- verbose_name='Height (U)'
+ verbose_name=_('Height (U)')
)
is_full_depth = models.BooleanField(
default=True,
- verbose_name='Is full depth',
- help_text='Device consumes both front and rear rack faces'
+ verbose_name=_('Is full depth'),
+ help_text=_('Device consumes both front and rear rack faces')
)
subdevice_role = models.CharField(
max_length=50,
choices=SubdeviceRoleChoices,
blank=True,
- verbose_name='Parent/child status',
- help_text='Parent devices house child devices in device bays. Leave blank '
- 'if this device type is neither a parent nor a child.'
+ verbose_name=_('Parent/child status'),
+ help_text=_('Parent devices house child devices in device bays. Leave blank '
+ 'if this device type is neither a parent nor a child.')
)
front_image = models.ImageField(
upload_to='devicetype-images',
@@ -1200,8 +1201,8 @@ class DeviceRole(ChangeLoggedModel):
)
vm_role = models.BooleanField(
default=True,
- verbose_name='VM Role',
- help_text='Virtual machines may be assigned to this role'
+ verbose_name=_('VM Role'),
+ help_text=_('Virtual machines may be assigned to this role')
)
description = models.CharField(
max_length=200,
@@ -1246,19 +1247,19 @@ class Platform(ChangeLoggedModel):
related_name='platforms',
blank=True,
null=True,
- help_text='Optionally limit this platform to devices of a certain manufacturer'
+ help_text=_('Optionally limit this platform to devices of a certain manufacturer')
)
napalm_driver = models.CharField(
max_length=50,
blank=True,
- verbose_name='NAPALM driver',
- help_text='The name of the NAPALM driver to use when interacting with devices'
+ verbose_name=_('NAPALM driver'),
+ help_text=_('The name of the NAPALM driver to use when interacting with devices')
)
napalm_args = JSONField(
blank=True,
null=True,
- verbose_name='NAPALM arguments',
- help_text='Additional arguments to pass when initiating the NAPALM driver (JSON format)'
+ verbose_name=_('NAPALM arguments'),
+ help_text=_('Additional arguments to pass when initiating the NAPALM driver (JSON format)')
)
description = models.CharField(
max_length=200,
@@ -1338,15 +1339,15 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
serial = models.CharField(
max_length=50,
blank=True,
- verbose_name='Serial number'
+ verbose_name=_('Serial number')
)
asset_tag = models.CharField(
max_length=50,
blank=True,
null=True,
unique=True,
- verbose_name='Asset tag',
- help_text='A unique tag used to identify this device'
+ verbose_name=_('Asset tag'),
+ help_text=_('A unique tag used to identify this device')
)
site = models.ForeignKey(
to='dcim.Site',
@@ -1364,14 +1365,14 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
blank=True,
null=True,
validators=[MinValueValidator(1)],
- verbose_name='Position (U)',
- help_text='The lowest-numbered unit occupied by the device'
+ verbose_name=_('Position (U)'),
+ help_text=_('The lowest-numbered unit occupied by the device')
)
face = models.CharField(
max_length=50,
blank=True,
choices=DeviceFaceChoices,
- verbose_name='Rack face'
+ verbose_name=_('Rack face')
)
status = models.CharField(
max_length=50,
@@ -1384,7 +1385,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='primary_ip4_for',
blank=True,
null=True,
- verbose_name='Primary IPv4'
+ verbose_name=_('Primary IPv4')
)
primary_ip6 = models.OneToOneField(
to='ipam.IPAddress',
@@ -1392,7 +1393,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='primary_ip6_for',
blank=True,
null=True,
- verbose_name='Primary IPv6'
+ verbose_name=_('Primary IPv6')
)
cluster = models.ForeignKey(
to='virtualization.Cluster',
@@ -1904,7 +1905,7 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
max_utilization = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(100)],
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
- help_text="Maximum permissible draw (percentage)"
+ help_text=_('Maximum permissible draw (percentage)')
)
available_power = models.PositiveIntegerField(
default=0,
diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py
index 164d37d77..4da365c52 100644
--- a/netbox/dcim/models/device_component_templates.py
+++ b/netbox/dcim/models/device_component_templates.py
@@ -1,6 +1,7 @@
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
+from django.utils.translation import gettext as _
from dcim.choices import *
from dcim.constants import *
@@ -154,13 +155,13 @@ class PowerPortTemplate(ComponentTemplateModel):
blank=True,
null=True,
validators=[MinValueValidator(1)],
- help_text="Maximum power draw (watts)"
+ help_text=_('Maximum power draw (watts)')
)
allocated_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
- help_text="Allocated power draw (watts)"
+ help_text=_('Allocated power draw (watts)')
)
class Meta:
@@ -213,7 +214,7 @@ class PowerOutletTemplate(ComponentTemplateModel):
max_length=50,
choices=PowerOutletFeedLegChoices,
blank=True,
- help_text="Phase (for three-phase feeds)"
+ help_text=_('Phase (for three-phase feeds)')
)
class Meta:
@@ -269,7 +270,7 @@ class InterfaceTemplate(ComponentTemplateModel):
)
mgmt_only = models.BooleanField(
default=False,
- verbose_name='Management only'
+ verbose_name=_('Management only')
)
class Meta:
diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py
index 0143b39d9..3dcd89572 100644
--- a/netbox/dcim/models/device_components.py
+++ b/netbox/dcim/models/device_components.py
@@ -6,6 +6,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Sum
from django.urls import reverse
+from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.choices import *
@@ -252,7 +253,7 @@ class ConsolePort(CableTermination, ComponentModel):
max_length=50,
choices=ConsolePortTypeChoices,
blank=True,
- help_text='Physical port type'
+ help_text=_('Physical port type')
)
connected_endpoint = models.OneToOneField(
to='dcim.ConsoleServerPort',
@@ -311,7 +312,7 @@ class ConsoleServerPort(CableTermination, ComponentModel):
max_length=50,
choices=ConsolePortTypeChoices,
blank=True,
- help_text='Physical port type'
+ help_text=_('Physical port type')
)
connection_status = models.NullBooleanField(
choices=CONNECTION_STATUS_CHOICES,
@@ -363,19 +364,19 @@ class PowerPort(CableTermination, ComponentModel):
max_length=50,
choices=PowerPortTypeChoices,
blank=True,
- help_text='Physical port type'
+ help_text=_('Physical port type')
)
maximum_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
- help_text="Maximum power draw (watts)"
+ help_text=_('Maximum power draw (watts)')
)
allocated_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
- help_text="Allocated power draw (watts)"
+ help_text=_('Allocated power draw (watts)')
)
_connected_poweroutlet = models.OneToOneField(
to='dcim.PowerOutlet',
@@ -523,7 +524,7 @@ class PowerOutlet(CableTermination, ComponentModel):
max_length=50,
choices=PowerOutletTypeChoices,
blank=True,
- help_text='Physical port type'
+ help_text=_('Physical port type')
)
power_port = models.ForeignKey(
to='dcim.PowerPort',
@@ -536,7 +537,7 @@ class PowerOutlet(CableTermination, ComponentModel):
max_length=50,
choices=PowerOutletFeedLegChoices,
blank=True,
- help_text="Phase (for three-phase feeds)"
+ help_text=_('Phase (for three-phase feeds)')
)
connection_status = models.NullBooleanField(
choices=CONNECTION_STATUS_CHOICES,
@@ -629,7 +630,7 @@ class Interface(CableTermination, ComponentModel):
related_name='member_interfaces',
null=True,
blank=True,
- verbose_name='Parent LAG'
+ verbose_name=_('Parent LAG')
)
type = models.CharField(
max_length=50,
@@ -641,18 +642,18 @@ class Interface(CableTermination, ComponentModel):
mac_address = MACAddressField(
null=True,
blank=True,
- verbose_name='MAC Address'
+ verbose_name=_('MAC Address')
)
mtu = models.PositiveIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1), MaxValueValidator(65536)],
- verbose_name='MTU'
+ verbose_name=_('MTU')
)
mgmt_only = models.BooleanField(
default=False,
- verbose_name='OOB Management',
- help_text='This interface is used only for out-of-band management'
+ verbose_name=_('OOB Management'),
+ help_text=_('This interface is used only for out-of-band management')
)
mode = models.CharField(
max_length=50,
@@ -665,13 +666,13 @@ class Interface(CableTermination, ComponentModel):
related_name='interfaces_as_untagged',
null=True,
blank=True,
- verbose_name='Untagged VLAN'
+ verbose_name=_('Untagged VLAN')
)
tagged_vlans = models.ManyToManyField(
to='ipam.VLAN',
related_name='interfaces_as_tagged',
blank=True,
- verbose_name='Tagged VLANs'
+ verbose_name=_('Tagged VLANs')
)
tags = TaggableManager(through=TaggedItem)
@@ -976,7 +977,7 @@ class DeviceBay(ComponentModel):
)
name = models.CharField(
max_length=50,
- verbose_name='Name'
+ verbose_name=_('Name')
)
_name = NaturalOrderingField(
target_field='name',
@@ -1056,7 +1057,7 @@ class InventoryItem(ComponentModel):
)
name = models.CharField(
max_length=50,
- verbose_name='Name'
+ verbose_name=_('Name')
)
_name = NaturalOrderingField(
target_field='name',
@@ -1072,13 +1073,13 @@ class InventoryItem(ComponentModel):
)
part_id = models.CharField(
max_length=50,
- verbose_name='Part ID',
+ verbose_name=_('Part ID'),
blank=True,
- help_text='Manufacturer-assigned part identifier'
+ help_text=_('Manufacturer-assigned part identifier')
)
serial = models.CharField(
max_length=50,
- verbose_name='Serial number',
+ verbose_name=_('Serial number'),
blank=True
)
asset_tag = models.CharField(
@@ -1086,12 +1087,12 @@ class InventoryItem(ComponentModel):
unique=True,
blank=True,
null=True,
- verbose_name='Asset tag',
- help_text='A unique tag used to identify this item'
+ verbose_name=_('Asset tag'),
+ help_text=_('A unique tag used to identify this item')
)
discovered = models.BooleanField(
default=False,
- help_text='This item was automatically discovered'
+ help_text=_('This item was automatically discovered')
)
tags = TaggableManager(through=TaggedItem)
diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py
index 93ef724e0..c0a742116 100644
--- a/netbox/dcim/tables.py
+++ b/netbox/dcim/tables.py
@@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
+from django.utils.translation import gettext as _
from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ColoredLabelColumn, TagColumn, ToggleColumn
@@ -196,7 +197,7 @@ class RegionTable(BaseTable):
orderable=False
)
site_count = tables.Column(
- verbose_name='Sites'
+ verbose_name=_('Sites')
)
actions = tables.TemplateColumn(
template_code=REGION_ACTIONS,
@@ -255,10 +256,10 @@ class RackGroupTable(BaseTable):
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')],
- verbose_name='Site'
+ verbose_name=_('Site')
)
rack_count = tables.Column(
- verbose_name='Racks'
+ verbose_name=_('Racks')
)
actions = tables.TemplateColumn(
template_code=RACKGROUP_ACTIONS,
@@ -315,7 +316,7 @@ class RackTable(BaseTable):
role = ColoredLabelColumn()
u_height = tables.TemplateColumn(
template_code="{{ record.u_height }}U",
- verbose_name='Height'
+ verbose_name=_('Height')
)
class Meta(BaseTable.Meta):
@@ -330,17 +331,17 @@ class RackTable(BaseTable):
class RackDetailTable(RackTable):
device_count = tables.TemplateColumn(
template_code=RACK_DEVICE_COUNT,
- verbose_name='Devices'
+ verbose_name=_('Devices')
)
get_utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False,
- verbose_name='Space'
+ verbose_name=_('Space')
)
get_power_utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False,
- verbose_name='Power'
+ verbose_name=_('Power')
)
tags = TagColumn(
url_name='dcim:rack_list'
@@ -382,7 +383,7 @@ class RackReservationTable(BaseTable):
)
unit_list = tables.Column(
orderable=False,
- verbose_name='Units'
+ verbose_name=_('Units')
)
actions = tables.TemplateColumn(
template_code=RACKRESERVATION_ACTIONS,
@@ -408,13 +409,13 @@ class ManufacturerTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
devicetype_count = tables.Column(
- verbose_name='Device Types'
+ verbose_name=_('Device Types')
)
inventoryitem_count = tables.Column(
- verbose_name='Inventory Items'
+ verbose_name=_('Inventory Items')
)
platform_count = tables.Column(
- verbose_name='Platforms'
+ verbose_name=_('Platforms')
)
slug = tables.Column()
actions = tables.TemplateColumn(
@@ -439,14 +440,14 @@ class DeviceTypeTable(BaseTable):
model = tables.LinkColumn(
viewname='dcim:devicetype',
args=[Accessor('pk')],
- verbose_name='Device Type'
+ verbose_name=_('Device Type')
)
is_full_depth = BooleanColumn(
- verbose_name='Full Depth'
+ verbose_name=_('Full Depth')
)
instance_count = tables.TemplateColumn(
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
- verbose_name='Instances'
+ verbose_name=_('Instances')
)
tags = TagColumn(
url_name='dcim:devicetype_list'
@@ -608,7 +609,7 @@ class InterfaceImportTable(BaseTable):
virtual_machine = tables.LinkColumn(
viewname='virtualization:virtualmachine',
args=[Accessor('virtual_machine.pk')],
- verbose_name='Virtual Machine'
+ verbose_name=_('Virtual Machine')
)
class Meta(BaseTable.Meta):
@@ -626,7 +627,7 @@ class FrontPortTemplateTable(BaseTable):
order_by=('_name',)
)
rear_port_position = tables.Column(
- verbose_name='Position'
+ verbose_name=_('Position')
)
actions = tables.TemplateColumn(
template_code=get_component_template_actions('frontporttemplate'),
@@ -706,15 +707,15 @@ class DeviceRoleTable(BaseTable):
pk = ToggleColumn()
device_count = tables.TemplateColumn(
template_code=DEVICEROLE_DEVICE_COUNT,
- verbose_name='Devices'
+ verbose_name=_('Devices')
)
vm_count = tables.TemplateColumn(
template_code=DEVICEROLE_VM_COUNT,
- verbose_name='VMs'
+ verbose_name=_('VMs')
)
color = tables.TemplateColumn(
template_code=COLOR_LABEL,
- verbose_name='Label'
+ verbose_name=_('Label')
)
vm_role = BooleanColumn()
actions = tables.TemplateColumn(
@@ -737,11 +738,11 @@ class PlatformTable(BaseTable):
pk = ToggleColumn()
device_count = tables.TemplateColumn(
template_code=PLATFORM_DEVICE_COUNT,
- verbose_name='Devices'
+ verbose_name=_('Devices')
)
vm_count = tables.TemplateColumn(
template_code=PLATFORM_VM_COUNT,
- verbose_name='VMs'
+ verbose_name=_('VMs')
)
actions = tables.TemplateColumn(
template_code=PLATFORM_ACTIONS,
@@ -785,28 +786,28 @@ class DeviceTable(BaseTable):
args=[Accessor('rack.pk')]
)
device_role = ColoredLabelColumn(
- verbose_name='Role'
+ verbose_name=_('Role')
)
device_type = tables.LinkColumn(
viewname='dcim:devicetype',
args=[Accessor('device_type.pk')],
- verbose_name='Type',
+ verbose_name=_('Type'),
text=lambda record: record.device_type.display_name
)
primary_ip = tables.TemplateColumn(
template_code=DEVICE_PRIMARY_IP,
orderable=False,
- verbose_name='IP Address'
+ verbose_name=_('IP Address')
)
primary_ip4 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip4.pk')],
- verbose_name='IPv4 Address'
+ verbose_name=_('IPv4 Address')
)
primary_ip6 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip6.pk')],
- verbose_name='IPv6 Address'
+ verbose_name=_('IPv6 Address')
)
cluster = tables.LinkColumn(
viewname='virtualization:cluster',
@@ -817,10 +818,10 @@ class DeviceTable(BaseTable):
args=[Accessor('virtual_chassis.pk')]
)
vc_position = tables.Column(
- verbose_name='VC Position'
+ verbose_name=_('VC Position')
)
vc_priority = tables.Column(
- verbose_name='VC Priority'
+ verbose_name=_('VC Priority')
)
tags = TagColumn(
url_name='dcim:device_list'
@@ -857,10 +858,10 @@ class DeviceImportTable(BaseTable):
args=[Accessor('rack.pk')]
)
device_role = tables.Column(
- verbose_name='Role'
+ verbose_name=_('Role')
)
device_type = tables.Column(
- verbose_name='Type'
+ verbose_name=_('Type')
)
class Meta(BaseTable.Meta):
@@ -1031,29 +1032,29 @@ class CableTable(BaseTable):
id = tables.LinkColumn(
viewname='dcim:cable',
args=[Accessor('pk')],
- verbose_name='ID'
+ verbose_name=_('ID')
)
termination_a_parent = tables.TemplateColumn(
template_code=CABLE_TERMINATION_PARENT,
accessor=Accessor('termination_a'),
orderable=False,
- verbose_name='Side A'
+ verbose_name=_('Side A')
)
termination_a = tables.LinkColumn(
accessor=Accessor('termination_a'),
orderable=False,
- verbose_name='Termination A'
+ verbose_name=_('Termination A')
)
termination_b_parent = tables.TemplateColumn(
template_code=CABLE_TERMINATION_PARENT,
accessor=Accessor('termination_b'),
orderable=False,
- verbose_name='Side B'
+ verbose_name=_('Side B')
)
termination_b = tables.LinkColumn(
accessor=Accessor('termination_b'),
orderable=False,
- verbose_name='Termination B'
+ verbose_name=_('Termination B')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@@ -1085,17 +1086,17 @@ class ConsoleConnectionTable(BaseTable):
viewname='dcim:device',
accessor=Accessor('connected_endpoint.device'),
args=[Accessor('connected_endpoint.device.pk')],
- verbose_name='Console Server'
+ verbose_name=_('Console Server')
)
connected_endpoint = tables.Column(
- verbose_name='Port'
+ verbose_name=_('Port')
)
device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
name = tables.Column(
- verbose_name='Console Port'
+ verbose_name=_('Console Port')
)
class Meta(BaseTable.Meta):
@@ -1109,18 +1110,18 @@ class PowerConnectionTable(BaseTable):
accessor=Accessor('connected_endpoint.device'),
args=[Accessor('connected_endpoint.device.pk')],
order_by='_connected_poweroutlet__device',
- verbose_name='PDU'
+ verbose_name=_('PDU')
)
outlet = tables.Column(
accessor=Accessor('_connected_poweroutlet'),
- verbose_name='Outlet'
+ verbose_name=_('Outlet')
)
device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
name = tables.Column(
- verbose_name='Power Port'
+ verbose_name=_('Power Port')
)
class Meta(BaseTable.Meta):
@@ -1133,25 +1134,25 @@ class InterfaceConnectionTable(BaseTable):
viewname='dcim:device',
accessor=Accessor('device'),
args=[Accessor('device.pk')],
- verbose_name='Device A'
+ verbose_name=_('Device A')
)
interface_a = tables.LinkColumn(
viewname='dcim:interface',
accessor=Accessor('name'),
args=[Accessor('pk')],
- verbose_name='Interface A'
+ verbose_name=_('Interface A')
)
device_b = tables.LinkColumn(
viewname='dcim:device',
accessor=Accessor('_connected_interface.device'),
args=[Accessor('_connected_interface.device.pk')],
- verbose_name='Device B'
+ verbose_name=_('Device B')
)
interface_b = tables.LinkColumn(
viewname='dcim:interface',
accessor=Accessor('_connected_interface'),
args=[Accessor('_connected_interface.pk')],
- verbose_name='Interface B'
+ verbose_name=_('Interface B')
)
class Meta(BaseTable.Meta):
@@ -1195,7 +1196,7 @@ class VirtualChassisTable(BaseTable):
linkify=True
)
member_count = tables.Column(
- verbose_name='Members'
+ verbose_name=_('Members')
)
tags = TagColumn(
url_name='dcim:virtualchassis_list'
@@ -1220,7 +1221,7 @@ class PowerPanelTable(BaseTable):
)
powerfeed_count = tables.TemplateColumn(
template_code=POWERPANEL_POWERFEED_COUNT,
- verbose_name='Feeds'
+ verbose_name=_('Feeds')
)
class Meta(BaseTable.Meta):
@@ -1254,7 +1255,7 @@ class PowerFeedTable(BaseTable):
template_code="{{ value }}%"
)
available_power = tables.Column(
- verbose_name='Available power (VA)'
+ verbose_name=_('Available power (VA)')
)
tags = TagColumn(
url_name='dcim:powerfeed_list'
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index e193813d2..de796c3cd 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -14,6 +14,7 @@ from django.urls import reverse
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext as _
from django.views.generic import View
from circuits.models import Circuit
@@ -74,7 +75,7 @@ class BulkRenameView(GetReturnURLMixin, View):
for obj in selected_objects:
obj.name = obj.new_name
obj.save()
- messages.success(request, "Renamed {} {}".format(
+ messages.success(request, _('Renamed {} {}').format(
len(selected_objects),
model._meta.verbose_name_plural
))
@@ -119,7 +120,7 @@ class BulkDisconnectView(GetReturnURLMixin, View):
obj.cable.delete()
count += 1
- messages.success(request, "Disconnected {} {}".format(
+ messages.success(request, _('Disconnected {} {}').format(
count, self.model._meta.verbose_name_plural
))
@@ -1853,7 +1854,7 @@ class DeviceBayPopulateView(PermissionRequiredMixin, View):
device_bay.installed_device = form.cleaned_data['installed_device']
device_bay.save()
- messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
+ messages.success(request, _('Added {} to {}.').format(device_bay.installed_device, device_bay))
return redirect('dcim:device', pk=device_bay.device.pk)
@@ -1888,7 +1889,7 @@ class DeviceBayDepopulateView(PermissionRequiredMixin, View):
removed_device = device_bay.installed_device
device_bay.installed_device = None
device_bay.save()
- messages.success(request, "{} has been removed from {}.".format(removed_device, device_bay))
+ messages.success(request, _('{} has been removed from {}.').format(removed_device, device_bay))
return redirect('dcim:device', pk=device_bay.device.pk)
diff --git a/netbox/extras/admin.py b/netbox/extras/admin.py
index 808d7ce32..b4192fbe2 100644
--- a/netbox/extras/admin.py
+++ b/netbox/extras/admin.py
@@ -1,5 +1,6 @@
from django import forms
from django.contrib import admin
+from django.utils.translation import gettext as _
from utilities.forms import LaxURLField
from .models import CustomField, CustomFieldChoice, CustomLink, Graph, ExportTemplate, ReportResult, Webhook
@@ -20,7 +21,7 @@ def order_content_types(field):
class WebhookForm(forms.ModelForm):
payload_url = LaxURLField(
- label='URL'
+ label=_('URL')
)
class Meta:
@@ -116,11 +117,11 @@ class CustomLinkForm(forms.ModelForm):
'url': forms.Textarea,
}
help_texts = {
- 'weight': 'A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
- 'first in a list.',
- 'text': 'Jinja2 template code for the link text. Reference the object as {{ obj }}
. Links '
- 'which render as empty text will not be displayed.',
- 'url': 'Jinja2 template code for the link URL. Reference the object as {{ obj }}
.',
+ 'weight': _('A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
+ 'first in a list.'),
+ 'text': _('Jinja2 template code for the link text. Reference the object as {{ obj }}
. Links '
+ 'which render as empty text will not be displayed.'),
+ 'url': _('Jinja2 template code for the link URL. Reference the object as {{ obj }}
.'),
}
def __init__(self, *args, **kwargs):
diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py
index 7ccdb1d86..1d8cb5f04 100644
--- a/netbox/extras/filters.py
+++ b/netbox/extras/filters.py
@@ -1,6 +1,7 @@
import django_filters
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
+from django.utils.translation import gettext as _
from dcim.models import DeviceRole, Platform, Region, Site
from tenancy.models import Tenant, TenantGroup
@@ -107,7 +108,7 @@ class ExportTemplateFilterSet(BaseFilterSet):
class TagFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
class Meta:
@@ -126,95 +127,95 @@ class TagFilterSet(BaseFilterSet):
class ConfigContextFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='regions',
queryset=Region.objects.all(),
- label='Region',
+ label=_('Region'),
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='regions__slug',
queryset=Region.objects.all(),
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='sites',
queryset=Site.objects.all(),
- label='Site',
+ label=_('Site'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='sites__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
field_name='roles',
queryset=DeviceRole.objects.all(),
- label='Role',
+ label=_('Role'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='roles__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
field_name='platforms',
queryset=Platform.objects.all(),
- label='Platform',
+ label=_('Platform'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platforms__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
- label='Platform (slug)',
+ label=_('Platform (slug)'),
)
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups',
queryset=ClusterGroup.objects.all(),
- label='Cluster group',
+ label=_('Cluster group'),
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
- label='Cluster group (slug)',
+ label=_('Cluster group (slug)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
field_name='clusters',
queryset=Cluster.objects.all(),
- label='Cluster',
+ label=_('Cluster'),
)
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups',
queryset=TenantGroup.objects.all(),
- label='Tenant group',
+ label=_('Tenant group'),
)
tenant_group = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
- label='Tenant group (slug)',
+ label=_('Tenant group (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenants',
queryset=Tenant.objects.all(),
- label='Tenant',
+ label=_('Tenant'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenants__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
- label='Tenant (slug)',
+ label=_('Tenant (slug)'),
)
tag = django_filters.ModelMultipleChoiceFilter(
field_name='tags__slug',
queryset=Tag.objects.all(),
to_field_name='slug',
- label='Tag (slug)',
+ label=_('Tag (slug)'),
)
class Meta:
@@ -238,7 +239,7 @@ class ConfigContextFilterSet(BaseFilterSet):
class LocalConfigContextFilterSet(django_filters.FilterSet):
local_context_data = django_filters.BooleanFilter(
method='_local_context_data',
- label='Has local config context data',
+ label=_('Has local config context data'),
)
def _local_context_data(self, queryset, name, value):
@@ -248,7 +249,7 @@ class LocalConfigContextFilterSet(django_filters.FilterSet):
class ObjectChangeFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
time = django_filters.DateTimeFromToRangeFilter()
diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py
index bff919005..3b0a4017f 100644
--- a/netbox/extras/forms.py
+++ b/netbox/extras/forms.py
@@ -1,6 +1,7 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import gettext as _
from mptt.forms import TreeNodeMultipleChoiceField
from taggit.forms import TagField as TagField_
@@ -181,7 +182,7 @@ class TagFilterForm(BootstrapMixin, forms.Form):
model = Tag
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
@@ -285,7 +286,7 @@ class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -330,7 +331,7 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
- label='Cluster'
+ label=_('Cluster')
)
tenant_group = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(),
@@ -365,7 +366,7 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
class LocalConfigContextFilterForm(forms.Form):
local_context_data = forms.NullBooleanField(
required=False,
- label='Has local config context data',
+ label=_('Has local config context data'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@@ -393,15 +394,15 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
model = ObjectChange
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
time_after = forms.DateTimeField(
- label='After',
+ label=_('After'),
required=False,
widget=DateTimePicker()
)
time_before = forms.DateTimeField(
- label='Before',
+ label=_('Before'),
required=False,
widget=DateTimePicker()
)
@@ -420,7 +421,7 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
queryset=ContentType.objects.order_by('model'),
required=False,
widget=ContentTypeSelect(),
- label='Object Type'
+ label=_('Object Type')
)
@@ -432,8 +433,8 @@ class ScriptForm(BootstrapMixin, forms.Form):
_commit = forms.BooleanField(
required=False,
initial=True,
- label="Commit changes",
- help_text="Commit changes to the database (uncheck for a dry-run)"
+ label=_('Commit changes'),
+ help_text=_('Commit changes to the database (uncheck for a dry-run)')
)
def __init__(self, *args, **kwargs):
diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py
index 62e2ca4df..7884a18d9 100644
--- a/netbox/extras/models/customfields.py
+++ b/netbox/extras/models/customfields.py
@@ -7,6 +7,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.validators import ValidationError
from django.db import models
+from django.utils.translation import gettext as _
from utilities.forms import CSVChoiceField, DatePicker, LaxURLField, StaticSelect2, add_blank_choice
from extras.choices import *
@@ -70,9 +71,9 @@ class CustomField(models.Model):
obj_type = models.ManyToManyField(
to=ContentType,
related_name='custom_fields',
- verbose_name='Object(s)',
+ verbose_name=_('Object(s)'),
limit_choices_to=FeatureQuery('custom_fields'),
- help_text='The object(s) to which this field applies.'
+ help_text=_('The object(s) to which this field applies.')
)
type = models.CharField(
max_length=50,
@@ -86,8 +87,8 @@ class CustomField(models.Model):
label = models.CharField(
max_length=50,
blank=True,
- help_text='Name of the field as displayed to users (if not provided, '
- 'the field\'s name will be used)'
+ help_text=_('Name of the field as displayed to users (if not provided, '
+ 'the field\'s name will be used)')
)
description = models.CharField(
max_length=200,
@@ -95,24 +96,24 @@ class CustomField(models.Model):
)
required = models.BooleanField(
default=False,
- help_text='If true, this field is required when creating new objects '
- 'or editing an existing object.'
+ help_text=_('If true, this field is required when creating new objects '
+ 'or editing an existing object.')
)
filter_logic = models.CharField(
max_length=50,
choices=CustomFieldFilterLogicChoices,
default=CustomFieldFilterLogicChoices.FILTER_LOOSE,
- help_text='Loose matches any instance of a given string; exact '
- 'matches the entire field.'
+ help_text=_('Loose matches any instance of a given string; exact '
+ 'matches the entire field.')
)
default = models.CharField(
max_length=100,
blank=True,
- help_text='Default value for the field. Use "true" or "false" for booleans.'
+ help_text=_('Default value for the field. Use "true" or "false" for booleans.')
)
weight = models.PositiveSmallIntegerField(
default=100,
- help_text='Fields with higher weights appear lower in a form.'
+ help_text=_('Fields with higher weights appear lower in a form.')
)
objects = CustomFieldManager()
@@ -284,7 +285,7 @@ class CustomFieldChoice(models.Model):
)
weight = models.PositiveSmallIntegerField(
default=100,
- help_text='Higher weights appear lower in the list'
+ help_text=_('Higher weights appear lower in the list')
)
class Meta:
diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py
index f98a7b34f..073fe6713 100644
--- a/netbox/extras/models/models.py
+++ b/netbox/extras/models/models.py
@@ -10,6 +10,7 @@ from django.db import models
from django.http import HttpResponse
from django.template import Template, Context
from django.urls import reverse
+from django.utils.translation import gettext as _
from rest_framework.utils.encoders import JSONEncoder
from utilities.utils import deepmerge, render_jinja2
@@ -32,9 +33,9 @@ class Webhook(models.Model):
obj_type = models.ManyToManyField(
to=ContentType,
related_name='webhooks',
- verbose_name='Object types',
+ verbose_name=_('Object types'),
limit_choices_to=FeatureQuery('webhooks'),
- help_text="The object(s) to which this Webhook applies."
+ help_text=_('The object(s) to which this Webhook applies.')
)
name = models.CharField(
max_length=150,
@@ -42,20 +43,20 @@ class Webhook(models.Model):
)
type_create = models.BooleanField(
default=False,
- help_text="Call this webhook when a matching object is created."
+ help_text=_('Call this webhook when a matching object is created.')
)
type_update = models.BooleanField(
default=False,
- help_text="Call this webhook when a matching object is updated."
+ help_text=_('Call this webhook when a matching object is updated.')
)
type_delete = models.BooleanField(
default=False,
- help_text="Call this webhook when a matching object is deleted."
+ help_text=_('Call this webhook when a matching object is deleted.')
)
payload_url = models.CharField(
max_length=500,
- verbose_name='URL',
- help_text="A POST will be sent to this URL when the webhook is called."
+ verbose_name=_('URL'),
+ help_text=_('A POST will be sent to this URL when the webhook is called.')
)
enabled = models.BooleanField(
default=True
@@ -64,47 +65,47 @@ class Webhook(models.Model):
max_length=30,
choices=WebhookHttpMethodChoices,
default=WebhookHttpMethodChoices.METHOD_POST,
- verbose_name='HTTP method'
+ verbose_name=_('HTTP method')
)
http_content_type = models.CharField(
max_length=100,
default=HTTP_CONTENT_TYPE_JSON,
- verbose_name='HTTP content type',
- help_text='The complete list of official content types is available '
- 'here.'
+ verbose_name=_('HTTP content type'),
+ help_text=_('The complete list of official content types is available '
+ 'here.')
)
additional_headers = models.TextField(
blank=True,
- help_text="User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. "
+ help_text=_("User-supplied HTTP headers to be sent with the request in addition to the HTTP content type."
"Headers should be defined in the format Name: Value
. Jinja2 template processing is "
- "support with the same context as the request body (below)."
+ "support with the same context as the request body (below).")
)
body_template = models.TextField(
blank=True,
- help_text='Jinja2 template for a custom request body. If blank, a JSON object representing the change will be '
+ help_text=_('Jinja2 template for a custom request body. If blank, a JSON object representing the change will be '
'included. Available context data includes: event
, model
, '
- 'timestamp
, username
, request_id
, and data
.'
+ 'timestamp
, username
, request_id
, and data
.')
)
secret = models.CharField(
max_length=255,
blank=True,
- help_text="When provided, the request will include a 'X-Hook-Signature' "
+ help_text=_("When provided, the request will include a 'X-Hook-Signature' "
"header containing a HMAC hex digest of the payload body using "
"the secret as the key. The secret is not transmitted in "
- "the request."
+ "the request.")
)
ssl_verification = models.BooleanField(
default=True,
- verbose_name='SSL verification',
- help_text="Enable SSL certificate verification. Disable with caution!"
+ verbose_name=_('SSL verification'),
+ help_text=_('Enable SSL certificate verification. Disable with caution!')
)
ca_file_path = models.CharField(
max_length=4096,
null=True,
blank=True,
- verbose_name='CA File Path',
- help_text='The specific CA certificate file to use for SSL verification. '
- 'Leave blank to use the system defaults.'
+ verbose_name=_('CA File Path'),
+ help_text=_('The specific CA certificate file to use for SSL verification. '
+ 'Leave blank to use the system defaults.')
)
class Meta:
@@ -168,12 +169,12 @@ class CustomLink(models.Model):
)
text = models.CharField(
max_length=500,
- help_text="Jinja2 template code for link text"
+ help_text=_('Jinja2 template code for link text')
)
url = models.CharField(
max_length=500,
- verbose_name='URL',
- help_text="Jinja2 template code for link URL"
+ verbose_name=_('URL'),
+ help_text=_('Jinja2 template code for link URL')
)
weight = models.PositiveSmallIntegerField(
default=100
@@ -181,16 +182,16 @@ class CustomLink(models.Model):
group_name = models.CharField(
max_length=50,
blank=True,
- help_text="Links with the same group will appear as a dropdown menu"
+ help_text=_('Links with the same group will appear as a dropdown menu')
)
button_class = models.CharField(
max_length=30,
choices=CustomLinkButtonClassChoices,
default=CustomLinkButtonClassChoices.CLASS_DEFAULT,
- help_text="The class of the first link in a group will be used for the dropdown button"
+ help_text=_('The class of the first link in a group will be used for the dropdown button')
)
new_window = models.BooleanField(
- help_text="Force link to open in a new window"
+ help_text=_('Force link to open in a new window')
)
class Meta:
@@ -215,7 +216,7 @@ class Graph(models.Model):
)
name = models.CharField(
max_length=100,
- verbose_name='Name'
+ verbose_name=_('Name')
)
template_language = models.CharField(
max_length=50,
@@ -224,11 +225,11 @@ class Graph(models.Model):
)
source = models.CharField(
max_length=500,
- verbose_name='Source URL'
+ verbose_name=_('Source URL')
)
link = models.URLField(
blank=True,
- verbose_name='Link URL'
+ verbose_name=_('Link URL')
)
class Meta:
@@ -284,18 +285,18 @@ class ExportTemplate(models.Model):
default=TemplateLanguageChoices.LANGUAGE_JINJA2
)
template_code = models.TextField(
- help_text='The list of objects being exported is passed as a context variable named queryset
.'
+ help_text=_('The list of objects being exported is passed as a context variable named queryset
.')
)
mime_type = models.CharField(
max_length=50,
blank=True,
- verbose_name='MIME type',
- help_text='Defaults to text/plain
'
+ verbose_name=_('MIME type'),
+ help_text=_('Defaults to text/plain
')
)
file_extension = models.CharField(
max_length=15,
blank=True,
- help_text='Extension to append to the rendered filename'
+ help_text=_('Extension to append to the rendered filename')
)
class Meta:
diff --git a/netbox/extras/tables.py b/netbox/extras/tables.py
index 7a78d4b19..65eb76273 100644
--- a/netbox/extras/tables.py
+++ b/netbox/extras/tables.py
@@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
+from django.utils.translation import gettext as _
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
from .models import ConfigContext, ObjectChange, Tag, TaggedItem
@@ -84,10 +85,10 @@ class TaggedItemTable(BaseTable):
content_object = tables.TemplateColumn(
template_code=TAGGED_ITEM,
orderable=False,
- verbose_name='Object'
+ verbose_name=_('Object')
)
content_type = tables.Column(
- verbose_name='Type'
+ verbose_name=_('Type')
)
class Meta(BaseTable.Meta):
@@ -99,7 +100,7 @@ class ConfigContextTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
is_active = BooleanColumn(
- verbose_name='Active'
+ verbose_name=_('Active')
)
class Meta(BaseTable.Meta):
@@ -119,15 +120,15 @@ class ObjectChangeTable(BaseTable):
template_code=OBJECTCHANGE_ACTION
)
changed_object_type = tables.Column(
- verbose_name='Type'
+ verbose_name=_('Type')
)
object_repr = tables.TemplateColumn(
template_code=OBJECTCHANGE_OBJECT,
- verbose_name='Object'
+ verbose_name=_('Object')
)
request_id = tables.TemplateColumn(
template_code=OBJECTCHANGE_REQUEST_ID,
- verbose_name='Request ID'
+ verbose_name=_('Request ID')
)
class Meta(BaseTable.Meta):
diff --git a/netbox/ipam/apps.py b/netbox/ipam/apps.py
index fd4af74b0..7fa8c81ee 100644
--- a/netbox/ipam/apps.py
+++ b/netbox/ipam/apps.py
@@ -1,6 +1,7 @@
from django.apps import AppConfig
+from django.utils.translation import gettext as _
class IPAMConfig(AppConfig):
name = "ipam"
- verbose_name = "IPAM"
+ verbose_name = _('IPAM')
diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py
index 6641d1c8b..64a89504c 100644
--- a/netbox/ipam/filters.py
+++ b/netbox/ipam/filters.py
@@ -2,6 +2,7 @@ import django_filters
import netaddr
from django.core.exceptions import ValidationError
from django.db.models import Q
+from django.utils.translation import gettext as _
from netaddr.core import AddrFormatError
from dcim.models import Device, Interface, Region, Site
@@ -32,7 +33,7 @@ __all__ = (
class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
tag = TagFilter()
@@ -60,7 +61,7 @@ class RIRFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
family = django_filters.NumberFilter(
field_name='prefix',
@@ -68,17 +69,17 @@ class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
)
prefix = django_filters.CharFilter(
method='filter_prefix',
- label='Prefix',
+ label=_('Prefix'),
)
rir_id = django_filters.ModelMultipleChoiceFilter(
queryset=RIR.objects.all(),
- label='RIR (ID)',
+ label=_('RIR (ID)'),
)
rir = django_filters.ModelMultipleChoiceFilter(
field_name='rir__slug',
queryset=RIR.objects.all(),
to_field_name='slug',
- label='RIR (slug)',
+ label=_('RIR (slug)'),
)
tag = TagFilter()
@@ -110,7 +111,7 @@ class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
class Meta:
@@ -121,7 +122,7 @@ class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
family = django_filters.NumberFilter(
field_name='prefix',
@@ -129,74 +130,74 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Cre
)
prefix = django_filters.CharFilter(
method='filter_prefix',
- label='Prefix',
+ label=_('Prefix'),
)
within = django_filters.CharFilter(
method='search_within',
- label='Within prefix',
+ label=_('Within prefix'),
)
within_include = django_filters.CharFilter(
method='search_within_include',
- label='Within and including prefix',
+ label=_('Within and including prefix'),
)
contains = django_filters.CharFilter(
method='search_contains',
- label='Prefixes which contain this prefix or IP',
+ label=_('Prefixes which contain this prefix or IP'),
)
mask_length = django_filters.NumberFilter(
method='filter_mask_length',
- label='Mask length',
+ label=_('Mask length'),
)
vrf_id = django_filters.ModelMultipleChoiceFilter(
queryset=VRF.objects.all(),
- label='VRF',
+ label=_('VRF'),
)
vrf = django_filters.ModelMultipleChoiceFilter(
field_name='vrf__rd',
queryset=VRF.objects.all(),
to_field_name='rd',
- label='VRF (RD)',
+ label=_('VRF (RD)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
vlan_id = django_filters.ModelMultipleChoiceFilter(
queryset=VLAN.objects.all(),
- label='VLAN (ID)',
+ label=_('VLAN (ID)'),
)
vlan_vid = django_filters.NumberFilter(
field_name='vlan__vid',
- label='VLAN number (1-4095)',
+ label=_('VLAN number (1-4095)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=Role.objects.all(),
- label='Role (ID)',
+ label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=Role.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=PrefixStatusChoices,
@@ -271,7 +272,7 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Cre
class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
family = django_filters.NumberFilter(
field_name='address',
@@ -279,60 +280,60 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet,
)
parent = django_filters.CharFilter(
method='search_by_parent',
- label='Parent prefix',
+ label=_('Parent prefix'),
)
address = MultiValueCharFilter(
method='filter_address',
- label='Address',
+ label=_('Address'),
)
mask_length = django_filters.NumberFilter(
method='filter_mask_length',
- label='Mask length',
+ label=_('Mask length'),
)
vrf_id = django_filters.ModelMultipleChoiceFilter(
queryset=VRF.objects.all(),
- label='VRF',
+ label=_('VRF'),
)
vrf = django_filters.ModelMultipleChoiceFilter(
field_name='vrf__rd',
queryset=VRF.objects.all(),
to_field_name='rd',
- label='VRF (RD)',
+ label=_('VRF (RD)'),
)
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
- label='Device (name)',
+ label=_('Device (name)'),
)
device_id = MultiValueNumberFilter(
method='filter_device',
field_name='pk',
- label='Device (ID)',
+ label=_('Device (ID)'),
)
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
field_name='interface__virtual_machine',
queryset=VirtualMachine.objects.all(),
- label='Virtual machine (ID)',
+ label=_('Virtual machine (ID)'),
)
virtual_machine = django_filters.ModelMultipleChoiceFilter(
field_name='interface__virtual_machine__name',
queryset=VirtualMachine.objects.all(),
to_field_name='name',
- label='Virtual machine (name)',
+ label=_('Virtual machine (name)'),
)
interface = django_filters.ModelMultipleChoiceFilter(
field_name='interface__name',
queryset=Interface.objects.all(),
to_field_name='name',
- label='Interface (ID)',
+ label=_('Interface (ID)'),
)
interface_id = django_filters.ModelMultipleChoiceFilter(
queryset=Interface.objects.all(),
- label='Interface (ID)',
+ label=_('Interface (ID)'),
)
assigned_to_interface = django_filters.BooleanFilter(
method='_assigned_to_interface',
- label='Is assigned to an interface',
+ label=_('Is assigned to an interface'),
)
status = django_filters.MultipleChoiceFilter(
choices=IPAddressStatusChoices,
@@ -397,24 +398,24 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
class Meta:
@@ -425,50 +426,50 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=VLANGroup.objects.all(),
- label='Group (ID)',
+ label=_('Group (ID)'),
)
group = django_filters.ModelMultipleChoiceFilter(
field_name='group__slug',
queryset=VLANGroup.objects.all(),
to_field_name='slug',
- label='Group',
+ label=_('Group'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=Role.objects.all(),
- label='Role (ID)',
+ label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=Role.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=VLANStatusChoices,
@@ -494,27 +495,27 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
- label='Device (ID)',
+ label=_('Device (ID)'),
)
device = django_filters.ModelMultipleChoiceFilter(
field_name='device__name',
queryset=Device.objects.all(),
to_field_name='name',
- label='Device (name)',
+ label=_('Device (name)'),
)
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
queryset=VirtualMachine.objects.all(),
- label='Virtual machine (ID)',
+ label=_('Virtual machine (ID)'),
)
virtual_machine = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine__name',
queryset=VirtualMachine.objects.all(),
to_field_name='name',
- label='Virtual machine (name)',
+ label=_('Virtual machine (name)'),
)
tag = TagFilter()
diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py
index 943d4d30a..85c5b6b0b 100644
--- a/netbox/ipam/forms.py
+++ b/netbox/ipam/forms.py
@@ -1,5 +1,6 @@
from django import forms
from django.core.validators import MaxValueValidator, MinValueValidator
+from django.utils.translation import gettext as _
from dcim.models import Device, Interface, Rack, Region, Site
from extras.forms import (
@@ -55,7 +56,7 @@ class VRFCSVForm(CustomFieldModelCSVForm):
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
class Meta:
@@ -75,7 +76,7 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm
enforce_unique = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect(),
- label='Enforce unique space'
+ label=_('Enforce unique space')
)
description = forms.CharField(
max_length=100,
@@ -93,7 +94,7 @@ class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
field_order = ['q', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
tag = TagFilterField(model)
@@ -119,14 +120,14 @@ class RIRCSVForm(CSVModelForm):
model = RIR
fields = RIR.csv_headers
help_texts = {
- 'name': 'RIR name',
+ 'name': _('RIR name'),
}
class RIRFilterForm(BootstrapMixin, forms.Form):
is_private = forms.NullBooleanField(
required=False,
- label='Private',
+ label=_('Private'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@@ -163,7 +164,7 @@ class AggregateCSVForm(CustomFieldModelCSVForm):
rir = CSVModelChoiceField(
queryset=RIR.objects.all(),
to_field_name='name',
- help_text='Assigned RIR'
+ help_text=_('Assigned RIR')
)
class Meta:
@@ -179,7 +180,7 @@ class AggregateBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
required=False,
- label='RIR'
+ label=_('RIR')
)
date_added = forms.DateField(
required=False
@@ -202,19 +203,19 @@ class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Aggregate
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
family = forms.ChoiceField(
required=False,
choices=add_blank_choice(IPAddressFamilyChoices),
- label='Address family',
+ label=_('Address family'),
widget=StaticSelect2()
)
rir = DynamicModelMultipleChoiceField(
queryset=RIR.objects.all(),
to_field_name='slug',
required=False,
- label='RIR',
+ label=_('RIR'),
widget=APISelectMultiple(
value_field="slug",
)
@@ -252,7 +253,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF'
+ label=_('VRF')
)
site = DynamicModelChoiceField(
queryset=Site.objects.all(),
@@ -270,7 +271,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vlan_group = DynamicModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
- label='VLAN group',
+ label=_('VLAN group'),
widget=APISelect(
filter_for={
'vlan': 'group_id'
@@ -283,7 +284,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
- label='VLAN',
+ label=_('VLAN'),
widget=APISelect(
display_field='display_name'
)
@@ -323,41 +324,41 @@ class PrefixCSVForm(CustomFieldModelCSVForm):
queryset=VRF.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned VRF'
+ help_text=_('Assigned VRF')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
site = CSVModelChoiceField(
queryset=Site.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
vlan_group = CSVModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
to_field_name='name',
- help_text="VLAN's group (if any)"
+ help_text=_("VLAN's group (if any)")
)
vlan = CSVModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
to_field_name='vid',
- help_text="Assigned VLAN"
+ help_text=_('Assigned VLAN')
)
status = CSVChoiceField(
choices=PrefixStatusChoices,
- help_text='Operational status'
+ help_text=_('Operational status')
)
role = CSVModelChoiceField(
queryset=Role.objects.all(),
required=False,
to_field_name='name',
- help_text='Functional role'
+ help_text=_('Functional role')
)
class Meta:
@@ -389,7 +390,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF'
+ label=_('VRF')
)
prefix_length = forms.IntegerField(
min_value=PREFIX_LENGTH_MIN,
@@ -412,7 +413,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
is_pool = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect(),
- label='Is a pool'
+ label=_('Is a pool')
)
description = forms.CharField(
max_length=100,
@@ -433,7 +434,7 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
]
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
within_include = forms.CharField(
required=False,
@@ -442,24 +443,24 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
'placeholder': 'Prefix',
}
),
- label='Search within'
+ label=_('Search within')
)
family = forms.ChoiceField(
required=False,
choices=add_blank_choice(IPAddressFamilyChoices),
- label='Address family',
+ label=_('Address family'),
widget=StaticSelect2()
)
mask_length = forms.ChoiceField(
required=False,
choices=PREFIX_MASK_LENGTH_CHOICES,
- label='Mask length',
+ label=_('Mask length'),
widget=StaticSelect2()
)
vrf_id = DynamicModelMultipleChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF',
+ label=_('VRF'),
widget=APISelectMultiple(
null_option=True,
)
@@ -500,14 +501,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
)
is_pool = forms.NullBooleanField(
required=False,
- label='Is a pool',
+ label=_('Is a pool'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
expand = forms.BooleanField(
required=False,
- label='Expand prefix hierarchy'
+ label=_('Expand prefix hierarchy')
)
tag = TagFilterField(model)
@@ -524,12 +525,12 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF'
+ label=_('VRF')
)
nat_site = DynamicModelChoiceField(
queryset=Site.objects.all(),
required=False,
- label='Site',
+ label=_('Site'),
widget=APISelect(
filter_for={
'nat_rack': 'site_id',
@@ -540,7 +541,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_rack = DynamicModelChoiceField(
queryset=Rack.objects.all(),
required=False,
- label='Rack',
+ label=_('Rack'),
widget=APISelect(
display_field='display_name',
filter_for={
@@ -554,7 +555,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_device = DynamicModelChoiceField(
queryset=Device.objects.all(),
required=False,
- label='Device',
+ label=_('Device'),
widget=APISelect(
display_field='display_name',
filter_for={
@@ -565,7 +566,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF',
+ label=_('VRF'),
widget=APISelect(
filter_for={
'nat_inside': 'vrf_id'
@@ -575,14 +576,14 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_inside = DynamicModelChoiceField(
queryset=IPAddress.objects.all(),
required=False,
- label='IP Address',
+ label=_('IP Address'),
widget=APISelect(
display_field='address'
)
)
primary_for_parent = forms.BooleanField(
required=False,
- label='Make this the primary IP for the device/VM'
+ label=_('Make this the primary IP for the device/VM')
)
tags = TagField(
required=False
@@ -671,7 +672,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
class IPAddressBulkCreateForm(BootstrapMixin, forms.Form):
pattern = ExpandableIPAddressField(
- label='Address pattern'
+ label=_('Address pattern')
)
@@ -679,7 +680,7 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF'
+ label=_('VRF')
)
tags = TagField(
required=False
@@ -705,43 +706,43 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
queryset=VRF.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned VRF'
+ help_text=_('Assigned VRF')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
status = CSVChoiceField(
choices=IPAddressStatusChoices,
- help_text='Operational status'
+ help_text=_('Operational status')
)
role = CSVChoiceField(
choices=IPAddressRoleChoices,
required=False,
- help_text='Functional role'
+ help_text=_('Functional role')
)
device = CSVModelChoiceField(
queryset=Device.objects.all(),
required=False,
to_field_name='name',
- help_text='Parent device of assigned interface (if any)'
+ help_text=_('Parent device of assigned interface (if any)')
)
virtual_machine = CSVModelChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
to_field_name='name',
- help_text='Parent VM of assigned interface (if any)'
+ help_text=_('Parent VM of assigned interface (if any)')
)
interface = CSVModelChoiceField(
queryset=Interface.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned interface'
+ help_text=_('Assigned interface')
)
is_primary = forms.BooleanField(
- help_text='Make this the primary IP for the assigned device',
+ help_text=_('Make this the primary IP for the assigned device'),
required=False
)
@@ -779,7 +780,7 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
# Validate is_primary
if is_primary and not device and not virtual_machine:
- raise forms.ValidationError("No device or virtual machine specified; cannot set as primary IP")
+ raise forms.ValidationError(_('No device or virtual machine specified; cannot set as primary IP'))
def save(self, *args, **kwargs):
@@ -805,7 +806,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF'
+ label=_('VRF')
)
mask_length = forms.IntegerField(
min_value=IPADDRESS_MASK_LENGTH_MIN,
@@ -845,12 +846,12 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form):
vrf_id = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF',
+ label=_('VRF'),
empty_label='Global'
)
q = forms.CharField(
required=False,
- label='Search',
+ label=_('Search'),
)
@@ -862,7 +863,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
]
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
parent = forms.CharField(
required=False,
@@ -871,24 +872,24 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
'placeholder': 'Prefix',
}
),
- label='Parent Prefix'
+ label=_('Parent Prefix')
)
family = forms.ChoiceField(
required=False,
choices=add_blank_choice(IPAddressFamilyChoices),
- label='Address family',
+ label=_('Address family'),
widget=StaticSelect2()
)
mask_length = forms.ChoiceField(
required=False,
choices=IPADDRESS_MASK_LENGTH_CHOICES,
- label='Mask length',
+ label=_('Mask length'),
widget=StaticSelect2()
)
vrf_id = DynamicModelMultipleChoiceField(
queryset=VRF.objects.all(),
required=False,
- label='VRF',
+ label=_('VRF'),
widget=APISelectMultiple(
null_option=True,
)
@@ -905,7 +906,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
)
assigned_to_interface = forms.NullBooleanField(
required=False,
- label='Assigned to an interface',
+ label=_('Assigned to an interface'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@@ -936,7 +937,7 @@ class VLANGroupCSVForm(CSVModelForm):
queryset=Site.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
slug = SlugField()
@@ -1018,37 +1019,37 @@ class VLANCSVForm(CustomFieldModelCSVForm):
queryset=Site.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
group = CSVModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned VLAN group'
+ help_text=_('Assigned VLAN group')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
status = CSVChoiceField(
choices=VLANStatusChoices,
- help_text='Operational status'
+ help_text=_('Operational status')
)
role = CSVModelChoiceField(
queryset=Role.objects.all(),
required=False,
to_field_name='name',
- help_text='Functional role'
+ help_text=_('Functional role')
)
class Meta:
model = VLAN
fields = VLAN.csv_headers
help_texts = {
- 'vid': 'Numeric VLAN ID (1-4095)',
- 'name': 'VLAN name',
+ 'vid': _('Numeric VLAN ID (1-4095)'),
+ 'name': _('VLAN name'),
}
def __init__(self, data=None, *args, **kwargs):
@@ -1108,7 +1109,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -1134,7 +1135,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
group_id = DynamicModelMultipleChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
- label='VLAN group',
+ label=_('VLAN group'),
widget=APISelectMultiple(
null_option=True,
)
@@ -1204,7 +1205,7 @@ class ServiceFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Service
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
protocol = forms.ChoiceField(
choices=add_blank_choice(ServiceProtocolChoices),
@@ -1222,17 +1223,17 @@ class ServiceCSVForm(CustomFieldModelCSVForm):
queryset=Device.objects.all(),
required=False,
to_field_name='name',
- help_text='Required if not assigned to a VM'
+ help_text=_('Required if not assigned to a VM')
)
virtual_machine = CSVModelChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
to_field_name='name',
- help_text='Required if not assigned to a device'
+ help_text=_('Required if not assigned to a device')
)
protocol = CSVChoiceField(
choices=ServiceProtocolChoices,
- help_text='IP protocol'
+ help_text=_('IP protocol')
)
class Meta:
diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py
index eeb985b7c..6b3bcb097 100644
--- a/netbox/ipam/models.py
+++ b/netbox/ipam/models.py
@@ -6,6 +6,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import F, Q
from django.urls import reverse
+from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.models import Device, Interface
@@ -50,8 +51,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
unique=True,
blank=True,
null=True,
- verbose_name='Route distinguisher',
- help_text='Unique route distinguisher (as defined in RFC 4364)'
+ verbose_name=_('Route distinguisher'),
+ help_text=_('Unique route distinguisher (as defined in RFC 4364)')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@@ -62,8 +63,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
)
enforce_unique = models.BooleanField(
default=True,
- verbose_name='Enforce unique space',
- help_text='Prevent duplicate prefixes/IP addresses within this VRF'
+ verbose_name=_('Enforce unique space'),
+ help_text=_('Prevent duplicate prefixes/IP addresses within this VRF')
)
description = models.CharField(
max_length=200,
@@ -84,7 +85,7 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
class Meta:
ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique
- verbose_name = 'VRF'
+ verbose_name = _('VRF')
verbose_name_plural = 'VRFs'
def __str__(self):
@@ -123,8 +124,8 @@ class RIR(ChangeLoggedModel):
)
is_private = models.BooleanField(
default=False,
- verbose_name='Private',
- help_text='IP space managed by this RIR is considered private'
+ verbose_name=_('Private'),
+ help_text=_('IP space managed by this RIR is considered private')
)
description = models.CharField(
max_length=200,
@@ -135,7 +136,7 @@ class RIR(ChangeLoggedModel):
class Meta:
ordering = ['name']
- verbose_name = 'RIR'
+ verbose_name = _('RIR')
verbose_name_plural = 'RIRs'
def __str__(self):
@@ -164,7 +165,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
to='ipam.RIR',
on_delete=models.PROTECT,
related_name='aggregates',
- verbose_name='RIR'
+ verbose_name=_('RIR')
)
date_added = models.DateField(
blank=True,
@@ -299,7 +300,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
assigned to a VLAN where appropriate.
"""
prefix = IPNetworkField(
- help_text='IPv4 or IPv6 network with mask'
+ help_text=_('IPv4 or IPv6 network with mask')
)
site = models.ForeignKey(
to='dcim.Site',
@@ -314,7 +315,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
related_name='prefixes',
blank=True,
null=True,
- verbose_name='VRF'
+ verbose_name=_('VRF')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@@ -329,14 +330,14 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
related_name='prefixes',
blank=True,
null=True,
- verbose_name='VLAN'
+ verbose_name=_('VLAN')
)
status = models.CharField(
max_length=50,
choices=PrefixStatusChoices,
default=PrefixStatusChoices.STATUS_ACTIVE,
- verbose_name='Status',
- help_text='Operational status of this prefix'
+ verbose_name=_('Status'),
+ help_text=_('Operational status of this prefix')
)
role = models.ForeignKey(
to='ipam.Role',
@@ -344,12 +345,12 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
related_name='prefixes',
blank=True,
null=True,
- help_text='The primary function of this prefix'
+ help_text=_('The primary function of this prefix')
)
is_pool = models.BooleanField(
- verbose_name='Is a pool',
+ verbose_name=_('Is a pool'),
default=False,
- help_text='All IP addresses within this prefix are considered usable'
+ help_text=_('All IP addresses within this prefix are considered usable')
)
description = models.CharField(
max_length=200,
@@ -570,7 +571,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP.
"""
address = IPAddressField(
- help_text='IPv4 or IPv6 address (with mask)'
+ help_text=_('IPv4 or IPv6 address (with mask)')
)
vrf = models.ForeignKey(
to='ipam.VRF',
@@ -578,7 +579,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
related_name='ip_addresses',
blank=True,
null=True,
- verbose_name='VRF'
+ verbose_name=_('VRF')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@@ -591,13 +592,13 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
max_length=50,
choices=IPAddressStatusChoices,
default=IPAddressStatusChoices.STATUS_ACTIVE,
- help_text='The operational status of this IP'
+ help_text=_('The operational status of this IP')
)
role = models.CharField(
max_length=50,
choices=IPAddressRoleChoices,
blank=True,
- help_text='The functional role of this IP'
+ help_text=_('The functional role of this IP')
)
interface = models.ForeignKey(
to='dcim.Interface',
@@ -612,15 +613,15 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
related_name='nat_outside',
blank=True,
null=True,
- verbose_name='NAT (Inside)',
- help_text='The IP for which this address is the "outside" IP'
+ verbose_name=_('NAT (Inside)'),
+ help_text=_('The IP for which this address is the "outside" IP')
)
dns_name = models.CharField(
max_length=255,
blank=True,
validators=[DNSValidator],
- verbose_name='DNS Name',
- help_text='Hostname or FQDN (not case-sensitive)'
+ verbose_name=_('DNS Name'),
+ help_text=_('Hostname or FQDN (not case-sensitive)')
)
description = models.CharField(
max_length=200,
@@ -663,7 +664,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
class Meta:
ordering = ('address', 'pk') # address may be non-unique
- verbose_name = 'IP address'
+ verbose_name = _('IP address')
verbose_name_plural = 'IP addresses'
def __str__(self):
@@ -836,7 +837,7 @@ class VLANGroup(ChangeLoggedModel):
['site', 'name'],
['site', 'slug'],
]
- verbose_name = 'VLAN group'
+ verbose_name = _('VLAN group')
verbose_name_plural = 'VLAN groups'
def __str__(self):
@@ -889,7 +890,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
null=True
)
vid = models.PositiveSmallIntegerField(
- verbose_name='ID',
+ verbose_name=_('ID'),
validators=[MinValueValidator(1), MaxValueValidator(4094)]
)
name = models.CharField(
@@ -943,7 +944,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
['group', 'vid'],
['group', 'name'],
]
- verbose_name = 'VLAN'
+ verbose_name = _('VLAN')
verbose_name_plural = 'VLANs'
def __str__(self):
@@ -999,7 +1000,7 @@ class Service(ChangeLoggedModel, CustomFieldModel):
to='dcim.Device',
on_delete=models.CASCADE,
related_name='services',
- verbose_name='device',
+ verbose_name=_('device'),
null=True,
blank=True
)
@@ -1022,13 +1023,13 @@ class Service(ChangeLoggedModel, CustomFieldModel):
MinValueValidator(SERVICE_PORT_MIN),
MaxValueValidator(SERVICE_PORT_MAX)
],
- verbose_name='Port number'
+ verbose_name=_('Port number')
)
ipaddresses = models.ManyToManyField(
to='ipam.IPAddress',
related_name='services',
blank=True,
- verbose_name='IP addresses'
+ verbose_name=_('IP addresses')
)
description = models.CharField(
max_length=200,
diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py
index d624ba134..3bbceb7f2 100644
--- a/netbox/ipam/tables.py
+++ b/netbox/ipam/tables.py
@@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
+from django.utils.translation import gettext as _
from dcim.models import Interface
from tenancy.tables import COL_TENANT
@@ -191,13 +192,13 @@ class VRFTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
rd = tables.Column(
- verbose_name='RD'
+ verbose_name=_('RD')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
enforce_unique = BooleanColumn(
- verbose_name='Unique'
+ verbose_name=_('Unique')
)
tags = TagColumn(
url_name='ipam:vrf_list'
@@ -217,10 +218,10 @@ class RIRTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
is_private = BooleanColumn(
- verbose_name='Private'
+ verbose_name=_('Private')
)
aggregate_count = tables.Column(
- verbose_name='Aggregates'
+ verbose_name=_('Aggregates')
)
actions = tables.TemplateColumn(
template_code=RIR_ACTIONS,
@@ -237,32 +238,32 @@ class RIRTable(BaseTable):
class RIRDetailTable(RIRTable):
stats_total = tables.Column(
accessor='stats.total',
- verbose_name='Total',
+ verbose_name=_('Total'),
footer=lambda table: sum(r.stats['total'] for r in table.data)
)
stats_active = tables.Column(
accessor='stats.active',
- verbose_name='Active',
+ verbose_name=_('Active'),
footer=lambda table: sum(r.stats['active'] for r in table.data)
)
stats_reserved = tables.Column(
accessor='stats.reserved',
- verbose_name='Reserved',
+ verbose_name=_('Reserved'),
footer=lambda table: sum(r.stats['reserved'] for r in table.data)
)
stats_deprecated = tables.Column(
accessor='stats.deprecated',
- verbose_name='Deprecated',
+ verbose_name=_('Deprecated'),
footer=lambda table: sum(r.stats['deprecated'] for r in table.data)
)
stats_available = tables.Column(
accessor='stats.available',
- verbose_name='Available',
+ verbose_name=_('Available'),
footer=lambda table: sum(r.stats['available'] for r in table.data)
)
utilization = tables.TemplateColumn(
template_code=RIR_UTILIZATION,
- verbose_name='Utilization'
+ verbose_name=_('Utilization')
)
class Meta(RIRTable.Meta):
@@ -283,11 +284,11 @@ class RIRDetailTable(RIRTable):
class AggregateTable(BaseTable):
pk = ToggleColumn()
prefix = tables.LinkColumn(
- verbose_name='Aggregate'
+ verbose_name=_('Aggregate')
)
date_added = tables.DateColumn(
format="Y-m-d",
- verbose_name='Added'
+ verbose_name=_('Added')
)
class Meta(BaseTable.Meta):
@@ -297,7 +298,7 @@ class AggregateTable(BaseTable):
class AggregateDetailTable(AggregateTable):
child_count = tables.Column(
- verbose_name='Prefixes'
+ verbose_name=_('Prefixes')
)
utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
@@ -320,11 +321,11 @@ class RoleTable(BaseTable):
pk = ToggleColumn()
prefix_count = tables.TemplateColumn(
template_code=ROLE_PREFIX_COUNT,
- verbose_name='Prefixes'
+ verbose_name=_('Prefixes')
)
vlan_count = tables.TemplateColumn(
template_code=ROLE_VLAN_COUNT,
- verbose_name='VLANs'
+ verbose_name=_('VLANs')
)
actions = tables.TemplateColumn(
template_code=ROLE_ACTIONS,
@@ -353,7 +354,7 @@ class PrefixTable(BaseTable):
)
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
- verbose_name='VRF'
+ verbose_name=_('VRF')
)
tenant = tables.TemplateColumn(
template_code=TENANT_LINK
@@ -365,13 +366,13 @@ class PrefixTable(BaseTable):
vlan = tables.LinkColumn(
viewname='ipam:vlan',
args=[Accessor('vlan.pk')],
- verbose_name='VLAN'
+ verbose_name=_('VLAN')
)
role = tables.TemplateColumn(
template_code=PREFIX_ROLE_LINK
)
is_pool = BooleanColumn(
- verbose_name='Pool'
+ verbose_name=_('Pool')
)
add_prefetch = False
@@ -415,11 +416,11 @@ class IPAddressTable(BaseTable):
pk = ToggleColumn()
address = tables.TemplateColumn(
template_code=IPADDRESS_LINK,
- verbose_name='IP Address'
+ verbose_name=_('IP Address')
)
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
- verbose_name='VRF'
+ verbose_name=_('VRF')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@@ -450,7 +451,7 @@ class IPAddressDetailTable(IPAddressTable):
viewname='ipam:ipaddress',
args=[Accessor('nat_inside.pk')],
orderable=False,
- verbose_name='NAT (Inside)'
+ verbose_name=_('NAT (Inside)')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
@@ -472,7 +473,7 @@ class IPAddressDetailTable(IPAddressTable):
class IPAddressAssignTable(BaseTable):
address = tables.TemplateColumn(
template_code=IPADDRESS_ASSIGN_LINK,
- verbose_name='IP Address'
+ verbose_name=_('IP Address')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@@ -496,11 +497,11 @@ class InterfaceIPAddressTable(BaseTable):
List IP addresses assigned to a specific Interface.
"""
address = tables.LinkColumn(
- verbose_name='IP Address'
+ verbose_name=_('IP Address')
)
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
- verbose_name='VRF'
+ verbose_name=_('VRF')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@@ -526,7 +527,7 @@ class VLANGroupTable(BaseTable):
args=[Accessor('site.slug')]
)
vlan_count = tables.Column(
- verbose_name='VLANs'
+ verbose_name=_('VLANs')
)
actions = tables.TemplateColumn(
template_code=VLANGROUP_ACTIONS,
@@ -548,7 +549,7 @@ class VLANTable(BaseTable):
pk = ToggleColumn()
vid = tables.TemplateColumn(
template_code=VLAN_LINK,
- verbose_name='ID'
+ verbose_name=_('ID')
)
site = tables.LinkColumn(
viewname='dcim:site',
@@ -580,7 +581,7 @@ class VLANDetailTable(VLANTable):
prefixes = tables.TemplateColumn(
template_code=VLAN_PREFIXES,
orderable=False,
- verbose_name='Prefixes'
+ verbose_name=_('Prefixes')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
@@ -599,7 +600,7 @@ class VLANMemberTable(BaseTable):
order_by=['device', 'virtual_machine']
)
name = tables.LinkColumn(
- verbose_name='Interface'
+ verbose_name=_('Interface')
)
untagged = tables.TemplateColumn(
template_code=VLAN_MEMBER_UNTAGGED,
@@ -623,7 +624,7 @@ class InterfaceVLANTable(BaseTable):
vid = tables.LinkColumn(
viewname='ipam:vlan',
args=[Accessor('pk')],
- verbose_name='ID'
+ verbose_name=_('ID')
)
tagged = BooleanColumn()
site = tables.LinkColumn(
@@ -632,7 +633,7 @@ class InterfaceVLANTable(BaseTable):
)
group = tables.Column(
accessor=Accessor('group.name'),
- verbose_name='Group'
+ verbose_name=_('Group')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
diff --git a/netbox/locale/zh_Hans/LC_MESSAGES/django.po b/netbox/locale/zh_Hans/LC_MESSAGES/django.po
new file mode 100644
index 000000000..06e851172
--- /dev/null
+++ b/netbox/locale/zh_Hans/LC_MESSAGES/django.po
@@ -0,0 +1,1968 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-08-05 19:22+0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: circuits/apps.py:7 circuits/tables.py:33 circuits/tables.py:55
+#: netbox/forms.py:8 netbox/forms.py:10
+msgid "Circuits"
+msgstr ""
+
+#: circuits/filters.py:25 circuits/filters.py:79 circuits/filters.py:151
+#: circuits/forms.py:104 circuits/forms.py:268 dcim/filters.py:84
+#: dcim/filters.py:183 dcim/filters.py:262 dcim/filters.py:328
+#: dcim/filters.py:509 dcim/filters.py:707 dcim/filters.py:821
+#: dcim/filters.py:950 dcim/filters.py:1023 dcim/filters.py:1079
+#: dcim/filters.py:1238 dcim/filters.py:1286 dcim/forms.py:69 dcim/forms.py:213
+#: dcim/forms.py:343 dcim/forms.py:659 dcim/forms.py:857 dcim/forms.py:966
+#: dcim/forms.py:2042 dcim/forms.py:3825 dcim/forms.py:4046 dcim/forms.py:4232
+#: dcim/forms.py:4357 dcim/forms.py:4575 extras/filters.py:111
+#: extras/filters.py:130 extras/filters.py:252 extras/forms.py:185
+#: extras/forms.py:289 extras/forms.py:397 ipam/filters.py:36
+#: ipam/filters.py:64 ipam/filters.py:114 ipam/filters.py:125
+#: ipam/filters.py:275 ipam/filters.py:429 ipam/filters.py:498 ipam/forms.py:97
+#: ipam/forms.py:206 ipam/forms.py:437 ipam/forms.py:854 ipam/forms.py:866
+#: ipam/forms.py:1112 ipam/forms.py:1208 netbox/forms.py:44
+#: secrets/filters.py:28 secrets/forms.py:183 tenancy/filters.py:37
+#: tenancy/forms.py:106 utilities/filters.py:278 virtualization/filters.py:41
+#: virtualization/filters.py:110 virtualization/filters.py:208
+#: virtualization/forms.py:489
+msgid "Search"
+msgstr ""
+
+#: circuits/filters.py:31 circuits/filters.py:120 dcim/filters.py:94
+#: dcim/filters.py:138 dcim/filters.py:189 dcim/filters.py:551
+#: dcim/filters.py:713 dcim/filters.py:956 dcim/filters.py:1029
+#: dcim/filters.py:1244 dcim/filters.py:1292 ipam/filters.py:165
+#: ipam/filters.py:401 ipam/filters.py:435 virtualization/filters.py:47
+#: virtualization/filters.py:146
+msgid "Region (ID)"
+msgstr ""
+
+#: circuits/filters.py:38 circuits/filters.py:127 dcim/filters.py:101
+#: dcim/filters.py:145 dcim/filters.py:196 dcim/filters.py:558
+#: dcim/filters.py:720 dcim/filters.py:963 dcim/filters.py:1036
+#: dcim/filters.py:1251 dcim/filters.py:1299 extras/filters.py:141
+#: ipam/filters.py:172 ipam/filters.py:408 ipam/filters.py:442
+#: virtualization/filters.py:54 virtualization/filters.py:153
+msgid "Region (slug)"
+msgstr ""
+
+#: circuits/filters.py:43 dcim/forms.py:3412 dcim/forms.py:3545
+#: dcim/forms.py:3584 dcim/tables.py:259 extras/filters.py:146
+#: ipam/forms.py:533
+msgid "Site"
+msgstr ""
+
+#: circuits/filters.py:49 circuits/filters.py:114 circuits/filters.py:165
+#: dcim/filters.py:155 dcim/filters.py:206 dcim/filters.py:277
+#: dcim/filters.py:1142 dcim/filters.py:1173 dcim/filters.py:1204
+#: extras/filters.py:152 ipam/filters.py:182 ipam/filters.py:418
+#: ipam/filters.py:452 virtualization/filters.py:64
+#: virtualization/filters.py:164
+msgid "Site (slug)"
+msgstr ""
+
+#: circuits/filters.py:83
+msgid "Provider (ID)"
+msgstr ""
+
+#: circuits/filters.py:89
+msgid "Provider (slug)"
+msgstr ""
+
+#: circuits/filters.py:93
+msgid "Circuit type (ID)"
+msgstr ""
+
+#: circuits/filters.py:99
+msgid "Circuit type (slug)"
+msgstr ""
+
+#: circuits/filters.py:108 circuits/filters.py:159 dcim/filters.py:149
+#: dcim/filters.py:200 dcim/filters.py:271 dcim/filters.py:562
+#: dcim/filters.py:725 dcim/filters.py:968 dcim/filters.py:1041
+#: dcim/filters.py:1255 dcim/filters.py:1304 ipam/filters.py:176
+#: ipam/filters.py:412 ipam/filters.py:446 virtualization/filters.py:58
+#: virtualization/filters.py:158
+msgid "Site (ID)"
+msgstr ""
+
+#: circuits/filters.py:155 dcim/forms.py:3555
+msgid "Circuit"
+msgstr ""
+
+#: circuits/forms.py:68 circuits/forms.py:127 circuits/models.py:42
+#: dcim/forms.py:320 dcim/models/__init__.py:191
+msgid "ASN"
+msgstr ""
+
+#: circuits/forms.py:73 circuits/models.py:48
+msgid "Account number"
+msgstr ""
+
+#: circuits/forms.py:77
+msgid "Portal"
+msgstr ""
+
+#: circuits/forms.py:82 circuits/models.py:56
+msgid "NOC contact"
+msgstr ""
+
+#: circuits/forms.py:87 circuits/models.py:60
+msgid "Admin contact"
+msgstr ""
+
+#: circuits/forms.py:91 circuits/forms.py:252 dcim/forms.py:645
+#: dcim/forms.py:4562 virtualization/forms.py:151 virtualization/forms.py:472
+msgid "Comments"
+msgstr ""
+
+#: circuits/forms.py:153
+msgid "Name of circuit type"
+msgstr ""
+
+#: circuits/forms.py:193
+msgid "Assigned provider"
+msgstr ""
+
+#: circuits/forms.py:198
+msgid "Type of circuit"
+msgstr ""
+
+#: circuits/forms.py:203 dcim/forms.py:271 dcim/forms.py:533 dcim/forms.py:1881
+#: dcim/forms.py:4466 ipam/forms.py:355 ipam/forms.py:719 ipam/forms.py:1038
+msgid "Operational status"
+msgstr ""
+
+#: circuits/forms.py:209 dcim/forms.py:283 dcim/forms.py:804 dcim/forms.py:1861
+#: ipam/forms.py:59 ipam/forms.py:333 ipam/forms.py:715 ipam/forms.py:1034
+#: virtualization/forms.py:120 virtualization/forms.py:410
+msgid "Assigned tenant"
+msgstr ""
+
+#: circuits/forms.py:244 circuits/forms.py:313
+msgid "Commit rate (Kbps)"
+msgstr ""
+
+#: circuits/models.py:43 dcim/models/__init__.py:192
+msgid "32-bit autonomous system number"
+msgstr ""
+
+#: circuits/models.py:52
+msgid "Portal URL"
+msgstr ""
+
+#: circuits/models.py:147
+msgid "Circuit ID"
+msgstr ""
+
+#: circuits/models.py:174
+msgid "Date installed"
+msgstr ""
+
+#: circuits/models.py:262
+msgid "Termination"
+msgstr ""
+
+#: circuits/models.py:281
+msgid "Port speed (Kbps)"
+msgstr ""
+
+#: circuits/models.py:286
+msgid "Upstream speed (Kbps)"
+msgstr ""
+
+#: circuits/models.py:287
+msgid "Upstream speed, if different from port speed"
+msgstr ""
+
+#: circuits/models.py:292
+msgid "Cross-connect ID"
+msgstr ""
+
+#: circuits/models.py:297
+msgid "Patch panel/port(s)"
+msgstr ""
+
+#: circuits/tables.py:76 dcim/tables.py:1035 ipam/models.py:893
+#: ipam/tables.py:552 ipam/tables.py:627
+msgid "ID"
+msgstr ""
+
+#: circuits/tables.py:89
+msgid "A Side"
+msgstr ""
+
+#: circuits/tables.py:92
+msgid "Z Side"
+msgstr ""
+
+#: circuits/views.py:254
+msgid "Swapped terminations for circuit {}."
+msgstr ""
+
+#: dcim/apps.py:6 netbox/forms.py:12
+msgid "DCIM"
+msgstr ""
+
+#: dcim/filters.py:67
+msgid "Parent region (ID)"
+msgstr ""
+
+#: dcim/filters.py:73
+msgid "Parent region (slug)"
+msgstr ""
+
+#: dcim/filters.py:159 dcim/filters.py:212 dcim/filters.py:283
+#: dcim/filters.py:574 dcim/filters.py:1267 dcim/forms.py:4384
+msgid "Rack group (ID)"
+msgstr ""
+
+#: dcim/filters.py:165 dcim/filters.py:219 dcim/filters.py:290
+msgid "Rack group (slug)"
+msgstr ""
+
+#: dcim/filters.py:227 dcim/filters.py:529 ipam/filters.py:194
+#: ipam/filters.py:466 secrets/filters.py:32 virtualization/filters.py:168
+msgid "Role (ID)"
+msgstr ""
+
+#: dcim/filters.py:233 dcim/filters.py:535 extras/filters.py:163
+#: ipam/filters.py:200 ipam/filters.py:472 secrets/filters.py:38
+#: virtualization/filters.py:174
+msgid "Role (slug)"
+msgstr ""
+
+#: dcim/filters.py:266 dcim/filters.py:579 dcim/filters.py:1319
+msgid "Rack (ID)"
+msgstr ""
+
+#: dcim/filters.py:294
+msgid "User (ID)"
+msgstr ""
+
+#: dcim/filters.py:300
+msgid "User (name)"
+msgstr ""
+
+#: dcim/filters.py:332 dcim/filters.py:486 dcim/filters.py:514
+#: dcim/filters.py:991
+msgid "Manufacturer (ID)"
+msgstr ""
+
+#: dcim/filters.py:338 dcim/filters.py:492 dcim/filters.py:520
+#: dcim/filters.py:997
+msgid "Manufacturer (slug)"
+msgstr ""
+
+#: dcim/filters.py:342 dcim/filters.py:621 dcim/forms.py:983 dcim/forms.py:2145
+msgid "Has console ports"
+msgstr ""
+
+#: dcim/filters.py:346 dcim/filters.py:625 dcim/forms.py:990 dcim/forms.py:2152
+msgid "Has console server ports"
+msgstr ""
+
+#: dcim/filters.py:350 dcim/filters.py:629 dcim/forms.py:997 dcim/forms.py:2159
+msgid "Has power ports"
+msgstr ""
+
+#: dcim/filters.py:354 dcim/filters.py:633 dcim/forms.py:1004
+#: dcim/forms.py:2166
+msgid "Has power outlets"
+msgstr ""
+
+#: dcim/filters.py:358 dcim/filters.py:637 dcim/forms.py:1011
+#: dcim/forms.py:2173
+msgid "Has interfaces"
+msgstr ""
+
+#: dcim/filters.py:362 dcim/filters.py:641 dcim/forms.py:1018
+#: dcim/forms.py:2180
+msgid "Has pass-through ports"
+msgstr ""
+
+#: dcim/filters.py:366 dcim/filters.py:645
+msgid "Has device bays"
+msgstr ""
+
+#: dcim/filters.py:415 dcim/filters.py:524
+msgid "Device type (ID)"
+msgstr ""
+
+#: dcim/filters.py:539 virtualization/filters.py:178
+msgid "Platform (ID)"
+msgstr ""
+
+#: dcim/filters.py:545 extras/filters.py:174 virtualization/filters.py:184
+msgid "Platform (slug)"
+msgstr ""
+
+#: dcim/filters.py:568 dcim/filters.py:731 dcim/filters.py:974
+#: dcim/filters.py:1047 dcim/filters.py:1261 dcim/filters.py:1310
+msgid "Site name (slug)"
+msgstr ""
+
+#: dcim/filters.py:583
+msgid "VM cluster (ID)"
+msgstr ""
+
+#: dcim/filters.py:589
+msgid "Device model (slug)"
+msgstr ""
+
+#: dcim/filters.py:597 dcim/forms.py:955 dcim/models/__init__.py:961
+msgid "Is full depth"
+msgstr ""
+
+#: dcim/filters.py:601 dcim/forms.py:2127 dcim/forms.py:2677
+#: virtualization/filters.py:188 virtualization/filters.py:222
+#: virtualization/forms.py:562
+msgid "MAC address"
+msgstr ""
+
+#: dcim/filters.py:608 dcim/forms.py:2131
+msgid "Has a primary IP"
+msgstr ""
+
+#: dcim/filters.py:613
+msgid "Virtual chassis (ID)"
+msgstr ""
+
+#: dcim/filters.py:617
+msgid "Is a virtual chassis member"
+msgstr ""
+
+#: dcim/filters.py:735 dcim/filters.py:833 dcim/filters.py:978
+#: ipam/filters.py:311 ipam/filters.py:502 secrets/filters.py:42
+msgid "Device (ID)"
+msgstr ""
+
+#: dcim/filters.py:741 dcim/filters.py:983 ipam/filters.py:306
+#: ipam/filters.py:508 secrets/filters.py:48
+msgid "Device (name)"
+msgstr ""
+
+#: dcim/filters.py:828 dcim/forms.py:96 dcim/forms.py:3436 dcim/forms.py:3879
+#: dcim/forms.py:3902 dcim/forms.py:3921 dcim/forms.py:3940 dcim/forms.py:4073
+#: ipam/forms.py:558
+msgid "Device"
+msgstr ""
+
+#: dcim/filters.py:842
+msgid "Kind of interface"
+msgstr ""
+
+#: dcim/filters.py:847
+msgid "LAG interface (ID)"
+msgstr ""
+
+#: dcim/filters.py:853 ipam/forms.py:351
+msgid "Assigned VLAN"
+msgstr ""
+
+#: dcim/filters.py:857
+msgid "Assigned VID"
+msgstr ""
+
+#: dcim/filters.py:987
+msgid "Parent inventory item (ID)"
+msgstr ""
+
+#: dcim/filters.py:1052 tenancy/filters.py:88
+msgid "Tenant (ID)"
+msgstr ""
+
+#: dcim/filters.py:1058 extras/filters.py:212 tenancy/filters.py:94
+msgid "Tenant (slug)"
+msgstr ""
+
+#: dcim/filters.py:1314
+msgid "Power panel (ID)"
+msgstr ""
+
+#: dcim/forms.py:140
+msgid "Use regular expressions"
+msgstr ""
+
+#: dcim/forms.py:201
+msgid "Name of parent region"
+msgstr ""
+
+#: dcim/forms.py:277
+msgid "Assigned region"
+msgstr ""
+
+#: dcim/forms.py:391 dcim/forms.py:1908 dcim/forms.py:4444 ipam/forms.py:339
+#: ipam/forms.py:940 ipam/forms.py:1022 virtualization/forms.py:114
+msgid "Assigned site"
+msgstr ""
+
+#: dcim/forms.py:397
+msgid "Parent rack group"
+msgstr ""
+
+#: dcim/forms.py:399
+msgid "Rack group not found."
+msgstr ""
+
+#: dcim/forms.py:528
+msgid "Name of assigned tenant"
+msgstr ""
+
+#: dcim/forms.py:539
+msgid "Name of assigned role"
+msgstr ""
+
+#: dcim/forms.py:544
+msgid "Rack type"
+msgstr ""
+
+#: dcim/forms.py:548
+msgid "Rail-to-rail width (in inches)"
+msgstr ""
+
+#: dcim/forms.py:553
+msgid "Unit for outer dimensions"
+msgstr ""
+
+#: dcim/forms.py:605 dcim/forms.py:2025
+msgid "Serial Number"
+msgstr ""
+
+#: dcim/forms.py:623 dcim/models/__init__.py:495 dcim/models/__init__.py:957
+msgid "Height (U)"
+msgstr ""
+
+#: dcim/forms.py:628 dcim/models/__init__.py:501
+msgid "Descending units"
+msgstr ""
+
+#: dcim/forms.py:688 dcim/forms.py:870 dcim/forms.py:2070
+msgid "Rack group"
+msgstr ""
+
+#: dcim/forms.py:718 dcim/forms.py:793 dcim/forms.py:2080 dcim/forms.py:3423
+#: dcim/forms.py:3853 dcim/forms.py:4461 dcim/forms.py:4611 ipam/forms.py:544
+msgid "Rack"
+msgstr ""
+
+#: dcim/forms.py:762
+msgid ""
+"Comma-separated list of numeric unit IDs. A range may be specified using a "
+"hyphen."
+msgstr ""
+
+#: dcim/forms.py:782
+msgid "Parent site"
+msgstr ""
+
+#: dcim/forms.py:788 dcim/forms.py:1914 dcim/forms.py:4455
+msgid "Rack's group (if any)"
+msgstr ""
+
+#: dcim/forms.py:798
+msgid "Comma-separated list of individual unit numbers"
+msgstr ""
+
+#: dcim/forms.py:1050 dcim/forms.py:2200 dcim/forms.py:2210 dcim/forms.py:3461
+#: dcim/forms.py:3471 dcim/forms.py:3481 dcim/forms.py:3491 dcim/forms.py:3501
+#: dcim/forms.py:3514 dcim/forms.py:3524 dcim/forms.py:3616 dcim/forms.py:3972
+#: dcim/models/device_components.py:980 dcim/models/device_components.py:1060
+#: extras/models/models.py:219 virtualization/forms.py:649
+#: virtualization/forms.py:799
+msgid "Name"
+msgstr ""
+
+#: dcim/forms.py:1142 dcim/forms.py:1164
+#: dcim/models/device_component_templates.py:158
+#: dcim/models/device_components.py:373
+msgid "Maximum power draw (watts)"
+msgstr ""
+
+#: dcim/forms.py:1147 dcim/forms.py:1169
+#: dcim/models/device_component_templates.py:164
+#: dcim/models/device_components.py:379
+msgid "Allocated power draw (watts)"
+msgstr ""
+
+#: dcim/forms.py:1286 dcim/forms.py:1303 dcim/forms.py:2776
+#: dcim/models/device_component_templates.py:273
+msgid "Management only"
+msgstr ""
+
+#: dcim/forms.py:1340 dcim/forms.py:3033
+msgid "Rear ports"
+msgstr ""
+
+#: dcim/forms.py:1341 dcim/forms.py:3034
+msgid "Select one rear port assignment for each front port being created."
+msgstr ""
+
+#: dcim/forms.py:1427 dcim/forms.py:3209
+msgid "The number of front ports which may be mapped to each rear port"
+msgstr ""
+
+#: dcim/forms.py:1645
+msgid "Limit platform assignments to this manufacturer"
+msgstr ""
+
+#: dcim/forms.py:1676 dcim/models/__init__.py:1369
+msgid "The lowest-numbered unit occupied by the device"
+msgstr ""
+
+#: dcim/forms.py:1855 secrets/forms.py:139
+msgid "Assigned role"
+msgstr ""
+
+#: dcim/forms.py:1866
+msgid "Device type manufacturer"
+msgstr ""
+
+#: dcim/forms.py:1871
+msgid "Device type model"
+msgstr ""
+
+#: dcim/forms.py:1877 virtualization/forms.py:416
+msgid "Assigned platform"
+msgstr ""
+
+#: dcim/forms.py:1887
+msgid "Virtualization cluster"
+msgstr ""
+
+#: dcim/forms.py:1920
+msgid "Assigned rack"
+msgstr ""
+
+#: dcim/forms.py:1925
+msgid "Mounted rack face"
+msgstr ""
+
+#: dcim/forms.py:1955
+msgid "Parent device"
+msgstr ""
+
+#: dcim/forms.py:1960
+msgid "Device bay in which this device is installed"
+msgstr ""
+
+#: dcim/forms.py:2096
+msgid "Manufacturer"
+msgstr ""
+
+#: dcim/forms.py:2106
+msgid "Model"
+msgstr ""
+
+#: dcim/forms.py:2138
+msgid "Virtual chassis member"
+msgstr ""
+
+#: dcim/forms.py:2432
+msgid "Maximum draw in watts"
+msgstr ""
+
+#: dcim/forms.py:2437
+msgid "Allocated draw in watts"
+msgstr ""
+
+#: dcim/forms.py:2622
+msgid "Local power port which feeds this outlet"
+msgstr ""
+
+#: dcim/forms.py:2627
+msgid "Electrical phase (for three-phase circuits)"
+msgstr ""
+
+#: dcim/forms.py:2686 dcim/models/device_components.py:669
+msgid "Untagged VLAN"
+msgstr ""
+
+#: dcim/forms.py:2698 dcim/models/device_components.py:675
+msgid "Tagged VLANs"
+msgstr ""
+
+#: dcim/forms.py:2724 virtualization/forms.py:610
+msgid "802.1Q Mode"
+msgstr ""
+
+#: dcim/forms.py:2761 dcim/models/device_components.py:633
+msgid "Parent LAG"
+msgstr ""
+
+#: dcim/forms.py:2768 dcim/models/device_components.py:651
+#: virtualization/forms.py:664 virtualization/forms.py:736
+msgid "MTU"
+msgstr ""
+
+#: dcim/forms.py:2772 dcim/models/device_components.py:645
+#: virtualization/forms.py:668
+msgid "MAC Address"
+msgstr ""
+
+#: dcim/forms.py:2777 dcim/models/device_components.py:656
+msgid "This interface is used only for out-of-band management"
+msgstr ""
+
+#: dcim/forms.py:2942
+msgid "Parent LAG interface"
+msgstr ""
+
+#: dcim/forms.py:2946
+msgid "Physical medium"
+msgstr ""
+
+#: dcim/forms.py:2951
+msgid "IEEE 802.1Q operational mode (for L2 interfaces)"
+msgstr ""
+
+#: dcim/forms.py:3133
+msgid "Corresponding rear port"
+msgstr ""
+
+#: dcim/forms.py:3137 dcim/forms.py:3260 dcim/forms.py:3691
+msgid "Physical medium classification"
+msgstr ""
+
+#: dcim/forms.py:3144
+msgid "Mapped position on corresponding rear port"
+msgstr ""
+
+#: dcim/forms.py:3268
+msgid "Number of front ports which may be mapped"
+msgstr ""
+
+#: dcim/forms.py:3305
+msgid "Child Device"
+msgstr ""
+
+#: dcim/forms.py:3306
+msgid ""
+"Child devices must first be created and assigned to the site/rack of the "
+"parent device."
+msgstr ""
+
+#: dcim/forms.py:3365
+msgid "Child device installed within this bay"
+msgstr ""
+
+#: dcim/forms.py:3367
+msgid "Child device not found."
+msgstr ""
+
+#: dcim/forms.py:3535
+msgid "Provider"
+msgstr ""
+
+#: dcim/forms.py:3564
+msgid "Side"
+msgstr ""
+
+#: dcim/forms.py:3596
+msgid "Rack Group"
+msgstr ""
+
+#: dcim/forms.py:3607
+msgid "Power Panel"
+msgstr ""
+
+#: dcim/forms.py:3644
+msgid "Maximum length is 32767 (any unit)"
+msgstr ""
+
+#: dcim/forms.py:3654
+msgid "Side A device"
+msgstr ""
+
+#: dcim/forms.py:3660
+msgid "Side A type"
+msgstr ""
+
+#: dcim/forms.py:3663
+msgid "Side A component name"
+msgstr ""
+
+#: dcim/forms.py:3670
+msgid "Side B device"
+msgstr ""
+
+#: dcim/forms.py:3676
+msgid "Side B type"
+msgstr ""
+
+#: dcim/forms.py:3679
+msgid "Side B component name"
+msgstr ""
+
+#: dcim/forms.py:3686
+msgid "Connection status"
+msgstr ""
+
+#: dcim/forms.py:3696
+msgid "Length unit"
+msgstr ""
+
+#: dcim/forms.py:3981 dcim/forms.py:4029 dcim/models/device_components.py:1076
+msgid "Part ID"
+msgstr ""
+
+#: dcim/forms.py:4142 dcim/tables.py:630
+msgid "Position"
+msgstr ""
+
+#: dcim/forms.py:4143
+msgid "Priority"
+msgstr ""
+
+#: dcim/forms.py:4306
+msgid "Name of parent site"
+msgstr ""
+
+#: dcim/forms.py:4449
+msgid "Upstream power panel"
+msgstr ""
+
+#: dcim/forms.py:4471
+msgid "Primary or redundant"
+msgstr ""
+
+#: dcim/forms.py:4476
+msgid "Supply type (AC/DC)"
+msgstr ""
+
+#: dcim/forms.py:4481
+msgid "Single or three-phase"
+msgstr ""
+
+#: dcim/forms.py:4603
+msgid "Power panel"
+msgstr ""
+
+#: dcim/models/__init__.py:186
+msgid "Local facility ID or description"
+msgstr ""
+
+#: dcim/models/__init__.py:214
+msgid "GPS coordinate (latitude)"
+msgstr ""
+
+#: dcim/models/__init__.py:221
+msgid "GPS coordinate (longitude)"
+msgstr ""
+
+#: dcim/models/__init__.py:233
+msgid "Contact E-mail"
+msgstr ""
+
+#: dcim/models/__init__.py:432
+msgid "Facility ID"
+msgstr ""
+
+#: dcim/models/__init__.py:433
+msgid "Locally-assigned identifier"
+msgstr ""
+
+#: dcim/models/__init__.py:446 tenancy/forms.py:78
+msgid "Assigned group"
+msgstr ""
+
+#: dcim/models/__init__.py:466 ipam/forms.py:361 ipam/forms.py:724
+#: ipam/forms.py:1044 virtualization/forms.py:404
+msgid "Functional role"
+msgstr ""
+
+#: dcim/models/__init__.py:471 dcim/models/__init__.py:1342
+#: dcim/models/device_components.py:1082
+msgid "Serial number"
+msgstr ""
+
+#: dcim/models/__init__.py:478 dcim/models/__init__.py:1349
+#: dcim/models/device_components.py:1090
+msgid "Asset tag"
+msgstr ""
+
+#: dcim/models/__init__.py:479
+msgid "A unique tag used to identify this rack"
+msgstr ""
+
+#: dcim/models/__init__.py:485 dcim/tables.py:794 dcim/tables.py:864
+#: extras/tables.py:91 extras/tables.py:123 netbox/forms.py:47
+msgid "Type"
+msgstr ""
+
+#: dcim/models/__init__.py:490
+msgid "Width"
+msgstr ""
+
+#: dcim/models/__init__.py:491
+msgid "Rail-to-rail width"
+msgstr ""
+
+#: dcim/models/__init__.py:497
+msgid "Height in rack units"
+msgstr ""
+
+#: dcim/models/__init__.py:502
+msgid "Units are numbered top-to-bottom"
+msgstr ""
+
+#: dcim/models/__init__.py:507
+msgid "Outer dimension of rack (width)"
+msgstr ""
+
+#: dcim/models/__init__.py:512
+msgid "Outer dimension of rack (depth)"
+msgstr ""
+
+#: dcim/models/__init__.py:953
+msgid "Discrete part number (optional)"
+msgstr ""
+
+#: dcim/models/__init__.py:962
+msgid "Device consumes both front and rear rack faces"
+msgstr ""
+
+#: dcim/models/__init__.py:968
+msgid "Parent/child status"
+msgstr ""
+
+#: dcim/models/__init__.py:969
+msgid ""
+"Parent devices house child devices in device bays. Leave blank if this "
+"device type is neither a parent nor a child."
+msgstr ""
+
+#: dcim/models/__init__.py:1204
+msgid "VM Role"
+msgstr ""
+
+#: dcim/models/__init__.py:1205
+msgid "Virtual machines may be assigned to this role"
+msgstr ""
+
+#: dcim/models/__init__.py:1250
+msgid "Optionally limit this platform to devices of a certain manufacturer"
+msgstr ""
+
+#: dcim/models/__init__.py:1255
+msgid "NAPALM driver"
+msgstr ""
+
+#: dcim/models/__init__.py:1256
+msgid "The name of the NAPALM driver to use when interacting with devices"
+msgstr ""
+
+#: dcim/models/__init__.py:1261
+msgid "NAPALM arguments"
+msgstr ""
+
+#: dcim/models/__init__.py:1262
+msgid ""
+"Additional arguments to pass when initiating the NAPALM driver (JSON format)"
+msgstr ""
+
+#: dcim/models/__init__.py:1350
+msgid "A unique tag used to identify this device"
+msgstr ""
+
+#: dcim/models/__init__.py:1368
+msgid "Position (U)"
+msgstr ""
+
+#: dcim/models/__init__.py:1375
+msgid "Rack face"
+msgstr ""
+
+#: dcim/models/__init__.py:1388 virtualization/models.py:240
+msgid "Primary IPv4"
+msgstr ""
+
+#: dcim/models/__init__.py:1396 virtualization/models.py:248
+msgid "Primary IPv6"
+msgstr ""
+
+#: dcim/models/__init__.py:1908
+msgid "Maximum permissible draw (percentage)"
+msgstr ""
+
+#: dcim/models/device_component_templates.py:217
+#: dcim/models/device_components.py:540
+msgid "Phase (for three-phase feeds)"
+msgstr ""
+
+#: dcim/models/device_components.py:256 dcim/models/device_components.py:315
+#: dcim/models/device_components.py:367 dcim/models/device_components.py:527
+msgid "Physical port type"
+msgstr ""
+
+#: dcim/models/device_components.py:655
+msgid "OOB Management"
+msgstr ""
+
+#: dcim/models/device_components.py:1078
+msgid "Manufacturer-assigned part identifier"
+msgstr ""
+
+#: dcim/models/device_components.py:1091
+msgid "A unique tag used to identify this item"
+msgstr ""
+
+#: dcim/models/device_components.py:1095
+msgid "This item was automatically discovered"
+msgstr ""
+
+#: dcim/tables.py:200 netbox/forms.py:13
+msgid "Sites"
+msgstr ""
+
+#: dcim/tables.py:262 netbox/forms.py:14
+msgid "Racks"
+msgstr ""
+
+#: dcim/tables.py:319
+msgid "Height"
+msgstr ""
+
+#: dcim/tables.py:334 dcim/tables.py:710 dcim/tables.py:741 netbox/forms.py:17
+#: virtualization/tables.py:108
+msgid "Devices"
+msgstr ""
+
+#: dcim/tables.py:339
+msgid "Space"
+msgstr ""
+
+#: dcim/tables.py:344
+msgid "Power"
+msgstr ""
+
+#: dcim/tables.py:386
+msgid "Units"
+msgstr ""
+
+#: dcim/tables.py:412
+msgid "Device Types"
+msgstr ""
+
+#: dcim/tables.py:415
+msgid "Inventory Items"
+msgstr ""
+
+#: dcim/tables.py:418
+msgid "Platforms"
+msgstr ""
+
+#: dcim/tables.py:443
+msgid "Device Type"
+msgstr ""
+
+#: dcim/tables.py:446
+msgid "Full Depth"
+msgstr ""
+
+#: dcim/tables.py:450
+msgid "Instances"
+msgstr ""
+
+#: dcim/tables.py:612
+msgid "Virtual Machine"
+msgstr ""
+
+#: dcim/tables.py:714 dcim/tables.py:745 virtualization/tables.py:112
+msgid "VMs"
+msgstr ""
+
+#: dcim/tables.py:718
+msgid "Label"
+msgstr ""
+
+#: dcim/tables.py:789 dcim/tables.py:861 extras/filters.py:157
+msgid "Role"
+msgstr ""
+
+#: dcim/tables.py:800 ipam/forms.py:579 ipam/tables.py:419 ipam/tables.py:476
+#: ipam/tables.py:500 virtualization/tables.py:161
+msgid "IP Address"
+msgstr ""
+
+#: dcim/tables.py:805 virtualization/tables.py:152
+msgid "IPv4 Address"
+msgstr ""
+
+#: dcim/tables.py:810 virtualization/tables.py:157
+msgid "IPv6 Address"
+msgstr ""
+
+#: dcim/tables.py:821
+msgid "VC Position"
+msgstr ""
+
+#: dcim/tables.py:824
+msgid "VC Priority"
+msgstr ""
+
+#: dcim/tables.py:1041
+msgid "Side A"
+msgstr ""
+
+#: dcim/tables.py:1046
+msgid "Termination A"
+msgstr ""
+
+#: dcim/tables.py:1052
+msgid "Side B"
+msgstr ""
+
+#: dcim/tables.py:1057
+msgid "Termination B"
+msgstr ""
+
+#: dcim/tables.py:1089
+msgid "Console Server"
+msgstr ""
+
+#: dcim/tables.py:1092
+msgid "Port"
+msgstr ""
+
+#: dcim/tables.py:1099
+msgid "Console Port"
+msgstr ""
+
+#: dcim/tables.py:1113
+msgid "PDU"
+msgstr ""
+
+#: dcim/tables.py:1117
+msgid "Outlet"
+msgstr ""
+
+#: dcim/tables.py:1124
+msgid "Power Port"
+msgstr ""
+
+#: dcim/tables.py:1137
+msgid "Device A"
+msgstr ""
+
+#: dcim/tables.py:1143
+msgid "Interface A"
+msgstr ""
+
+#: dcim/tables.py:1149
+msgid "Device B"
+msgstr ""
+
+#: dcim/tables.py:1155
+msgid "Interface B"
+msgstr ""
+
+#: dcim/tables.py:1199
+msgid "Members"
+msgstr ""
+
+#: dcim/tables.py:1224
+msgid "Feeds"
+msgstr ""
+
+#: dcim/tables.py:1258
+msgid "Available power (VA)"
+msgstr ""
+
+#: dcim/views.py:78
+msgid "Renamed {} {}"
+msgstr ""
+
+#: dcim/views.py:123
+msgid "Disconnected {} {}"
+msgstr ""
+
+#: dcim/views.py:1857
+msgid "Added {} to {}."
+msgstr ""
+
+#: dcim/views.py:1892
+msgid "{} has been removed from {}."
+msgstr ""
+
+#: extras/admin.py:24 extras/models/models.py:58 extras/models/models.py:176
+msgid "URL"
+msgstr ""
+
+#: extras/admin.py:120
+msgid ""
+"A numeric weight to influence the ordering of this link among its peers. "
+"Lower weights appear first in a list."
+msgstr ""
+
+#: extras/admin.py:122
+msgid ""
+"Jinja2 template code for the link text. Reference the object as "
+"{{ obj }}
. Links which render as empty text will not be "
+"displayed."
+msgstr ""
+
+#: extras/admin.py:124
+msgid ""
+"Jinja2 template code for the link URL. Reference the object as "
+"{{ obj }}
."
+msgstr ""
+
+#: extras/filters.py:135
+msgid "Region"
+msgstr ""
+
+#: extras/filters.py:168
+msgid "Platform"
+msgstr ""
+
+#: extras/filters.py:179
+msgid "Cluster group"
+msgstr ""
+
+#: extras/filters.py:185 virtualization/filters.py:125
+msgid "Cluster group (slug)"
+msgstr ""
+
+#: extras/filters.py:190 extras/forms.py:334 virtualization/forms.py:512
+msgid "Cluster"
+msgstr ""
+
+#: extras/filters.py:195
+msgid "Tenant group"
+msgstr ""
+
+#: extras/filters.py:201 tenancy/filters.py:50
+msgid "Tenant group (slug)"
+msgstr ""
+
+#: extras/filters.py:206
+msgid "Tenant"
+msgstr ""
+
+#: extras/filters.py:218
+msgid "Tag (slug)"
+msgstr ""
+
+#: extras/filters.py:242 extras/forms.py:369
+msgid "Has local config context data"
+msgstr ""
+
+#: extras/forms.py:400
+msgid "After"
+msgstr ""
+
+#: extras/forms.py:405
+msgid "Before"
+msgstr ""
+
+#: extras/forms.py:424
+msgid "Object Type"
+msgstr ""
+
+#: extras/forms.py:436
+msgid "Commit changes"
+msgstr ""
+
+#: extras/forms.py:437
+msgid "Commit changes to the database (uncheck for a dry-run)"
+msgstr ""
+
+#: extras/models/customfields.py:74
+msgid "Object(s)"
+msgstr ""
+
+#: extras/models/customfields.py:76
+msgid "The object(s) to which this field applies."
+msgstr ""
+
+#: extras/models/customfields.py:90
+msgid ""
+"Name of the field as displayed to users (if not provided, the field's name "
+"will be used)"
+msgstr ""
+
+#: extras/models/customfields.py:99
+msgid ""
+"If true, this field is required when creating new objects or editing an "
+"existing object."
+msgstr ""
+
+#: extras/models/customfields.py:106
+msgid ""
+"Loose matches any instance of a given string; exact matches the entire field."
+msgstr ""
+
+#: extras/models/customfields.py:112
+msgid "Default value for the field. Use \"true\" or \"false\" for booleans."
+msgstr ""
+
+#: extras/models/customfields.py:116
+msgid "Fields with higher weights appear lower in a form."
+msgstr ""
+
+#: extras/models/customfields.py:288
+msgid "Higher weights appear lower in the list"
+msgstr ""
+
+#: extras/models/models.py:36
+msgid "Object types"
+msgstr ""
+
+#: extras/models/models.py:38
+msgid "The object(s) to which this Webhook applies."
+msgstr ""
+
+#: extras/models/models.py:46
+msgid "Call this webhook when a matching object is created."
+msgstr ""
+
+#: extras/models/models.py:50
+msgid "Call this webhook when a matching object is updated."
+msgstr ""
+
+#: extras/models/models.py:54
+msgid "Call this webhook when a matching object is deleted."
+msgstr ""
+
+#: extras/models/models.py:59
+msgid "A POST will be sent to this URL when the webhook is called."
+msgstr ""
+
+#: extras/models/models.py:68
+msgid "HTTP method"
+msgstr ""
+
+#: extras/models/models.py:73
+msgid "HTTP content type"
+msgstr ""
+
+#: extras/models/models.py:74
+msgid ""
+"The complete list of official content types is available here."
+msgstr ""
+
+#: extras/models/models.py:79
+msgid ""
+"User-supplied HTTP headers to be sent with the request in addition to the "
+"HTTP content type.Headers should be defined in the format Name: Value"
+"code>. Jinja2 template processing is support with the same context as the "
+"request body (below)."
+msgstr ""
+
+#: extras/models/models.py:85
+msgid ""
+"Jinja2 template for a custom request body. If blank, a JSON object "
+"representing the change will be included. Available context data includes: "
+"event
, model
, timestamp
, "
+"username
, request_id
, and data
."
+msgstr ""
+
+#: extras/models/models.py:92
+msgid ""
+"When provided, the request will include a 'X-Hook-Signature' header "
+"containing a HMAC hex digest of the payload body using the secret as the "
+"key. The secret is not transmitted in the request."
+msgstr ""
+
+#: extras/models/models.py:99
+msgid "SSL verification"
+msgstr ""
+
+#: extras/models/models.py:100
+msgid "Enable SSL certificate verification. Disable with caution!"
+msgstr ""
+
+#: extras/models/models.py:106
+msgid "CA File Path"
+msgstr ""
+
+#: extras/models/models.py:107
+msgid ""
+"The specific CA certificate file to use for SSL verification. Leave blank to "
+"use the system defaults."
+msgstr ""
+
+#: extras/models/models.py:172
+msgid "Jinja2 template code for link text"
+msgstr ""
+
+#: extras/models/models.py:177
+msgid "Jinja2 template code for link URL"
+msgstr ""
+
+#: extras/models/models.py:185
+msgid "Links with the same group will appear as a dropdown menu"
+msgstr ""
+
+#: extras/models/models.py:191
+msgid ""
+"The class of the first link in a group will be used for the dropdown button"
+msgstr ""
+
+#: extras/models/models.py:194
+msgid "Force link to open in a new window"
+msgstr ""
+
+#: extras/models/models.py:228
+msgid "Source URL"
+msgstr ""
+
+#: extras/models/models.py:232
+msgid "Link URL"
+msgstr ""
+
+#: extras/models/models.py:288
+msgid ""
+"The list of objects being exported is passed as a context variable named "
+"queryset
."
+msgstr ""
+
+#: extras/models/models.py:293
+msgid "MIME type"
+msgstr ""
+
+#: extras/models/models.py:294
+msgid "Defaults to text/plain
"
+msgstr ""
+
+#: extras/models/models.py:299
+msgid "Extension to append to the rendered filename"
+msgstr ""
+
+#: extras/tables.py:88 extras/tables.py:127
+msgid "Object"
+msgstr ""
+
+#: extras/tables.py:103 ipam/tables.py:246
+msgid "Active"
+msgstr ""
+
+#: extras/tables.py:131
+msgid "Request ID"
+msgstr ""
+
+#: ipam/apps.py:7 netbox/forms.py:22
+msgid "IPAM"
+msgstr ""
+
+#: ipam/filters.py:72 ipam/filters.py:133
+msgid "Prefix"
+msgstr ""
+
+#: ipam/filters.py:76
+msgid "RIR (ID)"
+msgstr ""
+
+#: ipam/filters.py:82
+msgid "RIR (slug)"
+msgstr ""
+
+#: ipam/filters.py:137
+msgid "Within prefix"
+msgstr ""
+
+#: ipam/filters.py:141
+msgid "Within and including prefix"
+msgstr ""
+
+#: ipam/filters.py:145
+msgid "Prefixes which contain this prefix or IP"
+msgstr ""
+
+#: ipam/filters.py:149 ipam/filters.py:291 ipam/forms.py:457 ipam/forms.py:886
+msgid "Mask length"
+msgstr ""
+
+#: ipam/filters.py:153 ipam/filters.py:295 ipam/forms.py:256 ipam/forms.py:393
+#: ipam/forms.py:463 ipam/forms.py:528 ipam/forms.py:569 ipam/forms.py:683
+#: ipam/forms.py:809 ipam/forms.py:849 ipam/forms.py:892 ipam/models.py:88
+#: ipam/models.py:318 ipam/models.py:582 ipam/tables.py:357 ipam/tables.py:423
+#: ipam/tables.py:504
+msgid "VRF"
+msgstr ""
+
+#: ipam/filters.py:159 ipam/filters.py:301
+msgid "VRF (RD)"
+msgstr ""
+
+#: ipam/filters.py:186
+msgid "VLAN (ID)"
+msgstr ""
+
+#: ipam/filters.py:190
+msgid "VLAN number (1-4095)"
+msgstr ""
+
+#: ipam/filters.py:283
+msgid "Parent prefix"
+msgstr ""
+
+#: ipam/filters.py:287
+msgid "Address"
+msgstr ""
+
+#: ipam/filters.py:316 ipam/filters.py:512 virtualization/filters.py:213
+msgid "Virtual machine (ID)"
+msgstr ""
+
+#: ipam/filters.py:322 ipam/filters.py:518
+msgid "Virtual machine (name)"
+msgstr ""
+
+#: ipam/filters.py:328 ipam/filters.py:332
+msgid "Interface (ID)"
+msgstr ""
+
+#: ipam/filters.py:336
+msgid "Is assigned to an interface"
+msgstr ""
+
+#: ipam/filters.py:456
+msgid "Group (ID)"
+msgstr ""
+
+#: ipam/filters.py:462 ipam/tables.py:636
+msgid "Group"
+msgstr ""
+
+#: ipam/forms.py:79 ipam/models.py:66
+msgid "Enforce unique space"
+msgstr ""
+
+#: ipam/forms.py:123
+msgid "RIR name"
+msgstr ""
+
+#: ipam/forms.py:130 ipam/models.py:127 ipam/tables.py:221
+msgid "Private"
+msgstr ""
+
+#: ipam/forms.py:167
+msgid "Assigned RIR"
+msgstr ""
+
+#: ipam/forms.py:183 ipam/forms.py:218 ipam/models.py:139 ipam/models.py:168
+msgid "RIR"
+msgstr ""
+
+#: ipam/forms.py:211 ipam/forms.py:451 ipam/forms.py:880
+msgid "Address family"
+msgstr ""
+
+#: ipam/forms.py:274 ipam/forms.py:1138 ipam/models.py:840
+msgid "VLAN group"
+msgstr ""
+
+#: ipam/forms.py:287 ipam/models.py:333 ipam/models.py:947 ipam/tables.py:369
+msgid "VLAN"
+msgstr ""
+
+#: ipam/forms.py:327 ipam/forms.py:709
+msgid "Assigned VRF"
+msgstr ""
+
+#: ipam/forms.py:345
+msgid "VLAN's group (if any)"
+msgstr ""
+
+#: ipam/forms.py:416 ipam/forms.py:504 ipam/models.py:351
+msgid "Is a pool"
+msgstr ""
+
+#: ipam/forms.py:446
+msgid "Search within"
+msgstr ""
+
+#: ipam/forms.py:511
+msgid "Expand prefix hierarchy"
+msgstr ""
+
+#: ipam/forms.py:586
+msgid "Make this the primary IP for the device/VM"
+msgstr ""
+
+#: ipam/forms.py:675
+msgid "Address pattern"
+msgstr ""
+
+#: ipam/forms.py:730
+msgid "Parent device of assigned interface (if any)"
+msgstr ""
+
+#: ipam/forms.py:736
+msgid "Parent VM of assigned interface (if any)"
+msgstr ""
+
+#: ipam/forms.py:742
+msgid "Assigned interface"
+msgstr ""
+
+#: ipam/forms.py:745
+msgid "Make this the primary IP for the assigned device"
+msgstr ""
+
+#: ipam/forms.py:783
+msgid "No device or virtual machine specified; cannot set as primary IP"
+msgstr ""
+
+#: ipam/forms.py:875
+msgid "Parent Prefix"
+msgstr ""
+
+#: ipam/forms.py:909
+msgid "Assigned to an interface"
+msgstr ""
+
+#: ipam/forms.py:1028
+msgid "Assigned VLAN group"
+msgstr ""
+
+#: ipam/forms.py:1051
+msgid "Numeric VLAN ID (1-4095)"
+msgstr ""
+
+#: ipam/forms.py:1052
+msgid "VLAN name"
+msgstr ""
+
+#: ipam/forms.py:1226
+msgid "Required if not assigned to a VM"
+msgstr ""
+
+#: ipam/forms.py:1232
+msgid "Required if not assigned to a device"
+msgstr ""
+
+#: ipam/forms.py:1236
+msgid "IP protocol"
+msgstr ""
+
+#: ipam/models.py:54
+msgid "Route distinguisher"
+msgstr ""
+
+#: ipam/models.py:55
+msgid "Unique route distinguisher (as defined in RFC 4364)"
+msgstr ""
+
+#: ipam/models.py:67
+msgid "Prevent duplicate prefixes/IP addresses within this VRF"
+msgstr ""
+
+#: ipam/models.py:128
+msgid "IP space managed by this RIR is considered private"
+msgstr ""
+
+#: ipam/models.py:303
+msgid "IPv4 or IPv6 network with mask"
+msgstr ""
+
+#: ipam/models.py:339 virtualization/models.py:224
+msgid "Status"
+msgstr ""
+
+#: ipam/models.py:340
+msgid "Operational status of this prefix"
+msgstr ""
+
+#: ipam/models.py:348
+msgid "The primary function of this prefix"
+msgstr ""
+
+#: ipam/models.py:353
+msgid "All IP addresses within this prefix are considered usable"
+msgstr ""
+
+#: ipam/models.py:574
+msgid "IPv4 or IPv6 address (with mask)"
+msgstr ""
+
+#: ipam/models.py:595
+msgid "The operational status of this IP"
+msgstr ""
+
+#: ipam/models.py:601
+msgid "The functional role of this IP"
+msgstr ""
+
+#: ipam/models.py:616 ipam/tables.py:454
+msgid "NAT (Inside)"
+msgstr ""
+
+#: ipam/models.py:617
+msgid "The IP for which this address is the \"outside\" IP"
+msgstr ""
+
+#: ipam/models.py:623
+msgid "DNS Name"
+msgstr ""
+
+#: ipam/models.py:624
+msgid "Hostname or FQDN (not case-sensitive)"
+msgstr ""
+
+#: ipam/models.py:667
+msgid "IP address"
+msgstr ""
+
+#: ipam/models.py:1003
+msgid "device"
+msgstr ""
+
+#: ipam/models.py:1026
+msgid "Port number"
+msgstr ""
+
+#: ipam/models.py:1032 netbox/forms.py:26
+msgid "IP addresses"
+msgstr ""
+
+#: ipam/tables.py:195
+msgid "RD"
+msgstr ""
+
+#: ipam/tables.py:201
+msgid "Unique"
+msgstr ""
+
+#: ipam/tables.py:224 netbox/forms.py:24
+msgid "Aggregates"
+msgstr ""
+
+#: ipam/tables.py:241
+msgid "Total"
+msgstr ""
+
+#: ipam/tables.py:251
+msgid "Reserved"
+msgstr ""
+
+#: ipam/tables.py:256
+msgid "Deprecated"
+msgstr ""
+
+#: ipam/tables.py:261
+msgid "Available"
+msgstr ""
+
+#: ipam/tables.py:266
+msgid "Utilization"
+msgstr ""
+
+#: ipam/tables.py:287
+msgid "Aggregate"
+msgstr ""
+
+#: ipam/tables.py:291
+msgid "Added"
+msgstr ""
+
+#: ipam/tables.py:301 ipam/tables.py:324 ipam/tables.py:584 netbox/forms.py:25
+msgid "Prefixes"
+msgstr ""
+
+#: ipam/tables.py:328 ipam/tables.py:530 netbox/forms.py:27
+msgid "VLANs"
+msgstr ""
+
+#: ipam/tables.py:375
+msgid "Pool"
+msgstr ""
+
+#: ipam/tables.py:603
+msgid "Interface"
+msgstr ""
+
+#: netbox/forms.py:7
+msgid "All Objects"
+msgstr ""
+
+#: netbox/forms.py:9
+msgid "Providers"
+msgstr ""
+
+#: netbox/forms.py:15
+msgid "Rack Groups"
+msgstr ""
+
+#: netbox/forms.py:16
+msgid "Device types"
+msgstr ""
+
+#: netbox/forms.py:18
+msgid "Virtual Chassis"
+msgstr ""
+
+#: netbox/forms.py:19
+msgid "Cables"
+msgstr ""
+
+#: netbox/forms.py:20
+msgid "Power Feeds"
+msgstr ""
+
+#: netbox/forms.py:23
+msgid "VRFs"
+msgstr ""
+
+#: netbox/forms.py:29 netbox/forms.py:30 secrets/tables.py:25
+msgid "Secrets"
+msgstr ""
+
+#: netbox/forms.py:32
+msgid "Tenancy"
+msgstr ""
+
+#: netbox/forms.py:33 tenancy/tables.py:46
+msgid "Tenants"
+msgstr ""
+
+#: netbox/forms.py:35
+msgid "Virtualization"
+msgstr ""
+
+#: netbox/forms.py:36 virtualization/tables.py:55 virtualization/tables.py:77
+msgid "Clusters"
+msgstr ""
+
+#: netbox/forms.py:37
+msgid "Virtual machines"
+msgstr ""
+
+#: secrets/forms.py:24
+msgid ""
+"OpenSSH line format is not supported. Please ensure that your public is in "
+"PEM (base64) format."
+msgstr ""
+
+#: secrets/forms.py:28
+msgid "Invalid RSA key. Please ensure that your key is in PEM (base64) format."
+msgstr ""
+
+#: secrets/forms.py:32
+msgid "This looks like a public key. Please provide your private RSA key."
+msgstr ""
+
+#: secrets/forms.py:34
+msgid "This looks like a private key. Please provide your public RSA key."
+msgstr ""
+
+#: secrets/forms.py:38
+msgid ""
+"Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP."
+msgstr ""
+
+#: secrets/forms.py:78
+msgid "Plaintext"
+msgstr ""
+
+#: secrets/forms.py:88
+msgid "Plaintext (verify)"
+msgstr ""
+
+#: secrets/forms.py:134
+msgid "Assigned device"
+msgstr ""
+
+#: secrets/forms.py:142
+msgid "Plaintext secret data"
+msgstr ""
+
+#: secrets/forms.py:149
+msgid "Name or username"
+msgstr ""
+
+#: secrets/forms.py:225
+msgid "User Keys"
+msgstr ""
+
+#: secrets/forms.py:233
+msgid "Your private key"
+msgstr ""
+
+#: secrets/models.py:55
+msgid "RSA public key"
+msgstr ""
+
+#: secrets/views.py:121
+msgid "Added new secret: {}."
+msgstr ""
+
+#: secrets/views.py:168
+msgid "Modified secret {}."
+msgstr ""
+
+#: tenancy/filters.py:20 tenancy/filters.py:43
+msgid "Tenant group (ID)"
+msgstr ""
+
+#: tenancy/filters.py:26
+msgid "Tenant group group (slug)"
+msgstr ""
+
+#: tenancy/filters.py:77
+msgid "Tenant Group (ID)"
+msgstr ""
+
+#: tenancy/filters.py:84
+msgid "Tenant Group (slug)"
+msgstr ""
+
+#: tenancy/forms.py:41
+msgid "Parent group"
+msgstr ""
+
+#: users/admin.py:17
+msgid "Preferences"
+msgstr ""
+
+#: users/admin.py:31 users/forms.py:25
+msgid "If no key is provided, one will be generated automatically."
+msgstr ""
+
+#: users/models.py:166
+msgid "Permit create/update/delete operations using this key"
+msgstr ""
+
+#: users/views.py:68
+msgid "Logged in as {}."
+msgstr ""
+
+#: users/views.py:92
+msgid "You have logged out."
+msgstr ""
+
+#: users/views.py:134 utilities/views.py:210
+msgid "Your preferences have been updated."
+msgstr ""
+
+#: users/views.py:160
+msgid "Your password has been changed successfully."
+msgstr ""
+
+#: users/views.py:210
+msgid "Your user key has been saved."
+msgstr ""
+
+#: users/views.py:241
+msgid "Session key deleted"
+msgstr ""
+
+#: users/views.py:308
+msgid "Modified token {}"
+msgstr ""
+
+#: users/views.py:308
+msgid "Created token {}"
+msgstr ""
+
+#: users/views.py:348
+msgid "Token deleted"
+msgstr ""
+
+#: utilities/forms.py:591
+msgid "Tags"
+msgstr ""
+
+#: utilities/forms.py:744
+msgid ""
+"Enter object data in JSON or YAML format. Note: Only a single object/"
+"document is supported."
+msgstr ""
+
+#: utilities/forms.py:795
+msgid ""
+"Use the buttons below to arrange columns in the desired order, then select "
+"all columns to display."
+msgstr ""
+
+#: utilities/views.py:536
+msgid "Imported object: {}"
+msgstr ""
+
+#: utilities/views.py:950
+msgid "Added {} {}"
+msgstr ""
+
+#: virtualization/filters.py:68
+msgid "Parent group (ID)"
+msgstr ""
+
+#: virtualization/filters.py:74
+msgid "Parent group (slug)"
+msgstr ""
+
+#: virtualization/filters.py:78 virtualization/filters.py:130
+msgid "Cluster type (ID)"
+msgstr ""
+
+#: virtualization/filters.py:84 virtualization/filters.py:136
+msgid "Cluster type (slug)"
+msgstr ""
+
+#: virtualization/filters.py:119
+msgid "Cluster group (ID)"
+msgstr ""
+
+#: virtualization/filters.py:140
+msgid "Cluster (ID)"
+msgstr ""
+
+#: virtualization/filters.py:219
+msgid "Virtual machine"
+msgstr ""
+
+#: virtualization/forms.py:102
+msgid "Type of cluster"
+msgstr ""
+
+#: virtualization/forms.py:108
+msgid "Assigned cluster group"
+msgstr ""
+
+#: virtualization/forms.py:391
+msgid "Operational status of device"
+msgstr ""
+
+#: virtualization/forms.py:396
+msgid "Assigned cluster"
+msgstr ""
+
+#: virtualization/forms.py:460 virtualization/models.py:253
+msgid "vCPUs"
+msgstr ""
+
+#: virtualization/forms.py:464 virtualization/models.py:258
+msgid "Memory (MB)"
+msgstr ""
+
+#: virtualization/forms.py:468 virtualization/models.py:263
+msgid "Disk (GB)"
+msgstr ""
+
+#: virtualization/views.py:199
+msgid "Added {} devices to cluster {}"
+msgstr ""
+
+#: virtualization/views.py:232
+msgid "Removed {} devices from cluster {}"
+msgstr ""
diff --git a/netbox/netbox/forms.py b/netbox/netbox/forms.py
index 36198a384..78ac4c1e8 100644
--- a/netbox/netbox/forms.py
+++ b/netbox/netbox/forms.py
@@ -1,47 +1,48 @@
from django import forms
+from django.utils.translation import gettext as _
from utilities.forms import BootstrapMixin
OBJ_TYPE_CHOICES = (
- ('', 'All Objects'),
- ('Circuits', (
- ('provider', 'Providers'),
- ('circuit', 'Circuits'),
+ ('', _('All Objects')),
+ (_('Circuits'), (
+ ('provider', _('Providers')),
+ ('circuit', _('Circuits')),
)),
- ('DCIM', (
- ('site', 'Sites'),
- ('rack', 'Racks'),
- ('rackgroup', 'Rack Groups'),
- ('devicetype', 'Device types'),
- ('device', 'Devices'),
- ('virtualchassis', 'Virtual Chassis'),
- ('cable', 'Cables'),
- ('powerfeed', 'Power Feeds'),
+ (_('DCIM'), (
+ ('site', _('Sites')),
+ ('rack', _('Racks')),
+ ('rackgroup', _('Rack Groups')),
+ ('devicetype', _('Device types')),
+ ('device', _('Devices')),
+ ('virtualchassis', _('Virtual Chassis')),
+ ('cable', _('Cables')),
+ ('powerfeed', _('Power Feeds')),
)),
- ('IPAM', (
- ('vrf', 'VRFs'),
- ('aggregate', 'Aggregates'),
- ('prefix', 'Prefixes'),
- ('ipaddress', 'IP addresses'),
- ('vlan', 'VLANs'),
+ (_('IPAM'), (
+ ('vrf', _('VRFs')),
+ ('aggregate', _('Aggregates')),
+ ('prefix', _('Prefixes')),
+ ('ipaddress', _('IP addresses')),
+ ('vlan', _('VLANs')),
)),
- ('Secrets', (
- ('secret', 'Secrets'),
+ (_('Secrets'), (
+ ('secret', _('Secrets')),
)),
- ('Tenancy', (
- ('tenant', 'Tenants'),
+ (_('Tenancy'), (
+ ('tenant', _('Tenants')),
)),
- ('Virtualization', (
- ('cluster', 'Clusters'),
- ('virtualmachine', 'Virtual machines'),
+ (_('Virtualization'), (
+ ('cluster', _('Clusters')),
+ ('virtualmachine', _('Virtual machines')),
)),
)
class SearchForm(BootstrapMixin, forms.Form):
q = forms.CharField(
- label='Search'
+ label=_('Search')
)
obj_type = forms.ChoiceField(
- choices=OBJ_TYPE_CHOICES, required=False, label='Type'
+ choices=OBJ_TYPE_CHOICES, required=False, label=_('Type')
)
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
index 61020669f..3cb20a01e 100644
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
@@ -681,3 +681,12 @@ for plugin_name in PLUGINS:
CACHEOPS.update({
"{}.{}".format(plugin_name, key): value for key, value in plugin_config.caching_config.items()
})
+
+LOCALE_PATHS = (
+ os.path.join(BASE_DIR, 'locale'),
+)
+
+LANGUAGES = (
+ ('en', 'English'),
+ ('zh-Hans', '中文简体'),
+)
diff --git a/netbox/secrets/filters.py b/netbox/secrets/filters.py
index 78f25952a..e0fe9db62 100644
--- a/netbox/secrets/filters.py
+++ b/netbox/secrets/filters.py
@@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
+from django.utils.translation import gettext as _
from dcim.models import Device
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
@@ -20,30 +21,31 @@ class SecretRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
-class SecretFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
+class SecretFilterSet(
+ BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=SecretRole.objects.all(),
- label='Role (ID)',
+ label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=SecretRole.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
- label='Device (ID)',
+ label=_('Device (ID)'),
)
device = django_filters.ModelMultipleChoiceFilter(
field_name='device__name',
queryset=Device.objects.all(),
to_field_name='name',
- label='Device (name)',
+ label=_('Device (name)'),
)
tag = TagFilter()
diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py
index b4fe4eaff..82fc2f8f5 100644
--- a/netbox/secrets/forms.py
+++ b/netbox/secrets/forms.py
@@ -1,6 +1,7 @@
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from django import forms
+from django.utils.translation import gettext as _
from dcim.models import Device
from extras.forms import (
@@ -20,21 +21,21 @@ def validate_rsa_key(key, is_secret=True):
Validate the format and type of an RSA key.
"""
if key.startswith('ssh-rsa '):
- raise forms.ValidationError("OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.")
+ raise forms.ValidationError(_('OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.'))
try:
key = RSA.importKey(key)
except ValueError:
- raise forms.ValidationError("Invalid RSA key. Please ensure that your key is in PEM (base64) format.")
+ raise forms.ValidationError(_('Invalid RSA key. Please ensure that your key is in PEM (base64) format.'))
except Exception as e:
raise forms.ValidationError("Invalid key detected: {}".format(e))
if is_secret and not key.has_private():
- raise forms.ValidationError("This looks like a public key. Please provide your private RSA key.")
+ raise forms.ValidationError(_('This looks like a public key. Please provide your private RSA key.'))
elif not is_secret and key.has_private():
- raise forms.ValidationError("This looks like a private key. Please provide your public RSA key.")
+ raise forms.ValidationError(_('This looks like a private key. Please provide your public RSA key.'))
try:
PKCS1_OAEP.new(key)
except Exception:
- raise forms.ValidationError("Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP.")
+ raise forms.ValidationError(_('Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP.'))
#
@@ -74,7 +75,7 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
plaintext = forms.CharField(
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
required=False,
- label='Plaintext',
+ label=_('Plaintext'),
widget=forms.PasswordInput(
attrs={
'class': 'requires-session-key',
@@ -84,7 +85,7 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
plaintext2 = forms.CharField(
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
required=False,
- label='Plaintext (verify)',
+ label=_('Plaintext (verify)'),
widget=forms.PasswordInput()
)
role = DynamicModelChoiceField(
@@ -130,22 +131,22 @@ class SecretCSVForm(CustomFieldModelCSVForm):
device = CSVModelChoiceField(
queryset=Device.objects.all(),
to_field_name='name',
- help_text='Assigned device'
+ help_text=_('Assigned device')
)
role = CSVModelChoiceField(
queryset=SecretRole.objects.all(),
to_field_name='name',
- help_text='Assigned role'
+ help_text=_('Assigned role')
)
plaintext = forms.CharField(
- help_text='Plaintext secret data'
+ help_text=_('Plaintext secret data')
)
class Meta:
model = Secret
fields = Secret.csv_headers
help_texts = {
- 'name': 'Name or username',
+ 'name': _('Name or username'),
}
def save(self, *args, **kwargs):
@@ -154,7 +155,8 @@ class SecretCSVForm(CustomFieldModelCSVForm):
return s
-class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
+class SecretBulkEditForm(
+ BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=Secret.objects.all(),
widget=forms.MultipleHiddenInput()
@@ -178,7 +180,7 @@ class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Secret
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
role = DynamicModelMultipleChoiceField(
queryset=SecretRole.objects.all(),
@@ -220,7 +222,7 @@ class UserKeyForm(BootstrapMixin, forms.ModelForm):
class ActivateUserKeyForm(forms.Form):
_selected_action = forms.ModelMultipleChoiceField(
queryset=UserKey.objects.all(),
- label='User Keys'
+ label=_('User Keys')
)
secret_key = forms.CharField(
widget=forms.Textarea(
@@ -228,5 +230,5 @@ class ActivateUserKeyForm(forms.Form):
'class': 'vLargeTextField',
}
),
- label='Your private key'
+ label=_('Your private key')
)
diff --git a/netbox/secrets/models.py b/netbox/secrets/models.py
index 830e91096..1af8685ab 100644
--- a/netbox/secrets/models.py
+++ b/netbox/secrets/models.py
@@ -12,6 +12,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.encoding import force_bytes
+from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.models import Device
@@ -51,7 +52,7 @@ class UserKey(models.Model):
editable=False
)
public_key = models.TextField(
- verbose_name='RSA public key'
+ verbose_name=_('RSA public key')
)
master_key_cipher = models.BinaryField(
max_length=512,
diff --git a/netbox/secrets/tables.py b/netbox/secrets/tables.py
index f92c9216b..cad427111 100644
--- a/netbox/secrets/tables.py
+++ b/netbox/secrets/tables.py
@@ -1,4 +1,5 @@
import django_tables2 as tables
+from django.utils.translation import gettext as _
from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import SecretRole, Secret
@@ -21,7 +22,7 @@ class SecretRoleTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
secret_count = tables.Column(
- verbose_name='Secrets'
+ verbose_name=_('Secrets')
)
actions = tables.TemplateColumn(
template_code=SECRETROLE_ACTIONS,
diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py
index ed59f4392..168da9fc5 100644
--- a/netbox/secrets/views.py
+++ b/netbox/secrets/views.py
@@ -6,6 +6,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
+from django.utils.translation import gettext as _
from django.views.generic import View
from utilities.views import (
@@ -117,7 +118,7 @@ def secret_add(request):
secret.save()
form.save_m2m()
- messages.success(request, "Added new secret: {}.".format(secret))
+ messages.success(request, _("Added new secret: {}.").format(secret))
if '_addanother' in request.POST:
return redirect('secrets:secret_add')
else:
@@ -164,7 +165,7 @@ def secret_edit(request, pk):
secret.plaintext = form.cleaned_data['plaintext']
secret.encrypt(master_key)
secret.save()
- messages.success(request, "Modified secret {}.".format(secret))
+ messages.success(request, _("Modified secret {}.").format(secret))
return redirect('secrets:secret', pk=secret.pk)
else:
form.add_error(None, "Invalid session key. Unable to encrypt secret data.")
diff --git a/netbox/tenancy/filters.py b/netbox/tenancy/filters.py
index af5ee0b2c..47f33a5eb 100644
--- a/netbox/tenancy/filters.py
+++ b/netbox/tenancy/filters.py
@@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
+from django.utils.translation import gettext as _
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
@@ -16,13 +17,13 @@ __all__ = (
class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
- label='Tenant group (ID)',
+ label=_('Tenant group (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
- label='Tenant group group (slug)',
+ label=_('Tenant group group (slug)'),
)
class Meta:
@@ -33,20 +34,20 @@ class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class TenantFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
- label='Tenant group (ID)',
+ label=_('Tenant group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
- label='Tenant group (slug)',
+ label=_('Tenant group (slug)'),
)
tag = TagFilter()
@@ -73,22 +74,22 @@ class TenancyFilterSet(django_filters.FilterSet):
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
lookup_expr='in',
- label='Tenant Group (ID)',
+ label=_('Tenant Group (ID)'),
)
tenant_group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
to_field_name='slug',
lookup_expr='in',
- label='Tenant Group (slug)',
+ label=_('Tenant Group (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
- label='Tenant (ID)',
+ label=_('Tenant (ID)'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
field_name='tenant__slug',
to_field_name='slug',
- label='Tenant (slug)',
+ label=_('Tenant (slug)'),
)
diff --git a/netbox/tenancy/forms.py b/netbox/tenancy/forms.py
index bf100f43a..cb17aade7 100644
--- a/netbox/tenancy/forms.py
+++ b/netbox/tenancy/forms.py
@@ -1,4 +1,5 @@
from django import forms
+from django.utils.translation import gettext as _
from extras.forms import (
AddRemoveTagsForm, CustomFieldModelForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelCSVForm,
@@ -37,7 +38,7 @@ class TenantGroupCSVForm(CSVModelForm):
queryset=TenantGroup.objects.all(),
required=False,
to_field_name='name',
- help_text='Parent group'
+ help_text=_('Parent group')
)
slug = SlugField()
@@ -74,7 +75,7 @@ class TenantCSVForm(CustomFieldModelCSVForm):
queryset=TenantGroup.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned group'
+ help_text=_('Assigned group')
)
class Meta:
@@ -102,7 +103,7 @@ class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Tenant
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
group = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(),
diff --git a/netbox/tenancy/tables.py b/netbox/tenancy/tables.py
index 147a20707..1b122a979 100644
--- a/netbox/tenancy/tables.py
+++ b/netbox/tenancy/tables.py
@@ -1,4 +1,5 @@
import django_tables2 as tables
+from django.utils.translation import gettext as _
from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import Tenant, TenantGroup
@@ -42,7 +43,7 @@ class TenantGroupTable(BaseTable):
orderable=False
)
tenant_count = tables.Column(
- verbose_name='Tenants'
+ verbose_name=_('Tenants')
)
actions = tables.TemplateColumn(
template_code=TENANTGROUP_ACTIONS,
diff --git a/netbox/users/admin.py b/netbox/users/admin.py
index 42e651712..ae03771c7 100644
--- a/netbox/users/admin.py
+++ b/netbox/users/admin.py
@@ -2,6 +2,7 @@ from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as UserAdmin_
from django.contrib.auth.models import User
+from django.utils.translation import gettext as _
from .models import Token, UserConfig
@@ -13,7 +14,7 @@ class UserConfigInline(admin.TabularInline):
model = UserConfig
readonly_fields = ('data',)
can_delete = False
- verbose_name = 'Preferences'
+ verbose_name = _('Preferences')
@admin.register(User)
@@ -27,7 +28,7 @@ class UserAdmin(UserAdmin_):
class TokenAdminForm(forms.ModelForm):
key = forms.CharField(
required=False,
- help_text="If no key is provided, one will be generated automatically."
+ help_text=_('If no key is provided, one will be generated automatically.')
)
class Meta:
diff --git a/netbox/users/forms.py b/netbox/users/forms.py
index 495332d2c..4c7445b7e 100644
--- a/netbox/users/forms.py
+++ b/netbox/users/forms.py
@@ -1,5 +1,6 @@
from django import forms
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
+from django.utils.translation import gettext as _
from utilities.forms import BootstrapMixin, DateTimePicker
from .models import Token
@@ -21,7 +22,7 @@ class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
class TokenForm(BootstrapMixin, forms.ModelForm):
key = forms.CharField(
required=False,
- help_text="If no key is provided, one will be generated automatically."
+ help_text=_('If no key is provided, one will be generated automatically.')
)
class Meta:
diff --git a/netbox/users/models.py b/netbox/users/models.py
index ea5762232..45b086c27 100644
--- a/netbox/users/models.py
+++ b/netbox/users/models.py
@@ -8,6 +8,7 @@ from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone
+from django.utils.translation import gettext as _
from utilities.utils import flatten_dict
@@ -162,7 +163,7 @@ class Token(models.Model):
)
write_enabled = models.BooleanField(
default=True,
- help_text='Permit create/update/delete operations using this key'
+ help_text=_('Permit create/update/delete operations using this key')
)
description = models.CharField(
max_length=200,
diff --git a/netbox/users/views.py b/netbox/users/views.py
index 9053d7b70..25a980311 100644
--- a/netbox/users/views.py
+++ b/netbox/users/views.py
@@ -11,6 +11,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.http import is_safe_url
+from django.utils.translation import gettext as _
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import View
@@ -64,7 +65,7 @@ class LoginView(View):
# Authenticate user
auth_login(request, form.get_user())
logger.info(f"User {request.user} successfully authenticated")
- messages.info(request, "Logged in as {}.".format(request.user))
+ messages.info(request, _("Logged in as {}.").format(request.user))
logger.debug(f"Redirecting user to {redirect_to}")
return HttpResponseRedirect(redirect_to)
@@ -88,7 +89,7 @@ class LogoutView(View):
username = request.user
auth_logout(request)
logger.info(f"User {username} has logged out")
- messages.info(request, "You have logged out.")
+ messages.info(request, _('You have logged out.'))
# Delete session key cookie (if set) upon logout
response = HttpResponseRedirect(reverse('home'))
@@ -130,7 +131,7 @@ class UserConfigView(LoginRequiredMixin, View):
if key in data:
userconfig.clear(key)
userconfig.save()
- messages.success(request, "Your preferences have been updated.")
+ messages.success(request, _('Your preferences have been updated.'))
return redirect('user:preferences')
@@ -156,7 +157,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
- messages.success(request, "Your password has been changed successfully.")
+ messages.success(request, _('Your password has been changed successfully.'))
return redirect('user:profile')
return render(request, self.template_name, {
@@ -206,7 +207,7 @@ class UserKeyEditView(LoginRequiredMixin, View):
uk = form.save(commit=False)
uk.user = request.user
uk.save()
- messages.success(request, "Your user key has been saved.")
+ messages.success(request, _('Your user key has been saved.'))
return redirect('user:userkey')
return render(request, self.template_name, {
@@ -237,7 +238,7 @@ class SessionKeyDeleteView(LoginRequiredMixin, View):
# Delete session key
sessionkey.delete()
- messages.success(request, "Session key deleted")
+ messages.success(request, _('Session key deleted'))
# Delete cookie
response = redirect('user:userkey')
@@ -304,7 +305,7 @@ class TokenEditView(LoginRequiredMixin, View):
token.user = request.user
token.save()
- msg = "Modified token {}".format(token) if pk else "Created token {}".format(token)
+ msg = _("Modified token {}").format(token) if pk else _("Created token {}").format(token)
messages.success(request, msg)
if '_addanother' in request.POST:
@@ -344,7 +345,7 @@ class TokenDeleteView(PermissionRequiredMixin, View):
form = ConfirmationForm(request.POST)
if form.is_valid():
token.delete()
- messages.success(request, "Token deleted")
+ messages.success(request, _('Token deleted'))
return redirect('user:token_list')
return render(request, 'utilities/obj_delete.html', {
diff --git a/netbox/utilities/filters.py b/netbox/utilities/filters.py
index f628ca917..0f9f3064e 100644
--- a/netbox/utilities/filters.py
+++ b/netbox/utilities/filters.py
@@ -5,6 +5,7 @@ from django import forms
from django.conf import settings
from django.db import models
from django_filters.utils import get_model_field, resolve_field
+from django.utils.translation import gettext as _
from extras.models import Tag
from utilities.constants import (
@@ -274,7 +275,7 @@ class NameSlugSearchFilterSet(django_filters.FilterSet):
"""
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
def search(self, queryset, name, value):
diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py
index 539347aaa..c60cb3816 100644
--- a/netbox/utilities/forms.py
+++ b/netbox/utilities/forms.py
@@ -14,6 +14,7 @@ from django.db.models import Count
from django.forms import BoundField
from django.forms.models import fields_for_model
from django.urls import reverse
+from django.utils.translation import gettext as _
from .choices import ColorChoices, unpack_grouped_choices
from .validators import EnhancedURLValidator
@@ -587,7 +588,7 @@ class TagFilterField(forms.MultipleChoiceField):
return [(str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags]
# Choices are fetched each time the form is initialized
- super().__init__(label='Tags', choices=get_choices, required=False, *args, **kwargs)
+ super().__init__(label=_('Tags'), choices=get_choices, required=False, *args, **kwargs)
class DynamicModelChoiceMixin:
@@ -740,7 +741,7 @@ class ImportForm(BootstrapMixin, forms.Form):
"""
data = forms.CharField(
widget=forms.Textarea,
- help_text="Enter object data in JSON or YAML format. Note: Only a single object/document is supported."
+ help_text=_('Enter object data in JSON or YAML format. Note: Only a single object/document is supported.')
)
format = forms.ChoiceField(
choices=(
@@ -791,7 +792,7 @@ class TableConfigForm(BootstrapMixin, forms.Form):
widget=forms.SelectMultiple(
attrs={'size': 10}
),
- help_text="Use the buttons below to arrange columns in the desired order, then select all columns to display."
+ help_text=_('Use the buttons below to arrange columns in the desired order, then select all columns to display.')
)
def __init__(self, table, *args, **kwargs):
diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py
index ef073309f..de3d81bf1 100644
--- a/netbox/utilities/views.py
+++ b/netbox/utilities/views.py
@@ -18,6 +18,7 @@ from django.utils.decorators import method_decorator
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext as _
from django.views.decorators.csrf import requires_csrf_token
from django.views.defaults import ERROR_500_TEMPLATE_NAME
from django.views.generic import View
@@ -206,7 +207,7 @@ class ObjectListView(View):
request.user.config.set(preference_name, form.cleaned_data['columns'], commit=True)
elif 'clear' in request.POST:
request.user.config.clear(preference_name, commit=True)
- messages.success(request, "Your preferences have been updated.")
+ messages.success(request, _('Your preferences have been updated.'))
return redirect(request.get_full_path())
@@ -532,7 +533,7 @@ class ObjectImportView(GetReturnURLMixin, View):
if not model_form.errors:
logger.info(f"Import object {obj} (PK: {obj.pk})")
- messages.success(request, mark_safe('Imported object: {}'.format(
+ messages.success(request, mark_safe(_('Imported object: {}').format(
obj.get_absolute_url(), obj
)))
@@ -946,7 +947,7 @@ class ComponentCreateView(GetReturnURLMixin, View):
for component_form in new_components:
component_form.save()
- messages.success(request, "Added {} {}".format(
+ messages.success(request, _('Added {} {}').format(
len(new_components), self.model._meta.verbose_name_plural
))
if '_addanother' in request.POST:
diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py
index 5c9b64aa3..e3a239d4b 100644
--- a/netbox/virtualization/filters.py
+++ b/netbox/virtualization/filters.py
@@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
+from django.utils.translation import gettext as _
from dcim.models import DeviceRole, Interface, Platform, Region, Site
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
@@ -37,50 +38,50 @@ class ClusterGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=ClusterGroup.objects.all(),
- label='Parent group (ID)',
+ label=_('Parent group (ID)'),
)
group = django_filters.ModelMultipleChoiceFilter(
field_name='group__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
- label='Parent group (slug)',
+ label=_('Parent group (slug)'),
)
type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ClusterType.objects.all(),
- label='Cluster type (ID)',
+ label=_('Cluster type (ID)'),
)
type = django_filters.ModelMultipleChoiceFilter(
field_name='type__slug',
queryset=ClusterType.objects.all(),
to_field_name='slug',
- label='Cluster type (slug)',
+ label=_('Cluster type (slug)'),
)
tag = TagFilter()
@@ -106,7 +107,7 @@ class VirtualMachineFilterSet(
):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
status = django_filters.MultipleChoiceFilter(
choices=VirtualMachineStatusChoices,
@@ -115,76 +116,76 @@ class VirtualMachineFilterSet(
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__group',
queryset=ClusterGroup.objects.all(),
- label='Cluster group (ID)',
+ label=_('Cluster group (ID)'),
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__group__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
- label='Cluster group (slug)',
+ label=_('Cluster group (slug)'),
)
cluster_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__type',
queryset=ClusterType.objects.all(),
- label='Cluster type (ID)',
+ label=_('Cluster type (ID)'),
)
cluster_type = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__type__slug',
queryset=ClusterType.objects.all(),
to_field_name='slug',
- label='Cluster type (slug)',
+ label=_('Cluster type (slug)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cluster.objects.all(),
- label='Cluster (ID)',
+ label=_('Cluster (ID)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='cluster__site__region',
lookup_expr='in',
- label='Region (ID)',
+ label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='cluster__site__region',
lookup_expr='in',
to_field_name='slug',
- label='Region (slug)',
+ label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__site',
queryset=Site.objects.all(),
- label='Site (ID)',
+ label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
- label='Site (slug)',
+ label=_('Site (slug)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceRole.objects.all(),
- label='Role (ID)',
+ label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
- label='Role (slug)',
+ label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
- label='Platform (ID)',
+ label=_('Platform (ID)'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platform__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
- label='Platform (slug)',
+ label=_('Platform (slug)'),
)
mac_address = MultiValueMACAddressFilter(
field_name='interfaces__mac_address',
- label='MAC address',
+ label=_('MAC address'),
)
tag = TagFilter()
@@ -204,21 +205,21 @@ class VirtualMachineFilterSet(
class InterfaceFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
- label='Search',
+ label=_('Search'),
)
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine',
queryset=VirtualMachine.objects.all(),
- label='Virtual machine (ID)',
+ label=_('Virtual machine (ID)'),
)
virtual_machine = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine__name',
queryset=VirtualMachine.objects.all(),
to_field_name='name',
- label='Virtual machine',
+ label=_('Virtual machine'),
)
mac_address = MultiValueMACAddressFilter(
- label='MAC address',
+ label=_('MAC address'),
)
tag = TagFilter()
diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py
index 2f2ee4950..de7769784 100644
--- a/netbox/virtualization/forms.py
+++ b/netbox/virtualization/forms.py
@@ -1,5 +1,6 @@
from django import forms
from django.core.exceptions import ValidationError
+from django.utils.translation import gettext as _
from dcim.choices import InterfaceModeChoices
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
@@ -98,25 +99,25 @@ class ClusterCSVForm(CustomFieldModelCSVForm):
type = CSVModelChoiceField(
queryset=ClusterType.objects.all(),
to_field_name='name',
- help_text='Type of cluster'
+ help_text=_('Type of cluster')
)
group = CSVModelChoiceField(
queryset=ClusterGroup.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned cluster group'
+ help_text=_('Assigned cluster group')
)
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned site'
+ help_text=_('Assigned site')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
to_field_name='name',
required=False,
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
class Meta:
@@ -147,7 +148,7 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
)
comments = CommentField(
widget=SmallTextarea,
- label='Comments'
+ label=_('Comments')
)
class Meta:
@@ -387,12 +388,12 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
status = CSVChoiceField(
choices=VirtualMachineStatusChoices,
required=False,
- help_text='Operational status of device'
+ help_text=_('Operational status of device')
)
cluster = CSVModelChoiceField(
queryset=Cluster.objects.all(),
to_field_name='name',
- help_text='Assigned cluster'
+ help_text=_('Assigned cluster')
)
role = CSVModelChoiceField(
queryset=DeviceRole.objects.filter(
@@ -400,19 +401,19 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
),
required=False,
to_field_name='name',
- help_text='Functional role'
+ help_text=_('Functional role')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned tenant'
+ help_text=_('Assigned tenant')
)
platform = CSVModelChoiceField(
queryset=Platform.objects.all(),
required=False,
to_field_name='name',
- help_text='Assigned platform'
+ help_text=_('Assigned platform')
)
class Meta:
@@ -456,19 +457,19 @@ class VirtualMachineBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldB
)
vcpus = forms.IntegerField(
required=False,
- label='vCPUs'
+ label=_('vCPUs')
)
memory = forms.IntegerField(
required=False,
- label='Memory (MB)'
+ label=_('Memory (MB)')
)
disk = forms.IntegerField(
required=False,
- label='Disk (GB)'
+ label=_('Disk (GB)')
)
comments = CommentField(
widget=SmallTextarea,
- label='Comments'
+ label=_('Comments')
)
class Meta:
@@ -485,7 +486,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
]
q = forms.CharField(
required=False,
- label='Search'
+ label=_('Search')
)
cluster_group = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(),
@@ -508,7 +509,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
- label='Cluster'
+ label=_('Cluster')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@@ -558,7 +559,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
)
mac_address = forms.CharField(
required=False,
- label='MAC address'
+ label=_('MAC address')
)
tag = TagFilterField(model)
@@ -606,7 +607,7 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm):
'mode': StaticSelect2()
}
labels = {
- 'mode': '802.1Q Mode',
+ 'mode': _('802.1Q Mode'),
}
help_texts = {
'mode': INTERFACE_MODE_HELP_TEXT,
@@ -645,7 +646,7 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
widget=forms.HiddenInput()
)
name_pattern = ExpandableNameField(
- label='Name'
+ label=_('Name')
)
type = forms.ChoiceField(
choices=VMInterfaceTypeChoices,
@@ -660,11 +661,11 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
required=False,
min_value=INTERFACE_MTU_MIN,
max_value=INTERFACE_MTU_MAX,
- label='MTU'
+ label=_('MTU')
)
mac_address = forms.CharField(
required=False,
- label='MAC Address'
+ label=_('MAC Address')
)
description = forms.CharField(
max_length=100,
@@ -732,7 +733,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
required=False,
min_value=INTERFACE_MTU_MIN,
max_value=INTERFACE_MTU_MAX,
- label='MTU'
+ label=_('MTU')
)
description = forms.CharField(
max_length=100,
@@ -795,7 +796,7 @@ class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
widget=forms.MultipleHiddenInput()
)
name_pattern = ExpandableNameField(
- label='Name'
+ label=_('Name')
)
def clean_tags(self):
diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py
index 3daeff013..d79fbc3a0 100644
--- a/netbox/virtualization/models.py
+++ b/netbox/virtualization/models.py
@@ -3,6 +3,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 as _
from taggit.managers import TaggableManager
from dcim.models import Device
@@ -220,7 +221,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
max_length=50,
choices=VirtualMachineStatusChoices,
default=VirtualMachineStatusChoices.STATUS_ACTIVE,
- verbose_name='Status'
+ verbose_name=_('Status')
)
role = models.ForeignKey(
to='dcim.DeviceRole',
@@ -236,7 +237,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='+',
blank=True,
null=True,
- verbose_name='Primary IPv4'
+ verbose_name=_('Primary IPv4')
)
primary_ip6 = models.OneToOneField(
to='ipam.IPAddress',
@@ -244,22 +245,22 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='+',
blank=True,
null=True,
- verbose_name='Primary IPv6'
+ verbose_name=_('Primary IPv6')
)
vcpus = models.PositiveSmallIntegerField(
blank=True,
null=True,
- verbose_name='vCPUs'
+ verbose_name=_('vCPUs')
)
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)')
)
comments = models.TextField(
blank=True
diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py
index 6972637d0..d19274c2e 100644
--- a/netbox/virtualization/tables.py
+++ b/netbox/virtualization/tables.py
@@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
+from django.utils.translation import gettext as _
from dcim.models import Interface
from tenancy.tables import COL_TENANT
@@ -51,7 +52,7 @@ class ClusterTypeTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
cluster_count = tables.Column(
- verbose_name='Clusters'
+ verbose_name=_('Clusters')
)
actions = tables.TemplateColumn(
template_code=CLUSTERTYPE_ACTIONS,
@@ -73,7 +74,7 @@ class ClusterGroupTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
cluster_count = tables.Column(
- verbose_name='Clusters'
+ verbose_name=_('Clusters')
)
actions = tables.TemplateColumn(
template_code=CLUSTERGROUP_ACTIONS,
@@ -104,11 +105,11 @@ class ClusterTable(BaseTable):
)
device_count = tables.TemplateColumn(
template_code=CLUSTER_DEVICE_COUNT,
- verbose_name='Devices'
+ verbose_name=_('Devices')
)
vm_count = tables.TemplateColumn(
template_code=CLUSTER_VM_COUNT,
- verbose_name='VMs'
+ verbose_name=_('VMs')
)
tags = TagColumn(
url_name='virtualization:cluster_list'
@@ -148,16 +149,16 @@ class VirtualMachineDetailTable(VirtualMachineTable):
primary_ip4 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip4.pk')],
- verbose_name='IPv4 Address'
+ verbose_name=_('IPv4 Address')
)
primary_ip6 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip6.pk')],
- verbose_name='IPv6 Address'
+ verbose_name=_('IPv6 Address')
)
primary_ip = tables.TemplateColumn(
orderable=False,
- verbose_name='IP Address',
+ verbose_name=_('IP Address'),
template_code=VIRTUALMACHINE_PRIMARY_IP
)
tags = TagColumn(
diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py
index 4da7ee313..fc383578e 100644
--- a/netbox/virtualization/views.py
+++ b/netbox/virtualization/views.py
@@ -4,6 +4,7 @@ from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
+from django.utils.translation import gettext as _
from django.views.generic import View
from dcim.models import Device, Interface
@@ -195,7 +196,7 @@ class ClusterAddDevicesView(PermissionRequiredMixin, View):
device.cluster = cluster
device.save()
- messages.success(request, "Added {} devices to cluster {}".format(
+ messages.success(request, _('Added {} devices to cluster {}').format(
len(device_pks), cluster
))
return redirect(cluster.get_absolute_url())
@@ -228,7 +229,7 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View):
device.cluster = None
device.save()
- messages.success(request, "Removed {} devices from cluster {}".format(
+ messages.success(request, _('Removed {} devices from cluster {}').format(
len(device_pks), cluster
))
return redirect(cluster.get_absolute_url())