Use ngettext for strings with count #9872

Closed
opened 2025-12-29 21:23:47 +01:00 by adam · 11 comments
Owner

Originally created by @afranke on GitHub (Jun 19, 2024).

Proposed Changes

netbox/dcim/views.py has _("Disconnected {count} {type}") where count is as far as I can tell an arbitrary number and type a pre-baked plural. This cannot be properly translated in some languages, because plurals don’t work the same way as they do in English.

You need to:

  1. use ngettext for the count part, so plural forms can be handled properly.
  2. use one full string per type, because that’s the only way nouns can be declined as necessary.

This would result in something along the lines of

…
ngettext("Disconnected 1 device", "Disconnected {count} devices", count)
…
ngettext("Disconnected 1 client", "Disconnected {count} clients", count)
…

with something like a switch-case to match a given type with a given ngettext call.

Sorry for using client and device instead of actual types, I couldn’t easily find the type list.

Justification

You shouldn’t make up your own plural handling. There are tools for that, and good reasons to use them.

See #16659 for a similar but different situation.

Originally created by @afranke on GitHub (Jun 19, 2024). ### Proposed Changes `netbox/dcim/views.py` has `_("Disconnected {count} {type}")` where `count` is as far as I can tell an arbitrary number and `type` a pre-baked plural. This cannot be properly translated in some languages, because plurals don’t work the same way as they do in English. You need to: 1. use [`ngettext`](https://docs.djangoproject.com/en/5.0/topics/i18n/translation/#pluralization) for the count part, so plural forms can be handled properly. 2. use one full string per type, because that’s the only way nouns can be declined as necessary. This would result in something along the lines of ``` … ngettext("Disconnected 1 device", "Disconnected {count} devices", count) … ngettext("Disconnected 1 client", "Disconnected {count} clients", count) … ``` with something like a switch-case to match a given type with a given ngettext call. Sorry for using client and device instead of actual types, I couldn’t easily find the type list. ### Justification You shouldn’t make up your own plural handling. There are tools for that, and good reasons to use them. See #16659 for a similar but different situation.
adam closed this issue 2025-12-29 21:23:48 +01:00
Author
Owner

@afranke commented on GitHub (Jun 19, 2024):

I only mentioned one string because it’s the one I could find, but there may be other ones that are affected by this issue.

@afranke commented on GitHub (Jun 19, 2024): I only mentioned one string because it’s the one I could find, but there may be other ones that are affected by this issue.
Author
Owner

@afranke commented on GitHub (Jun 19, 2024):

And of course right after I post that comment, I find more:

  • netbox/virtualization/models/clusters.py has "{count} devices are assigned as hosts for this cluster but are not in site {site}"
  • netbox/utilities/error_handlers.py has "Unable to delete <strong>{objects}</strong>. {count} dependent objects were found: "
  • netbox/utilities/forms/utils.py has "Row {row}: Expected {count_expected} columns but found {count_found}"
  • netbox/dcim/models/device_component_templates.py has "Invalid rear port position ({position}); rear port {name} has only {count} positions"

I may still be missing some more.

@afranke commented on GitHub (Jun 19, 2024): And of course right after I post that comment, I find more: * `netbox/virtualization/models/clusters.py` has `"{count} devices are assigned as hosts for this cluster but are not in site {site}"` * `netbox/utilities/error_handlers.py` has `"Unable to delete <strong>{objects}</strong>. {count} dependent objects were found: "` * `netbox/utilities/forms/utils.py` has `"Row {row}: Expected {count_expected} columns but found {count_found}"` * `netbox/dcim/models/device_component_templates.py` has `"Invalid rear port position ({position}); rear port {name} has only {count} positions"` I may still be missing some more.
Author
Owner

@jeremystretch commented on GitHub (Jun 21, 2024):

@afranke thanks for bringing this to our attention. Would you be willing to contribute a PR to make the proposed changes?

@jeremystretch commented on GitHub (Jun 21, 2024): @afranke thanks for bringing this to our attention. Would you be willing to contribute a PR to make the proposed changes?
Author
Owner

@afranke commented on GitHub (Jun 22, 2024):

I’m sorry but I won’t be able to. I can review a PR to check if it’s doing the right thing from an i18n perspective though.

@afranke commented on GitHub (Jun 22, 2024): I’m sorry but I won’t be able to. I can review a PR to check if it’s doing the right thing from an i18n perspective though.
Author
Owner

@Julio-Oliveira-Encora commented on GitHub (Jun 25, 2024):

Could you please assign it to me?

@Julio-Oliveira-Encora commented on GitHub (Jun 25, 2024): Could you please assign it to me?
Author
Owner

@arthanson commented on GitHub (Jul 9, 2024):

@afranke In this case In netbox/dcim/views.py if we used the line:

                messages.success(request, _(ngettext(
                    "Disconnected 1 {type}",
                    "Disconnected {count} {type_plural}",
                    count,
                )).format(
                    count=count,
                    type=self.queryset.model._meta.verbose_name,
                    type_plural=self.queryset.model._meta.verbose_name_plural
                ))

Would that work for pluralization as you mentioned for Polish? We just have the singular and pluralized model name.

@arthanson commented on GitHub (Jul 9, 2024): @afranke In this case In netbox/dcim/views.py if we used the line: ``` messages.success(request, _(ngettext( "Disconnected 1 {type}", "Disconnected {count} {type_plural}", count, )).format( count=count, type=self.queryset.model._meta.verbose_name, type_plural=self.queryset.model._meta.verbose_name_plural )) ``` Would that work for pluralization as you mentioned for Polish? We just have the singular and pluralized model name.
Author
Owner

@afranke commented on GitHub (Jul 10, 2024):

No, it wouldn’t work. See point 2 in the issue description.

@afranke commented on GitHub (Jul 10, 2024): No, it wouldn’t work. See point 2 in the issue description.
Author
Owner

@arthanson commented on GitHub (Jul 16, 2024):

Putting this back in the backlog - more research needs to be done on a viable solution as it's probably not viable to create a separate string per type there.

@arthanson commented on GitHub (Jul 16, 2024): Putting this back in the backlog - more research needs to be done on a viable solution as it's probably not viable to create a separate string per type there.
Author
Owner

@github-actions[bot] commented on GitHub (Oct 15, 2024):

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. Do not attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our contributing guide.

@github-actions[bot] commented on GitHub (Oct 15, 2024): This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. NetBox is governed by a small group of core maintainers which means not all opened issues may receive direct feedback. **Do not** attempt to circumvent this process by "bumping" the issue; doing so will result in its immediate closure and you may be barred from participating in any future discussions. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md).
Author
Owner

@github-actions[bot] commented on GitHub (Dec 2, 2024):

This issue has been automatically closed due to lack of activity. In an effort to reduce noise, please do not comment any further. Note that the core maintainers may elect to reopen this issue at a later date if deemed necessary.

@github-actions[bot] commented on GitHub (Dec 2, 2024): This issue has been automatically closed due to lack of activity. In an effort to reduce noise, please do not comment any further. Note that the core maintainers may elect to reopen this issue at a later date if deemed necessary.
Author
Owner

@afranke commented on GitHub (Dec 2, 2024):

Let me try one more time, and add a bit more context. First of all, I am neither a user nor a translator for your software. I am a free software translator, both as a volunteer and professionally, with twenty years of experience, including as an internationalisation coordinator for GNOME. A friend of mine uses your software and told me about it. Out of curiosity, I looked into the quality of your translations, because that’s something I do: sharing my expertise with smaller projects who aren’t lucky enough to have someone with the necessary knowledge, in order to improve the commons. That is not to say that you should blindly trust me, but hopefully it helps you understand where I’m coming from and indeed trust me a bit more.

Putting this back in the backlog - more research needs to be done on a viable solution as it's probably not viable to create a separate string per type there.

But it is the one correct way to do this. You shouldn’t be worried about adding a dozen more strings (which is peanuts compared to the 3k+ strings you already have). You should be worried about doing things correctly so translators can effectively do their work.

@afranke commented on GitHub (Dec 2, 2024): Let me try one more time, and add a bit more context. First of all, I am neither a user nor a translator for your software. I am a free software translator, both as a volunteer and professionally, with twenty years of experience, including as an internationalisation coordinator for GNOME. A friend of mine uses your software and told me about it. Out of curiosity, I looked into the quality of your translations, because that’s something I do: sharing my expertise with smaller projects who aren’t lucky enough to have someone with the necessary knowledge, in order to improve the commons. That is not to say that you should blindly trust me, but hopefully it helps you understand where I’m coming from and indeed trust me a bit more. > Putting this back in the backlog - more research needs to be done on a viable solution as it's probably not viable to create a separate string per type there. But it is the one correct way to do this. You shouldn’t be worried about adding a dozen more strings (which is peanuts compared to the 3k+ strings you already have). You should be worried about doing things correctly so translators can effectively do their work.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#9872