Compare commits

...

8 commits

Author SHA1 Message Date
b5d52426b6 Merge pull request 'feat: add i18n' (#3) from i18n into main
All checks were successful
Checks / check (push) Successful in -6m24s
Build and Push Docker Image / Build and Push Image (push) Successful in -6m1s
Reviewed-on: #3
2024-12-12 18:56:58 +02:00
65308f8bda
style: mix format
All checks were successful
Checks / check (pull_request) Successful in -6m26s
2024-12-12 18:52:36 +02:00
f7f0ebbb25
trans: errors.po 2024-12-12 18:52:07 +02:00
7380d3da3c
trans: switch to ngettext for "n days left"
Some checks failed
Checks / check (pull_request) Failing after -6m14s
2024-12-12 18:19:53 +02:00
a5df93e4b7 Merge branch 'main' into i18n
Some checks failed
Checks / check (pull_request) Failing after -6m27s
2024-12-11 22:38:13 +02:00
094b0f0324
style: mix format
All checks were successful
Checks / check (pull_request) Successful in -6m20s
2024-12-11 22:22:40 +02:00
9e9d8ffdaf
trans: settings and new exam modal
Some checks failed
Checks / check (pull_request) Failing after -6m8s
translated those to russian
2024-12-11 22:18:34 +02:00
cf28aa5295
feat: add i18n
Some checks failed
Checks / check (pull_request) Failing after -6m26s
right not, the most of the app is translated, but there are some parts that are not translated yet, like the settings page.
2024-12-10 00:39:52 +02:00
17 changed files with 952 additions and 76 deletions

View file

@ -83,7 +83,7 @@ defmodule ExmrWeb.CoreComponents do
</button>
</div>
<div id={"#{@id}-content"}>
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</div>
</.focus_wrap>
</div>
@ -128,9 +128,9 @@ defmodule ExmrWeb.CoreComponents do
<p :if={@title} class="flex items-center gap-1.5 text-sm font-semibold leading-6">
<.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 %>
{@title}
</p>
<p class="mt-2 text-sm leading-5"><%= msg %></p>
<p class="mt-2 text-sm leading-5">{msg}</p>
<button type="button" class="group absolute top-1 right-1 p-2" aria-label={gettext("close")}>
<.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
</button>
@ -161,7 +161,7 @@ defmodule ExmrWeb.CoreComponents do
phx-connected={hide("#client-error")}
hidden
>
<%= gettext("Attempting to reconnect") %>
{gettext("Attempting to reconnect")}
<.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
</.flash>
@ -173,7 +173,7 @@ defmodule ExmrWeb.CoreComponents do
phx-connected={hide("#server-error")}
hidden
>
<%= gettext("Hang in there while we get back on track") %>
{gettext("Hang in there while we get back on track")}
<.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
</.flash>
</div>
@ -207,12 +207,12 @@ defmodule ExmrWeb.CoreComponents do
~H"""
<.form :let={f} for={@for} as={@as} {@rest}>
<div class="mt-10 space-y-8 bg-white dark:bg-zinc-950">
<%= render_slot(@inner_block, f) %>
{render_slot(@inner_block, f)}
<div
:for={action <- @actions}
class="mt-2 flex items-center justify-between gap-6 dark:text-zinc-100"
>
<%= render_slot(action, f) %>
{render_slot(action, f)}
</div>
</div>
</.form>
@ -244,7 +244,7 @@ defmodule ExmrWeb.CoreComponents do
]}
{@rest}
>
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</button>
"""
end
@ -328,9 +328,9 @@ defmodule ExmrWeb.CoreComponents do
class="rounded border-zinc-300 text-zinc-900 focus:ring-0"
{@rest}
/>
<%= @label %>
{@label}
</label>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error :for={msg <- @errors}>{msg}</.error>
</div>
"""
end
@ -338,7 +338,7 @@ defmodule ExmrWeb.CoreComponents do
def input(%{type: "select"} = assigns) do
~H"""
<div>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>{@label}</.label>
<select
id={@id}
name={@name}
@ -346,10 +346,10 @@ defmodule ExmrWeb.CoreComponents do
multiple={@multiple}
{@rest}
>
<option :if={@prompt} value=""><%= @prompt %></option>
<%= Phoenix.HTML.Form.options_for_select(@options, @value) %>
<option :if={@prompt} value="">{@prompt}</option>
{Phoenix.HTML.Form.options_for_select(@options, @value)}
</select>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error :for={msg <- @errors}>{msg}</.error>
</div>
"""
end
@ -357,7 +357,7 @@ defmodule ExmrWeb.CoreComponents do
def input(%{type: "textarea"} = assigns) do
~H"""
<div>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>{@label}</.label>
<textarea
id={@id}
name={@name}
@ -368,7 +368,7 @@ defmodule ExmrWeb.CoreComponents do
]}
{@rest}
><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error :for={msg <- @errors}>{msg}</.error>
</div>
"""
end
@ -377,7 +377,7 @@ defmodule ExmrWeb.CoreComponents do
def input(assigns) do
~H"""
<div>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>{@label}</.label>
<input
type={@type}
name={@name}
@ -390,7 +390,7 @@ defmodule ExmrWeb.CoreComponents do
]}
{@rest}
/>
<.error :for={msg <- @errors}><%= msg %></.error>
<.error :for={msg <- @errors}>{msg}</.error>
</div>
"""
end
@ -404,7 +404,7 @@ defmodule ExmrWeb.CoreComponents do
def label(assigns) do
~H"""
<label for={@for} class="block text-sm font-semibold leading-6 text-zinc-800 dark:text-zinc-200">
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</label>
"""
end
@ -418,7 +418,7 @@ defmodule ExmrWeb.CoreComponents do
~H"""
<p class="mt-3 flex gap-3 text-sm leading-6 text-rose-600">
<.icon name="hero-exclamation-circle-mini" class="mt-0.5 h-5 w-5 flex-none" />
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</p>
"""
end
@ -437,13 +437,13 @@ defmodule ExmrWeb.CoreComponents do
<header class={[@actions != [] && "flex items-center justify-between gap-6", @class]}>
<div>
<h1 class="text-lg font-semibold leading-8 text-zinc-800 dark:text-zinc-200">
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</h1>
<p :if={@subtitle != []} class="mt-2 text-sm leading-6 text-zinc-600 dark:text-zinc-100">
<%= render_slot(@subtitle) %>
{render_slot(@subtitle)}
</p>
</div>
<div class="flex-none"><%= render_slot(@actions) %></div>
<div class="flex-none">{render_slot(@actions)}</div>
</header>
"""
end
@ -484,9 +484,9 @@ defmodule ExmrWeb.CoreComponents do
<table class="w-[40rem] mt-11 sm:w-full">
<thead class="text-sm text-left leading-6 text-zinc-500">
<tr>
<th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%= col[:label] %></th>
<th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal">{col[:label]}</th>
<th :if={@action != []} class="relative p-0 pb-4">
<span class="sr-only"><%= gettext("Actions") %></span>
<span class="sr-only">{gettext("Actions")}</span>
</th>
</tr>
</thead>
@ -504,7 +504,7 @@ defmodule ExmrWeb.CoreComponents do
<div class="block py-4 pr-6">
<span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
<span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
<%= render_slot(col, @row_item.(row)) %>
{render_slot(col, @row_item.(row))}
</span>
</div>
</td>
@ -515,7 +515,7 @@ defmodule ExmrWeb.CoreComponents do
:for={action <- @action}
class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
>
<%= render_slot(action, @row_item.(row)) %>
{render_slot(action, @row_item.(row))}
</span>
</div>
</td>
@ -545,8 +545,8 @@ defmodule ExmrWeb.CoreComponents do
<div class="mt-14">
<dl class="-my-4 divide-y divide-zinc-100">
<div :for={item <- @item} class="flex gap-4 py-4 text-sm leading-6 sm:gap-8">
<dt class="w-1/4 flex-none text-zinc-500"><%= item.title %></dt>
<dd class="text-zinc-700"><%= render_slot(item) %></dd>
<dt class="w-1/4 flex-none text-zinc-500">{item.title}</dt>
<dd class="text-zinc-700">{render_slot(item)}</dd>
</div>
</dl>
</div>
@ -571,7 +571,7 @@ defmodule ExmrWeb.CoreComponents do
class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
>
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
<%= render_slot(@inner_block) %>
{render_slot(@inner_block)}
</.link>
</div>
"""

View file

@ -5,7 +5,7 @@
<img src={~p"/images/logo.svg"} width="36" />
</a>
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
v<%= Application.spec(:exmr, :vsn) %>
v{Application.spec(:exmr, :vsn)}
</p>
</div>
</div>
@ -13,6 +13,6 @@
<main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-2xl">
<.flash_group flash={@flash} />
<%= @inner_content %>
{@inner_content}
</div>
</main>

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} />
<.live_title suffix=" · Phoenix Framework">
<%= assigns[:page_title] || "Exmr" %>
{assigns[:page_title] || "Exmr"}
</.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<script>
@ -23,14 +23,14 @@
<DarkMode.button />
<%= if @current_user do %>
<li class="text-[0.8125rem] leading-6 text-zinc-900 dark:text-zinc-300">
<%= @current_user.email %>
{@current_user.email}
</li>
<li>
<.link
href={~p"/users/settings"}
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700 dark:text-zinc-300 dark:hover:text-zinc-100"
>
Settings
{gettext("Settings")}
</.link>
</li>
<li>
@ -39,7 +39,7 @@
method="delete"
class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700 dark:text-zinc-300 dark:hover:text-zinc-100"
>
Log out
{gettext("Log out")}
</.link>
</li>
<% else %>
@ -61,6 +61,6 @@
</li>
<% end %>
</ul>
<%= @inner_content %>
{@inner_content}
</body>
</html>

View file

@ -44,20 +44,20 @@
<h1 class="mt-10 flex items-center text-sm font-semibold leading-6 text-purple-400">
ExMR
<small class="bg-brand/5 text-[0.8125rem] ml-3 rounded-full px-2 font-medium leading-6 text-purple-400">
v<%= Application.spec(:exmr, :vsn) %>
v{Application.spec(:exmr, :vsn)}
</small>
</h1>
<p class="text-[2rem] mt-4 font-semibold leading-10 tracking-tighter text-zinc-900 dark:text-zinc-200/90 text-balance">
hell yeah
{gettext("A simple, modern, and fast exam management system.")}
</p>
<p class="mt-4 text-base leading-7 text-zinc-600 dark:text-zinc-300">
y'all really though i'm gonna be serious? lol
{gettext("Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado")}
</p>
<div class="flex">
<div class="w-full sm:w-auto">
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
<a
href="https://git.vavakado.com/vavakado/exmr"
href="https://git.vavakado.xyz/vavakado/exmr"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-zinc-900 sm:py-6"
>
<span class="absolute inset-0 rounded-2xl bg-zinc-50 transition group-hover:bg-zinc-100 dark:bg-zinc-800 dark:group-hover:bg-zinc-900/95 sm:group-hover:scale-105">
@ -93,7 +93,7 @@
</div>
<div class="text-center py-8 font-bold text-3xl text-red-600">
<.link navigate={~p"/exams"} class="underline hover:text-red-400 transition-all ease-in-out">
exams
{gettext("Exams")}
</.link>
</div>
</div>

7
lib/exmr_web/helpers.ex Normal file
View file

@ -0,0 +1,7 @@
defmodule ExmrWeb.LiveHelpers do
def on_mount(:default, _params, session, socket) do
locale = session["locale"] || "en"
Gettext.put_locale(locale)
{:cont, socket}
end
end

View file

@ -8,8 +8,10 @@ defmodule ExmrWeb.ExamLive.FormComponent do
~H"""
<div>
<.header>
<%= @title %>
<:subtitle>Use this form to manage exam records in your database.</:subtitle>
{@title}
<:subtitle>
{gettext("Use this form to manage exam records in your database.")}
</:subtitle>
</.header>
<.simple_form
@ -19,11 +21,11 @@ defmodule ExmrWeb.ExamLive.FormComponent do
phx-change="validate"
phx-submit="save"
>
<.input field={@form[:subject]} type="text" label="Subject" />
<.input field={@form[:description]} type="text" label="Description" />
<.input field={@form[:date]} type="date" label="Date" />
<.input field={@form[:subject]} type="text" label={gettext("Subject")} />
<.input field={@form[:description]} type="text" label={gettext("Description")} />
<.input field={@form[:date]} type="date" label={gettext("Date")} />
<:actions>
<.button phx-disable-with="Saving...">Save Exam</.button>
<.button phx-disable-with={gettext("Saving...")}>{gettext("Save Exam")}</.button>
</:actions>
</.simple_form>
</div>

View file

@ -29,7 +29,7 @@ defmodule ExmrWeb.ExamLive.Index do
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "New Exam")
|> assign(:page_title, gettext("New Exam"))
|> assign(:exam, %Exam{})
end

View file

@ -1,15 +1,19 @@
<.header>
Listing Exams
{gettext("Listing Exams")}
<div>
<button phx-click="sort" phx-value-by="subject" class="font-light">Sort by Subject</button>
<button phx-click="sort" phx-value-by="subject" class="font-light">
{gettext("Sort by Subject")}
</button>
<a>|</a>
<button phx-click="sort" phx-value-by="date" class="font-light">Sort by Date</button>
<button phx-click="sort" phx-value-by="date" class="font-light">
{gettext("Sort by Date")}
</button>
</div>
<:actions>
<.link patch={~p"/exams/new"}>
<.button class="dark:bg-sky-300 text-white dark:text-black/80 dark:hover:bg-sky-400">
New Exam
{gettext("New Exam")}
</.button>
</.link>
</:actions>
@ -18,9 +22,17 @@
<div class="divide-y">
<div :for={exam <- @exams} class="py-2 flex gap-2">
<div class="grow">
<div class="font-bold"><%= exam.subject %></div>
<div class="font-bold">{exam.subject}</div>
<div class="font-sm">
<%= exam.date %> | In <b><%= Date.diff(exam.date, Date.utc_today()) %></b> days
{exam.date} |
<b>
{case Date.diff(exam.date, Date.utc_today()) do
0 -> gettext("Today")
1 -> gettext("Tommorow")
x when x > 1 -> ngettext("Tomorrow", "%{count} days left", x)
x when x < 0 -> ngettext("Yesterday", "%{count} days passed", x * -1)
end}
</b>
</div>
</div>
<button
@ -28,14 +40,14 @@
phx-value-id={exam.id}
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-3 rounded-md"
>
Edit
{gettext("Edit")}
</button>
<button
phx-click="remove"
phx-value-id={exam.id}
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-3 rounded-md"
>
Remove
{gettext("Remove")}
</button>
</div>
</div>

View file

@ -1,6 +1,6 @@
<.header>
exam №<%= @exam.id %>
<:subtitle>очередная контрольная работа по <strong><%= @exam.subject %></strong></:subtitle>
exam №{@exam.id}
<:subtitle>очередная контрольная работа по <strong>{@exam.subject}</strong></:subtitle>
<:actions>
<.link patch={~p"/exams/#{@exam}/show/edit"} phx-click={JS.push_focus()}>
<.button>изменить</.button>
@ -9,8 +9,8 @@
</.header>
<.list>
<:item title="Description"><%= @exam.description %></:item>
<:item title="Date"><%= @exam.date %></:item>
<:item title="Description">{@exam.description}</:item>
<:item title="Date">{@exam.date}</:item>
</.list>
<.back navigate={~p"/exams"}>Back to exams</.back>

View file

@ -6,8 +6,8 @@ defmodule ExmrWeb.UserSettingsLive do
def render(assigns) do
~H"""
<.header class="text-center">
Account Settings
<:subtitle>Manage your account email address and password settings</:subtitle>
{gettext("Account Settings")}
<:subtitle>{gettext("Manage your account email address and password settings")}</:subtitle>
</.header>
<div class="space-y-12 divide-y">
@ -18,18 +18,20 @@ defmodule ExmrWeb.UserSettingsLive do
phx-submit="update_email"
phx-change="validate_email"
>
<.input field={@email_form[:email]} type="email" label="Email" required />
<.input field={@email_form[:email]} type="email" label={gettext("Email")} required />
<.input
field={@email_form[:current_password]}
name="current_password"
id="current_password_for_email"
type="password"
label="Current password"
label={gettext("Current password")}
value={@email_form_current_password}
required
/>
<:actions>
<.button phx-disable-with="Changing...">Change Email</.button>
<.button phx-disable-with={gettext("Changing...")}>
{gettext("Change Email")}
</.button>
</:actions>
</.simple_form>
</div>
@ -49,23 +51,30 @@ defmodule ExmrWeb.UserSettingsLive do
id="hidden_user_email"
value={@current_email}
/>
<.input field={@password_form[:password]} type="password" label="New password" required />
<.input
field={@password_form[:password]}
type="password"
label={gettext("New password")}
required
/>
<.input
field={@password_form[:password_confirmation]}
type="password"
label="Confirm new password"
label={gettext("Confirm new password")}
/>
<.input
field={@password_form[:current_password]}
name="current_password"
type="password"
label="Current password"
label={gettext("Current password")}
id="current_password_for_password"
value={@current_password}
required
/>
<:actions>
<.button phx-disable-with="Changing...">Change Password</.button>
<.button phx-disable-with={gettext("Changing...")}>
{gettext("Change Password")}
</.button>
</:actions>
</.simple_form>
</div>
@ -77,10 +86,10 @@ defmodule ExmrWeb.UserSettingsLive do
socket =
case Users.update_user_email(socket.assigns.current_user, token) do
:ok ->
put_flash(socket, :info, "Email changed successfully.")
put_flash(socket, :info, gettext("Email changed successfully."))
:error ->
put_flash(socket, :error, "Email change link is invalid or it has expired.")
put_flash(socket, :error, gettext("Email change link is invalid or it has expired."))
end
{:ok, push_navigate(socket, to: ~p"/users/settings")}
@ -127,7 +136,7 @@ defmodule ExmrWeb.UserSettingsLive do
&url(~p"/users/settings/confirm_email/#{&1}")
)
info = "A link to confirm your email change has been sent to the new address."
info = gettext("A link to confirm your email change has been sent to the new address.")
{:noreply, socket |> put_flash(:info, info) |> assign(email_form_current_password: nil)}
{:error, changeset} ->

View file

@ -0,0 +1,67 @@
defmodule ExmrWeb.Plugs.Locale do
import Plug.Conn
def init(_opts), do: nil
def call(conn, _opts) do
accepted_languages = extract_accept_language(conn)
known_locales = Gettext.known_locales(ExmrWeb.Gettext)
accepted_languages =
known_locales --
known_locales -- accepted_languages
case accepted_languages do
[locale | _] ->
Gettext.put_locale(ExmrWeb.Gettext, locale)
conn
|> put_session(:locale, locale)
_ ->
conn
end
end
# Copied from
# https://raw.githubusercontent.com/smeevil/set_locale/fd35624e25d79d61e70742e42ade955e5ff857b8/lib/headers.ex
def extract_accept_language(conn) do
case Plug.Conn.get_req_header(conn, "accept-language") do
[value | _] ->
value
|> String.split(",")
|> Enum.map(&parse_language_option/1)
|> Enum.sort(&(&1.quality > &2.quality))
|> Enum.map(& &1.tag)
|> Enum.reject(&is_nil/1)
|> ensure_language_fallbacks()
_ ->
[]
end
end
defp parse_language_option(string) do
captures = Regex.named_captures(~r/^\s?(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i, string)
quality =
case Float.parse(captures["quality"] || "1.0") do
{val, _} -> val
_ -> 1.0
end
%{tag: captures["tag"], quality: quality}
end
defp ensure_language_fallbacks(tags) do
Enum.flat_map(tags, fn tag ->
case String.split(tag, "-") do
[language, _country_variant] ->
if Enum.member?(tags, language), do: [tag], else: [tag, language]
[_language] ->
[tag]
end
end)
end
end

View file

@ -12,6 +12,7 @@ defmodule ExmrWeb.Router do
plug :put_root_layout, html: {ExmrWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
plug ExmrWeb.Plugs.Locale
plug :fetch_current_user
end
@ -53,7 +54,7 @@ defmodule ExmrWeb.Router do
pipe_through [:browser, :redirect_if_user_is_authenticated]
live_session :redirect_if_user_is_authenticated,
on_mount: [{ExmrWeb.UserAuth, :redirect_if_user_is_authenticated}] do
on_mount: [{ExmrWeb.UserAuth, :redirect_if_user_is_authenticated}, ExmrWeb.LiveHelpers] do
if Exmr.enable_registration() != "false" do
live "/users/register", UserRegistrationLive, :new
end
@ -70,7 +71,7 @@ defmodule ExmrWeb.Router do
pipe_through [:browser, :require_authenticated_user]
live_session :require_authenticated_user,
on_mount: [{ExmrWeb.UserAuth, :ensure_authenticated}] do
on_mount: [{ExmrWeb.UserAuth, :ensure_authenticated}, ExmrWeb.LiveHelpers] do
if Exmr.enable_registration() == "false" do
live "/users/register", UserRegistrationLive, :new
end
@ -93,7 +94,7 @@ defmodule ExmrWeb.Router do
delete "/users/log_out", UserSessionController, :delete
live_session :current_user,
on_mount: [{ExmrWeb.UserAuth, :mount_current_user}] do
on_mount: [{ExmrWeb.UserAuth, :mount_current_user}, ExmrWeb.LiveHelpers] do
live "/users/confirm/:token", UserConfirmationLive, :edit
live "/users/confirm", UserConfirmationInstructionsLive, :new
end

225
priv/gettext/default.pot Normal file
View file

@ -0,0 +1,225 @@
## 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/exmr_web/controllers/page_html/home.html.heex:51
#, elixir-autogen, elixir-format
msgid "A simple, modern, and fast exam management system."
msgstr ""
#: lib/exmr_web/components/core_components.ex:489
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
#: lib/exmr_web/components/core_components.ex:164
#, elixir-autogen, elixir-format
msgid "Attempting to reconnect"
msgstr ""
#: lib/exmr_web/controllers/page_html/home.html.heex:54
#, elixir-autogen, elixir-format
msgid "Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:44
#, elixir-autogen, elixir-format
msgid "Edit"
msgstr ""
#: lib/exmr_web/components/core_components.ex:155
#, elixir-autogen, elixir-format
msgid "Error!"
msgstr ""
#: lib/exmr_web/controllers/page_html/home.html.heex:96
#, elixir-autogen, elixir-format
msgid "Exams"
msgstr ""
#: lib/exmr_web/components/core_components.ex:176
#, elixir-autogen, elixir-format
msgid "Hang in there while we get back on track"
msgstr ""
#: lib/exmr_web/live/exam_live/index.ex:32
#: lib/exmr_web/live/exam_live/index.html.heex:16
#, elixir-autogen, elixir-format
msgid "New Exam"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:51
#, elixir-autogen, elixir-format
msgid "Remove"
msgstr ""
#: lib/exmr_web/components/core_components.ex:171
#, elixir-autogen, elixir-format
msgid "Something went wrong!"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:10
#, elixir-autogen, elixir-format
msgid "Sort by Date"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:5
#, elixir-autogen, elixir-format
msgid "Sort by Subject"
msgstr ""
#: lib/exmr_web/components/core_components.ex:154
#, elixir-autogen, elixir-format
msgid "Success!"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:31
#, elixir-autogen, elixir-format
msgid "Today"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:33
#, elixir-autogen, elixir-format
msgid "Tomorrow"
msgid_plural "%{count} days left"
msgstr[0] ""
msgstr[1] ""
#: lib/exmr_web/components/core_components.ex:159
#, elixir-autogen, elixir-format
msgid "We can't find the internet"
msgstr ""
#: lib/exmr_web/components/core_components.ex:80
#: lib/exmr_web/components/core_components.ex:134
#, elixir-autogen, elixir-format
msgid "close"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:139
#, elixir-autogen, elixir-format
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:9
#, elixir-autogen, elixir-format
msgid "Account Settings"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:33
#, elixir-autogen, elixir-format
msgid "Change Email"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:76
#, elixir-autogen, elixir-format
msgid "Change Password"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:32
#: lib/exmr_web/live/user_settings_live.ex:75
#, elixir-autogen, elixir-format
msgid "Changing..."
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:63
#, elixir-autogen, elixir-format
msgid "Confirm new password"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:27
#: lib/exmr_web/live/user_settings_live.ex:69
#, elixir-autogen, elixir-format
msgid "Current password"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:21
#, elixir-autogen, elixir-format
msgid "Email"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:92
#, elixir-autogen, elixir-format
msgid "Email change link is invalid or it has expired."
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:89
#, elixir-autogen, elixir-format
msgid "Email changed successfully."
msgstr ""
#: lib/exmr_web/components/layouts/root.html.heex:42
#, elixir-autogen, elixir-format
msgid "Log out"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:10
#, elixir-autogen, elixir-format
msgid "Manage your account email address and password settings"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:57
#, elixir-autogen, elixir-format
msgid "New password"
msgstr ""
#: lib/exmr_web/components/layouts/root.html.heex:33
#, elixir-autogen, elixir-format
msgid "Settings"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:26
#, elixir-autogen, elixir-format
msgid "Date"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:25
#, elixir-autogen, elixir-format
msgid "Description"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:24
#, elixir-autogen, elixir-format
msgid "Subject"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:13
#, elixir-autogen, elixir-format
msgid "Use this form to manage exam records in your database."
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:2
#, elixir-autogen, elixir-format
msgid "Listing Exams"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:28
#, elixir-autogen, elixir-format
msgid "Save Exam"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:28
#, elixir-autogen, elixir-format
msgid "Saving..."
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:34
#, elixir-autogen, elixir-format
msgid "Yesterday"
msgid_plural "%{count} days passed"
msgstr[0] ""
msgstr[1] ""
#: lib/exmr_web/live/exam_live/index.html.heex:32
#, elixir-autogen, elixir-format
msgid "Tommorow"
msgstr ""

View file

@ -0,0 +1,225 @@
## "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/exmr_web/controllers/page_html/home.html.heex:51
#, elixir-autogen, elixir-format
msgid "A simple, modern, and fast exam management system."
msgstr ""
#: lib/exmr_web/components/core_components.ex:489
#, elixir-autogen, elixir-format
msgid "Actions"
msgstr ""
#: lib/exmr_web/components/core_components.ex:164
#, elixir-autogen, elixir-format
msgid "Attempting to reconnect"
msgstr ""
#: lib/exmr_web/controllers/page_html/home.html.heex:54
#, elixir-autogen, elixir-format
msgid "Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:44
#, elixir-autogen, elixir-format
msgid "Edit"
msgstr ""
#: lib/exmr_web/components/core_components.ex:155
#, elixir-autogen, elixir-format
msgid "Error!"
msgstr ""
#: lib/exmr_web/controllers/page_html/home.html.heex:96
#, elixir-autogen, elixir-format
msgid "Exams"
msgstr ""
#: lib/exmr_web/components/core_components.ex:176
#, elixir-autogen, elixir-format
msgid "Hang in there while we get back on track"
msgstr ""
#: lib/exmr_web/live/exam_live/index.ex:32
#: lib/exmr_web/live/exam_live/index.html.heex:16
#, elixir-autogen, elixir-format
msgid "New Exam"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:51
#, elixir-autogen, elixir-format
msgid "Remove"
msgstr ""
#: lib/exmr_web/components/core_components.ex:171
#, elixir-autogen, elixir-format
msgid "Something went wrong!"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:10
#, elixir-autogen, elixir-format
msgid "Sort by Date"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:5
#, elixir-autogen, elixir-format
msgid "Sort by Subject"
msgstr ""
#: lib/exmr_web/components/core_components.ex:154
#, elixir-autogen, elixir-format
msgid "Success!"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:31
#, elixir-autogen, elixir-format
msgid "Today"
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:33
#, elixir-autogen, elixir-format
msgid "Tomorrow"
msgid_plural "%{count} days left"
msgstr[0] ""
msgstr[1] ""
#: lib/exmr_web/components/core_components.ex:159
#, elixir-autogen, elixir-format
msgid "We can't find the internet"
msgstr ""
#: lib/exmr_web/components/core_components.ex:80
#: lib/exmr_web/components/core_components.ex:134
#, elixir-autogen, elixir-format
msgid "close"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:139
#, elixir-autogen, elixir-format
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:9
#, elixir-autogen, elixir-format
msgid "Account Settings"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:33
#, elixir-autogen, elixir-format
msgid "Change Email"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:76
#, elixir-autogen, elixir-format
msgid "Change Password"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:32
#: lib/exmr_web/live/user_settings_live.ex:75
#, elixir-autogen, elixir-format
msgid "Changing..."
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:63
#, elixir-autogen, elixir-format
msgid "Confirm new password"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:27
#: lib/exmr_web/live/user_settings_live.ex:69
#, elixir-autogen, elixir-format
msgid "Current password"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:21
#, elixir-autogen, elixir-format
msgid "Email"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:92
#, elixir-autogen, elixir-format
msgid "Email change link is invalid or it has expired."
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:89
#, elixir-autogen, elixir-format
msgid "Email changed successfully."
msgstr ""
#: lib/exmr_web/components/layouts/root.html.heex:42
#, elixir-autogen, elixir-format
msgid "Log out"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:10
#, elixir-autogen, elixir-format
msgid "Manage your account email address and password settings"
msgstr ""
#: lib/exmr_web/live/user_settings_live.ex:57
#, elixir-autogen, elixir-format
msgid "New password"
msgstr ""
#: lib/exmr_web/components/layouts/root.html.heex:33
#, elixir-autogen, elixir-format
msgid "Settings"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:26
#, elixir-autogen, elixir-format
msgid "Date"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:25
#, elixir-autogen, elixir-format
msgid "Description"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:24
#, elixir-autogen, elixir-format
msgid "Subject"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:13
#, elixir-autogen, elixir-format
msgid "Use this form to manage exam records in your database."
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:2
#, elixir-autogen, elixir-format
msgid "Listing Exams"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:28
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Exam"
msgstr ""
#: lib/exmr_web/live/exam_live/form_component.ex:28
#, elixir-autogen, elixir-format
msgid "Saving..."
msgstr ""
#: lib/exmr_web/live/exam_live/index.html.heex:34
#, elixir-autogen, elixir-format
msgid "Yesterday"
msgid_plural "%{count} days passed"
msgstr[0] ""
msgstr[1] ""
#: lib/exmr_web/live/exam_live/index.html.heex:32
#, elixir-autogen, elixir-format, fuzzy
msgid "Tommorow"
msgstr ""

Binary file not shown.

View file

@ -0,0 +1,206 @@
# # 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.
# Vladimir Rubin <vavakado@proton.me>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: unnamed project\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2024-12-12 18:19+0200\n"
"Last-Translator: Vladimir Rubin <vavakado@proton.me>\n"
"Language-Team: Russian\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Gtranslator 47.1\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/exmr_web/controllers/page_html/home.html.heex:51
msgid "A simple, modern, and fast exam management system."
msgstr "Простая, современная и быстрая система управления экзаменами."
#: lib/exmr_web/components/core_components.ex:489
msgid "Actions"
msgstr "Действия"
#: lib/exmr_web/components/core_components.ex:164
msgid "Attempting to reconnect"
msgstr "Попытка восстановить соединение"
#: lib/exmr_web/controllers/page_html/home.html.heex:54
msgid "Built using Phoenix LiveView, Ecto, and TailwindCSS by @vavakado"
msgstr ""
"Создано с использованием Phoenix LiveView, Ecto и TailwindCSS в исполнении "
"@vavakado"
#: lib/exmr_web/live/exam_live/index.html.heex:44
msgid "Edit"
msgstr "Изменить"
#: lib/exmr_web/components/core_components.ex:155
msgid "Error!"
msgstr "Ошибка!"
#: lib/exmr_web/controllers/page_html/home.html.heex:96
msgid "Exams"
msgstr "Экзамены"
#: lib/exmr_web/components/core_components.ex:176
msgid "Hang in there while we get back on track"
msgstr "Держитесь, пока мы не вернемся в строй"
#: lib/exmr_web/live/exam_live/index.ex:32
#: lib/exmr_web/live/exam_live/index.html.heex:16
msgid "New Exam"
msgstr "Новый экзамен"
#: lib/exmr_web/live/exam_live/index.html.heex:51
msgid "Remove"
msgstr "Удалить"
#: lib/exmr_web/components/core_components.ex:171
msgid "Something went wrong!"
msgstr "Что-то пошло не так!"
#: lib/exmr_web/live/exam_live/index.html.heex:10
msgid "Sort by Date"
msgstr "Сортировать по дате"
#: lib/exmr_web/live/exam_live/index.html.heex:5
msgid "Sort by Subject"
msgstr "Сортировать по предмету"
#: lib/exmr_web/components/core_components.ex:154
msgid "Success!"
msgstr "Успех!"
#: lib/exmr_web/live/exam_live/index.html.heex:31
msgid "Today"
msgstr "Сегодня"
#: lib/exmr_web/live/exam_live/index.html.heex:33
msgid "Tomorrow"
msgid_plural "%{count} days left"
msgstr[0] "%{count} день остался"
msgstr[1] "%{count} дня осталось"
msgstr[2] "%{count} дней осталось"
#: lib/exmr_web/components/core_components.ex:159
msgid "We can't find the internet"
msgstr "Мы не можем найти интернет"
#: lib/exmr_web/components/core_components.ex:80
#: lib/exmr_web/components/core_components.ex:134
msgid "close"
msgstr "закрыть"
#: lib/exmr_web/live/user_settings_live.ex:139
msgid "A link to confirm your email change has been sent to the new address."
msgstr ""
"На новый адрес отправлена ссылка для подтверждения изменения электронной "
"почты."
#: lib/exmr_web/live/user_settings_live.ex:9
msgid "Account Settings"
msgstr "Настройки аккаунта"
#: lib/exmr_web/live/user_settings_live.ex:33
msgid "Change Email"
msgstr "Изменить электронную почту"
#: lib/exmr_web/live/user_settings_live.ex:76
msgid "Change Password"
msgstr "Изменить пароль"
#: lib/exmr_web/live/user_settings_live.ex:32
#: lib/exmr_web/live/user_settings_live.ex:75
msgid "Changing..."
msgstr "Меняется..."
#: lib/exmr_web/live/user_settings_live.ex:63
msgid "Confirm new password"
msgstr "Подтвердите новый пароль"
#: lib/exmr_web/live/user_settings_live.ex:27
#: lib/exmr_web/live/user_settings_live.ex:69
msgid "Current password"
msgstr "Текущий пароль"
#: lib/exmr_web/live/user_settings_live.ex:21
msgid "Email"
msgstr "Электронная почта"
#: lib/exmr_web/live/user_settings_live.ex:92
msgid "Email change link is invalid or it has expired."
msgstr ""
"Ссылка для изменения электронной почты недействительна или срок ее действия "
"истек."
#: lib/exmr_web/live/user_settings_live.ex:89
msgid "Email changed successfully."
msgstr "Электронная почта успешно изменена."
#: lib/exmr_web/components/layouts/root.html.heex:42
msgid "Log out"
msgstr "Выйти из системы"
#: lib/exmr_web/live/user_settings_live.ex:10
msgid "Manage your account email address and password settings"
msgstr "Управление адресом электронной почты и настройками пароля"
#: lib/exmr_web/live/user_settings_live.ex:57
msgid "New password"
msgstr "Новый пароль"
#: lib/exmr_web/components/layouts/root.html.heex:33
msgid "Settings"
msgstr "Настройки"
#: lib/exmr_web/live/exam_live/form_component.ex:26
msgid "Date"
msgstr "Дата"
#: lib/exmr_web/live/exam_live/form_component.ex:25
msgid "Description"
msgstr "Описание"
#: lib/exmr_web/live/exam_live/form_component.ex:24
msgid "Subject"
msgstr "Предмет"
#: lib/exmr_web/live/exam_live/form_component.ex:13
msgid "Use this form to manage exam records in your database."
msgstr ""
"Используйте эту форму для управления записями экзаменов в вашей базе данных."
#: lib/exmr_web/live/exam_live/index.html.heex:2
msgid "Listing Exams"
msgstr "Список Экзаменов"
#: lib/exmr_web/live/exam_live/form_component.ex:28
msgid "Save Exam"
msgstr "Сохранить экзамен"
#: lib/exmr_web/live/exam_live/form_component.ex:28
msgid "Saving..."
msgstr "Сохранение..."
#: lib/exmr_web/live/exam_live/index.html.heex:34
msgid "Yesterday"
msgid_plural "%{count} days passed"
msgstr[0] "%{count} день прошёл"
msgstr[1] "%{count} дня прошло"
msgstr[2] "%{count} дней прошло"
#: lib/exmr_web/live/exam_live/index.html.heex:32
msgid "Tommorow"
msgstr "Завтра"

View file

@ -0,0 +1,122 @@
# # "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.
# Vladimir Rubin <vavakado@proton.me>, 2024.
#
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"
"MIME-Version: 1.0\n"
"Project-Id-Version: unnamed project\n"
"Last-Translator: Vladimir Rubin <vavakado@proton.me>\n"
"Language-Team: Russian\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2024-12-12 18:52+0200\n"
"X-Generator: Gtranslator 47.1\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] "должен иметь %{count} элементов"
msgstr[1] "должен иметь %{count} элемента"
msgstr[2] "должен иметь %{count} элементов"
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] "должен быть %{count} символов"
msgstr[1] "должен быть %{count} символа"
msgstr[2] "должен быть %{count} символов"
msgid "should be %{count} byte(s)"
msgid_plural "should be %{count} byte(s)"
msgstr[0] "должно быть %{count} байт"
msgstr[1] "должно быть %{count} байта"
msgstr[2] "должно быть %{count} байтов"
msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] "должно быть не менее %{count} элементов"
msgstr[1] "должно быть не менее %{count} элемента"
msgstr[2] "должно быть не менее %{count} элементов"
msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] "должно быть не менее %{count} символов"
msgstr[1] "должно быть не менее %{count} символа"
msgstr[2] "должно быть не менее %{count} символов"
msgid "should be at least %{count} byte(s)"
msgid_plural "should be at least %{count} byte(s)"
msgstr[0] "должно быть хотя бы %{count} байт"
msgstr[1] "должно быть хотя бы %{count} байта"
msgstr[2] "должно быть хотя бы %{count} байтов"
msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] "должно содержать не более %{count} элементов"
msgstr[1] "должно содержать не более %{count} элементов"
msgstr[2] "должно содержать не более %{count} элементов"
msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] "должно быть не более %{count} символа(ов)"
msgstr[1] "должно быть не более %{count} символа(ов)"
msgstr[2] "должно быть не более %{count} символа(ов)"
msgid "should be at most %{count} byte(s)"
msgid_plural "should be at most %{count} byte(s)"
msgstr[0] "должно быть не более %{count} байт(ов)."
msgstr[1] "должно быть не более %{count} байт(ов)."
msgstr[2] "должно быть не более %{count} байт(ов)."
msgid "must be less than %{number}"
msgstr "должно быть меньше, чем %{number}"
msgid "must be greater than %{number}"
msgstr "должно быть больше, чем %{number}"
msgid "must be less than or equal to %{number}"
msgstr "должно быть меньше или равно %{number}"
msgid "must be greater than or equal to %{number}"
msgstr "должно быть больше или равно %{number}"
msgid "must be equal to %{number}"
msgstr "должно быть равно %{number}"