# `Plurality.Custom`
[🔗](https://github.com/jeffhuen/plurality/blob/main/lib/plurality/custom.ex#L1)

Compile-time overrides for domain-specific inflection.

Use this module to create a custom inflection module that adds your own
irregulars and uncountables on top of Plurality's built-in data. Custom
entries are checked first and take priority; unknown words fall through
to the default `Plurality` module.

## Usage

    defmodule MyApp.Inflection do
      use Plurality.Custom,
        irregulars: [
          {"regex", "regexen"},
          {"pokemon", "pokemon"},
          {"elasticsearch", "elasticsearch"}
        ],
        uncountables: [
          "kubernetes",
          "graphql",
          "redis"
        ]
    end

Then call your module directly:

    MyApp.Inflection.pluralize("regex")            #=> "regexen"
    MyApp.Inflection.pluralize("kubernetes")       #=> "kubernetes"
    MyApp.Inflection.pluralize("leaf")             #=> "leaves"
    MyApp.Inflection.singularize("regexen")        #=> "regex"
    MyApp.Inflection.plural?("kubernetes")         #=> true
    MyApp.Inflection.singular?("kubernetes")       #=> true

## How it works

`use Plurality.Custom` generates five functions in your module at compile
time:

* `pluralize/2` — checks custom uncountables, then custom irregulars, then
  delegates to `Plurality.pluralize/2`
* `singularize/1` — checks custom uncountables, then custom reverse
  irregulars, then delegates to `Plurality.singularize/1`
* `plural?/1` — checks custom data, then delegates to `Plurality.plural?/1`
* `singular?/1` — checks custom data, then delegates to `Plurality.singular?/1`
* `inflect/2` — count-based delegation to `singularize/1` or `pluralize/1`

Custom irregulars and uncountables are stored as module attributes
(`@custom_irregulars_s2p`, `@custom_irregulars_p2s`, `@custom_uncountables`)
and compiled into the module's bytecode. There is no runtime overhead
beyond a `Map`/`MapSet` lookup.

## Options

  * `:irregulars` — a list of `{singular, plural}` tuples. Each pair is
    stored in both a forward map (singular → plural) and a reverse map
    (plural → singular). Identity pairs like `{"pokemon", "pokemon"}` work
    correctly — the word is returned unchanged in both directions.

  * `:uncountables` — a list of strings. These words are returned unchanged
    by both `pluralize/2` and `singularize/1`, and return `true` for both
    `plural?/1` and `singular?/1`.

## Design rationale

Custom modules are called directly rather than being registered globally.
This is intentional:

* **Explicit** — no hidden app-wide config or implicit delegation
* **Composable** — different parts of your app can use different custom
  modules
* **Zero overhead** — no ETS lookups or runtime configuration reads
* **Compile-time verified** — typos in irregular pairs are caught at
  compile time, not at runtime

# `custom_opts`

```elixir
@type custom_opts() :: [irregulars: [irregular_pair()], uncountables: [String.t()]]
```

Options accepted by `use Plurality.Custom`.

  * `:irregulars` — list of `t:irregular_pair/0`
  * `:uncountables` — list of `t:String.t/0`

# `irregular_pair`

```elixir
@type irregular_pair() :: {singular :: String.t(), plural :: String.t()}
```

A singular/plural pair for custom irregular words.

The first element is the singular form, the second is the plural form.
Identity pairs (where both are the same) are valid and indicate that
the word should be returned unchanged in both directions.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
