# Translating validation messages on client \[proposal\]

**URL:** <https://discourse.dry-rb.org/t/translating-validation-messages-on-client-proposal/416>\
**Category:** dry-validation\
**Created:** [November 29, 2017, 2:15pm UTC](https://discourse.dry-rb.org/t/translating-validation-messages-on-client-proposal/416 "2017-11-29T14:15:44Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![smaximov](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/smaximov/32/165_2.png) [@smaximov](https://discourse.dry-rb.org/u/smaximov)\
**Post date:** [November 29, 2017, 2:15pm UTC](https://discourse.dry-rb.org/t/translating-validation-messages-on-client-proposal/416/1 "2017-11-29T14:15:44Z")

</div>

Hello! Consider the use-case when internationalization (including translating error messages) is done entirely on the client. This means that instead of returning the client a message string for each validation error we need to provide the information which can be used to construct these messages on the client. The essential information consists of the name of the failed predicate and an optional `variable` =\> `value` map for messages which require interpolation.

I have a proof-of-concept implementation [here](https://github.com/smaximov/dry-validation/tree/client-messages) (disregard `Messages::NoOp`, naming is hard).

A “message” is a hash with following members:

- `:predicate` — the name of a failed predicate (e.g., `:size?`);
- `:variables` — a hash of interpolation variables with their values (e.g., `{ size_left: 4, size_right: 8 }` for the `:size?` predicate when the argument type is `Range`);
- `:options` — other information like what rule has failed or what are the types of arguments and values.

Example of generated errors:

```ruby

schema = Dry::Validation.Schema do
  configure do
    config.messages = :no_op

    def palindrome?(value)
      value = value.downcase
      value == value.reverse
    end
  end

  required(:name).filled(:str?, :palindrome?, size?: 4..8)
end

schema.call(name: 3).errors
# => {:name=>[{:predicate=>:str?, :options=>{:path=>[:name], :rule=>:name, :message_type=>:failure, :locale=>:en, :arg_type=>"default", :val_type=>"default", :input=>3}, :variables=>{:input=>3}}]}

schema.call(name: 'ivi').errors
# => {:name=>
# [{:predicate=>:size?,
# :options=>{:path=>[:name], :rule=>:name, :message_type=>:failure, :locale=>:en, :arg_type=>"range", :val_type=>"string", :size_left=>4, :size_right=>8, :input=>"ivi"},
# :variables=>{:size_left=>4, :size_right=>8, :input=>"ivi"}}]}

schema.call(name: 'leya').errors
# => {:name=>[{:predicate=>:palindrome?, :options=>{:path=>[:name], :rule=>:name, :message_type=>:failure, :locale=>:en, :arg_type=>"default", :val_type=>"string", :value=>"leya"}, :variables=>{:value=>"leya"}}]}

```

Full messages get weird though:

```ruby
schema.call(name: 'maya').errors(full: true)
# => {:name=>
# ["name {:predicate=>:palindrome?, :options=>{:path=>[:name], :rule=>:name, :message_type=>:failure, :locale=>:en, :arg_type=>\"default\", :val_type=>\"string\", :value=>\"maya\"}, :variables=>{:value=>\"maya\"}}"]}

```

What do you think about it?

* * *

On an unrelated note, the [contribution guidelines](https://github.com/dry-rb/dry-validation/blob/master/CONTRIBUTING.md) are not clear to me. PR guidelines say that a PR must address a previously reported issue, issue guidelines say that feature requests should be reported only after discussing it here. Does it mean that to create a PR I must create a corresponding topic on the discussion forum first, then (after discussing it) convert it to a Github issue, then create a PR?

---

<div class="post-metadata">

**Author:** ![smaximov](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/smaximov/32/165_2.png) [@smaximov](https://discourse.dry-rb.org/u/smaximov)\
**Post date:** [November 29, 2017, 2:28pm UTC](https://discourse.dry-rb.org/t/translating-validation-messages-on-client-proposal/416/2 "2017-11-29T14:28:53Z")

</div>

> [@smaximov](#):
>
> Full messages get weird though:

This can be solved by introducing a new method to `Messages::Abstract` and using it in [`MessageCompiler#message_text`](https://github.com/dry-rb/dry-validation/blob/e14a8bb0792c2a2978beb4e75cb653431eb1eaef/lib/dry/validation/message_compiler.rb#L169), then `Messages::NoOp` can override it to do nothing.
