Skip to content

Commit

Permalink
15094 Add missing gettext to error strings for internationalization (#…
Browse files Browse the repository at this point in the history
…15155)

* 15049 add missing gettext to error strings

* 15049 add missing gettext to error strings

* 15094 review change

* 15094 review change

* Formatting cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
  • Loading branch information
arthanson and jeremystretch authored Feb 20, 2024
1 parent 29f029d commit af27bf5
Show file tree
Hide file tree
Showing 47 changed files with 275 additions and 133 deletions.
4 changes: 2 additions & 2 deletions netbox/circuits/models/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,9 @@ def clean(self):

# Must define either site *or* provider network
if self.site is None and self.provider_network is None:
raise ValidationError("A circuit termination must attach to either a site or a provider network.")
raise ValidationError(_("A circuit termination must attach to either a site or a provider network."))
if self.site and self.provider_network:
raise ValidationError("A circuit termination cannot attach to both a site and a provider network.")
raise ValidationError(_("A circuit termination cannot attach to both a site and a provider network."))

def to_objectchange(self, action):
objectchange = super().to_objectchange(action)
Expand Down
2 changes: 1 addition & 1 deletion netbox/core/data_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def fetch(self):
try:
porcelain.clone(self.url, local_path.name, **clone_args)
except BaseException as e:
raise SyncError(f"Fetching remote data failed ({type(e).__name__}): {e}")
raise SyncError(_("Fetching remote data failed ({name}): {error}").format(name=type(e).__name__, error=e))

yield local_path.name

Expand Down
4 changes: 2 additions & 2 deletions netbox/core/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ def clean(self):
super().clean()

if self.cleaned_data.get('upload_file') and self.cleaned_data.get('data_file'):
raise forms.ValidationError("Cannot upload a file and sync from an existing file")
raise forms.ValidationError(_("Cannot upload a file and sync from an existing file"))
if not self.cleaned_data.get('upload_file') and not self.cleaned_data.get('data_file'):
raise forms.ValidationError("Must upload a file or select a data file to sync")
raise forms.ValidationError(_("Must upload a file or select a data file to sync"))

return self.cleaned_data

Expand Down
4 changes: 2 additions & 2 deletions netbox/core/models/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def sync(self):
Create/update/delete child DataFiles as necessary to synchronize with the remote source.
"""
if self.status == DataSourceStatusChoices.SYNCING:
raise SyncError("Cannot initiate sync; syncing already in progress.")
raise SyncError(_("Cannot initiate sync; syncing already in progress."))

# Emit the pre_sync signal
pre_sync.send(sender=self.__class__, instance=self)
Expand All @@ -190,7 +190,7 @@ def sync(self):
backend = self.get_backend()
except ModuleNotFoundError as e:
raise SyncError(
f"There was an error initializing the backend. A dependency needs to be installed: {e}"
_("There was an error initializing the backend. A dependency needs to be installed: ") + str(e)
)
with backend.fetch() as local_path:

Expand Down
6 changes: 5 additions & 1 deletion netbox/core/models/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,11 @@ def terminate(self, status=JobStatusChoices.STATUS_COMPLETED, error=None):
"""
valid_statuses = JobStatusChoices.TERMINAL_STATE_CHOICES
if status not in valid_statuses:
raise ValueError(f"Invalid status for job termination. Choices are: {', '.join(valid_statuses)}")
raise ValueError(
_("Invalid status for job termination. Choices are: {choices}").format(
choices=', '.join(valid_statuses)
)
)

# Mark the job as completed
self.status = status
Expand Down
5 changes: 3 additions & 2 deletions netbox/dcim/fields.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext as _
from netaddr import AddrFormatError, EUI, eui64_unix_expanded, mac_unix_expanded

from .lookups import PathContains
Expand Down Expand Up @@ -41,7 +42,7 @@ def to_python(self, value):
try:
return EUI(value, version=48, dialect=mac_unix_expanded_uppercase)
except AddrFormatError:
raise ValidationError(f"Invalid MAC address format: {value}")
raise ValidationError(_("Invalid MAC address format: {value}").format(value=value))

def db_type(self, connection):
return 'macaddr'
Expand All @@ -67,7 +68,7 @@ def to_python(self, value):
try:
return EUI(value, version=64, dialect=eui64_unix_expanded_uppercase)
except AddrFormatError:
raise ValidationError(f"Invalid WWN format: {value}")
raise ValidationError(_("Invalid WWN format: {value}").format(value=value))

def db_type(self, connection):
return 'macaddr8'
Expand Down
25 changes: 20 additions & 5 deletions netbox/dcim/forms/bulk_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,11 @@ def clean_enabled(self):
def clean_vdcs(self):
for vdc in self.cleaned_data['vdcs']:
if vdc.device != self.cleaned_data['device']:
raise forms.ValidationError(f"VDC {vdc} is not assigned to device {self.cleaned_data['device']}")
raise forms.ValidationError(
_("VDC {vdc} is not assigned to device {device}").format(
vdc=vdc, device=self.cleaned_data['device']
)
)
return self.cleaned_data['vdcs']


Expand Down Expand Up @@ -1075,7 +1079,11 @@ def clean_component_name(self):
component = model.objects.get(device=device, name=component_name)
self.instance.component = component
except ObjectDoesNotExist:
raise forms.ValidationError(f"Component not found: {device} - {component_name}")
raise forms.ValidationError(
_("Component not found: {device} - {component_name}").format(
device=device, component_name=component_name
)
)


#
Expand Down Expand Up @@ -1193,10 +1201,17 @@ def _clean_side(self, side):
else:
termination_object = model.objects.get(device=device, name=name)
if termination_object.cable is not None and termination_object.cable != self.instance:
raise forms.ValidationError(f"Side {side.upper()}: {device} {termination_object} is already connected")
raise forms.ValidationError(
_("Side {side_upper}: {device} {termination_object} is already connected").format(
side_upper=side.upper(), device=device, termination_object=termination_object
)
)
except ObjectDoesNotExist:
raise forms.ValidationError(f"{side.upper()} side termination not found: {device} {name}")

raise forms.ValidationError(
_("{side_upper} side termination not found: {device} {name}").format(
side_upper=side.upper(), device=device, name=name
)
)
setattr(self.instance, f'{side}_terminations', [termination_object])
return termination_object

Expand Down
28 changes: 18 additions & 10 deletions netbox/dcim/models/cables.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,25 +160,26 @@ def clean(self):

# Validate length and length_unit
if self.length is not None and not self.length_unit:
raise ValidationError("Must specify a unit when setting a cable length")
raise ValidationError(_("Must specify a unit when setting a cable length"))

if self.pk is None and (not self.a_terminations or not self.b_terminations):
raise ValidationError("Must define A and B terminations when creating a new cable.")
raise ValidationError(_("Must define A and B terminations when creating a new cable."))

if self._terminations_modified:

# Check that all termination objects for either end are of the same type
for terms in (self.a_terminations, self.b_terminations):
if len(terms) > 1 and not all(isinstance(t, type(terms[0])) for t in terms[1:]):
raise ValidationError("Cannot connect different termination types to same end of cable.")
raise ValidationError(_("Cannot connect different termination types to same end of cable."))

# Check that termination types are compatible
if self.a_terminations and self.b_terminations:
a_type = self.a_terminations[0]._meta.model_name
b_type = self.b_terminations[0]._meta.model_name
if b_type not in COMPATIBLE_TERMINATION_TYPES.get(a_type):
raise ValidationError(f"Incompatible termination types: {a_type} and {b_type}")

raise ValidationError(
_("Incompatible termination types: {type_a} and {type_b}").format(type_a=a_type, type_b=b_type)
)
if a_type == b_type:
# can't directly use self.a_terminations here as possible they
# don't have pk yet
Expand Down Expand Up @@ -323,17 +324,24 @@ def clean(self):
).first()
if existing_termination is not None:
raise ValidationError(
f"Duplicate termination found for {self.termination_type.app_label}.{self.termination_type.model} "
f"{self.termination_id}: cable {existing_termination.cable.pk}"
_("Duplicate termination found for {app_label}.{model} {termination_id}: cable {cable_pk}".format(
app_label=self.termination_type.app_label,
model=self.termination_type.model,
termination_id=self.termination_id,
cable_pk=existing_termination.cable.pk
))
)

# Validate interface type (if applicable)
if self.termination_type.model == 'interface' and self.termination.type in NONCONNECTABLE_IFACE_TYPES:
raise ValidationError(f"Cables cannot be terminated to {self.termination.get_type_display()} interfaces")
raise ValidationError(
_("Cables cannot be terminated to {type_display} interfaces").format(
type_display=self.termination.get_type_display()
)
)

# A CircuitTermination attached to a ProviderNetwork cannot have a Cable
if self.termination_type.model == 'circuittermination' and self.termination.provider_network is not None:
raise ValidationError("Circuit terminations attached to a provider network may not be cabled.")
raise ValidationError(_("Circuit terminations attached to a provider network may not be cabled."))

def save(self, *args, **kwargs):

Expand Down
3 changes: 2 additions & 1 deletion netbox/dcim/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib.auth import get_user_model
from django.test import override_settings
from django.urls import reverse
from django.utils.translation import gettext as _
from rest_framework import status

from dcim.choices import *
Expand Down Expand Up @@ -45,7 +46,7 @@ def test_trace(self):
name='Peer Device'
)
if self.peer_termination_type is None:
raise NotImplementedError("Test case must set peer_termination_type")
raise NotImplementedError(_("Test case must set peer_termination_type"))
peer_obj = self.peer_termination_type.objects.create(
device=peer_device,
name='Peer Termination'
Expand Down
3 changes: 2 additions & 1 deletion netbox/extras/api/customfields.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext as _
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes
from rest_framework.fields import Field
Expand Down Expand Up @@ -88,7 +89,7 @@ def to_internal_value(self, data):
if serializer.is_valid():
data[cf.name] = [obj['id'] for obj in serializer.data] if many else serializer.data['id']
else:
raise ValidationError(f"Unknown related object(s): {data[cf.name]}")
raise ValidationError(_("Unknown related object(s): {name}").format(name=data[cf.name]))

# If updating an existing instance, start with existing custom_field_data
if self.parent.instance:
Expand Down
11 changes: 6 additions & 5 deletions netbox/extras/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
Expand Down Expand Up @@ -150,7 +151,7 @@ class Meta:

def validate_type(self, value):
if self.instance and self.instance.type != value:
raise serializers.ValidationError('Changing the type of custom fields is not supported.')
raise serializers.ValidationError(_('Changing the type of custom fields is not supported.'))

return value

Expand Down Expand Up @@ -545,12 +546,12 @@ class ReportInputSerializer(serializers.Serializer):

def validate_schedule_at(self, value):
if value and not self.context['report'].scheduling_enabled:
raise serializers.ValidationError("Scheduling is not enabled for this report.")
raise serializers.ValidationError(_("Scheduling is not enabled for this report."))
return value

def validate_interval(self, value):
if value and not self.context['report'].scheduling_enabled:
raise serializers.ValidationError("Scheduling is not enabled for this report.")
raise serializers.ValidationError(_("Scheduling is not enabled for this report."))
return value


Expand Down Expand Up @@ -595,12 +596,12 @@ class ScriptInputSerializer(serializers.Serializer):

def validate_schedule_at(self, value):
if value and not self.context['script'].scheduling_enabled:
raise serializers.ValidationError("Scheduling is not enabled for this script.")
raise serializers.ValidationError(_("Scheduling is not enabled for this script."))
return value

def validate_interval(self, value):
if value and not self.context['script'].scheduling_enabled:
raise serializers.ValidationError("Scheduling is not enabled for this script.")
raise serializers.ValidationError(_("Scheduling is not enabled for this script."))
return value


Expand Down
18 changes: 12 additions & 6 deletions netbox/extras/conditions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import functools
import re
from django.utils.translation import gettext as _

__all__ = (
'Condition',
Expand Down Expand Up @@ -50,11 +51,13 @@ class Condition:

def __init__(self, attr, value, op=EQ, negate=False):
if op not in self.OPERATORS:
raise ValueError(f"Unknown operator: {op}. Must be one of: {', '.join(self.OPERATORS)}")
raise ValueError(_("Unknown operator: {op}. Must be one of: {operators}").format(
op=op, operators=', '.join(self.OPERATORS)
))
if type(value) not in self.TYPES:
raise ValueError(f"Unsupported value type: {type(value)}")
raise ValueError(_("Unsupported value type: {value}").format(value=type(value)))
if op not in self.TYPES[type(value)]:
raise ValueError(f"Invalid type for {op} operation: {type(value)}")
raise ValueError(_("Invalid type for {op} operation: {value}").format(op=op, value=type(value)))

self.attr = attr
self.value = value
Expand Down Expand Up @@ -131,14 +134,17 @@ class ConditionSet:
"""
def __init__(self, ruleset):
if type(ruleset) is not dict:
raise ValueError(f"Ruleset must be a dictionary, not {type(ruleset)}.")
raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
if len(ruleset) != 1:
raise ValueError(f"Ruleset must have exactly one logical operator (found {len(ruleset)})")
raise ValueError(_("Ruleset must have exactly one logical operator (found {ruleset})").format(
ruleset=len(ruleset)))

# Determine the logic type
logic = list(ruleset.keys())[0]
if type(logic) is not str or logic.lower() not in (AND, OR):
raise ValueError(f"Invalid logic type: {logic} (must be '{AND}' or '{OR}')")
raise ValueError(_("Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')").format(
logic=logic, op_and=AND, op_or=OR
))
self.logic = logic.lower()

# Compile the set of Conditions
Expand Down
3 changes: 2 additions & 1 deletion netbox/extras/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext as _

from netbox.registry import registry
from extras.constants import DEFAULT_DASHBOARD
Expand Down Expand Up @@ -32,7 +33,7 @@ def get_widget_class(name):
try:
return registry['widgets'][name]
except KeyError:
raise ValueError(f"Unregistered widget class: {name}")
raise ValueError(_("Unregistered widget class: {name}").format(name=name))


def get_dashboard(user):
Expand Down
8 changes: 5 additions & 3 deletions netbox/extras/dashboard/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ def render(self, request):
Params:
request: The current request
"""
raise NotImplementedError(f"{self.__class__} must define a render() method.")
raise NotImplementedError(_("{class_name} must define a render() method.").format(
class_name=self.__class__
))

@property
def name(self):
Expand Down Expand Up @@ -178,7 +180,7 @@ def clean_filters(self):
try:
dict(data)
except TypeError:
raise forms.ValidationError("Invalid format. Object filters must be passed as a dictionary.")
raise forms.ValidationError(_("Invalid format. Object filters must be passed as a dictionary."))
return data

def render(self, request):
Expand Down Expand Up @@ -232,7 +234,7 @@ def clean_url_params(self):
try:
urlencode(data)
except (TypeError, ValueError):
raise forms.ValidationError("Invalid format. URL parameters must be passed as a dictionary.")
raise forms.ValidationError(_("Invalid format. URL parameters must be passed as a dictionary."))
return data

def render(self, request):
Expand Down
7 changes: 5 additions & 2 deletions netbox/extras/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
from django.utils.module_loading import import_string
from django.utils.translation import gettext as _
from django_rq import get_queue

from core.models import Job
Expand Down Expand Up @@ -129,7 +130,9 @@ def process_event_rules(event_rules, model_name, event, data, username=None, sna
)

else:
raise ValueError(f"Unknown action type for an event rule: {event_rule.action_type}")
raise ValueError(_("Unknown action type for an event rule: {action_type}").format(
action_type=event_rule.action_type
))


def process_event_queue(events):
Expand Down Expand Up @@ -175,4 +178,4 @@ def flush_events(queue):
func = import_string(name)
func(queue)
except Exception as e:
logger.error(f"Cannot import events pipeline {name} error: {e}")
logger.error(_("Cannot import events pipeline {name} error: {error}").format(name=name, error=e))
Loading

0 comments on commit af27bf5

Please sign in to comment.