DEV Community

John Eliud Odhiambo
John Eliud Odhiambo

Posted on

A Story About Pluralization In Code (2 Items vs 2 Boxes)

I was building a small console application. A simple cart. It was nothing fancy but a chance to experiment with input, calculations, and formatted output. Here’s the first version I wrote:

import java.util.Scanner;

public class Cart {
    static void cart() {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter item name: ");
        String item = scanner.nextLine();

        System.out.print("Enter price for each: ");
        double price = scanner.nextDouble();

        if (price <= 0) {
            throw new IllegalArgumentException("Price must be positive.");
        }

        System.out.print("Enter quantity to buy: ");
        int quantity = scanner.nextInt();

        if (quantity <= 0) {
            throw new IllegalArgumentException("Quantity must be positive.");
        }

        double total = price * quantity;

        String itemLabel = quantity == 1 ? item : item + "s";

        System.out.printf(
            "You bought %d %s. Your grand total is KES %.2f.%n",
            quantity, itemLabel, total
        );
    }

    static void main(String[] args) {
        cart();
    }
}
Enter fullscreen mode Exit fullscreen mode

When I ran it with something simple like apple:

Enter item name: apple
Enter price for each: 100
Enter quantity to buy: 2
Enter fullscreen mode Exit fullscreen mode

The output was:

You bought 2 apples. Your grand total is KES 200.00.
Enter fullscreen mode Exit fullscreen mode

Perfect, simple and predictable. It worked exactly as expected.

The First Spark Of Curiosity

But then I tried a word that wasn’t so straightforward:

Enter item name: box
Enter price for each: 100
Enter quantity to buy: 2
Enter fullscreen mode Exit fullscreen mode

And the program printed:

You bought 2 boxs. Your grand total is KES 200.00.
Enter fullscreen mode Exit fullscreen mode

I paused and thought I could maybe just add some rules:

  • Words ending in x get appended with es. This would also apply for s, sh, and ch.

  • Words ending with y become ies.

  • But what about words like mouse and child?

It was exciting as it felt like executing the real craft of programming beyond just making something work.

First Attempt Towards A Smarter Solution

I decided to write a small helper function to handle the obvious cases:

static String pluralize(String word, int quantity) {
    if (quantity == 1) return word;

    if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z")
        || word.endsWith("ch") || word.endsWith("sh")) {
        return word + "es";
    }

    if (word.endsWith("y") && word.length() > 1 
        && !"aeiou".contains("" + word.charAt(word.length() - 2))) {
        return word.substring(0, word.length() - 1) + "ies";
    }

    return word + "s";
}
Enter fullscreen mode Exit fullscreen mode

Now, I could run tests with:

System.out.println(pluralize("box", 2));    // boxes
System.out.println(pluralize("city", 2));   // cities
System.out.println(pluralize("class", 2));  // classes
System.out.println(pluralize("apple", 2));  // apples
System.out.println(pluralize("mouse", 2));  // mouses
Enter fullscreen mode Exit fullscreen mode

The results were mostly correct; boxes, cities, classes but mouse to mouses stood out. Irregular nouns weren’t going to be solved by simple rules. And every new edge case made the code longer and more fragile.

This felt like the 'even numbers are hard' meme

At this point, I realized that my 'make it perfect' curiosity was bumping into the limitations of language itself.

Discovering ICU4J

Searching for solutions, I found ICU4J's Java library used in production for internationalization and pluralization and it looked promising. However after reading the documentation more carefully, I realized ICU4J doesn’t actually change the spelling of words. It decides when to use a singular versus plural form. For example, it can help you display:

You have 1 item
You have 2 items
Enter fullscreen mode Exit fullscreen mode

But it does not automatically turn box into boxes or child into children. It solves the category problem, not the word-inflection problem.

The Tempting Idea

It was disappointing after realizing that rule-based pluralization was incomplete and that even production-grade libraries like ICU4J weren’t meant to pluralize raw nouns.

Just as I was about to give up, another thought crossed my mind:

What if I just ask an AI to do this?
Enter fullscreen mode Exit fullscreen mode

It felt reasonable as modern AI models are excellent at language. They know that:

  • box pluralizes to boxes
  • class to classes
  • mouse to mice
  • child to children

In theory, this meant making an API call, passing the noun, and getting back the correct plural. This guaranteed no grammar rules, and no irregular noun lists. Just letting the model handle the complexity.

For a moment, this felt like the perfect solution.

Why the AI Idea Actually Makes Sense (At First)

To be fair, this approach isn’t foolish. In fact, it has real strengths because an AI model:

  • Understands irregular nouns
  • Handles borrowed words and edge cases
  • Adapts to language naturally
  • Requires very little code on my end

For a small personal project, the idea is genuinely attractive. I wouldn’t need to maintain pluralization logic at all. I’d just delegate the problem to the AI via an API.

But then I paused and started thinking like a system designer, not just a coder.

The First Red Flag: Cost

Pluralization is a low-value, high-frequency operation. If every time a user adds an item to a cart the app needs to:

  • Make a network request
  • Pay for tokens
  • Wait for a response

That cost adds up quickly. Paying for AI inference to decide whether to print boxes or items is hard to justify, especially when that same operation could be done locally with zero cost.

For small scripts or experiments, this might be fine. At scale, it becomes expensive very fast.

The Second Red Flag: Latency

Pluralization sits on the critical path of user feedback. It happens:

  • While rendering UI
  • While printing output
  • During fast interactions

An AI call introduces:

  • Network latency
  • Timeout risk
  • Retry logic
  • Failure modes you don’t control (500 errors)

Something as simple as printing a sentence suddenly depends on the availability of an external service. That outright felt wrong.

The Third Red Flag: Non-Determinism

This was by far the biggest concern. Language is flexible, and AI reflects that. For example:

  • cactus to cacti
  • cactus to cactuses

Both are correct but if the application prints:

You bought 2 cacti
Enter fullscreen mode Exit fullscreen mode

and later:

You bought 2 cactuses
Enter fullscreen mode Exit fullscreen mode

You just introduced inconsistency. An answer that is 'sometimes right' is often worse than a simpler one that is always consistent.

The Fourth Red Flag: Control and Safety

To pluralize a word using AI, I have to send user input to an external service. This raises the questions:

  • What if the item name contains sensitive information?
  • What if this data must stay on-device?
  • What happens if the API changes behavior?

Suddenly, a simple console program is tied to:

  • Network access -API keys
  • Privacy considerations

At this point, the picture became clear. Opting to use AI for pluralization solves a hard linguistic problem but introduces cost, latency, inconsistency, dependency and still does not guarantee perfect results.

The Practical Tradeoff

At this point, I had to step back and ask myself what the real goal of my program is.

  • Showing the number of items bought
  • Showing the total cost

The exact spelling of the product name in plural wasn’t critical.

That’s when it clicked almost every app I use does the same thing. Shopping sites, notification systems, and dashboards don’t attempt to pluralize arbitrary nouns. They simply use a safe, controlled noun:

You bought 2 items
You have 5 notifications
Your cart contains 3 products
Enter fullscreen mode Exit fullscreen mode

It is predictable, safe, and scales well since there are no edge cases to handle and also works perfectly if you later translate your application into other languages.

Final Code Snippet

Here’s how my original shopping cart code evolved after all this reflection:

double total = price * quantity;

String message = quantity == 1 ? "item" : "items";

System.out.printf(
    "You bought %d %s. Your grand total is KES %.2f.%n",
    quantity, message, total
);
Enter fullscreen mode Exit fullscreen mode

Output:

You bought 1 item. Your grand total is KES 100.00.
You bought 2 items. Your grand total is KES 200.00.
Enter fullscreen mode Exit fullscreen mode

No edge cases, no dictionary of irregular nouns, no fragile rules. This approach is exactly what large production systems do.

Lessons Learned

  1. Curiosity is important. Trying to improve the code teaches you a lot about programming, language, and tradeoffs.
  2. Language is messy and even simple pluralization can have dozens of edge cases.
  3. Production code values simplicity, correctness, and consistency.
  4. Using ICU4J helps in the right context and should be used for plural-aware messages and not for spelling every noun correctly.
  5. Opting for controlled vocabulary wins,is safe, predictable, and maintainable.

Thought

The journey from apple to apples to understanding why 2 boxes is rarely shown in apps is more than a lesson in pluralization. It is a lesson in designing software systems that works reliably, even when human language does not cooperate.

Sometimes, the simplest solution like displaying 2 items is also the most elegant.

Top comments (10)

Collapse
 
dashaun profile image
DaShaun

I loved where this was going! I really enjoyed when you brought in the LLM. I thought I knew where you would end up, but I was wrong.

Why not use Redis, to capture your pluralizations?

Low latency, no duplication, still have control.

A local LLM or even SLM could be used also!

Thank you so much for writing this up! Keep up the good work!

Collapse
 
johneliud profile image
John Eliud Odhiambo

Thank you very much. I appreciate you reading this and glad you enjoyed it.

Collapse
 
acessays profile image
Anna

I love how this highlights the little details that make code feel polished! Pluralization seems small, but it really improves user experience and readability. Thanks for sharing these practical insights!

Collapse
 
johneliud profile image
John Eliud Odhiambo

Thank you very much. I appreciate your time to go through this and your insights as well.

Collapse
 
victor_webdev profile image
Victor

Good one. This is quite refreshing. I am not a dev yet, but I was able to read your solution clearly and understand 100%.

Collapse
 
johneliud profile image
John Eliud Odhiambo

Thank you very much. I appreciate your time to go through this and glad you found it easy to understand despite having a technical background.

Collapse
 
abdullah_abdulraheem_58ee profile image
Abdullah Abdulraheem

This is really helpful.

Collapse
 
johneliud profile image
John Eliud Odhiambo

Thank you very much. I appreciate your time to go through this and glad you liked it.

Collapse
 
oxtimilehin profile image
Oluwatimilehin Awoniyi • Edited

Technically funny 😁

Collapse
 
johneliud profile image
John Eliud Odhiambo

Thank you very much. I appreciate your time to read the article and leave a comment.