# `Localize.Inflection.Concept`
[🔗](https://github.com/elixir-localize/localize/blob/v1.4.0/lib/localize/inflection/concept.ex#L1)

An inflectable string with grammatical constraints.

A concept wraps a word or phrase for a locale. Constraints (such
as `number=plural` or `definiteness=definite`) are applied with
`put_constraint/3`; grammatical properties are queried with
`feature_value/2`; and `to_speakable_string/1` renders the phrase
with all constraints applied.

A concept can also carry forms of its own for sets of constraints,
given as `:display_data` to `new/3`. It then renders as the first
of its forms that holds every constraint put on it, and otherwise
as the locale's rules inflect the phrase.

This is the equivalent of the upstream `InflectableStringConcept`,
and with display data of the upstream `SemanticConcept`.

# `t`

```elixir
@type t() :: %Localize.Inflection.Concept{
  constraints: %{optional(binary()) =&gt; binary()},
  display_data: [
    {Localize.Inflection.SpeakableString.t(), %{optional(binary()) =&gt; binary()}}
  ],
  guess: boolean(),
  initial: %{optional(binary()) =&gt; binary()},
  locale: atom(),
  value: Localize.Inflection.SpeakableString.t()
}
```

# `exists?`

Returns true when the concept can be rendered with its
constraints without guessing.

Rendering without guessing includes the synthesizers' unchanged
passthrough: a word the dictionary does not know may still render
as itself, so `exists?/1` answers "does rendering produce
something?", not "is the requested form attested in the
dictionary?". Use `Localize.Inflection.known?/2` to test
dictionary membership.

### Arguments

* `concept` is a concept from `new/3`.

### Returns

* `true` when rendering produces a form, and `false` otherwise or
  when `concept` is not a concept.

### Examples

    iex> {:ok, concept} = Localize.Inflection.Concept.new(:en, "cat", constraints: %{number: :plural})
    iex> Localize.Inflection.Concept.exists?(concept)
    true

# `feature_value`

Returns the value of a feature for this concept.

A stored constraint takes precedence. A concept with display data
next takes the value its rendered form has; otherwise the locale's
default feature function computes the value from the (possibly
inflected) display string.

### Arguments

* `concept` is a concept from `new/3`.

* `name` is a feature name such as `:number` or `"gender"`.

### Returns

* A speakable string, or nil when the feature has no value.

### Examples

    iex> {:ok, concept} = Localize.Inflection.Concept.new(:en, "light")
    iex> Localize.Inflection.Concept.feature_value(concept, :number)
    :singular

# `new`

Creates a concept for a word or phrase.

### Arguments

* `locale` is a locale atom or string, canonically BCP47 (`:en`, `"zh-TW"`), for which data has been generated.

* `value` is the word or phrase, either a binary or a
  `{print, speak}` tuple.

* `options` is a keyword list of options.

### Options

* `:constraints` is a map of feature constraints to apply, as if
  set with `put_constraint/3`.

* `:initial` is a map of features known to hold for the value
  itself (for example a known grammatical gender), used when
  deriving other features rather than requested for rendering.

* `:display_data` is a list of `{form, constraints}` entries, forms
  of the concept and the features each has, such as
  `{"octopodes", %{number: :plural}}`. A form is a binary or a
  `{print, speak}` tuple. The concept renders as the first form,
  starting with `value` and its `:initial` features, that holds
  every constraint put on it, and takes its features from that
  form; when no form does, the locale's rules inflect `value`.

### Returns

* `{:ok, concept}` or `{:error, reason}` when the locale data is
  unavailable, or a constraint or display data entry is invalid.

### Examples

    iex> {:ok, concept} = Localize.Inflection.Concept.new(:en, "cat", constraints: %{number: :plural})
    iex> Localize.Inflection.Concept.to_speakable_string(concept)
    "cats"

    iex> {:error, error} = Localize.Inflection.Concept.new(:en, "cat", constraints: %{number: :dual})
    iex> error.value
    "dual"
    iex> error.allowed_values
    ["plural", "singular"]

    iex> display_data = [{"octopodes", %{number: :plural}}]
    iex> {:ok, concept} = Localize.Inflection.Concept.new(:en, "octopus", display_data: display_data)
    iex> {:ok, concept} = Localize.Inflection.Concept.put_constraint(concept, :number, :plural)
    iex> Localize.Inflection.Concept.to_speakable_string(concept)
    "octopodes"

# `put_constraint`

Puts a constraint on the concept.

### Arguments

* `concept` is a concept from `new/3`.

* `name` is a feature name such as "number" or "definiteness".

* `value` is the constraint value such as "plural".

### Returns

* `{:ok, concept}` or `{:error, reason}` when the feature is
  unknown or the value is not valid for a bounded feature.

### Examples

    iex> {:ok, concept} = Localize.Inflection.Concept.new(:en, "light")
    iex> {:ok, concept} = Localize.Inflection.Concept.put_constraint(concept, :number, :plural)
    iex> Localize.Inflection.Concept.to_speakable_string(concept)
    "lights"

# `to_speakable_string`

Renders the concept with all constraints applied.

The synthesizers may fall back to suffix-exemplar guessing for
words the dictionary does not know. Setting the concept's
`guess` field to false disables guessing: forms then change
only through attested dictionary paths, and a failed render
returns the value unchanged.

### Arguments

* `concept` is a `t:t/0`, as `new/3` returns it.

### Returns

* A speakable string (a binary, or `{print, speak}` when the
  spoken form differs).

### Examples

    iex> {:ok, concept} = Localize.Inflection.Concept.new(:en, "cat", constraints: %{case: :genitive})
    iex> Localize.Inflection.Concept.to_speakable_string(concept)
    "cat’s"

---

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