diff --git a/assets/css/app.css b/assets/css/app.css
index 4fcb28b..ab44006 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -1,4 +1,5 @@
@import "./core_components.css";
+@import "./flash.css";
:root {
--bg: #13151a;
@@ -46,3 +47,8 @@ footer {
width: 100%;
margin: auto;
}
+
+.icon {
+ width: 20px;
+ height: 20px;
+}
diff --git a/assets/css/core_components.css b/assets/css/core_components.css
index 787ecd4..71d7707 100644
--- a/assets/css/core_components.css
+++ b/assets/css/core_components.css
@@ -126,46 +126,6 @@
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
-.flash {
- position: fixed;
- top: 0.5rem;
- right: 0.5rem;
- z-index: 50;
- padding: 0.75rem;
- margin-right: 0.5rem;
- border-radius: 0.5rem;
- box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
- width: 20rem;
-}
-
-@media (min-width: 640px) {
- .flash {
- width: 24rem;
- }
-}
-
-.flash-title {
- display: flex;
- gap: 0.375rem;
- align-items: center;
- font-size: 0.875rem;
- font-weight: 600;
- line-height: 1.5rem;
-}
-
-.flash-close-button {
- position: absolute;
- top: 0.25rem;
- right: 0.25rem;
- padding: 0.5rem;
-}
-
-.flash-close-button-icon {
- width: 1.25rem;
- height: 1.25rem;
- opacity: 0.4;
-}
-
.simple-form {
margin-top: 2rem;
background-color: #ffffff;
diff --git a/assets/css/flash.css b/assets/css/flash.css
new file mode 100644
index 0000000..7c39cef
--- /dev/null
+++ b/assets/css/flash.css
@@ -0,0 +1,51 @@
+.flash {
+ position: fixed;
+ top: 0.5rem;
+ right: 0.5rem;
+ z-index: 50;
+ padding: 16px;
+ border-radius: 0.3rem;
+ box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+ width: 20rem;
+}
+
+.flash-info {
+ background-color: #15803d;
+}
+
+.flash-error {
+ background-color: #b91c1c;
+}
+
+@media (min-width: 640px) {
+ .flash {
+ width: 24rem;
+ }
+}
+
+.flash-title {
+ margin: 0;
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ font-weight: 600;
+}
+
+.flash-body {
+ margin-bottom: 0;
+ text-indent: 28px;
+}
+
+.flash-close-button {
+ padding: 0;
+ position: absolute;
+ width: 28px;
+ height: 28px;
+ top: 0.5rem;
+ right: 0.5rem;
+ cursor: pointer;
+}
+
+.flash-close-button-icon {
+ opacity: 0.6;
+}
diff --git a/config/config.exs b/config/config.exs
index 634e180..8bcbdc1 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -11,6 +11,8 @@ config :comfycamp,
ecto_repos: [Comfycamp.Repo],
generators: [timestamp_type: :utc_datetime]
+config :comfycamp, ComfycampWeb.Gettext, locales: ~w(en ru), default_locale: "ru"
+
# Configures the endpoint
config :comfycamp, ComfycampWeb.Endpoint,
url: [host: "localhost"],
diff --git a/lib/comfycamp_web.ex b/lib/comfycamp_web.ex
index fca54d4..4cb7922 100644
--- a/lib/comfycamp_web.ex
+++ b/lib/comfycamp_web.ex
@@ -84,7 +84,9 @@ defmodule ComfycampWeb do
# HTML escaping functionality
import Phoenix.HTML
# Core UI components and translation
+ import ComfycampWeb.Icons
import ComfycampWeb.CoreComponents
+ import ComfycampWeb.Flash
import ComfycampWeb.Gettext
# Shortcut for generating JS commands
diff --git a/lib/comfycamp_web/components/core_components.ex b/lib/comfycamp_web/components/core_components.ex
index 77dfe6b..0d96ae4 100644
--- a/lib/comfycamp_web/components/core_components.ex
+++ b/lib/comfycamp_web/components/core_components.ex
@@ -18,6 +18,7 @@ defmodule ComfycampWeb.CoreComponents do
alias Phoenix.LiveView.JS
import ComfycampWeb.Gettext
+ import ComfycampWeb.Icons
@doc """
Renders a modal.
@@ -85,93 +86,6 @@ defmodule ComfycampWeb.CoreComponents do
"""
end
- @doc """
- Renders flash notices.
-
- ## Examples
-
- <.flash kind={:info} flash={@flash} />
- <.flash kind={:info} phx-mounted={show("#flash")}>Welcome Back!
- """
- attr :id, :string, doc: "the optional id of flash container"
- attr :flash, :map, default: %{}, doc: "the map of flash messages to display"
- attr :title, :string, default: nil
- attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
- attr :rest, :global, doc: "the arbitrary HTML attributes to add to the flash container"
-
- slot :inner_block, doc: "the optional inner block that renders the flash message"
-
- def flash(assigns) do
- assigns = assign_new(assigns, :id, fn -> "flash-#{assigns.kind}" end)
-
- ~H"""
-
hide("##{@id}")}
- role="alert"
- class={[
- "flash",
- @kind == :info && "flash-info",
- @kind == :error && "flash-error"
- ]}
- {@rest}
- >
-
- <.icon :if={@kind == :info} name="hero-information-circle-mini" class="h-4 w-4" />
- <.icon :if={@kind == :error} name="hero-exclamation-circle-mini" class="h-4 w-4" />
- <%= @title %>
-
-
<%= msg %>
-
- <.icon name="hero-x-mark-solid" class="flash-close-button-icon" />
-
-
- """
- end
-
- @doc """
- Shows the flash group with standard titles and content.
-
- ## Examples
-
- <.flash_group flash={@flash} />
- """
- attr :flash, :map, required: true, doc: "the map of flash messages"
- attr :id, :string, default: "flash-group", doc: "the optional id of flash container"
-
- def flash_group(assigns) do
- ~H"""
-
- <.flash kind={:info} title={gettext("Success!")} flash={@flash} />
- <.flash kind={:error} title={gettext("Error!")} flash={@flash} />
- <.flash
- id="client-error"
- kind={:error}
- title={gettext("We can't find the internet")}
- phx-disconnected={show(".phx-client-error #client-error")}
- phx-connected={hide("#client-error")}
- hidden
- >
- <%= gettext("Attempting to reconnect") %>
- <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
-
-
- <.flash
- id="server-error"
- kind={:error}
- title={gettext("Something went wrong!")}
- phx-disconnected={show(".phx-server-error #server-error")}
- phx-connected={hide("#server-error")}
- hidden
- >
- <%= gettext("Hang in there while we get back on track") %>
- <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
-
-
- """
- end
-
@doc """
Renders a simple form.
@@ -565,33 +479,6 @@ defmodule ComfycampWeb.CoreComponents do
"""
end
- @doc """
- Renders a [Heroicon](https://heroicons.com).
-
- Heroicons come in three styles – outline, solid, and mini.
- By default, the outline style is used, but solid and mini may
- be applied by using the `-solid` and `-mini` suffix.
-
- You can customize the size and colors of the icons by setting
- width, height, and background color classes.
-
- Icons are extracted from the `deps/heroicons` directory and bundled within
- your compiled app.css by the plugin in your `assets/tailwind.config.js`.
-
- ## Examples
-
- <.icon name="hero-x-mark-solid" />
- <.icon name="hero-arrow-path" class="ml-1 w-3 h-3 animate-spin" />
- """
- attr :name, :string, required: true
- attr :class, :string, default: nil
-
- def icon(%{name: "hero-" <> _} = assigns) do
- ~H"""
-
- """
- end
-
## JS Commands
def show(js \\ %JS{}, selector) do
diff --git a/lib/comfycamp_web/components/flash.ex b/lib/comfycamp_web/components/flash.ex
new file mode 100644
index 0000000..005778e
--- /dev/null
+++ b/lib/comfycamp_web/components/flash.ex
@@ -0,0 +1,97 @@
+defmodule ComfycampWeb.Flash do
+ @moduledoc """
+ Default flash component.
+ """
+ use Phoenix.Component
+ alias Phoenix.LiveView.JS
+ import ComfycampWeb.Gettext
+ import ComfycampWeb.CoreComponents
+ import ComfycampWeb.Icons
+
+ @doc """
+ Renders flash notices.
+
+ ## Examples
+
+ <.flash kind={:info} flash={@flash} />
+ <.flash kind={:info} phx-mounted={show("#flash")}>Welcome Back!
+ """
+ attr :id, :string, doc: "the optional id of flash container"
+ attr :flash, :map, default: %{}, doc: "the map of flash messages to display"
+ attr :title, :string, default: nil
+ attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
+ attr :rest, :global, doc: "the arbitrary HTML attributes to add to the flash container"
+
+ slot :inner_block, doc: "the optional inner block that renders the flash message"
+
+ def flash(assigns) do
+ assigns = assign_new(assigns, :id, fn -> "flash-#{assigns.kind}" end)
+
+ ~H"""
+ hide("##{@id}")}
+ role="alert"
+ class={[
+ "flash",
+ @kind == :info && "flash-info",
+ @kind == :error && "flash-error"
+ ]}
+ {@rest}
+ >
+
+ <.icon :if={@kind == :error} name="hero-exclamation-circle" />
+ <.icon :if={@kind == :info} name="hero-information-circle" />
+ <%= @title %>
+
+
<%= msg %>
+
+ <.icon name="hero-x-mark" class="flash-close-button-icon" />
+
+
+ """
+ end
+
+ @doc """
+ Shows the flash group with standard titles and content.
+
+ ## Examples
+
+ <.flash_group flash={@flash} />
+ """
+ attr :flash, :map, required: true, doc: "the map of flash messages"
+ attr :id, :string, default: "flash-group", doc: "the optional id of flash container"
+
+ def flash_group(assigns) do
+ ~H"""
+
+ <.flash kind={:info} title={gettext("Success!")} flash={@flash} />
+ <.flash kind={:error} title={gettext("Error!")} flash={@flash} />
+ <.flash
+ id="client-error"
+ kind={:error}
+ title={gettext("We can't find the internet")}
+ phx-disconnected={show(".phx-client-error #client-error")}
+ phx-connected={hide("#client-error")}
+ hidden
+ >
+ <%= gettext("Attempting to reconnect") %>
+ <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+
+
+ <.flash
+ id="server-error"
+ kind={:error}
+ title={gettext("Something went wrong!")}
+ phx-disconnected={show(".phx-server-error #server-error")}
+ phx-connected={hide("#server-error")}
+ hidden
+ >
+ <%= gettext("Hang in there while we get back on track") %>
+ <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+
+
+ """
+ end
+end
diff --git a/lib/comfycamp_web/components/icons.ex b/lib/comfycamp_web/components/icons.ex
new file mode 100644
index 0000000..a2451da
--- /dev/null
+++ b/lib/comfycamp_web/components/icons.ex
@@ -0,0 +1,58 @@
+defmodule ComfycampWeb.Icons do
+ @moduledoc """
+ Provides reusable svg icons.
+
+ I would like to store icons as .svg files,
+ but I don’t want to write a custom loader.
+
+ When trying to use the img element, we lose the ability to inherit color.
+
+ For now, I find this approach tolerable.
+ """
+ use Phoenix.Component
+
+ @doc """
+ Icon component.
+
+ The default icon is face-frown from heroicons.
+
+ ## Examples
+
+ <.icon name="hero-x-mark-solid" />
+ <.icon name="hero-arrow-path" class="custom-class" />
+ """
+ attr :name, :string, required: true
+ attr :class, :string, default: nil
+
+ def icon(%{name: "hero-exclamation-circle"} = assigns) do
+ ~H"""
+
+
+
+ """
+ end
+
+ def icon(%{name: "hero-information-circle"} = assigns) do
+ ~H"""
+
+
+
+ """
+ end
+
+ def icon(%{name: "hero-x-mark"} = assigns) do
+ ~H"""
+
+
+
+ """
+ end
+
+ def icon(assigns) do
+ ~H"""
+
+
+
+ """
+ end
+end
diff --git a/mix.exs b/mix.exs
index 7eb214c..0990638 100644
--- a/mix.exs
+++ b/mix.exs
@@ -42,13 +42,6 @@ defmodule Comfycamp.MixProject do
{:floki, ">= 0.30.0", only: :test},
{:phoenix_live_dashboard, "~> 0.8.3"},
{:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
- {:heroicons,
- github: "tailwindlabs/heroicons",
- tag: "v2.1.1",
- sparse: "optimized",
- app: false,
- compile: false,
- depth: 1},
{:swoosh, "~> 1.5"},
{:finch, "~> 0.13"},
{:telemetry_metrics, "~> 1.0"},
diff --git a/mix.lock b/mix.lock
index a4dad25..92cfadf 100644
--- a/mix.lock
+++ b/mix.lock
@@ -12,7 +12,6 @@
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
- "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized"]},
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
new file mode 100644
index 0000000..c6d242f
--- /dev/null
+++ b/priv/gettext/default.pot
@@ -0,0 +1,53 @@
+## This file is a PO Template file.
+##
+## "msgid"s here are often extracted from source code.
+## Add new messages manually only if they're dynamic
+## messages that can't be statically extracted.
+##
+## Run "mix gettext.extract" to bring this file up to
+## date. Leave "msgstr"s empty as changing them here has no
+## effect: edit them in PO (.po) files instead.
+#
+msgid ""
+msgstr ""
+
+#: lib/comfycamp_web/components/core_components.ex:390
+#, elixir-autogen, elixir-format
+msgid "Actions"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:78
+#, elixir-autogen, elixir-format
+msgid "Attempting to reconnect"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:69
+#, elixir-autogen, elixir-format
+msgid "Error!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:90
+#, elixir-autogen, elixir-format
+msgid "Hang in there while we get back on track"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:85
+#, elixir-autogen, elixir-format
+msgid "Something went wrong!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:68
+#, elixir-autogen, elixir-format
+msgid "Success!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:73
+#, elixir-autogen, elixir-format
+msgid "We can't find the internet"
+msgstr ""
+
+#: lib/comfycamp_web/components/core_components.ex:74
+#: lib/comfycamp_web/components/flash.ex:48
+#, elixir-autogen, elixir-format
+msgid "close"
+msgstr ""
diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po
new file mode 100644
index 0000000..4ed7b27
--- /dev/null
+++ b/priv/gettext/en/LC_MESSAGES/default.po
@@ -0,0 +1,53 @@
+## "msgid"s in this file come from POT (.pot) files.
+###
+### Do not add, change, or remove "msgid"s manually here as
+### they're tied to the ones in the corresponding POT file
+### (with the same domain).
+###
+### Use "mix gettext.extract --merge" or "mix gettext.merge"
+### to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: en\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: lib/comfycamp_web/components/core_components.ex:390
+#, elixir-autogen, elixir-format
+msgid "Actions"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:78
+#, elixir-autogen, elixir-format
+msgid "Attempting to reconnect"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:69
+#, elixir-autogen, elixir-format
+msgid "Error!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:90
+#, elixir-autogen, elixir-format
+msgid "Hang in there while we get back on track"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:85
+#, elixir-autogen, elixir-format
+msgid "Something went wrong!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:68
+#, elixir-autogen, elixir-format
+msgid "Success!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:73
+#, elixir-autogen, elixir-format
+msgid "We can't find the internet"
+msgstr ""
+
+#: lib/comfycamp_web/components/core_components.ex:74
+#: lib/comfycamp_web/components/flash.ex:48
+#, elixir-autogen, elixir-format
+msgid "close"
+msgstr ""
diff --git a/priv/gettext/ru/LC_MESSAGES/default.po b/priv/gettext/ru/LC_MESSAGES/default.po
new file mode 100644
index 0000000..52d0193
--- /dev/null
+++ b/priv/gettext/ru/LC_MESSAGES/default.po
@@ -0,0 +1,53 @@
+## "msgid"s in this file come from POT (.pot) files.
+###
+### Do not add, change, or remove "msgid"s manually here as
+### they're tied to the ones in the corresponding POT file
+### (with the same domain).
+###
+### Use "mix gettext.extract --merge" or "mix gettext.merge"
+### to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: ru\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100 != 11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10||n%100>=20) ? 1 : 2);\n"
+
+#: lib/comfycamp_web/components/core_components.ex:390
+#, elixir-autogen, elixir-format
+msgid "Actions"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:78
+#, elixir-autogen, elixir-format
+msgid "Attempting to reconnect"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:69
+#, elixir-autogen, elixir-format
+msgid "Error!"
+msgstr "Ошибка!"
+
+#: lib/comfycamp_web/components/flash.ex:90
+#, elixir-autogen, elixir-format
+msgid "Hang in there while we get back on track"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:85
+#, elixir-autogen, elixir-format
+msgid "Something went wrong!"
+msgstr ""
+
+#: lib/comfycamp_web/components/flash.ex:68
+#, elixir-autogen, elixir-format
+msgid "Success!"
+msgstr "Успех!"
+
+#: lib/comfycamp_web/components/flash.ex:73
+#, elixir-autogen, elixir-format
+msgid "We can't find the internet"
+msgstr ""
+
+#: lib/comfycamp_web/components/core_components.ex:74
+#: lib/comfycamp_web/components/flash.ex:48
+#, elixir-autogen, elixir-format
+msgid "close"
+msgstr ""
diff --git a/priv/gettext/ru/LC_MESSAGES/errors.po b/priv/gettext/ru/LC_MESSAGES/errors.po
new file mode 100644
index 0000000..66e9a13
--- /dev/null
+++ b/priv/gettext/ru/LC_MESSAGES/errors.po
@@ -0,0 +1,111 @@
+## "msgid"s in this file come from POT (.pot) files.
+###
+### Do not add, change, or remove "msgid"s manually here as
+### they're tied to the ones in the corresponding POT file
+### (with the same domain).
+###
+### Use "mix gettext.extract --merge" or "mix gettext.merge"
+### to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: ru\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100 != 11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10||n%100>=20) ? 1 : 2);\n"
+
+msgid "can't be blank"
+msgstr ""
+
+msgid "has already been taken"
+msgstr ""
+
+msgid "is invalid"
+msgstr ""
+
+msgid "must be accepted"
+msgstr ""
+
+msgid "has invalid format"
+msgstr ""
+
+msgid "has an invalid entry"
+msgstr ""
+
+msgid "is reserved"
+msgstr ""
+
+msgid "does not match confirmation"
+msgstr ""
+
+msgid "is still associated with this entry"
+msgstr ""
+
+msgid "are still associated with this entry"
+msgstr ""
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should be %{count} byte(s)"
+msgid_plural "should be %{count} byte(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should be at least %{count} byte(s)"
+msgid_plural "should be at least %{count} byte(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "should be at most %{count} byte(s)"
+msgid_plural "should be at most %{count} byte(s)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "must be less than %{number}"
+msgstr ""
+
+msgid "must be greater than %{number}"
+msgstr ""
+
+msgid "must be less than or equal to %{number}"
+msgstr ""
+
+msgid "must be greater than or equal to %{number}"
+msgstr ""
+
+msgid "must be equal to %{number}"
+msgstr ""