Opened 9 months ago
Last modified 5 days ago
#36884 assigned Bug
Translation of admin log messages fails due to case inconsistency in field names
| Reported by: | Meiyer | Owned by: | Sahil Advani |
|---|---|---|---|
| Component: | contrib.admin | Version: | 3.0 |
| Severity: | Normal | Keywords: | |
| Cc: | jaffar Khan | Triage Stage: | Accepted |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
The Django Admin builds the log messages using construct_change_message, which looks at the fields of the change form, specifically their labels (https://github.com/django/django/blob/9814676ea75bfe5c5d5244c50ccfe1f652a2f058/django/contrib/admin/utils.py#L618). The helper function turns off the translation when building the change JSON, to apply the translation when a LogMessage is displayed. However, there is a flaw in the logic:
- the
verbose_nameparameter of the model field would typically be lower-cased (from the docs: “The convention is not to capitalize the first letter of the verbose_name. Django will automatically capitalize the first letter where it needs to.” ). - the labels generated by the forms have the first letter capitalized (from the docs: “the default label for a Field is generated from the field name by converting all underscores to spaces and upper-casing the first letter”) where for Model Forms, “Django will fall back on that model field’s verbose_name and then the field’s attribute name”.
This results in translation mismatch at render time. For example, the log message would include "fields": ["Occupation"] while the translation catalog has only the key “occupation”, coming from that field’s verbose_name. As a result, the field names are not properly translated.
I tracked the issue back to changes introduced in 3.0 and it persists in all versions up to 6.0.
Change History (18)
follow-up: 9 comment:1 by , 9 months ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:2 by , 9 months ago
| Summary: | Translation of fields in admin log messages works incorrectly → Translation of admin log messages fails due to case inconsistency in field names |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:4 by , 9 months ago
Want to work on this ticket if the current assignee is no longer interested
comment:5 by , 9 months ago
| Cc: | added |
|---|
comment:6 by , 9 months ago
I confirm the issue.
The capitalisation is probably done in db\models\fields\__init__.py with "label": capfirst(self.verbose_name),.
For example, this column content: [{'changed': {'fields': ['Status']}}] is rendered in French str as: Modification de « t2>t2:90 » — Modification de Status.
According to the app .po file, the end of the text should be état., or maybe État.
comment:7 by , 9 months ago
Here is a proposed patch of contrib/admin/utils.py:
--- C:/D/tmp/utils_old.py Tue Feb 10 11:20:50 2026
+++ C:/D/tmp/utils_new.py Tue Feb 10 11:21:01 2026
@@ -1,8 +1,10 @@
def _get_changed_field_labels_from_form(form, changed_data):
changed_field_labels = []
+ opts = form.instance._meta
for field_name in changed_data:
try:
- verbose_field_name = form.fields[field_name].label or field_name
+ # can't use form.fields[field_name].label because it is wrapped in capfirst()
+ verbose_field_name = opts.get_field(field_name).verbose_name or field_name
except KeyError:
verbose_field_name = field_name
changed_field_labels.append(str(verbose_field_name))
Now, my example is:
column content: [{"changed": {"fields": ["status"]}}]
str of logentry: Modification de « t2>t2:90 » — Modification de état.
comment:8 by , 8 months ago
| Has patch: | set |
|---|---|
| Patch needs improvement: | unset |
comment:9 by , 8 months ago
Replying to Thrishagowdabl:
Hi @Thrishagowdabl, I submitted a PR for this ticket (https://github.com/django/django/pull/20748). Are you still working on this? If so, I'm happy to close my PR or help with your patch instead. Let me know!
comment:10 by , 6 weeks ago
| Owner: | changed from to |
|---|
comment:11 by , 6 weeks ago
| Has patch: | unset |
|---|
comment:12 by , 3 weeks ago
| Owner: | removed |
|---|---|
| Status: | assigned → new |
comment:13 by , 2 weeks ago
Hi, I noticed that the ticket is now unassigned. Is anyone currently working on this issue or planning to submit an updated patch? I'd be interested in working on it if it's available, and I want to avoid duplicating anyone's effort. Thanks!
comment:14 by , 2 weeks ago
| Has patch: | set |
|---|---|
| Owner: | set to |
| Status: | new → assigned |
comment:15 by , 2 weeks ago
| Has patch: | unset |
|---|---|
| Owner: | removed |
| Status: | assigned → new |
| Triage Stage: | Accepted → Unreviewed |
comment:16 by , 12 days ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:17 by , 10 days ago
Hi, I'm interested in working on this ticket as my next Django contribution. I noticed that PR #22151 was closed due to the PR submission requirements rather than a code review. Nayan, are you planning to resubmit your fix, or would it be okay for me to take this forward? I want to avoid duplicating active work. Thanks!
comment:18 by , 5 days ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
Thanks for the clear report and diagnosis.