diff --git a/assets/js/features/weather_charts.ts b/assets/js/features/weather_charts.ts index f3b45ef..3281d5d 100644 --- a/assets/js/features/weather_charts.ts +++ b/assets/js/features/weather_charts.ts @@ -59,6 +59,16 @@ function unregisterChartInstance(el: HTMLElement) { } } +function getLabels(el: HTMLElement) { + const raw = el.getAttribute('data-chart-labels'); + if (!raw) return {}; + try { + return JSON.parse(raw); + } catch { + return {}; + } +} + // All hooks are typed as 'any' for LiveView context compatibility export const WeatherChartHooks: Record = { ChartJSTempChart: { @@ -83,6 +93,7 @@ export const WeatherChartHooks: Record = { const self = this as any; if (self.chart) self.chart.destroy(); const data: WeatherHistoryDatum[] = JSON.parse(self.el.dataset.weatherHistory!); + const labels = getLabels(self.el); const times = data.map(d => new Date(d.timestamp)); const temps = data.map(d => d.temperature); const dews = data.map(d => d.dew_point); @@ -94,20 +105,20 @@ export const WeatherChartHooks: Record = { data: { labels: times, datasets: [ - { label: 'Temperature (°F)', data: temps, borderColor: 'red', backgroundColor: 'rgba(255, 0, 0, 0.1)', tension: 0.1, pointRadius: 0 }, - { label: 'Dew Point (°F)', data: dews, borderColor: 'blue', backgroundColor: 'rgba(0, 0, 255, 0.1)', tension: 0.1, pointRadius: 0 } + { label: labels.temp_label || 'Temperature (°F)', data: temps, borderColor: 'red', backgroundColor: 'rgba(255, 0, 0, 0.1)', tension: 0.1, pointRadius: 0 }, + { label: labels.dew_label || 'Dew Point (°F)', data: dews, borderColor: 'blue', backgroundColor: 'rgba(0, 0, 255, 0.1)', tension: 0.1, pointRadius: 0 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { - title: { display: true, text: 'Temperature & Dew Point (°F)', color: colors.text }, + title: { display: true, text: labels.temp_title || 'Temperature & Dew Point (°F)', color: colors.text }, legend: { labels: { color: colors.text } } }, scales: { - x: { type: 'time', time: { displayFormats: { hour: 'HH:mm', minute: 'HH:mm' } }, title: { display: true, text: 'Time', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } }, - y: { title: { display: true, text: '°F', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } } + x: { type: 'time', time: { displayFormats: { hour: 'HH:mm', minute: 'HH:mm' } }, title: { display: true, text: labels.time || 'Time', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } }, + y: { title: { display: true, text: labels.degf || '°F', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } } } } }); @@ -135,6 +146,7 @@ export const WeatherChartHooks: Record = { const self = this as any; if (self.chart) self.chart.destroy(); const data: WeatherHistoryDatum[] = JSON.parse(self.el.dataset.weatherHistory!); + const labels = getLabels(self.el); const times = data.map(d => new Date(d.timestamp)); const humidity = data.map(d => d.humidity); const colors = getThemeColors(); @@ -142,12 +154,12 @@ export const WeatherChartHooks: Record = { if (!canvas) return; self.chart = new window.Chart(canvas, { type: 'line', - data: { labels: times, datasets: [{ label: 'Humidity (%)', data: humidity, borderColor: 'green', backgroundColor: 'rgba(0, 255, 0, 0.1)', tension: 0.1, pointRadius: 0 }] }, + data: { labels: times, datasets: [{ label: labels.humidity_label || 'Humidity (%)', data: humidity, borderColor: 'green', backgroundColor: 'rgba(0, 255, 0, 0.1)', tension: 0.1, pointRadius: 0 }] }, options: { responsive: true, maintainAspectRatio: false, - plugins: { title: { display: true, text: 'Humidity (%)', color: colors.text }, legend: { labels: { color: colors.text } } }, - scales: { x: { type: 'time', time: { displayFormats: { hour: 'HH:mm', minute: 'HH:mm' } }, title: { display: true, text: 'Time', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } }, y: { title: { display: true, text: '%', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } } } + plugins: { title: { display: true, text: labels.humidity_title || 'Humidity (%)', color: colors.text }, legend: { labels: { color: colors.text } } }, + scales: { x: { type: 'time', time: { displayFormats: { hour: 'HH:mm', minute: 'HH:mm' } }, title: { display: true, text: labels.time || 'Time', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } }, y: { title: { display: true, text: labels.percent || '%', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } } } } }); } @@ -174,6 +186,7 @@ export const WeatherChartHooks: Record = { const self = this as any; if (self.chart) self.chart.destroy(); const data: WeatherHistoryDatum[] = JSON.parse(self.el.dataset.weatherHistory!); + const labels = getLabels(self.el); const times = data.map(d => new Date(d.timestamp)); const pressure = data.map(d => d.pressure); const colors = getThemeColors(); @@ -181,12 +194,12 @@ export const WeatherChartHooks: Record = { if (!canvas) return; self.chart = new window.Chart(canvas, { type: 'line', - data: { labels: times, datasets: [{ label: 'Pressure (mb)', data: pressure, borderColor: 'purple', backgroundColor: 'rgba(128, 0, 128, 0.1)', tension: 0.1, pointRadius: 0 }] }, + data: { labels: times, datasets: [{ label: labels.pressure_label || 'Pressure (mb)', data: pressure, borderColor: 'purple', backgroundColor: 'rgba(128, 0, 128, 0.1)', tension: 0.1, pointRadius: 0 }] }, options: { responsive: true, maintainAspectRatio: false, - plugins: { title: { display: true, text: 'Pressure (mb)', color: colors.text }, legend: { labels: { color: colors.text } } }, - scales: { x: { type: 'time', time: { displayFormats: { hour: 'HH:mm', minute: 'HH:mm' } }, title: { display: true, text: 'Time', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } }, y: { title: { display: true, text: 'mb', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } } } + plugins: { title: { display: true, text: labels.pressure_title || 'Pressure (mb)', color: colors.text }, legend: { labels: { color: colors.text } } }, + scales: { x: { type: 'time', time: { displayFormats: { hour: 'HH:mm', minute: 'HH:mm' } }, title: { display: true, text: labels.time || 'Time', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } }, y: { title: { display: true, text: labels.mb || 'mb', color: colors.text }, ticks: { color: colors.text }, grid: { color: colors.grid } } } } }); } diff --git a/config/config.exs b/config/config.exs index e64f6da..a58bde6 100644 --- a/config/config.exs +++ b/config/config.exs @@ -26,6 +26,11 @@ config :aprsme, AprsmeWeb.Endpoint, pubsub_server: Aprsme.PubSub, live_view: [signing_salt: "ees098qG"] +# Configure Gettext +config :aprsme, AprsmeWeb.Gettext, + locales: ~w(en es), + default_locale: "en" + # Configure Oban for background jobs config :aprsme, Oban, repo: Aprsme.Repo, diff --git a/lib/aprsme/application.ex b/lib/aprsme/application.ex index 6137642..b3c076a 100644 --- a/lib/aprsme/application.ex +++ b/lib/aprsme/application.ex @@ -68,6 +68,8 @@ defmodule Aprsme.Application do defp migrate do auto_migrate = Application.get_env(:aprsme, :auto_migrate, true) do_migrate(auto_migrate) + + # Gettext translations are automatically compiled during Mix compilation rescue error -> require Logger diff --git a/lib/aprsme/device_identification.ex b/lib/aprsme/device_identification.ex index c981d5b..bd5b9fc 100644 --- a/lib/aprsme/device_identification.ex +++ b/lib/aprsme/device_identification.ex @@ -2,6 +2,7 @@ defmodule Aprsme.DeviceIdentification do @moduledoc """ Handles APRS device identification based on the APRS device identification database. """ + use Gettext, backend: AprsmeWeb.Gettext import Ecto.Query @@ -52,7 +53,7 @@ defmodule Aprsme.DeviceIdentification do """ @spec identify_device(String.t()) :: String.t() def identify_device(symbols) do - Enum.find_value(@device_patterns, "Unknown", fn {regex, name} -> + Enum.find_value(@device_patterns, gettext("Unknown"), fn {regex, name} -> if Regex.match?(regex, symbols) do name end diff --git a/lib/aprsme/release.ex b/lib/aprsme/release.ex index 5dba83d..6374a93 100644 --- a/lib/aprsme/release.ex +++ b/lib/aprsme/release.ex @@ -11,6 +11,8 @@ defmodule Aprsme.Release do deployed_at = init() Logger.info("Deployment timestamp: #{deployed_at}") + # Gettext translations are automatically compiled during Mix compilation + # Run migrations {:ok, _, _} = Ecto.Migrator.with_repo(Aprsme.Repo, &Ecto.Migrator.run(&1, :up, all: true)) end diff --git a/lib/aprsme_web/controllers/api/v1/fallback_controller.ex b/lib/aprsme_web/controllers/api/v1/fallback_controller.ex index 193f6f6..c28e42c 100644 --- a/lib/aprsme_web/controllers/api/v1/fallback_controller.ex +++ b/lib/aprsme_web/controllers/api/v1/fallback_controller.ex @@ -5,6 +5,7 @@ defmodule AprsmeWeb.Api.V1.FallbackController do See `Phoenix.Controller.action_fallback/1` for more details. """ use AprsmeWeb, :controller + use Gettext, backend: AprsmeWeb.Gettext alias AprsmeWeb.Api.V1.ErrorJSON @@ -53,7 +54,7 @@ defmodule AprsmeWeb.Api.V1.FallbackController do conn |> put_status(:request_timeout) |> put_view(json: ErrorJSON) - |> render(:error, message: "Request timeout") + |> render(:error, message: gettext("Request timeout")) end # Default fallback for unexpected errors @@ -64,9 +65,9 @@ defmodule AprsmeWeb.Api.V1.FallbackController do |> render(:"500") end - defp format_error_message(:not_found), do: "Resource not found" - defp format_error_message(:unauthorized), do: "Unauthorized access" - defp format_error_message(:forbidden), do: "Access forbidden" - defp format_error_message(:bad_request), do: "Bad request" - defp format_error_message(reason), do: "An error occurred: #{reason}" + defp format_error_message(:not_found), do: gettext("Resource not found") + defp format_error_message(:unauthorized), do: gettext("Unauthorized access") + defp format_error_message(:forbidden), do: gettext("Access forbidden") + defp format_error_message(:bad_request), do: gettext("Bad request") + defp format_error_message(reason), do: gettext("An error occurred: %{reason}", reason: reason) end diff --git a/lib/aprsme_web/controllers/api/v1/json/changeset_json.ex b/lib/aprsme_web/controllers/api/v1/json/changeset_json.ex index 42cb7ac..31bfd49 100644 --- a/lib/aprsme_web/controllers/api/v1/json/changeset_json.ex +++ b/lib/aprsme_web/controllers/api/v1/json/changeset_json.ex @@ -2,6 +2,7 @@ defmodule AprsmeWeb.Api.V1.ChangesetJSON do @moduledoc """ Renders changeset errors for API v1. """ + use Gettext, backend: AprsmeWeb.Gettext @doc """ Renders changeset errors. @@ -9,7 +10,7 @@ defmodule AprsmeWeb.Api.V1.ChangesetJSON do def render("error.json", %{changeset: changeset}) do %{ error: %{ - message: "Validation failed", + message: gettext("Validation failed"), code: "validation_error", details: translate_errors(changeset) } diff --git a/lib/aprsme_web/controllers/api/v1/json/error_json.ex b/lib/aprsme_web/controllers/api/v1/json/error_json.ex index 9ebfd34..e3c3a3c 100644 --- a/lib/aprsme_web/controllers/api/v1/json/error_json.ex +++ b/lib/aprsme_web/controllers/api/v1/json/error_json.ex @@ -2,6 +2,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do @moduledoc """ Renders error responses for API v1. """ + use Gettext, backend: AprsmeWeb.Gettext def render("error.json", %{message: message}) do %{ @@ -15,7 +16,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("404.json", _assigns) do %{ error: %{ - message: "Resource not found", + message: gettext("Resource not found"), code: "not_found" } } @@ -24,7 +25,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("500.json", _assigns) do %{ error: %{ - message: "Internal server error", + message: gettext("Internal server error"), code: "internal_server_error" } } @@ -33,7 +34,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("422.json", _assigns) do %{ error: %{ - message: "Unprocessable entity", + message: gettext("Unprocessable entity"), code: "unprocessable_entity" } } @@ -42,7 +43,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("400.json", _assigns) do %{ error: %{ - message: "Bad request", + message: gettext("Bad request"), code: "bad_request" } } @@ -51,7 +52,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("401.json", _assigns) do %{ error: %{ - message: "Unauthorized", + message: gettext("Unauthorized"), code: "unauthorized" } } @@ -60,7 +61,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("403.json", _assigns) do %{ error: %{ - message: "Forbidden", + message: gettext("Forbidden"), code: "forbidden" } } @@ -69,7 +70,7 @@ defmodule AprsmeWeb.Api.V1.ErrorJSON do def render("408.json", _assigns) do %{ error: %{ - message: "Request timeout", + message: gettext("Request timeout"), code: "request_timeout" } } diff --git a/lib/aprsme_web/live/about_live.ex b/lib/aprsme_web/live/about_live.ex index a3af7f9..677cf75 100644 --- a/lib/aprsme_web/live/about_live.ex +++ b/lib/aprsme_web/live/about_live.ex @@ -1,6 +1,7 @@ defmodule AprsmeWeb.AboutLive do @moduledoc false use AprsmeWeb, :live_view + use Gettext, backend: AprsmeWeb.Gettext @impl true def mount(_params, _session, socket) do diff --git a/lib/aprsme_web/live/about_live.html.heex b/lib/aprsme_web/live/about_live.html.heex index a023740..c87091d 100644 --- a/lib/aprsme_web/live/about_live.html.heex +++ b/lib/aprsme_web/live/about_live.html.heex @@ -1,7 +1,7 @@
-

Under heavy construction

+

{gettext("Under heavy construction")}

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

@@ -9,7 +9,7 @@ Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.

-

Built with Modern Technologies

+

{gettext("Built with Modern Technologies")}

Elixir Phoenix @@ -18,7 +18,7 @@ Tailwind CSS
-

Join Our Community

+

{gettext("Join Our Community")}

Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.

@@ -32,7 +32,7 @@ d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-1.447-.894L15 4m0 13V4m-6 3l6-3" /> - Explore the Map + {gettext("Explore the Map")}
diff --git a/lib/aprsme_web/live/bad_packets_live/index.html.heex b/lib/aprsme_web/live/bad_packets_live/index.html.heex index eae8e12..1277bdb 100644 --- a/lib/aprsme_web/live/bad_packets_live/index.html.heex +++ b/lib/aprsme_web/live/bad_packets_live/index.html.heex @@ -6,7 +6,7 @@
- Auto-refreshes every 5 seconds + {gettext("Auto-refreshes every 5 seconds")} <%= if @loading do %> @@ -32,12 +32,12 @@ > - Updating... + {gettext("Updating...")} <% end %>
<.table id="bad_packets" rows={@bad_packets}> - <:col :let={bad_packet} label="Attempted At"> + <:col :let={bad_packet} label={gettext("Attempted At")}> {Calendar.strftime( bad_packet.attempted_at || bad_packet.inserted_at, @@ -45,17 +45,17 @@ )} - <:col :let={bad_packet} label="Error Type"> + <:col :let={bad_packet} label={gettext("Error Type")}> - {bad_packet.error_type || "Unknown"} + {bad_packet.error_type || gettext("Unknown")} - <:col :let={bad_packet} label="Error Message"> + <:col :let={bad_packet} label={gettext("Error Message")}> - {bad_packet.error_message || "No error message"} + {bad_packet.error_message || gettext("No error message")} - <:col :let={bad_packet} label="Raw Packet"> + <:col :let={bad_packet} label={gettext("Raw Packet")}>
<%= if is_binary(bad_packet.raw_packet) do %> @@ -81,8 +81,8 @@ />
-

No bad packets

-

All packets are parsing successfully!

+

{gettext("No bad packets")}

+

{gettext("All packets are parsing successfully!")}

<% end %>
diff --git a/lib/aprsme_web/live/info_live/show.ex b/lib/aprsme_web/live/info_live/show.ex index 798f440..c0e2ded 100644 --- a/lib/aprsme_web/live/info_live/show.ex +++ b/lib/aprsme_web/live/info_live/show.ex @@ -1,6 +1,7 @@ defmodule AprsmeWeb.InfoLive.Show do @moduledoc false use AprsmeWeb, :live_view + use Gettext, backend: AprsmeWeb.Gettext alias Aprsme.Packets alias AprsmeWeb.MapLive.PacketUtils @@ -273,38 +274,38 @@ defmodule AprsmeWeb.InfoLive.Show do # WIDE digipeaters String.starts_with?(element, "WIDE") -> case element do - "WIDE1-1" -> "WIDE1-1 (Wide area digipeater, 1 hop)" - "WIDE2-1" -> "WIDE2-1 (Wide area digipeater, 2 hops)" - "WIDE3-1" -> "WIDE3-1 (Wide area digipeater, 3 hops)" - "WIDE4-1" -> "WIDE4-1 (Wide area digipeater, 4 hops)" - "WIDE5-1" -> "WIDE5-1 (Wide area digipeater, 5 hops)" - "WIDE6-1" -> "WIDE6-1 (Wide area digipeater, 6 hops)" - "WIDE7-1" -> "WIDE7-1 (WIDE area digipeater, 7 hops)" - "WIDE1-2" -> "WIDE1-2 (Wide area digipeater, 1 hop, 2nd attempt)" - "WIDE2-2" -> "WIDE2-2 (Wide area digipeater, 2 hops, 2nd attempt)" - _ -> "WIDE digipeater (#{element})" + "WIDE1-1" -> gettext("WIDE1-1 (Wide area digipeater, 1 hop)") + "WIDE2-1" -> gettext("WIDE2-1 (Wide area digipeater, 2 hops)") + "WIDE3-1" -> gettext("WIDE3-1 (Wide area digipeater, 3 hops)") + "WIDE4-1" -> gettext("WIDE4-1 (Wide area digipeater, 4 hops)") + "WIDE5-1" -> gettext("WIDE5-1 (Wide area digipeater, 5 hops)") + "WIDE6-1" -> gettext("WIDE6-1 (Wide area digipeater, 6 hops)") + "WIDE7-1" -> gettext("WIDE7-1 (WIDE area digipeater, 7 hops)") + "WIDE1-2" -> gettext("WIDE1-2 (Wide area digipeater, 1 hop, 2nd attempt)") + "WIDE2-2" -> gettext("WIDE2-2 (Wide area digipeater, 2 hops, 2nd attempt)") + _ -> gettext("WIDE digipeater (%{element})", element: element) end # TRACE digipeaters String.starts_with?(element, "TRACE") -> case element do - "TRACE1-1" -> "TRACE1-1 (Trace digipeater, 1 hop)" - "TRACE2-1" -> "TRACE2-1 (Trace digipeater, 2 hops)" - "TRACE3-1" -> "TRACE3-1 (Trace digipeater, 3 hops)" - "TRACE4-1" -> "TRACE4-1 (Trace digipeater, 4 hops)" - "TRACE5-1" -> "TRACE5-1 (Trace digipeater, 5 hops)" - "TRACE6-1" -> "TRACE6-1 (Trace digipeater, 6 hops)" - "TRACE7-1" -> "TRACE7-1 (Trace digipeater, 7 hops)" - _ -> "TRACE digipeater (#{element})" + "TRACE1-1" -> gettext("TRACE1-1 (Trace digipeater, 1 hop)") + "TRACE2-1" -> gettext("TRACE2-1 (Trace digipeater, 2 hops)") + "TRACE3-1" -> gettext("TRACE3-1 (Trace digipeater, 3 hops)") + "TRACE4-1" -> gettext("TRACE4-1 (Trace digipeater, 4 hops)") + "TRACE5-1" -> gettext("TRACE5-1 (Trace digipeater, 5 hops)") + "TRACE6-1" -> gettext("TRACE6-1 (Trace digipeater, 6 hops)") + "TRACE7-1" -> gettext("TRACE7-1 (Trace digipeater, 7 hops)") + _ -> gettext("TRACE digipeater (%{element})", element: element) end # RELAY digipeaters String.starts_with?(element, "RELAY") -> case element do - "RELAY" -> "RELAY (Relay digipeater)" - "RELAY-1" -> "RELAY-1 (Relay digipeater, 1 hop)" - "RELAY-2" -> "RELAY-2 (Relay digipeater, 2 hops)" - _ -> "RELAY digipeater (#{element})" + "RELAY" -> gettext("RELAY (Relay digipeater)") + "RELAY-1" -> gettext("RELAY-1 (Relay digipeater, 1 hop)") + "RELAY-2" -> gettext("RELAY-2 (Relay digipeater, 2 hops)") + _ -> gettext("RELAY digipeater (%{element})", element: element) end # qAC (APRS-IS connection) @@ -466,22 +467,22 @@ defmodule AprsmeWeb.InfoLive.Show do # TCPIP digipeaters String.starts_with?(element, "TCPIP") -> case element do - "TCPIP" -> "TCPIP (Internet gateway)" - "TCPIP*" -> "TCPIP* (Internet gateway, no forward)" - _ -> "TCPIP gateway (#{element})" + "TCPIP" -> gettext("TCPIP (Internet gateway)") + "TCPIP*" -> gettext("TCPIP* (Internet gateway, no forward)") + _ -> gettext("TCPIP gateway (%{element})", element: element) end # Generic callsign with SSID (likely a digipeater) Regex.match?(~r/^[A-Z0-9]+-\d+$/, element) -> - "#{element} (Digipeater)" + gettext("%{element} (Digipeater)", element: element) # Generic callsign without SSID Regex.match?(~r/^[A-Z0-9]+$/, element) -> - "#{element} (Station)" + gettext("%{element} (Station)", element: element) # Default case true -> - "#{element} (Unknown)" + gettext("%{element} (Unknown)", element: element) end end end diff --git a/lib/aprsme_web/live/info_live/show.html.heex b/lib/aprsme_web/live/info_live/show.html.heex index 6f170b5..2ff961b 100644 --- a/lib/aprsme_web/live/info_live/show.html.heex +++ b/lib/aprsme_web/live/info_live/show.html.heex @@ -24,7 +24,7 @@

- APRS Station + {gettext("APRS Station")}

@@ -64,11 +64,11 @@
<.link navigate={~p"/packets/#{@callsign}"} class="btn btn-outline btn-sm"> - View packets + {gettext("View packets")} <%= if @has_weather_packets do %> <.link navigate={~p"/weather/#{@callsign}"} class="btn btn-primary btn-sm"> - Weather charts + {gettext("Weather charts")} <% end %>
@@ -106,19 +106,19 @@
-

Position Information

+

{gettext("Position Information")}

-
Location
+
{gettext("Location")}
{@packet.lat || ""}, {@packet.lon || ""}
<%= if @packet.lat && @packet.lon do %>
-
Grid Square
+
{gettext("Grid Square")}
<%= case Gridsquare.encode(@packet.lon, @packet.lat) do %> <% %Gridsquare.EncodeResult{grid_reference: grid_ref} -> %> @@ -130,7 +130,7 @@
<% end %>
-
Last Position
+
{gettext("Last Position")}
<%= if @packet.received_at do %>
@@ -140,25 +140,25 @@ {Calendar.strftime(@packet.received_at, "%Y-%m-%d %H:%M:%S UTC")}
<% else %> - Unknown + {gettext("Unknown")} <% end %>
<%= if @packet.altitude do %>
-
Altitude
+
{gettext("Altitude")}
{@packet.altitude} ft
<% end %> <%= if @packet.course do %>
-
Course
+
{gettext("Course")}
{@packet.course}°
<% end %> <%= if @packet.speed do %>
-
Speed
+
{gettext("Speed")}
{@packet.speed} MPH
<% end %> @@ -186,12 +186,12 @@
-

Device Information

+

{gettext("Device Information")}

-
Device
+
{gettext("Device")}
<%= if @packet.device_model || @packet.device_vendor do %> {[ @@ -206,25 +206,25 @@ <% end %> <% else %> - {@packet.device_identifier || "Unknown"} + {@packet.device_identifier || gettext("Unknown")} <% end %>
-
Path
+
{gettext("Path")}
{@packet.path || ""}
<%= if @packet.path && @packet.path != "" do %>
-
Decoded Path
+
{gettext("Decoded Path")}
{decode_aprs_path(@packet.path)}
<% end %>
-
Raw Packet
+
{gettext("Raw Packet")}
<%= if is_binary(@packet.raw_packet) do %> {Aprsme.EncodingUtils.sanitize_string(@packet.raw_packet)} @@ -259,7 +259,7 @@
-

Other SSIDs

+

{gettext("Other SSIDs")}

@@ -267,13 +267,13 @@ - Callsign + {gettext("Callsign")} - Distance & Direction + {gettext("Distance & Direction")} - Last Heard + {gettext("Last Heard")} @@ -377,7 +377,7 @@
-

Stations Near Current Position

+

{gettext("Stations Near Current Position")}

@@ -385,13 +385,13 @@ - Callsign + {gettext("Callsign")} - Distance & Course + {gettext("Distance & Course")} - Last Heard + {gettext("Last Heard")} @@ -466,9 +466,9 @@ d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423L16.5 15.75l.394 1.183a2.25 2.25 0 001.423 1.423L19.5 18.75l-1.183.394a2.25 2.25 0 00-1.423 1.423z" /> -

No data available

+

{gettext("No data available")}

- No recent packet data available for this callsign. + {gettext("No recent packet data available for this callsign.")}

<% end %> diff --git a/lib/aprsme_web/live/locale_hook.ex b/lib/aprsme_web/live/locale_hook.ex new file mode 100644 index 0000000..7c9a11c --- /dev/null +++ b/lib/aprsme_web/live/locale_hook.ex @@ -0,0 +1,25 @@ +defmodule AprsmeWeb.LocaleHook do + @moduledoc """ + LiveView hook for setting locale based on Accept-Language header. + This ensures that LiveView updates maintain the correct locale. + """ + import Phoenix.Component + + @supported_locales ["en", "es"] + + def on_mount(:set_locale, _params, session, socket) do + locale = get_locale_from_session(session) || "en" + Gettext.put_locale(AprsmeWeb.Gettext, locale) + {:cont, assign(socket, locale: locale)} + end + + defp get_locale_from_session(session) do + case session do + %{"locale" => locale} when locale in @supported_locales -> + locale + + _ -> + nil + end + end +end diff --git a/lib/aprsme_web/live/map_live/index.ex b/lib/aprsme_web/live/map_live/index.ex index a6c4828..2e9d833 100644 --- a/lib/aprsme_web/live/map_live/index.ex +++ b/lib/aprsme_web/live/map_live/index.ex @@ -607,7 +607,11 @@ defmodule AprsmeWeb.MapLive.Index do > - @@ -772,19 +780,31 @@ defmodule AprsmeWeb.MapLive.Index do d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /> - Trail Duration + {Gettext.gettext(AprsmeWeb.Gettext, "Trail Duration")}
- Historical Data + {Gettext.gettext(AprsmeWeb.Gettext, "Historical Data")}
- Navigation + {Gettext.gettext(AprsmeWeb.Gettext, "Navigation")}
<.navigation variant={:vertical} class="text-sm" />
@@ -873,7 +903,7 @@ defmodule AprsmeWeb.MapLive.Index do d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /> - Last Deploy + {Gettext.gettext(AprsmeWeb.Gettext, "Last Deploy")}
{time_ago_in_words(@deployed_at)}
diff --git a/lib/aprsme_web/live/map_live/popup_component.ex b/lib/aprsme_web/live/map_live/popup_component.ex index a9b716a..863ae16 100644 --- a/lib/aprsme_web/live/map_live/popup_component.ex +++ b/lib/aprsme_web/live/map_live/popup_component.ex @@ -3,19 +3,24 @@ defmodule AprsmeWeb.MapLive.PopupComponent do use Phoenix.Component import AprsmeWeb.TimeHelpers + import Gettext def popup(assigns) do ~H"""
<.link navigate={"/#{@callsign}"}>{@callsign} - <.link navigate={"/info/#{@callsign}"} class="aprs-info-link">info + <.link navigate={"/info/#{@callsign}"} class="aprs-info-link"> + {gettext(AprsmeWeb.Gettext, "info")} + <%= if @weather_link do %> - <.link navigate={"/weather/#{@callsign}"} class="aprs-info-link">weather charts + <.link navigate={"/weather/#{@callsign}"} class="aprs-info-link"> + {gettext(AprsmeWeb.Gettext, "weather charts")} + <% end %>
<%= if @weather do %> -
Weather Report
+
{gettext(AprsmeWeb.Gettext, "Weather Report")}
<% else %> <%= if @comment != "" do %>
{@comment}
@@ -30,11 +35,17 @@ defmodule AprsmeWeb.MapLive.PopupComponent do
<% end %> <%= if @weather do %> -
Temperature: {@temperature}°F
- Humidity: {@humidity}%
- Wind: {@wind_direction}° at {@wind_speed} mph, gusts to {@wind_gust} mph
- Pressure: {@pressure} hPa
Rain (1h): {@rain_1h} in.
- Rain (24h): {@rain_24h} in.
Rain (since midnight): {@rain_since_midnight} in.
+
+ {gettext(AprsmeWeb.Gettext, "Temperature:")} {@temperature}°F
+ {gettext(AprsmeWeb.Gettext, "Humidity:")} {@humidity}%
+ {gettext(AprsmeWeb.Gettext, "Wind:")} {@wind_direction}° {gettext(AprsmeWeb.Gettext, "at")} {@wind_speed} mph, {gettext( + AprsmeWeb.Gettext, + "gusts to" + )} {@wind_gust} mph
+ {gettext(AprsmeWeb.Gettext, "Pressure:")} {@pressure} hPa
+ {gettext(AprsmeWeb.Gettext, "Rain (1h):")} {@rain_1h} in.
+ {gettext(AprsmeWeb.Gettext, "Rain (24h):")} {@rain_24h} in.
+ {gettext(AprsmeWeb.Gettext, "Rain (since midnight):")} {@rain_since_midnight} in.
<% end %>
""" diff --git a/lib/aprsme_web/live/packets_live/index.ex b/lib/aprsme_web/live/packets_live/index.ex index d69938f..9a127b5 100644 --- a/lib/aprsme_web/live/packets_live/index.ex +++ b/lib/aprsme_web/live/packets_live/index.ex @@ -1,6 +1,7 @@ defmodule AprsmeWeb.PacketsLive.Index do @moduledoc false use AprsmeWeb, :live_view + use Gettext, backend: AprsmeWeb.Gettext alias Aprsme.EncodingUtils alias AprsmeWeb.Endpoint diff --git a/lib/aprsme_web/live/packets_live/index.html.heex b/lib/aprsme_web/live/packets_live/index.html.heex index 95d6396..b1b4e46 100644 --- a/lib/aprsme_web/live/packets_live/index.html.heex +++ b/lib/aprsme_web/live/packets_live/index.html.heex @@ -5,15 +5,15 @@ - - - - - - - - - + + + + + + + + + @@ -161,8 +161,8 @@ /> -

No packets

-

Packets will appear here as they are received.

+

{gettext("No packets")}

+

{gettext("Packets will appear here as they are received.")}

<% end %> diff --git a/lib/aprsme_web/live/status_live/index.ex b/lib/aprsme_web/live/status_live/index.ex index 6e064a3..bdc55b9 100644 --- a/lib/aprsme_web/live/status_live/index.ex +++ b/lib/aprsme_web/live/status_live/index.ex @@ -41,9 +41,9 @@ defmodule AprsmeWeb.StatusLive.Index do
-

APRS.me System Status

+

{gettext("APRS.me System Status")}

- Last updated: + {gettext("Last updated:")} {Calendar.strftime(@current_time, "%H:%M:%S UTC")}
@@ -65,9 +65,11 @@ defmodule AprsmeWeb.StatusLive.Index do />
-

APRS-IS Connection Issue

+

{gettext("APRS-IS Connection Issue")}

- The system is currently disconnected from the APRS-IS network. This may be due to network issues or server maintenance. + {gettext( + "The system is currently disconnected from the APRS-IS network. This may be due to network issues or server maintenance." + )}
@@ -75,50 +77,52 @@ defmodule AprsmeWeb.StatusLive.Index do
-

APRS-IS Connection

+

{gettext("APRS-IS Connection")}

- Status: + {gettext("Status:")} <%= if @aprs_status.connected do %>
- Connected + {gettext("Connected")}
<% else %>
- Disconnected + {gettext("Disconnected")}
<% end %>
- Server: + {gettext("Server:")} {@aprs_status.server}:{@aprs_status.port}
- Login ID: + {gettext("Login ID:")} {@aprs_status.login_id}
- Connected Since: + + {gettext("Connected Since:")} + <%= if @aprs_status.connected_at do %> {Calendar.strftime(@aprs_status.connected_at, "%Y-%m-%d %H:%M:%S UTC")} <% else %> - Not connected + {gettext("Not connected")} <% end %>
- Uptime: + {gettext("Uptime:")}
- Filter: + {gettext("Filter:")}
{@aprs_status.filter}
@@ -138,42 +142,52 @@ defmodule AprsmeWeb.StatusLive.Index do
-

Packet Statistics

+

{gettext("Packet Statistics")}

- Total Packets: + + {gettext("Total Packets:")} + {format_number(@aprs_status.packet_stats.total_packets)}
- Packets/Sec: + + {gettext("Packets/Sec:")} + {@aprs_status.packet_stats.packets_per_second}
- Packets/Min: + + {gettext("Packets/Min:")} + {@aprs_status.packet_stats.packets_per_second * 60}
- Last Packet: + + {gettext("Last Packet:")} + <%= if @aprs_status.packet_stats.last_packet_at do %> {format_time_ago(@aprs_status.packet_stats.last_packet_at)} <% else %> - None + {gettext("None")} <% end %>
- Stored Packets: + + {gettext("Stored Packets:")} + {format_number(@aprs_status.stored_packet_count)} @@ -184,7 +198,7 @@ defmodule AprsmeWeb.StatusLive.Index do
- Connection Health: + {gettext("Connection Health:")}
<%= for i <- 1..5 do %> @@ -259,7 +273,7 @@ defmodule AprsmeWeb.StatusLive.Index do Process.send_after(self(), :refresh_status, @refresh_interval) end - def format_uptime(seconds) when seconds <= 0, do: "Not connected" + def format_uptime(seconds) when seconds <= 0, do: gettext("Not connected") def format_uptime(seconds) do days = div(seconds, 86_400) @@ -291,12 +305,12 @@ defmodule AprsmeWeb.StatusLive.Index do def get_health_description(score, connected) do case {score, connected} do - {1, false} -> "Disconnected - Connection issues detected" - {2, true} -> "Recently connected - Monitoring stability" - {3, true} -> "Good - Connection stable for less than 1 hour" - {4, true} -> "Very good - Connection stable for less than 1 day" - {5, true} -> "Excellent - Long-term stable connection" - _ -> "Unknown status" + {1, false} -> gettext("Disconnected - Connection issues detected") + {2, true} -> gettext("Recently connected - Monitoring stability") + {3, true} -> gettext("Good - Connection stable for less than 1 hour") + {4, true} -> gettext("Very good - Connection stable for less than 1 day") + {5, true} -> gettext("Excellent - Long-term stable connection") + _ -> gettext("Unknown status") end end @@ -304,10 +318,10 @@ defmodule AprsmeWeb.StatusLive.Index do diff_seconds = DateTime.diff(DateTime.utc_now(), datetime) cond do - diff_seconds < 60 -> "#{diff_seconds} seconds ago" - diff_seconds < 3600 -> "#{div(diff_seconds, 60)} minutes ago" - diff_seconds < 86_400 -> "#{div(diff_seconds, 3600)} hours ago" - true -> "#{div(diff_seconds, 86_400)} days ago" + diff_seconds < 60 -> gettext("%{count} seconds ago", count: diff_seconds) + diff_seconds < 3600 -> gettext("%{count} minutes ago", count: div(diff_seconds, 60)) + diff_seconds < 86_400 -> gettext("%{count} hours ago", count: div(diff_seconds, 3600)) + true -> gettext("%{count} days ago", count: div(diff_seconds, 86_400)) end end diff --git a/lib/aprsme_web/live/weather_live/callsign_view.ex b/lib/aprsme_web/live/weather_live/callsign_view.ex index 107e7c0..29c6d6f 100644 --- a/lib/aprsme_web/live/weather_live/callsign_view.ex +++ b/lib/aprsme_web/live/weather_live/callsign_view.ex @@ -1,6 +1,7 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do @moduledoc false use AprsmeWeb, :live_view + use Gettext, backend: AprsmeWeb.Gettext import Phoenix.LiveView, only: [push_event: 3, connected?: 1] @@ -45,6 +46,24 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do end) |> Jason.encode!() + # Add chart labels for translation + chart_labels = %{ + temp_title: gettext("Temperature & Dew Point (°F)"), + temp_label: gettext("Temperature (°F)"), + dew_label: gettext("Dew Point (°F)"), + humidity_title: gettext("Humidity (%)"), + humidity_label: gettext("Humidity (%)"), + pressure_title: gettext("Pressure (mb)"), + pressure_label: gettext("Pressure (mb)"), + wind_title: gettext("Wind (mph)"), + wind_label: gettext("Wind Speed (mph)"), + gust_label: gettext("Wind Gust (mph)"), + time: gettext("Time"), + degf: gettext("°F"), + percent: gettext("%"), + mb: gettext("mb") + } + socket = socket |> assign(:callsign, normalized_callsign) @@ -52,6 +71,7 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do |> assign(:page_title, "Weather for #{normalized_callsign}") |> assign(:weather_history, weather_history) |> assign(:weather_history_json, weather_history_json) + |> assign(:chart_labels, chart_labels) {:ok, socket} end diff --git a/lib/aprsme_web/live/weather_live/callsign_view.html.heex b/lib/aprsme_web/live/weather_live/callsign_view.html.heex index 737f5b5..b130702 100644 --- a/lib/aprsme_web/live/weather_live/callsign_view.html.heex +++ b/lib/aprsme_web/live/weather_live/callsign_view.html.heex @@ -5,7 +5,7 @@

- Weather Station + {gettext("Weather Station")}

@@ -40,13 +40,13 @@
<.link navigate={~p"/info/#{@callsign}"} class="btn btn-outline btn-sm"> - View info + {gettext("View info")} <.link navigate={~p"/packets/#{@callsign}"} class="btn btn-outline btn-sm"> - View packets + {gettext("View packets")} <.link navigate={~p"/#{@callsign}"} class="btn btn-primary btn-sm"> - View on map + {gettext("View on map")}
@@ -76,12 +76,12 @@
-

Weather Details

+

{gettext("Weather Details")}

-
Last WX report
+
{gettext("Last WX report")}
<%= if dt do %> {Calendar.strftime(dt, "%Y-%m-%d %H:%M:%S UTC")} ({AprsmeWeb.TimeHelpers.time_ago_in_words( @@ -89,24 +89,24 @@ )})
<%!-- {Calendar.strftime(dt, "%Y-%m-%d %H:%M:%S %Z")} local time at {Map.get(@weather_packet, :region) || Map.get(@weather_packet, "region") || "Unknown location"} [?] --%> <% else %> - Unknown + {gettext("Unknown")} <% end %>
-
Temperature
+
{gettext("Temperature")}
{PacketUtils.get_weather_field(@weather_packet, :temperature)}°F
-
Humidity
+
{gettext("Humidity")}
{PacketUtils.get_weather_field(@weather_packet, :humidity)}%
-
Wind
+
{gettext("Wind")}
{PacketUtils.get_weather_field(@weather_packet, :wind_direction)}° @ {PacketUtils.get_weather_field( @weather_packet, @@ -115,37 +115,37 @@
-
Gusts
+
{gettext("Gusts")}
{PacketUtils.get_weather_field(@weather_packet, :wind_gust)} mph
-
Pressure
+
{gettext("Pressure")}
{PacketUtils.get_weather_field(@weather_packet, :pressure)} hPa
-
Rain (1h)
+
{gettext("Rain (1h)")}
{get_weather_field_zero(@weather_packet, :rain_1h)} in
-
Rain (24h)
+
{gettext("Rain (24h)")}
{get_weather_field_zero(@weather_packet, :rain_24h)} in
-
Rain (since midnight)
+
{gettext("Rain (since midnight)")}
{get_weather_field_zero(@weather_packet, :rain_since_midnight)} in
-
Raw Packet
+
{gettext("Raw Packet")}
<%= if is_binary(@weather_packet.raw_packet) do %> {Aprsme.EncodingUtils.sanitize_string(@weather_packet.raw_packet)} @@ -156,18 +156,20 @@
- Raw comment: {@weather_packet.comment || @weather_packet["comment"]} + {gettext("Raw comment")}: {@weather_packet.comment || + @weather_packet["comment"]}
-

Weather History Graphs

+

{gettext("Weather History Graphs")}

<%= if Enum.any?(@weather_history, &(not is_nil(Map.get(&1, :temperature, nil)))) do %>
@@ -179,6 +181,7 @@ id="humidity-chart" phx-hook="ChartJSHumidityChart" data-weather-history={@weather_history_json} + data-chart-labels={Jason.encode!(@chart_labels)} class="bg-base-200 rounded-lg p-4 mb-4" style="height:250px;" > @@ -190,6 +193,7 @@ id="pressure-chart" phx-hook="ChartJSPressureChart" data-weather-history={@weather_history_json} + data-chart-labels={Jason.encode!(@chart_labels)} class="bg-base-200 rounded-lg p-4 mb-4" style="height:250px;" > @@ -201,6 +205,7 @@ id="wind-chart" phx-hook="ChartJSWindChart" data-weather-history={@weather_history_json} + data-chart-labels={Jason.encode!(@chart_labels)} class="bg-base-200 rounded-lg p-4 mb-4" style="height:250px;" > @@ -212,6 +217,7 @@ id="rain-chart" phx-hook="ChartJSRainChart" data-weather-history={@weather_history_json} + data-chart-labels={Jason.encode!(@chart_labels)} class="bg-base-200 rounded-lg p-4 mb-4" style="height:250px;" > @@ -223,6 +229,7 @@ id="luminosity-chart" phx-hook="ChartJSLuminosityChart" data-weather-history={@weather_history_json} + data-chart-labels={Jason.encode!(@chart_labels)} class="bg-base-200 rounded-lg p-4 mb-4" style="height:250px;" > diff --git a/lib/aprsme_web/plugs/set_locale.ex b/lib/aprsme_web/plugs/set_locale.ex new file mode 100644 index 0000000..8c67dbe --- /dev/null +++ b/lib/aprsme_web/plugs/set_locale.ex @@ -0,0 +1,53 @@ +defmodule AprsmeWeb.Plugs.SetLocale do + @moduledoc """ + A plug that sets the locale based on the Accept-Language header. + Falls back to English if the requested locale is not available. + """ + import Plug.Conn + + @supported_locales ["en", "es"] + + def init(opts), do: opts + + def call(conn, _opts) do + locale = get_locale_from_header(conn) || "en" + IO.puts("DEBUG: SetLocale - Final locale: #{locale}") + Gettext.put_locale(AprsmeWeb.Gettext, locale) + # Store locale in session for LiveView to access + conn = put_session(conn, :locale, locale) + conn + end + + defp get_locale_from_header(conn) do + case get_req_header(conn, "accept-language") do + [accept_language | _] -> + IO.puts("DEBUG: SetLocale - Accept-Language header: #{inspect(accept_language)}") + parse_accept_language(accept_language) + + _ -> + IO.puts("DEBUG: SetLocale - No Accept-Language header found") + nil + end + end + + defp parse_accept_language(accept_language) do + accept_language + |> String.split(",") + |> Enum.map(&parse_language_tag/1) + |> Enum.find(&supported_locale?/1) + end + + defp parse_language_tag(tag) do + tag + |> String.trim() + |> String.split(";") + |> List.first() + |> String.split("-") + |> List.first() + |> String.downcase() + end + + defp supported_locale?(locale) do + locale in @supported_locales + end +end diff --git a/lib/aprsme_web/router.ex b/lib/aprsme_web/router.ex index e7884e5..4ce78fd 100644 --- a/lib/aprsme_web/router.ex +++ b/lib/aprsme_web/router.ex @@ -12,6 +12,7 @@ defmodule AprsmeWeb.Router do plug :protect_from_forgery plug :put_secure_browser_headers plug :fetch_current_user + plug AprsmeWeb.Plugs.SetLocale end pipeline :api do @@ -30,7 +31,7 @@ defmodule AprsmeWeb.Router do live_dashboard "/dashboard", metrics: AprsmeWeb.Telemetry - live_session :regular_pages do + live_session :regular_pages, on_mount: [{AprsmeWeb.LocaleHook, :set_locale}] do live "/status", StatusLive.Index, :index live "/packets", PacketsLive.Index, :index live "/packets/:callsign", PacketsLive.CallsignView, :index @@ -73,7 +74,7 @@ defmodule AprsmeWeb.Router do pipe_through [:browser, :redirect_if_user_is_authenticated] live_session :redirect_if_user_is_authenticated, - on_mount: [{AprsmeWeb.UserAuth, :redirect_if_user_is_authenticated}] do + on_mount: [{AprsmeWeb.UserAuth, :redirect_if_user_is_authenticated}, {AprsmeWeb.LocaleHook, :set_locale}] do live "/users/register", UserRegistrationLive, :new live "/users/log_in", UserLoginLive, :new live "/users/reset_password", UserForgotPasswordLive, :new @@ -87,7 +88,7 @@ defmodule AprsmeWeb.Router do pipe_through [:browser, :require_authenticated_user] live_session :require_authenticated_user, - on_mount: [{AprsmeWeb.UserAuth, :ensure_authenticated}] do + on_mount: [{AprsmeWeb.UserAuth, :ensure_authenticated}, {AprsmeWeb.LocaleHook, :set_locale}] do live "/users/settings", UserSettingsLive, :edit live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email end @@ -99,7 +100,7 @@ defmodule AprsmeWeb.Router do delete "/users/log_out", UserSessionController, :delete live_session :current_user, - on_mount: [{AprsmeWeb.UserAuth, :mount_current_user}] do + on_mount: [{AprsmeWeb.UserAuth, :mount_current_user}, {AprsmeWeb.LocaleHook, :set_locale}] do live "/users/confirm/:token", UserConfirmationLive, :edit live "/users/confirm", UserConfirmationInstructionsLive, :new end diff --git a/lib/aprsme_web/time_helpers.ex b/lib/aprsme_web/time_helpers.ex index 7da3993..c68a9d9 100644 --- a/lib/aprsme_web/time_helpers.ex +++ b/lib/aprsme_web/time_helpers.ex @@ -2,6 +2,7 @@ defmodule AprsmeWeb.TimeHelpers do @moduledoc """ Shared helpers for formatting time and dates in the web layer. """ + use Gettext, backend: AprsmeWeb.Gettext @doc """ Returns a human-readable string for how long ago the given DateTime was. @@ -10,22 +11,22 @@ defmodule AprsmeWeb.TimeHelpers do now = DateTime.utc_now() diff_seconds = DateTime.diff(now, datetime, :second) - format_time_diff(diff_seconds) <> " ago" + format_time_diff(diff_seconds) <> " " <> gettext("ago") end - defp format_time_diff(seconds) when seconds < 60, do: "less than a minute" - defp format_time_diff(seconds) when seconds < 120, do: "1 minute" - defp format_time_diff(seconds) when seconds < 3600, do: "#{div(seconds, 60)} minutes" - defp format_time_diff(seconds) when seconds < 7200, do: "1 hour" - defp format_time_diff(seconds) when seconds < 86_400, do: "#{div(seconds, 3600)} hours" - defp format_time_diff(seconds) when seconds < 172_800, do: "1 day" - defp format_time_diff(seconds) when seconds < 2_592_000, do: "#{div(seconds, 86_400)} days" - defp format_time_diff(seconds) when seconds < 5_184_000, do: "1 month" + defp format_time_diff(seconds) when seconds < 60, do: gettext("less than a minute") + defp format_time_diff(seconds) when seconds < 120, do: gettext("1 minute") + defp format_time_diff(seconds) when seconds < 3600, do: gettext("%{count} minutes", count: div(seconds, 60)) + defp format_time_diff(seconds) when seconds < 7200, do: gettext("1 hour") + defp format_time_diff(seconds) when seconds < 86_400, do: gettext("%{count} hours", count: div(seconds, 3600)) + defp format_time_diff(seconds) when seconds < 172_800, do: gettext("1 day") + defp format_time_diff(seconds) when seconds < 2_592_000, do: gettext("%{count} days", count: div(seconds, 86_400)) + defp format_time_diff(seconds) when seconds < 5_184_000, do: gettext("1 month") - defp format_time_diff(seconds) when seconds < 31_536_000, do: "#{div(seconds, 2_592_000)} months" + defp format_time_diff(seconds) when seconds < 31_536_000, do: gettext("%{count} months", count: div(seconds, 2_592_000)) - defp format_time_diff(seconds) when seconds < 63_072_000, do: "1 year" - defp format_time_diff(seconds), do: "#{div(seconds, 31_536_000)} years" + defp format_time_diff(seconds) when seconds < 63_072_000, do: gettext("1 year") + defp format_time_diff(seconds), do: gettext("%{count} years", count: div(seconds, 31_536_000)) @doc """ Converts various timestamp formats into a standard DateTime object. diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot new file mode 100644 index 0000000..ad33429 --- /dev/null +++ b/priv/gettext/default.pot @@ -0,0 +1,815 @@ +## 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/aprsme_web/components/core_components.ex:570 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "" + +#: lib/aprsme_web/components/core_components.ex:117 +#: lib/aprsme_web/components/core_components.ex:202 +#, elixir-autogen, elixir-format +msgid "close" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:23 +#, elixir-autogen, elixir-format +msgid "%{count} days" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:310 +#, elixir-autogen, elixir-format +msgid "%{count} days ago" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:21 +#, elixir-autogen, elixir-format +msgid "%{count} hours" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:309 +#, elixir-autogen, elixir-format +msgid "%{count} hours ago" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:19 +#, elixir-autogen, elixir-format +msgid "%{count} minutes" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:308 +#, elixir-autogen, elixir-format +msgid "%{count} minutes ago" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:26 +#, elixir-autogen, elixir-format +msgid "%{count} months" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:307 +#, elixir-autogen, elixir-format +msgid "%{count} seconds ago" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:29 +#, elixir-autogen, elixir-format +msgid "%{count} years" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:477 +#, elixir-autogen, elixir-format +msgid "%{element} (Digipeater)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:481 +#, elixir-autogen, elixir-format +msgid "%{element} (Station)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:485 +#, elixir-autogen, elixir-format +msgid "%{element} (Unknown)" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:22 +#, elixir-autogen, elixir-format +msgid "1 day" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:20 +#, elixir-autogen, elixir-format +msgid "1 hour" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:18 +#, elixir-autogen, elixir-format +msgid "1 minute" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:24 +#, elixir-autogen, elixir-format +msgid "1 month" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:28 +#, elixir-autogen, elixir-format +msgid "1 year" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:78 +#, elixir-autogen, elixir-format +msgid "APRS-IS Connection" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:68 +#, elixir-autogen, elixir-format +msgid "APRS-IS Connection Issue" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:44 +#, elixir-autogen, elixir-format +msgid "APRS.me System Status" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:70 +#, elixir-autogen, elixir-format +msgid "Access forbidden" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:85 +#, elixir-autogen, elixir-format +msgid "All packets are parsing successfully!" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "An error occurred: %{reason}" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:40 +#, elixir-autogen, elixir-format +msgid "Attempted At" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:9 +#, elixir-autogen, elixir-format +msgid "Auto-refreshes every 5 seconds" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:71 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:46 +#, elixir-autogen, elixir-format +msgid "Bad request" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:87 +#, elixir-autogen, elixir-format +msgid "Connected" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:110 +#, elixir-autogen, elixir-format +msgid "Connected Since:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:187 +#, elixir-autogen, elixir-format +msgid "Connection Health:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:92 +#, elixir-autogen, elixir-format +msgid "Disconnected" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:294 +#, elixir-autogen, elixir-format +msgid "Disconnected - Connection issues detected" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:53 +#, elixir-autogen, elixir-format +msgid "Error Message" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:48 +#, elixir-autogen, elixir-format +msgid "Error Type" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:298 +#, elixir-autogen, elixir-format +msgid "Excellent - Long-term stable connection" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:131 +#, elixir-autogen, elixir-format +msgid "Filter:" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:64 +#, elixir-autogen, elixir-format +msgid "Forbidden" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:296 +#, elixir-autogen, elixir-format +msgid "Good - Connection stable for less than 1 hour" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:28 +#, elixir-autogen, elixir-format +msgid "Internal server error" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:165 +#, elixir-autogen, elixir-format +msgid "Last Packet:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:46 +#, elixir-autogen, elixir-format +msgid "Last updated:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:105 +#, elixir-autogen, elixir-format +msgid "Login ID:" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:84 +#, elixir-autogen, elixir-format +msgid "No bad packets" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:55 +#, elixir-autogen, elixir-format +msgid "No error message" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:170 +#, elixir-autogen, elixir-format +msgid "None" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:115 +#: lib/aprsme_web/live/status_live/index.ex:262 +#, elixir-autogen, elixir-format +msgid "Not connected" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:141 +#, elixir-autogen, elixir-format +msgid "Packet Statistics" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:158 +#, elixir-autogen, elixir-format +msgid "Packets/Min:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:151 +#, elixir-autogen, elixir-format +msgid "Packets/Sec:" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:305 +#, elixir-autogen, elixir-format +msgid "RELAY (Relay digipeater)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:308 +#, elixir-autogen, elixir-format +msgid "RELAY digipeater (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:306 +#, elixir-autogen, elixir-format +msgid "RELAY-1 (Relay digipeater, 1 hop)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:307 +#, elixir-autogen, elixir-format +msgid "RELAY-2 (Relay digipeater, 2 hops)" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:58 +#: lib/aprsme_web/live/info_live/show.html.heex:227 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:148 +#, elixir-autogen, elixir-format +msgid "Raw Packet" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:295 +#, elixir-autogen, elixir-format +msgid "Recently connected - Monitoring stability" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:57 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:73 +#, elixir-autogen, elixir-format +msgid "Request timeout" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:68 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:19 +#, elixir-autogen, elixir-format +msgid "Resource not found" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:98 +#, elixir-autogen, elixir-format +msgid "Server:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:83 +#, elixir-autogen, elixir-format +msgid "Status:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:176 +#, elixir-autogen, elixir-format +msgid "Stored Packets:" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:470 +#, elixir-autogen, elixir-format +msgid "TCPIP (Internet gateway)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:472 +#, elixir-autogen, elixir-format +msgid "TCPIP gateway (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:471 +#, elixir-autogen, elixir-format +msgid "TCPIP* (Internet gateway, no forward)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:299 +#, elixir-autogen, elixir-format +msgid "TRACE digipeater (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:292 +#, elixir-autogen, elixir-format +msgid "TRACE1-1 (Trace digipeater, 1 hop)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:293 +#, elixir-autogen, elixir-format +msgid "TRACE2-1 (Trace digipeater, 2 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:294 +#, elixir-autogen, elixir-format +msgid "TRACE3-1 (Trace digipeater, 3 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:295 +#, elixir-autogen, elixir-format +msgid "TRACE4-1 (Trace digipeater, 4 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:296 +#, elixir-autogen, elixir-format +msgid "TRACE5-1 (Trace digipeater, 5 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:297 +#, elixir-autogen, elixir-format +msgid "TRACE6-1 (Trace digipeater, 6 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:298 +#, elixir-autogen, elixir-format +msgid "TRACE7-1 (Trace digipeater, 7 hops)" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:70 +#, elixir-autogen, elixir-format +msgid "The system is currently disconnected from the APRS-IS network. This may be due to network issues or server maintenance." +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:144 +#, elixir-autogen, elixir-format +msgid "Total Packets:" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:55 +#, elixir-autogen, elixir-format +msgid "Unauthorized" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:69 +#, elixir-autogen, elixir-format +msgid "Unauthorized access" +msgstr "" + +#: lib/aprsme/device_identification.ex:56 +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:50 +#: lib/aprsme_web/live/info_live/show.html.heex:143 +#: lib/aprsme_web/live/info_live/show.html.heex:209 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:92 +#, elixir-autogen, elixir-format +msgid "Unknown" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:299 +#, elixir-autogen, elixir-format +msgid "Unknown status" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:37 +#, elixir-autogen, elixir-format +msgid "Unprocessable entity" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:35 +#, elixir-autogen, elixir-format +msgid "Updating..." +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:121 +#, elixir-autogen, elixir-format +msgid "Uptime:" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/changeset_json.ex:13 +#, elixir-autogen, elixir-format +msgid "Validation failed" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:297 +#, elixir-autogen, elixir-format +msgid "Very good - Connection stable for less than 1 day" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:286 +#, elixir-autogen, elixir-format +msgid "WIDE digipeater (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:277 +#, elixir-autogen, elixir-format +msgid "WIDE1-1 (Wide area digipeater, 1 hop)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:284 +#, elixir-autogen, elixir-format +msgid "WIDE1-2 (Wide area digipeater, 1 hop, 2nd attempt)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:278 +#, elixir-autogen, elixir-format +msgid "WIDE2-1 (Wide area digipeater, 2 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:285 +#, elixir-autogen, elixir-format +msgid "WIDE2-2 (Wide area digipeater, 2 hops, 2nd attempt)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:279 +#, elixir-autogen, elixir-format +msgid "WIDE3-1 (Wide area digipeater, 3 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:280 +#, elixir-autogen, elixir-format +msgid "WIDE4-1 (Wide area digipeater, 4 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:281 +#, elixir-autogen, elixir-format +msgid "WIDE5-1 (Wide area digipeater, 5 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:282 +#, elixir-autogen, elixir-format +msgid "WIDE6-1 (Wide area digipeater, 6 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:283 +#, elixir-autogen, elixir-format +msgid "WIDE7-1 (WIDE area digipeater, 7 hops)" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:17 +#, elixir-autogen, elixir-format +msgid "less than a minute" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:118 +#, elixir-autogen, elixir-format +msgid "Gusts" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:103 +#, elixir-autogen, elixir-format +msgid "Humidity" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:84 +#, elixir-autogen, elixir-format +msgid "Last WX report" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:124 +#, elixir-autogen, elixir-format +msgid "Pressure" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:130 +#, elixir-autogen, elixir-format +msgid "Rain (1h)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:136 +#, elixir-autogen, elixir-format +msgid "Rain (24h)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:142 +#, elixir-autogen, elixir-format +msgid "Rain (since midnight)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:159 +#, elixir-autogen, elixir-format +msgid "Raw comment" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:97 +#, elixir-autogen, elixir-format +msgid "Temperature" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:43 +#, elixir-autogen, elixir-format +msgid "View info" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:49 +#, elixir-autogen, elixir-format +msgid "View on map" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:67 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:46 +#, elixir-autogen, elixir-format +msgid "View packets" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:79 +#, elixir-autogen, elixir-format +msgid "Weather Details" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:165 +#, elixir-autogen, elixir-format +msgid "Weather History Graphs" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:8 +#, elixir-autogen, elixir-format +msgid "Weather Station" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:109 +#, elixir-autogen, elixir-format +msgid "Wind" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:27 +#, elixir-autogen, elixir-format +msgid "APRS Station" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:149 +#, elixir-autogen, elixir-format +msgid "Altitude" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:10 +#, elixir-autogen, elixir-format +msgid "Base Callsign" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:12 +#, elixir-autogen, elixir-format +msgid "Built with Modern Technologies" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:270 +#: lib/aprsme_web/live/info_live/show.html.heex:388 +#, elixir-autogen, elixir-format +msgid "Callsign" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:155 +#, elixir-autogen, elixir-format +msgid "Course" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:11 +#, elixir-autogen, elixir-format +msgid "Data Type" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:220 +#, elixir-autogen, elixir-format +msgid "Decoded Path" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:194 +#: lib/aprsme_web/live/packets_live/index.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Device" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:189 +#, elixir-autogen, elixir-format +msgid "Device Information" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:391 +#, elixir-autogen, elixir-format +msgid "Distance & Course" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:273 +#, elixir-autogen, elixir-format +msgid "Distance & Direction" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:35 +#, elixir-autogen, elixir-format +msgid "Explore the Map" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:121 +#, elixir-autogen, elixir-format +msgid "Grid Square" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:21 +#, elixir-autogen, elixir-format +msgid "Join Our Community" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:276 +#: lib/aprsme_web/live/info_live/show.html.heex:394 +#, elixir-autogen, elixir-format +msgid "Last Heard" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:133 +#, elixir-autogen, elixir-format +msgid "Last Position" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:14 +#, elixir-autogen, elixir-format +msgid "Latitude" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:114 +#, elixir-autogen, elixir-format +msgid "Location" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:15 +#, elixir-autogen, elixir-format +msgid "Longitude" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:469 +#, elixir-autogen, elixir-format +msgid "No data available" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:164 +#, elixir-autogen, elixir-format +msgid "No packets" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:471 +#, elixir-autogen, elixir-format +msgid "No recent packet data available for this callsign." +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:262 +#, elixir-autogen, elixir-format +msgid "Other SSIDs" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:165 +#, elixir-autogen, elixir-format +msgid "Packets will appear here as they are received." +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:215 +#: lib/aprsme_web/live/packets_live/index.html.heex:13 +#, elixir-autogen, elixir-format +msgid "Path" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:109 +#, elixir-autogen, elixir-format +msgid "Position Information" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:9 +#, elixir-autogen, elixir-format +msgid "SSID" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:8 +#, elixir-autogen, elixir-format +msgid "Sender" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:161 +#, elixir-autogen, elixir-format +msgid "Speed" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:380 +#, elixir-autogen, elixir-format +msgid "Stations Near Current Position" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:12 +#, elixir-autogen, elixir-format +msgid "Symbol" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:4 +#, elixir-autogen, elixir-format +msgid "Under heavy construction" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:71 +#, elixir-autogen, elixir-format +msgid "Weather charts" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:63 +#, elixir-autogen, elixir-format +msgid "%" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:53 +#, elixir-autogen, elixir-format +msgid "Dew Point (°F)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:54 +#: lib/aprsme_web/live/weather_live/callsign_view.ex:55 +#, elixir-autogen, elixir-format +msgid "Humidity (%)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:56 +#: lib/aprsme_web/live/weather_live/callsign_view.ex:57 +#, elixir-autogen, elixir-format +msgid "Pressure (mb)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:51 +#, elixir-autogen, elixir-format +msgid "Temperature & Dew Point (°F)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:52 +#, elixir-autogen, elixir-format +msgid "Temperature (°F)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:61 +#, elixir-autogen, elixir-format +msgid "Time" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:58 +#, elixir-autogen, elixir-format +msgid "Wind (mph)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:60 +#, elixir-autogen, elixir-format +msgid "Wind Gust (mph)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:59 +#, elixir-autogen, elixir-format +msgid "Wind Speed (mph)" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:14 +#, elixir-autogen, elixir-format +msgid "ago" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:64 +#, elixir-autogen, elixir-format +msgid "mb" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:62 +#, elixir-autogen, elixir-format +msgid "°F" +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..e98564f --- /dev/null +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -0,0 +1,815 @@ +## "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/aprsme_web/components/core_components.ex:570 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "Actions" + +#: lib/aprsme_web/components/core_components.ex:117 +#: lib/aprsme_web/components/core_components.ex:202 +#, elixir-autogen, elixir-format +msgid "close" +msgstr "close" + +#: lib/aprsme_web/time_helpers.ex:23 +#, elixir-autogen, elixir-format +msgid "%{count} days" +msgstr "%{count} days" + +#: lib/aprsme_web/live/status_live/index.ex:310 +#, elixir-autogen, elixir-format +msgid "%{count} days ago" +msgstr "%{count} days ago" + +#: lib/aprsme_web/time_helpers.ex:21 +#, elixir-autogen, elixir-format +msgid "%{count} hours" +msgstr "%{count} hours" + +#: lib/aprsme_web/live/status_live/index.ex:309 +#, elixir-autogen, elixir-format +msgid "%{count} hours ago" +msgstr "%{count} hours ago" + +#: lib/aprsme_web/time_helpers.ex:19 +#, elixir-autogen, elixir-format +msgid "%{count} minutes" +msgstr "%{count} minutes" + +#: lib/aprsme_web/live/status_live/index.ex:308 +#, elixir-autogen, elixir-format +msgid "%{count} minutes ago" +msgstr "%{count} minutes ago" + +#: lib/aprsme_web/time_helpers.ex:26 +#, elixir-autogen, elixir-format +msgid "%{count} months" +msgstr "%{count} months" + +#: lib/aprsme_web/live/status_live/index.ex:307 +#, elixir-autogen, elixir-format +msgid "%{count} seconds ago" +msgstr "%{count} seconds ago" + +#: lib/aprsme_web/time_helpers.ex:29 +#, elixir-autogen, elixir-format +msgid "%{count} years" +msgstr "%{count} years" + +#: lib/aprsme_web/live/info_live/show.ex:477 +#, elixir-autogen, elixir-format +msgid "%{element} (Digipeater)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:481 +#, elixir-autogen, elixir-format +msgid "%{element} (Station)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:485 +#, elixir-autogen, elixir-format +msgid "%{element} (Unknown)" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:22 +#, elixir-autogen, elixir-format +msgid "1 day" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:20 +#, elixir-autogen, elixir-format +msgid "1 hour" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:18 +#, elixir-autogen, elixir-format +msgid "1 minute" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:24 +#, elixir-autogen, elixir-format +msgid "1 month" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:28 +#, elixir-autogen, elixir-format +msgid "1 year" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:78 +#, elixir-autogen, elixir-format +msgid "APRS-IS Connection" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:68 +#, elixir-autogen, elixir-format +msgid "APRS-IS Connection Issue" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:44 +#, elixir-autogen, elixir-format +msgid "APRS.me System Status" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:70 +#, elixir-autogen, elixir-format +msgid "Access forbidden" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:85 +#, elixir-autogen, elixir-format +msgid "All packets are parsing successfully!" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "An error occurred: %{reason}" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:40 +#, elixir-autogen, elixir-format +msgid "Attempted At" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:9 +#, elixir-autogen, elixir-format +msgid "Auto-refreshes every 5 seconds" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:71 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:46 +#, elixir-autogen, elixir-format +msgid "Bad request" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:87 +#, elixir-autogen, elixir-format +msgid "Connected" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:110 +#, elixir-autogen, elixir-format +msgid "Connected Since:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:187 +#, elixir-autogen, elixir-format +msgid "Connection Health:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:92 +#, elixir-autogen, elixir-format +msgid "Disconnected" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:294 +#, elixir-autogen, elixir-format +msgid "Disconnected - Connection issues detected" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:53 +#, elixir-autogen, elixir-format +msgid "Error Message" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:48 +#, elixir-autogen, elixir-format +msgid "Error Type" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:298 +#, elixir-autogen, elixir-format +msgid "Excellent - Long-term stable connection" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:131 +#, elixir-autogen, elixir-format +msgid "Filter:" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:64 +#, elixir-autogen, elixir-format +msgid "Forbidden" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:296 +#, elixir-autogen, elixir-format +msgid "Good - Connection stable for less than 1 hour" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:28 +#, elixir-autogen, elixir-format +msgid "Internal server error" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:165 +#, elixir-autogen, elixir-format +msgid "Last Packet:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:46 +#, elixir-autogen, elixir-format +msgid "Last updated:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:105 +#, elixir-autogen, elixir-format +msgid "Login ID:" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:84 +#, elixir-autogen, elixir-format +msgid "No bad packets" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:55 +#, elixir-autogen, elixir-format +msgid "No error message" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:170 +#, elixir-autogen, elixir-format +msgid "None" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:115 +#: lib/aprsme_web/live/status_live/index.ex:262 +#, elixir-autogen, elixir-format +msgid "Not connected" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:141 +#, elixir-autogen, elixir-format +msgid "Packet Statistics" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:158 +#, elixir-autogen, elixir-format +msgid "Packets/Min:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:151 +#, elixir-autogen, elixir-format +msgid "Packets/Sec:" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:305 +#, elixir-autogen, elixir-format +msgid "RELAY (Relay digipeater)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:308 +#, elixir-autogen, elixir-format +msgid "RELAY digipeater (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:306 +#, elixir-autogen, elixir-format +msgid "RELAY-1 (Relay digipeater, 1 hop)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:307 +#, elixir-autogen, elixir-format +msgid "RELAY-2 (Relay digipeater, 2 hops)" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:58 +#: lib/aprsme_web/live/info_live/show.html.heex:227 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:148 +#, elixir-autogen, elixir-format +msgid "Raw Packet" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:295 +#, elixir-autogen, elixir-format +msgid "Recently connected - Monitoring stability" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:57 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:73 +#, elixir-autogen, elixir-format +msgid "Request timeout" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:68 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:19 +#, elixir-autogen, elixir-format +msgid "Resource not found" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:98 +#, elixir-autogen, elixir-format +msgid "Server:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:83 +#, elixir-autogen, elixir-format +msgid "Status:" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:176 +#, elixir-autogen, elixir-format +msgid "Stored Packets:" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:470 +#, elixir-autogen, elixir-format +msgid "TCPIP (Internet gateway)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:472 +#, elixir-autogen, elixir-format +msgid "TCPIP gateway (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:471 +#, elixir-autogen, elixir-format +msgid "TCPIP* (Internet gateway, no forward)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:299 +#, elixir-autogen, elixir-format +msgid "TRACE digipeater (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:292 +#, elixir-autogen, elixir-format +msgid "TRACE1-1 (Trace digipeater, 1 hop)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:293 +#, elixir-autogen, elixir-format +msgid "TRACE2-1 (Trace digipeater, 2 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:294 +#, elixir-autogen, elixir-format +msgid "TRACE3-1 (Trace digipeater, 3 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:295 +#, elixir-autogen, elixir-format +msgid "TRACE4-1 (Trace digipeater, 4 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:296 +#, elixir-autogen, elixir-format +msgid "TRACE5-1 (Trace digipeater, 5 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:297 +#, elixir-autogen, elixir-format +msgid "TRACE6-1 (Trace digipeater, 6 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:298 +#, elixir-autogen, elixir-format +msgid "TRACE7-1 (Trace digipeater, 7 hops)" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:70 +#, elixir-autogen, elixir-format +msgid "The system is currently disconnected from the APRS-IS network. This may be due to network issues or server maintenance." +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:144 +#, elixir-autogen, elixir-format +msgid "Total Packets:" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:55 +#, elixir-autogen, elixir-format +msgid "Unauthorized" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:69 +#, elixir-autogen, elixir-format +msgid "Unauthorized access" +msgstr "" + +#: lib/aprsme/device_identification.ex:56 +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:50 +#: lib/aprsme_web/live/info_live/show.html.heex:143 +#: lib/aprsme_web/live/info_live/show.html.heex:209 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:92 +#, elixir-autogen, elixir-format +msgid "Unknown" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:299 +#, elixir-autogen, elixir-format +msgid "Unknown status" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:37 +#, elixir-autogen, elixir-format +msgid "Unprocessable entity" +msgstr "" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:35 +#, elixir-autogen, elixir-format +msgid "Updating..." +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:121 +#, elixir-autogen, elixir-format +msgid "Uptime:" +msgstr "" + +#: lib/aprsme_web/controllers/api/v1/json/changeset_json.ex:13 +#, elixir-autogen, elixir-format +msgid "Validation failed" +msgstr "" + +#: lib/aprsme_web/live/status_live/index.ex:297 +#, elixir-autogen, elixir-format +msgid "Very good - Connection stable for less than 1 day" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:286 +#, elixir-autogen, elixir-format +msgid "WIDE digipeater (%{element})" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:277 +#, elixir-autogen, elixir-format +msgid "WIDE1-1 (Wide area digipeater, 1 hop)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:284 +#, elixir-autogen, elixir-format +msgid "WIDE1-2 (Wide area digipeater, 1 hop, 2nd attempt)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:278 +#, elixir-autogen, elixir-format +msgid "WIDE2-1 (Wide area digipeater, 2 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:285 +#, elixir-autogen, elixir-format +msgid "WIDE2-2 (Wide area digipeater, 2 hops, 2nd attempt)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:279 +#, elixir-autogen, elixir-format +msgid "WIDE3-1 (Wide area digipeater, 3 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:280 +#, elixir-autogen, elixir-format +msgid "WIDE4-1 (Wide area digipeater, 4 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:281 +#, elixir-autogen, elixir-format +msgid "WIDE5-1 (Wide area digipeater, 5 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:282 +#, elixir-autogen, elixir-format +msgid "WIDE6-1 (Wide area digipeater, 6 hops)" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.ex:283 +#, elixir-autogen, elixir-format +msgid "WIDE7-1 (WIDE area digipeater, 7 hops)" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:17 +#, elixir-autogen, elixir-format +msgid "less than a minute" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:118 +#, elixir-autogen, elixir-format +msgid "Gusts" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:103 +#, elixir-autogen, elixir-format +msgid "Humidity" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:84 +#, elixir-autogen, elixir-format +msgid "Last WX report" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:124 +#, elixir-autogen, elixir-format +msgid "Pressure" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:130 +#, elixir-autogen, elixir-format +msgid "Rain (1h)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:136 +#, elixir-autogen, elixir-format +msgid "Rain (24h)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:142 +#, elixir-autogen, elixir-format +msgid "Rain (since midnight)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:159 +#, elixir-autogen, elixir-format +msgid "Raw comment" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:97 +#, elixir-autogen, elixir-format +msgid "Temperature" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:43 +#, elixir-autogen, elixir-format +msgid "View info" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:49 +#, elixir-autogen, elixir-format +msgid "View on map" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:67 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:46 +#, elixir-autogen, elixir-format +msgid "View packets" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:79 +#, elixir-autogen, elixir-format +msgid "Weather Details" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:165 +#, elixir-autogen, elixir-format +msgid "Weather History Graphs" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:8 +#, elixir-autogen, elixir-format +msgid "Weather Station" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:109 +#, elixir-autogen, elixir-format +msgid "Wind" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:27 +#, elixir-autogen, elixir-format +msgid "APRS Station" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:149 +#, elixir-autogen, elixir-format +msgid "Altitude" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:10 +#, elixir-autogen, elixir-format +msgid "Base Callsign" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:12 +#, elixir-autogen, elixir-format +msgid "Built with Modern Technologies" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:270 +#: lib/aprsme_web/live/info_live/show.html.heex:388 +#, elixir-autogen, elixir-format +msgid "Callsign" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:155 +#, elixir-autogen, elixir-format +msgid "Course" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:11 +#, elixir-autogen, elixir-format +msgid "Data Type" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:220 +#, elixir-autogen, elixir-format +msgid "Decoded Path" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:194 +#: lib/aprsme_web/live/packets_live/index.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Device" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:189 +#, elixir-autogen, elixir-format +msgid "Device Information" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:391 +#, elixir-autogen, elixir-format +msgid "Distance & Course" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:273 +#, elixir-autogen, elixir-format +msgid "Distance & Direction" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:35 +#, elixir-autogen, elixir-format +msgid "Explore the Map" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:121 +#, elixir-autogen, elixir-format +msgid "Grid Square" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:21 +#, elixir-autogen, elixir-format +msgid "Join Our Community" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:276 +#: lib/aprsme_web/live/info_live/show.html.heex:394 +#, elixir-autogen, elixir-format +msgid "Last Heard" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:133 +#, elixir-autogen, elixir-format +msgid "Last Position" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:14 +#, elixir-autogen, elixir-format +msgid "Latitude" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:114 +#, elixir-autogen, elixir-format +msgid "Location" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:15 +#, elixir-autogen, elixir-format +msgid "Longitude" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:469 +#, elixir-autogen, elixir-format +msgid "No data available" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:164 +#, elixir-autogen, elixir-format, fuzzy +msgid "No packets" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:471 +#, elixir-autogen, elixir-format +msgid "No recent packet data available for this callsign." +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:262 +#, elixir-autogen, elixir-format +msgid "Other SSIDs" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:165 +#, elixir-autogen, elixir-format +msgid "Packets will appear here as they are received." +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:215 +#: lib/aprsme_web/live/packets_live/index.html.heex:13 +#, elixir-autogen, elixir-format +msgid "Path" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:109 +#, elixir-autogen, elixir-format +msgid "Position Information" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:9 +#, elixir-autogen, elixir-format +msgid "SSID" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:8 +#, elixir-autogen, elixir-format +msgid "Sender" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:161 +#, elixir-autogen, elixir-format +msgid "Speed" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:380 +#, elixir-autogen, elixir-format +msgid "Stations Near Current Position" +msgstr "" + +#: lib/aprsme_web/live/packets_live/index.html.heex:12 +#, elixir-autogen, elixir-format +msgid "Symbol" +msgstr "" + +#: lib/aprsme_web/live/about_live.html.heex:4 +#, elixir-autogen, elixir-format +msgid "Under heavy construction" +msgstr "" + +#: lib/aprsme_web/live/info_live/show.html.heex:71 +#, elixir-autogen, elixir-format, fuzzy +msgid "Weather charts" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:63 +#, elixir-autogen, elixir-format +msgid "%" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:53 +#, elixir-autogen, elixir-format +msgid "Dew Point (°F)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:54 +#: lib/aprsme_web/live/weather_live/callsign_view.ex:55 +#, elixir-autogen, elixir-format, fuzzy +msgid "Humidity (%)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:56 +#: lib/aprsme_web/live/weather_live/callsign_view.ex:57 +#, elixir-autogen, elixir-format, fuzzy +msgid "Pressure (mb)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:51 +#, elixir-autogen, elixir-format +msgid "Temperature & Dew Point (°F)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:52 +#, elixir-autogen, elixir-format, fuzzy +msgid "Temperature (°F)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:61 +#, elixir-autogen, elixir-format +msgid "Time" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:58 +#, elixir-autogen, elixir-format +msgid "Wind (mph)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:60 +#, elixir-autogen, elixir-format +msgid "Wind Gust (mph)" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:59 +#, elixir-autogen, elixir-format +msgid "Wind Speed (mph)" +msgstr "" + +#: lib/aprsme_web/time_helpers.ex:14 +#, elixir-autogen, elixir-format +msgid "ago" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:64 +#, elixir-autogen, elixir-format +msgid "mb" +msgstr "" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:62 +#, elixir-autogen, elixir-format +msgid "°F" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po index 844c4f5..235d38a 100644 --- a/priv/gettext/en/LC_MESSAGES/errors.po +++ b/priv/gettext/en/LC_MESSAGES/errors.po @@ -12,15 +12,15 @@ msgstr "" ## From Ecto.Changeset.cast/4 msgid "can't be blank" -msgstr "" +msgstr "can't be blank" ## From Ecto.Changeset.unique_constraint/3 msgid "has already been taken" -msgstr "" +msgstr "has already been taken" ## From Ecto.Changeset.put_change/3 msgid "is invalid" -msgstr "" +msgstr "is invalid" ## From Ecto.Changeset.validate_acceptance/3 msgid "must be accepted" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index ccf5c68..eef2de2 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -7,7 +7,6 @@ ## 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. - ## From Ecto.Changeset.cast/4 msgid "can't be blank" msgstr "" diff --git a/priv/gettext/es/LC_MESSAGES/default.po b/priv/gettext/es/LC_MESSAGES/default.po new file mode 100644 index 0000000..e2bf513 --- /dev/null +++ b/priv/gettext/es/LC_MESSAGES/default.po @@ -0,0 +1,905 @@ +## "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: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: lib/aprsme_web/components/core_components.ex:570 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "Acciones" + +#: lib/aprsme_web/components/core_components.ex:117 +#: lib/aprsme_web/components/core_components.ex:202 +#, elixir-autogen, elixir-format +msgid "close" +msgstr "cerrar" + +#: lib/aprsme_web/time_helpers.ex:23 +#, elixir-autogen, elixir-format +msgid "%{count} days" +msgstr "%{count} días" + +#: lib/aprsme_web/live/status_live/index.ex:310 +#, elixir-autogen, elixir-format +msgid "%{count} days ago" +msgstr "hace %{count} días" + +#: lib/aprsme_web/time_helpers.ex:21 +#, elixir-autogen, elixir-format +msgid "%{count} hours" +msgstr "%{count} horas" + +#: lib/aprsme_web/live/status_live/index.ex:309 +#, elixir-autogen, elixir-format +msgid "%{count} hours ago" +msgstr "hace %{count} horas" + +#: lib/aprsme_web/time_helpers.ex:19 +#, elixir-autogen, elixir-format +msgid "%{count} minutes" +msgstr "%{count} minutos" + +#: lib/aprsme_web/live/status_live/index.ex:308 +#, elixir-autogen, elixir-format +msgid "%{count} minutes ago" +msgstr "hace %{count} minutos" + +#: lib/aprsme_web/time_helpers.ex:26 +#, elixir-autogen, elixir-format +msgid "%{count} months" +msgstr "%{count} meses" + +#: lib/aprsme_web/live/status_live/index.ex:307 +#, elixir-autogen, elixir-format +msgid "%{count} seconds ago" +msgstr "hace %{count} segundos" + +#: lib/aprsme_web/time_helpers.ex:29 +#, elixir-autogen, elixir-format +msgid "%{count} years" +msgstr "%{count} años" + +#: lib/aprsme_web/live/info_live/show.ex:477 +#, elixir-autogen, elixir-format +msgid "%{element} (Digipeater)" +msgstr "%{element} (Digipeater)" + +#: lib/aprsme_web/live/info_live/show.ex:481 +#, elixir-autogen, elixir-format +msgid "%{element} (Station)" +msgstr "%{element} (Estación)" + +#: lib/aprsme_web/live/info_live/show.ex:485 +#, elixir-autogen, elixir-format +msgid "%{element} (Unknown)" +msgstr "%{element} (Desconocido)" + +#: lib/aprsme_web/time_helpers.ex:22 +#, elixir-autogen, elixir-format +msgid "1 day" +msgstr "1 día" + +#: lib/aprsme_web/time_helpers.ex:20 +#, elixir-autogen, elixir-format +msgid "1 hour" +msgstr "1 hora" + +#: lib/aprsme_web/time_helpers.ex:18 +#, elixir-autogen, elixir-format +msgid "1 minute" +msgstr "1 minuto" + +#: lib/aprsme_web/time_helpers.ex:24 +#, elixir-autogen, elixir-format +msgid "1 month" +msgstr "1 mes" + +#: lib/aprsme_web/time_helpers.ex:28 +#, elixir-autogen, elixir-format +msgid "1 year" +msgstr "1 año" + +#: lib/aprsme_web/live/status_live/index.ex:78 +#, elixir-autogen, elixir-format +msgid "APRS-IS Connection" +msgstr "Conexión APRS-IS" + +#: lib/aprsme_web/live/status_live/index.ex:68 +#, elixir-autogen, elixir-format +msgid "APRS-IS Connection Issue" +msgstr "Problema de Conexión APRS-IS" + +#: lib/aprsme_web/live/status_live/index.ex:44 +#, elixir-autogen, elixir-format +msgid "APRS.me System Status" +msgstr "Estado del Sistema APRS.me" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:70 +#, elixir-autogen, elixir-format +msgid "Access forbidden" +msgstr "Acceso prohibido" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:85 +#, elixir-autogen, elixir-format +msgid "All packets are parsing successfully!" +msgstr "¡Todos los paquetes se están analizando correctamente!" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:72 +#, elixir-autogen, elixir-format +msgid "An error occurred: %{reason}" +msgstr "Ocurrió un error: %{reason}" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:40 +#, elixir-autogen, elixir-format +msgid "Attempted At" +msgstr "Intentado En" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:9 +#, elixir-autogen, elixir-format +msgid "Auto-refreshes every 5 seconds" +msgstr "Se actualiza automáticamente cada 5 segundos" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:71 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:46 +#, elixir-autogen, elixir-format +msgid "Bad request" +msgstr "Solicitud incorrecta" + +#: lib/aprsme_web/live/status_live/index.ex:87 +#, elixir-autogen, elixir-format +msgid "Connected" +msgstr "Conectado" + +#: lib/aprsme_web/live/status_live/index.ex:110 +#, elixir-autogen, elixir-format +msgid "Connected Since:" +msgstr "Conectado Desde:" + +#: lib/aprsme_web/live/status_live/index.ex:187 +#, elixir-autogen, elixir-format +msgid "Connection Health:" +msgstr "Estado de la Conexión:" + +#: lib/aprsme_web/live/status_live/index.ex:92 +#, elixir-autogen, elixir-format +msgid "Disconnected" +msgstr "Desconectado" + +#: lib/aprsme_web/live/status_live/index.ex:294 +#, elixir-autogen, elixir-format +msgid "Disconnected - Connection issues detected" +msgstr "Desconectado - Problemas de conexión detectados" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:53 +#, elixir-autogen, elixir-format +msgid "Error Message" +msgstr "Mensaje de Error" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:48 +#, elixir-autogen, elixir-format +msgid "Error Type" +msgstr "Tipo de Error" + +#: lib/aprsme_web/live/status_live/index.ex:298 +#, elixir-autogen, elixir-format +msgid "Excellent - Long-term stable connection" +msgstr "Excelente - Conexión estable a largo plazo" + +#: lib/aprsme_web/live/status_live/index.ex:131 +#, elixir-autogen, elixir-format +msgid "Filter:" +msgstr "Filtro:" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:64 +#, elixir-autogen, elixir-format +msgid "Forbidden" +msgstr "Prohibido" + +#: lib/aprsme_web/live/status_live/index.ex:296 +#, elixir-autogen, elixir-format +msgid "Good - Connection stable for less than 1 hour" +msgstr "Bueno - Conexión estable por menos de 1 hora" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:28 +#, elixir-autogen, elixir-format +msgid "Internal server error" +msgstr "Error interno del servidor" + +#: lib/aprsme_web/live/status_live/index.ex:165 +#, elixir-autogen, elixir-format +msgid "Last Packet:" +msgstr "Último Paquete:" + +#: lib/aprsme_web/live/status_live/index.ex:46 +#, elixir-autogen, elixir-format +msgid "Last updated:" +msgstr "Última actualización:" + +#: lib/aprsme_web/live/status_live/index.ex:105 +#, elixir-autogen, elixir-format +msgid "Login ID:" +msgstr "ID de Inicio de Sesión:" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:84 +#, elixir-autogen, elixir-format +msgid "No bad packets" +msgstr "No hay paquetes malos" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:55 +#, elixir-autogen, elixir-format +msgid "No error message" +msgstr "Sin mensaje de error" + +#: lib/aprsme_web/live/status_live/index.ex:170 +#, elixir-autogen, elixir-format +msgid "None" +msgstr "Ninguno" + +#: lib/aprsme_web/live/status_live/index.ex:115 +#: lib/aprsme_web/live/status_live/index.ex:262 +#, elixir-autogen, elixir-format +msgid "Not connected" +msgstr "No conectado" + +#: lib/aprsme_web/live/status_live/index.ex:141 +#, elixir-autogen, elixir-format +msgid "Packet Statistics" +msgstr "Estadísticas de Paquetes" + +#: lib/aprsme_web/live/status_live/index.ex:158 +#, elixir-autogen, elixir-format +msgid "Packets/Min:" +msgstr "Paquetes/Min:" + +#: lib/aprsme_web/live/status_live/index.ex:151 +#, elixir-autogen, elixir-format +msgid "Packets/Sec:" +msgstr "Paquetes/Seg:" + +#: lib/aprsme_web/live/info_live/show.ex:305 +#, elixir-autogen, elixir-format +msgid "RELAY (Relay digipeater)" +msgstr "RELAY (Digipeater de retransmisión)" + +#: lib/aprsme_web/live/info_live/show.ex:308 +#, elixir-autogen, elixir-format +msgid "RELAY digipeater (%{element})" +msgstr "Digipeater RELAY (%{element})" + +#: lib/aprsme_web/live/info_live/show.ex:306 +#, elixir-autogen, elixir-format +msgid "RELAY-1 (Relay digipeater, 1 hop)" +msgstr "RELAY-1 (Digipeater de retransmisión, 1 salto)" + +#: lib/aprsme_web/live/info_live/show.ex:307 +#, elixir-autogen, elixir-format +msgid "RELAY-2 (Relay digipeater, 2 hops)" +msgstr "RELAY-2 (Digipeater de retransmisión, 2 saltos)" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:58 +#: lib/aprsme_web/live/info_live/show.html.heex:227 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:148 +#, elixir-autogen, elixir-format +msgid "Raw Packet" +msgstr "Paquete Sin Procesar" + +#: lib/aprsme_web/live/status_live/index.ex:295 +#, elixir-autogen, elixir-format +msgid "Recently connected - Monitoring stability" +msgstr "Recientemente conectado - Monitoreando estabilidad" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:57 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:73 +#, elixir-autogen, elixir-format +msgid "Request timeout" +msgstr "Tiempo de espera de la solicitud" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:68 +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:19 +#, elixir-autogen, elixir-format +msgid "Resource not found" +msgstr "Recurso no encontrado" + +#: lib/aprsme_web/live/status_live/index.ex:98 +#, elixir-autogen, elixir-format +msgid "Server:" +msgstr "Servidor:" + +#: lib/aprsme_web/live/status_live/index.ex:83 +#, elixir-autogen, elixir-format +msgid "Status:" +msgstr "Estado:" + +#: lib/aprsme_web/live/status_live/index.ex:176 +#, elixir-autogen, elixir-format +msgid "Stored Packets:" +msgstr "Paquetes Almacenados:" + +#: lib/aprsme_web/live/info_live/show.ex:470 +#, elixir-autogen, elixir-format +msgid "TCPIP (Internet gateway)" +msgstr "TCPIP (Puerta de enlace de Internet)" + +#: lib/aprsme_web/live/info_live/show.ex:472 +#, elixir-autogen, elixir-format +msgid "TCPIP gateway (%{element})" +msgstr "Puerta de enlace TCPIP (%{element})" + +#: lib/aprsme_web/live/info_live/show.ex:471 +#, elixir-autogen, elixir-format +msgid "TCPIP* (Internet gateway, no forward)" +msgstr "TCPIP* (Puerta de enlace de Internet, sin reenvío)" + +#: lib/aprsme_web/live/info_live/show.ex:299 +#, elixir-autogen, elixir-format +msgid "TRACE digipeater (%{element})" +msgstr "Digipeater TRACE (%{element})" + +#: lib/aprsme_web/live/info_live/show.ex:292 +#, elixir-autogen, elixir-format +msgid "TRACE1-1 (Trace digipeater, 1 hop)" +msgstr "TRACE1-1 (Digipeater de rastreo, 1 salto)" + +#: lib/aprsme_web/live/info_live/show.ex:293 +#, elixir-autogen, elixir-format +msgid "TRACE2-1 (Trace digipeater, 2 hops)" +msgstr "TRACE2-1 (Digipeater de rastreo, 2 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:294 +#, elixir-autogen, elixir-format +msgid "TRACE3-1 (Trace digipeater, 3 hops)" +msgstr "TRACE3-1 (Digipeater de rastreo, 3 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:295 +#, elixir-autogen, elixir-format +msgid "TRACE4-1 (Trace digipeater, 4 hops)" +msgstr "TRACE4-1 (Digipeater de rastreo, 4 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:296 +#, elixir-autogen, elixir-format +msgid "TRACE5-1 (Trace digipeater, 5 hops)" +msgstr "TRACE5-1 (Digipeater de rastreo, 5 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:297 +#, elixir-autogen, elixir-format +msgid "TRACE6-1 (Trace digipeater, 6 hops)" +msgstr "TRACE6-1 (Digipeater de rastreo, 6 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:298 +#, elixir-autogen, elixir-format +msgid "TRACE7-1 (Trace digipeater, 7 hops)" +msgstr "TRACE7-1 (Digipeater de rastreo, 7 saltos)" + +#: lib/aprsme_web/live/status_live/index.ex:70 +#, elixir-autogen, elixir-format +msgid "The system is currently disconnected from the APRS-IS network. This may be due to network issues or server maintenance." +msgstr "El sistema está actualmente desconectado de la red APRS-IS. Esto puede deberse a problemas de red o mantenimiento del servidor." + +#: lib/aprsme_web/live/status_live/index.ex:144 +#, elixir-autogen, elixir-format +msgid "Total Packets:" +msgstr "Total de Paquetes:" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:55 +#, elixir-autogen, elixir-format +msgid "Unauthorized" +msgstr "No autorizado" + +#: lib/aprsme_web/controllers/api/v1/fallback_controller.ex:69 +#, elixir-autogen, elixir-format +msgid "Unauthorized access" +msgstr "Acceso no autorizado" + +#: lib/aprsme/device_identification.ex:56 +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:50 +#: lib/aprsme_web/live/info_live/show.html.heex:143 +#: lib/aprsme_web/live/info_live/show.html.heex:209 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:92 +#, elixir-autogen, elixir-format +msgid "Unknown" +msgstr "Desconocido" + +#: lib/aprsme_web/live/status_live/index.ex:299 +#, elixir-autogen, elixir-format +msgid "Unknown status" +msgstr "Estado desconocido" + +#: lib/aprsme_web/controllers/api/v1/json/error_json.ex:37 +#, elixir-autogen, elixir-format +msgid "Unprocessable entity" +msgstr "Entidad no procesable" + +#: lib/aprsme_web/live/bad_packets_live/index.html.heex:35 +#, elixir-autogen, elixir-format +msgid "Updating..." +msgstr "Actualizando..." + +#: lib/aprsme_web/live/status_live/index.ex:121 +#, elixir-autogen, elixir-format +msgid "Uptime:" +msgstr "Tiempo de Actividad:" + +#: lib/aprsme_web/controllers/api/v1/json/changeset_json.ex:13 +#, elixir-autogen, elixir-format +msgid "Validation failed" +msgstr "Validación fallida" + +#: lib/aprsme_web/live/status_live/index.ex:297 +#, elixir-autogen, elixir-format +msgid "Very good - Connection stable for less than 1 day" +msgstr "Muy bueno - Conexión estable por menos de 1 día" + +#: lib/aprsme_web/live/info_live/show.ex:286 +#, elixir-autogen, elixir-format +msgid "WIDE digipeater (%{element})" +msgstr "Digipeater WIDE (%{element})" + +#: lib/aprsme_web/live/info_live/show.ex:277 +#, elixir-autogen, elixir-format +msgid "WIDE1-1 (Wide area digipeater, 1 hop)" +msgstr "WIDE1-1 (Digipeater de área amplia, 1 salto)" + +#: lib/aprsme_web/live/info_live/show.ex:284 +#, elixir-autogen, elixir-format +msgid "WIDE1-2 (Wide area digipeater, 1 hop, 2nd attempt)" +msgstr "WIDE1-2 (Digipeater de área amplia, 1 salto, 2º intento)" + +#: lib/aprsme_web/live/info_live/show.ex:278 +#, elixir-autogen, elixir-format +msgid "WIDE2-1 (Wide area digipeater, 2 hops)" +msgstr "WIDE2-1 (Digipeater de área amplia, 2 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:285 +#, elixir-autogen, elixir-format +msgid "WIDE2-2 (Wide area digipeater, 2 hops, 2nd attempt)" +msgstr "WIDE2-2 (Digipeater de área amplia, 2 saltos, 2º intento)" + +#: lib/aprsme_web/live/info_live/show.ex:279 +#, elixir-autogen, elixir-format +msgid "WIDE3-1 (Wide area digipeater, 3 hops)" +msgstr "WIDE3-1 (Digipeater de área amplia, 3 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:280 +#, elixir-autogen, elixir-format +msgid "WIDE4-1 (Wide area digipeater, 4 hops)" +msgstr "WIDE4-1 (Digipeater de área amplia, 4 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:281 +#, elixir-autogen, elixir-format +msgid "WIDE5-1 (Wide area digipeater, 5 hops)" +msgstr "WIDE5-1 (Digipeater de área amplia, 5 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:282 +#, elixir-autogen, elixir-format +msgid "WIDE6-1 (Wide area digipeater, 6 hops)" +msgstr "WIDE6-1 (Digipeater de área amplia, 6 saltos)" + +#: lib/aprsme_web/live/info_live/show.ex:283 +#, elixir-autogen, elixir-format +msgid "WIDE7-1 (WIDE area digipeater, 7 hops)" +msgstr "WIDE7-1 (Digipeater de área amplia, 7 saltos)" + +#: lib/aprsme_web/time_helpers.ex:17 +#, elixir-autogen, elixir-format +msgid "less than a minute" +msgstr "menos de un minuto" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:118 +#, elixir-autogen, elixir-format +msgid "Gusts" +msgstr "Ráfagas" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:103 +#, elixir-autogen, elixir-format +msgid "Humidity" +msgstr "Humedad" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:84 +#, elixir-autogen, elixir-format +msgid "Last WX report" +msgstr "Último reporte WX" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:124 +#, elixir-autogen, elixir-format +msgid "Pressure" +msgstr "Presión" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:130 +#, elixir-autogen, elixir-format +msgid "Rain (1h)" +msgstr "Lluvia (1h)" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:136 +#, elixir-autogen, elixir-format +msgid "Rain (24h)" +msgstr "Lluvia (24h)" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:142 +#, elixir-autogen, elixir-format +msgid "Rain (since midnight)" +msgstr "Lluvia (desde medianoche)" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:159 +#, elixir-autogen, elixir-format +msgid "Raw comment" +msgstr "Comentario crudo" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:97 +#, elixir-autogen, elixir-format +msgid "Temperature" +msgstr "Temperatura" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:43 +#, elixir-autogen, elixir-format +msgid "View info" +msgstr "Ver información" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:49 +#, elixir-autogen, elixir-format +msgid "View on map" +msgstr "Ver en mapa" + +#: lib/aprsme_web/live/info_live/show.html.heex:67 +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:46 +#, elixir-autogen, elixir-format +msgid "View packets" +msgstr "Ver paquetes" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:79 +#, elixir-autogen, elixir-format +msgid "Weather Details" +msgstr "Detalles del Clima" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:165 +#, elixir-autogen, elixir-format +msgid "Weather History Graphs" +msgstr "Gráficos del Historial del Clima" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:8 +#, elixir-autogen, elixir-format +msgid "Weather Station" +msgstr "Estación Meteorológica" + +#: lib/aprsme_web/live/weather_live/callsign_view.html.heex:109 +#, elixir-autogen, elixir-format +msgid "Wind" +msgstr "Viento" + +#: lib/aprsme_web/live/info_live/show.html.heex:27 +#, elixir-autogen, elixir-format +msgid "APRS Station" +msgstr "Estación APRS" + +#: lib/aprsme_web/live/info_live/show.html.heex:149 +#, elixir-autogen, elixir-format +msgid "Altitude" +msgstr "Altitud" + +#: lib/aprsme_web/live/packets_live/index.html.heex:10 +#, elixir-autogen, elixir-format +msgid "Base Callsign" +msgstr "Indicativo Base" + +#: lib/aprsme_web/live/about_live.html.heex:12 +#, elixir-autogen, elixir-format +msgid "Built with Modern Technologies" +msgstr "Construido con Tecnologías Modernas" + +#: lib/aprsme_web/live/info_live/show.html.heex:270 +#: lib/aprsme_web/live/info_live/show.html.heex:388 +#, elixir-autogen, elixir-format +msgid "Callsign" +msgstr "Indicativo" + +#: lib/aprsme_web/live/info_live/show.html.heex:155 +#, elixir-autogen, elixir-format +msgid "Course" +msgstr "Rumbo" + +#: lib/aprsme_web/live/packets_live/index.html.heex:11 +#, elixir-autogen, elixir-format +msgid "Data Type" +msgstr "Tipo de Datos" + +#: lib/aprsme_web/live/info_live/show.html.heex:220 +#, elixir-autogen, elixir-format +msgid "Decoded Path" +msgstr "Ruta Decodificada" + +#: lib/aprsme_web/live/info_live/show.html.heex:194 +#: lib/aprsme_web/live/packets_live/index.html.heex:16 +#, elixir-autogen, elixir-format +msgid "Device" +msgstr "Dispositivo" + +#: lib/aprsme_web/live/info_live/show.html.heex:189 +#, elixir-autogen, elixir-format +msgid "Device Information" +msgstr "Información del Dispositivo" + +#: lib/aprsme_web/live/info_live/show.html.heex:391 +#, elixir-autogen, elixir-format +msgid "Distance & Course" +msgstr "Distancia y Rumbo" + +#: lib/aprsme_web/live/info_live/show.html.heex:273 +#, elixir-autogen, elixir-format +msgid "Distance & Direction" +msgstr "Distancia y Dirección" + +#: lib/aprsme_web/live/about_live.html.heex:35 +#, elixir-autogen, elixir-format +msgid "Explore the Map" +msgstr "Explorar el Mapa" + +#: lib/aprsme_web/live/info_live/show.html.heex:121 +#, elixir-autogen, elixir-format +msgid "Grid Square" +msgstr "Cuadrícula" + +#: lib/aprsme_web/live/about_live.html.heex:21 +#, elixir-autogen, elixir-format +msgid "Join Our Community" +msgstr "Únete a Nuestra Comunidad" + +#: lib/aprsme_web/live/info_live/show.html.heex:276 +#: lib/aprsme_web/live/info_live/show.html.heex:394 +#, elixir-autogen, elixir-format +msgid "Last Heard" +msgstr "Último Escuchado" + +#: lib/aprsme_web/live/info_live/show.html.heex:133 +#, elixir-autogen, elixir-format +msgid "Last Position" +msgstr "Última Posición" + +#: lib/aprsme_web/live/packets_live/index.html.heex:14 +#, elixir-autogen, elixir-format +msgid "Latitude" +msgstr "Latitud" + +#: lib/aprsme_web/live/info_live/show.html.heex:114 +#, elixir-autogen, elixir-format +msgid "Location" +msgstr "Ubicación" + +#: lib/aprsme_web/live/packets_live/index.html.heex:15 +#, elixir-autogen, elixir-format +msgid "Longitude" +msgstr "Longitud" + +#: lib/aprsme_web/live/info_live/show.html.heex:469 +#, elixir-autogen, elixir-format +msgid "No data available" +msgstr "No hay datos disponibles" + +#: lib/aprsme_web/live/packets_live/index.html.heex:164 +#, elixir-autogen, elixir-format, fuzzy +msgid "No packets" +msgstr "No hay paquetes" + +#: lib/aprsme_web/live/info_live/show.html.heex:471 +#, elixir-autogen, elixir-format +msgid "No recent packet data available for this callsign." +msgstr "No hay datos de paquetes recientes disponibles para este indicativo." + +#: lib/aprsme_web/live/info_live/show.html.heex:262 +#, elixir-autogen, elixir-format +msgid "Other SSIDs" +msgstr "Otros SSIDs" + +#: lib/aprsme_web/live/packets_live/index.html.heex:165 +#, elixir-autogen, elixir-format +msgid "Packets will appear here as they are received." +msgstr "Los paquetes aparecerán aquí a medida que sean recibidos." + +#: lib/aprsme_web/live/info_live/show.html.heex:215 +#: lib/aprsme_web/live/packets_live/index.html.heex:13 +#, elixir-autogen, elixir-format +msgid "Path" +msgstr "Ruta" + +#: lib/aprsme_web/live/info_live/show.html.heex:109 +#, elixir-autogen, elixir-format +msgid "Position Information" +msgstr "Información de Posición" + +#: lib/aprsme_web/live/packets_live/index.html.heex:9 +#, elixir-autogen, elixir-format +msgid "SSID" +msgstr "SSID" + +#: lib/aprsme_web/live/packets_live/index.html.heex:8 +#, elixir-autogen, elixir-format +msgid "Sender" +msgstr "Remitente" + +#: lib/aprsme_web/live/info_live/show.html.heex:161 +#, elixir-autogen, elixir-format +msgid "Speed" +msgstr "Velocidad" + +#: lib/aprsme_web/live/info_live/show.html.heex:380 +#, elixir-autogen, elixir-format +msgid "Stations Near Current Position" +msgstr "Estaciones Cerca de la Posición Actual" + +#: lib/aprsme_web/live/packets_live/index.html.heex:12 +#, elixir-autogen, elixir-format +msgid "Symbol" +msgstr "Símbolo" + +#: lib/aprsme_web/live/about_live.html.heex:4 +#, elixir-autogen, elixir-format +msgid "Under heavy construction" +msgstr "En construcción intensiva" + +#: lib/aprsme_web/live/info_live/show.html.heex:71 +#, elixir-autogen, elixir-format, fuzzy +msgid "Weather charts" +msgstr "Gráficos del clima" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:63 +#, elixir-autogen, elixir-format +msgid "%" +msgstr "%" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:53 +#, elixir-autogen, elixir-format +msgid "Dew Point (°F)" +msgstr "Punto de Rocío (°F)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:54 +#: lib/aprsme_web/live/weather_live/callsign_view.ex:55 +#, elixir-autogen, elixir-format +msgid "Humidity (%)" +msgstr "Humedad (%)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:56 +#: lib/aprsme_web/live/weather_live/callsign_view.ex:57 +#, elixir-autogen, elixir-format +msgid "Pressure (mb)" +msgstr "Presión (mb)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:51 +#, elixir-autogen, elixir-format +msgid "Temperature & Dew Point (°F)" +msgstr "Temperatura y Punto de Rocío (°F)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:52 +#, elixir-autogen, elixir-format +msgid "Temperature (°F)" +msgstr "Temperatura (°F)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:61 +#, elixir-autogen, elixir-format +msgid "Time" +msgstr "Tiempo" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:58 +#, elixir-autogen, elixir-format +msgid "Wind (mph)" +msgstr "Viento (mph)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:60 +#, elixir-autogen, elixir-format +msgid "Wind Gust (mph)" +msgstr "Ráfaga de Viento (mph)" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:59 +#, elixir-autogen, elixir-format +msgid "Wind Speed (mph)" +msgstr "Velocidad del Viento (mph)" + +#: lib/aprsme_web/time_helpers.ex:14 +#, elixir-autogen, elixir-format +msgid "ago" +msgstr "hace" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:64 +#, elixir-autogen, elixir-format +msgid "mb" +msgstr "mb" + +#: lib/aprsme_web/live/weather_live/callsign_view.ex:62 +#, elixir-autogen, elixir-format +msgid "°F" +msgstr "°F" + +#: lib/aprsme_web/live/map_live/index.ex:611 +#, elixir-autogen, elixir-format +msgid "Find my location" +msgstr "Encontrar mi ubicación" + +#: lib/aprsme_web/live/map_live/index.ex:633 +#, elixir-autogen, elixir-format +msgid "Hide controls" +msgstr "Ocultar controles" + +#: lib/aprsme_web/live/map_live/index.ex:633 +#, elixir-autogen, elixir-format +msgid "Show controls" +msgstr "Mostrar controles" + +#: lib/aprsme_web/live/map_live/index.ex:670 +#, elixir-autogen, elixir-format +msgid "Close controls" +msgstr "Cerrar controles" + +#: lib/aprsme_web/live/map_live/index.ex:690 +#, elixir-autogen, elixir-format +msgid "Search Callsign" +msgstr "Buscar Indicativo" + +#: lib/aprsme_web/live/map_live/index.ex:695 +#, elixir-autogen, elixir-format +msgid "Enter callsign..." +msgstr "Ingrese indicativo..." + +#: lib/aprsme_web/live/map_live/index.ex:700 +#, elixir-autogen, elixir-format +msgid "Search" +msgstr "Buscar" + +#: lib/aprsme_web/live/map_live/index.ex:710 +#, elixir-autogen, elixir-format +msgid "Trail Duration" +msgstr "Duración del Rastro" + +#: lib/aprsme_web/live/map_live/index.ex:715 +#, elixir-autogen, elixir-format +msgid "1 Hour" +msgstr "1 Hora" + +#: lib/aprsme_web/live/map_live/index.ex:716 +#, elixir-autogen, elixir-format +msgid "6 Hours" +msgstr "6 Horas" + +#: lib/aprsme_web/live/map_live/index.ex:717 +#, elixir-autogen, elixir-format +msgid "12 Hours" +msgstr "12 Horas" + +#: lib/aprsme_web/live/map_live/index.ex:718 +#, elixir-autogen, elixir-format +msgid "24 Hours" +msgstr "24 Horas" + +#: lib/aprsme_web/live/map_live/index.ex:719 +#, elixir-autogen, elixir-format +msgid "48 Hours" +msgstr "48 Horas" + +#: lib/aprsme_web/live/map_live/index.ex:720 +#, elixir-autogen, elixir-format +msgid "1 Week" +msgstr "1 Semana" + +#: lib/aprsme_web/live/map_live/index.ex:730 +#, elixir-autogen, elixir-format +msgid "Historical Data" +msgstr "Datos Históricos" + +#: lib/aprsme_web/live/map_live/index.ex:735 +#, elixir-autogen, elixir-format +msgid "3 Hours" +msgstr "3 Horas" + +#: lib/aprsme_web/live/map_live/index.ex:750 +#, elixir-autogen, elixir-format +msgid "Navigation" +msgstr "Navegación" + +#: lib/aprsme_web/live/map_live/index.ex:760 +#, elixir-autogen, elixir-format +msgid "Last Deploy" +msgstr "Último Despliegue" diff --git a/priv/gettext/es/LC_MESSAGES/errors.po b/priv/gettext/es/LC_MESSAGES/errors.po new file mode 100644 index 0000000..27f44b1 --- /dev/null +++ b/priv/gettext/es/LC_MESSAGES/errors.po @@ -0,0 +1,102 @@ +## "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: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "can't be blank" +msgstr "no puede estar en blanco" + +msgid "has already been taken" +msgstr "ya ha sido tomado" + +msgid "is invalid" +msgstr "no es válido" + +msgid "must be accepted" +msgstr "debe ser aceptado" + +msgid "has invalid format" +msgstr "tiene formato inválido" + +msgid "has an invalid entry" +msgstr "tiene una entrada inválida" + +msgid "is reserved" +msgstr "está reservado" + +msgid "does not match confirmation" +msgstr "no coincide con la confirmación" + +msgid "is still associated with this entry" +msgstr "todavía está asociado con esta entrada" + +msgid "are still associated with this entry" +msgstr "todavía están asociados con esta entrada" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "debe tener %{count} elemento" +msgstr[1] "debe tener %{count} elementos" + +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "debe ser %{count} carácter" +msgstr[1] "debe ser %{count} caracteres" + +msgid "should be %{count} byte(s)" +msgid_plural "should be %{count} byte(s)" +msgstr[0] "debe ser %{count} byte" +msgstr[1] "debe ser %{count} bytes" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "debe tener al menos %{count} elemento" +msgstr[1] "debe tener al menos %{count} elementos" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "debe ser al menos %{count} carácter" +msgstr[1] "debe ser al menos %{count} caracteres" + +msgid "should be at least %{count} byte(s)" +msgid_plural "should be at least %{count} byte(s)" +msgstr[0] "debe ser al menos %{count} byte" +msgstr[1] "debe ser al menos %{count} bytes" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "debe tener como máximo %{count} elemento" +msgstr[1] "debe tener como máximo %{count} elementos" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "debe ser como máximo %{count} carácter" +msgstr[1] "debe ser como máximo %{count} caracteres" + +msgid "should be at most %{count} byte(s)" +msgid_plural "should be at most %{count} byte(s)" +msgstr[0] "debe ser como máximo %{count} byte" +msgstr[1] "debe ser como máximo %{count} bytes" + +msgid "must be less than %{number}" +msgstr "debe ser menor que %{number}" + +msgid "must be greater than %{number}" +msgstr "debe ser mayor que %{number}" + +msgid "must be less than or equal to %{number}" +msgstr "debe ser menor o igual a %{number}" + +msgid "must be greater than or equal to %{number}" +msgstr "debe ser mayor o igual a %{number}" + +msgid "must be equal to %{number}" +msgstr "debe ser igual a %{number}" diff --git a/test/aprsme_web/plugs/set_locale_test.exs b/test/aprsme_web/plugs/set_locale_test.exs new file mode 100644 index 0000000..0261dda --- /dev/null +++ b/test/aprsme_web/plugs/set_locale_test.exs @@ -0,0 +1,54 @@ +defmodule AprsmeWeb.Plugs.SetLocaleTest do + use AprsmeWeb.ConnCase + + alias AprsmeWeb.Plugs.SetLocale + + test "sets Spanish locale when Accept-Language header contains es", %{conn: conn} do + conn + |> Plug.Session.call(Plug.Session.init(store: :cookie, key: "_aprs_key", signing_salt: "test")) + |> fetch_session() + |> put_req_header("accept-language", "es-ES,es;q=0.9,en;q=0.8") + |> SetLocale.call([]) + + assert Gettext.get_locale(AprsmeWeb.Gettext) == "es" + end + + test "sets English locale when Accept-Language header contains en", %{conn: conn} do + conn + |> Plug.Session.call(Plug.Session.init(store: :cookie, key: "_aprs_key", signing_salt: "test")) + |> fetch_session() + |> put_req_header("accept-language", "en-US,en;q=0.9,es;q=0.8") + |> SetLocale.call([]) + + assert Gettext.get_locale(AprsmeWeb.Gettext) == "en" + end + + test "falls back to English when Accept-Language header contains unsupported locale", %{conn: conn} do + conn + |> Plug.Session.call(Plug.Session.init(store: :cookie, key: "_aprs_key", signing_salt: "test")) + |> fetch_session() + |> put_req_header("accept-language", "fr-FR,fr;q=0.9,en;q=0.8") + |> SetLocale.call([]) + + assert Gettext.get_locale(AprsmeWeb.Gettext) == "en" + end + + test "falls back to English when no Accept-Language header is present", %{conn: conn} do + conn + |> Plug.Session.call(Plug.Session.init(store: :cookie, key: "_aprs_key", signing_salt: "test")) + |> fetch_session() + |> SetLocale.call([]) + + assert Gettext.get_locale(AprsmeWeb.Gettext) == "en" + end + + test "prefers first supported locale in Accept-Language header", %{conn: conn} do + conn + |> Plug.Session.call(Plug.Session.init(store: :cookie, key: "_aprs_key", signing_salt: "test")) + |> fetch_session() + |> put_req_header("accept-language", "fr-FR,es-ES,en-US;q=0.9") + |> SetLocale.call([]) + + assert Gettext.get_locale(AprsmeWeb.Gettext) == "es" + end +end
SenderSSIDBase CallsignData TypeSymbolPathLatitudeLongitudeDevice{gettext("Sender")}{gettext("SSID")}{gettext("Base Callsign")}{gettext("Data Type")}{gettext("Symbol")}{gettext("Path")}{gettext("Latitude")}{gettext("Longitude")}{gettext("Device")}