Pluralization is a problem in its sphere. We need to always correctly define grammar in our apps based on the singular/plural value. E.g
1 stop 5 stops
Some websites use the (s). So that it is up to the reader to remove (s) or add (s) when reading it.
1 stop(s)
5 stop(s)
But as a front end developer who cares about the user experience, I always want to correct it for them.
At the beginning, I ended up to do the check for every word, so the code to display singular/plural will looks like:
<p>Number of stops: {{ numberOfStops }} stop {{ numberOfStops > 1 ? "s" : ""}}</p>
But more and more code like that could be error-prone, what if my team member when he is too tired for coding, he will do the check as numberOfStops >= 1
. So that I wrote a simple plural
pipe which takes a number as an argument and based on that to return the plural form of a word. As long as you are using this pipe, the result will be displayed as expected. No more funny check and error-prone.
By default it will append s
to the end of the word. But of course, you could specify the plural form of a noun because not all of them ended with s
in a plural form. Such as bus
will become buses
or hero
will become heroes
.
The code of PluralPipe
looks like below, you can open the stackblitz to see it in action.
@Pipe({ name: 'plural' })
export class CustomPluralPipe implements PipeTransform {
transform(input: number, customPluralForm: string = 's'): string {
return input > 1 ? customPluralForm : ''
}
}
To use it in the template, super easy.
<p>
Number of stops: {{ numberOfStops }} stop {{ numberOfStops | plural}}
</p>
<p>
Number of heroes: {{ numberOfHeroes }} hero {{ numberOfHeroes | plural:"es" }}
</p>
The problem becomes interesting when you have to do the plural form for some special words such as person
, as it will come people
in a plural form. Or child
, will become children
. I didn’t have the problem in a real project so what I can think of is to do a check like inputNumber > 1 ? "children": "child"
. In the long run, you could consider to abstract that condition to a simple pipe that takes a numeric value and two input noun and then renders it accordingly.
You might not know that Angular has a built-in directive called NgPlural for this need. You can use this directive for the use case I mentioned above with my awesome custom pipe code. But I still prefer my approach in a normal use case :)) I don’t know, I just get used to it.
So basically, the NgPlural in action will look like:
<p [ngPlural]="numberOfStops">
Number of stops: {{ numberOfStops }
<ng-template ngPluralCase="=1">stop</ng-template>
<ng-template ngPluralCase=">1">stops</ng-template>
</p>
Actually the above code only runs if with the first use case when the number is 1. If I increase it to 2, I got an error on the console ERROR Error: No plural message found for value "2"
. Anyway I will come back to this directive If I need to use it in the future, maybe with i18n need.
Based on Andy Bronson comments, you could try the code below for NgPlural. It should works very well. Noted that the UI will still using the plural form if the value is 0
<p [ngPlural]="numberOfStops">
Number of stops: {{ numberOfStops }
<ng-template ngPluralCase="=1">stop</ng-template>
<ng-template ngPluralCase="other">stops</ng-template>
</p>
Output
0 stops
1 stop
2 stops