Sanjibukai
How string interpolation is done within the error messages of changesets (using %{})?
Hello everybody,
When I look at what an error looks like in a Changeset, it’s something like this:
[username: {"should be at most %{count} character(s)", [count: 20, validation: :length, kind: :max, type: :string]}]
# which renders to
# "should be at most 20 character(s) max"
It seems that there is some kind of templating with interpolation done inside %{keyword} as soon as the keyword is present as a key in the given list of keywords..
I don’t think it’s something native to elixir like printf in C (or I never heard of on my learning path) but more a convenience or a syntactic sugar provided by Phoenix..
So I wanted to know where this happens?
This behavior seems to be related to the Gettext module but when I follow down the path it seems (at least to me btw) overly complicated with functions with 6 to 8 parameters with odd names (dngettext, dpngettext etc.)
And I expected to find calls to String.replace or Regex.replace in order to replace %{} but I don’t find anything..
Does anyone have any clues?
Thank you..
Marked As Solved
Also Liked
hauleth
This is common pattern in Erlang (and by extension - Elixir) that you use “quasi” io lists for that. So you have type like template() :: maybe_improper_list(template() | iolist()) and then you can easily traverse it and replace all atoms with proper values (while building another io list). So this “Goldberg machine” does exactly that - first compile all the strings into simpler forms and then replacing all needed values within the io list.
nicanor
Looks like the suggestion is to use Changeset.traverse_errors to get the errors in the format you want.
According to Elixir School:
You may be surprised that the error message contains the cryptic
%{count}— this is to aid translation to other languages; if you want to display the errors to the user directly, you can make them human readable usingtraverse_errors/2— take a look at the example provided in the docs.
If you are using Phoenix, you can see how this error format is managed for you using Gettext in the file web/views/error_helpers.ex.
Update:
There is a nice example on how to use the traverse_errors function to replace the values yourself:
{"should be at least %{count} characters", [count: 3, validation: :length, min: 3]}
iex> traverse_errors(changeset, fn {msg, opts} ->
...> Enum.reduce(opts, msg, fn {key, value}, acc ->
...> String.replace(acc, "%{#{key}}", to_string(value))
...> end)
...> end)
%{title: ["should be at least 3 characters"]}
nicanor
Oh, yes, now I understand your question!
And yes, it’s a very complicated code.
Looks like what you are looking for is happening in Gettext.Interpolation
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








