I'm wondering if there is a preferred or at least more readable/pretty/pythonic way to make a sentence plural based on data being put into it.
Here's how I'm doing it now
ret = f'Done! {deleted} entr{"y was" if deleted == 1 else "ies were"} removed.'
I know this works, but it just doesn't sit well when I go and read it. I've thought of either making a function or using a dict-switch (which is something I kinda like) but maybe someone knows a better way.
Solutions I've tried:
using a dict
plural = {1:'y was'}
ret = f'Done! {deleted} entr{plural.get(deleted, "ies were")} removed.'
using a function
def plural(num: int):
return 'y was' if num == 1 else 'ies were'
using boolean operators
ret = f'Done! {deleted} entr{deleted != 1 and "ies were" or "y was"} removed.'
Those are really the only other ways I can think of right now that makes sense, also the reason I want this is because I have multiple places where I'd need to make words plural. The issue is, I may want to use different wording in places and would like to keep from repeating myself with making word(s) plural for human-readable format.
Maybe in this single instance (if we are talking a few strings to be declinated) then such approaches are sufficient.
But in general case, localization (L10N) and internationalization (I12N) is best handled with suitable libraries.
In case of python we have a standard library gettext
that can be used for this purpose. It is effectively a python API to GNU gettext which is a neat option for a start.
There are supplementary resources:
In short, there is much more to L10N and I12N than plurals...
While I found @sophros answer very knowledgeable and informative, I decided it was more than I needed. I decided to write my own custom class as I needed something cheap and easy and that I can reuse for different words/sentences. Feel free to use yourself if you like it!
class Plural:
__slots__ = 'word', 'value', 'singular', 'plural', 'zero', 'ignore_negatives'
def __init__(self, value: int, word: str = "", **kwargs):
"""
Parameters
----------
value : int
The determining value
word : str, optional
The word to make plural. (defaults to "")
singular : str, optional
Appended to `word` if `value` == 1. (defaults to '')
plural : str, optional
Appended to `word` if `value` > 1. (defaults to 's')
zero : str, optional
Replaces `value` if `value` == 0. (defaults to 0)
ignore_negatives : bool, optional
This will raise ValueError if `value` is negative. (defaults to False)
Set to True if you don't care about negative values.
"""
self.value, self.word = value, word
self.singular = kwargs.pop('singular', '')
self.plural = kwargs.pop('plural', 's')
self.zero = kwargs.pop('zero', 0)
self.ignore_negatives = kwargs.pop('ignore_negatives', False)
def __str__(self):
v = self.value
pluralizer = self.plural if abs(v) > 1 else self.singular
if v < 0 and not self.ignore_negatives:
raise ValueError
return f"{v or self.zero} {self.word}{pluralizer}"
Test that it works
print(Plural(-2, singular="entry", plural="entries", ignore_negatives = True))
#-2 entries
print(Plural(-1, singular="entry", plural="entries", ignore_negatives = True))
#-1 entry
print(Plural(0, singular="entry", plural="entries"))
#0 entries
print(Plural(1, singular="entry", plural="entries"))
#1 entry
print(Plural(2, singular="entry", plural="entries"))
#2 entries
With Negative Value
print(Plural(-1, singular="entry", plural="entries"))
#Traceback (most recent call last):
#File "/home/main.py", line 53, in <module>
# print(Plural(-1, singular="entry", plural="entries"))
# File "/home/main.py", line 43, in __str__
# raise ValueError
#ValueError
Other use cases
print(Plural(1, "entr", singular="y", plural="ies"))
#1 entry
print(Plural(2, "entr", singular="y", plural="ies"))
#2 entries
print(Plural(0, "value", zero="No"))
#No value
print(Plural(1, "value"))
#1 Value
print(Plural(2, "value"))
#2 Values
If you just want a quick and dirty fix
Either use one of my examples in the question or make a method like in my question but here's a tweaked version as @Ev. Kounis and @9769953 suggested in the comments (hope ya'll don't mind me putting ur suggestions in the answer)
def plural(num: int, word: str = "", single: str = "", mult: str = 's'):
return f"{num} {(plural, singular)[abs(num) == 1]}"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With