more i18n
This commit is contained in:
parent
cba86fb082
commit
524c8208c3
8 changed files with 331 additions and 62 deletions
|
|
@ -9,4 +9,47 @@ defmodule Aprsme.Convert do
|
|||
|
||||
@spec speed(number(), :knots, :mph) :: float()
|
||||
def speed(value, :knots, :mph), do: Float.round(value * 1.15077945, 2)
|
||||
|
||||
# Weather unit conversions
|
||||
@doc """
|
||||
Converts Fahrenheit to Celsius
|
||||
"""
|
||||
def f_to_c(fahrenheit) when is_number(fahrenheit) do
|
||||
(fahrenheit - 32) * 5 / 9
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts Celsius to Fahrenheit
|
||||
"""
|
||||
def c_to_f(celsius) when is_number(celsius) do
|
||||
celsius * 9 / 5 + 32
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts mph to km/h
|
||||
"""
|
||||
def mph_to_kph(mph) when is_number(mph) do
|
||||
mph * 1.60934
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts km/h to mph
|
||||
"""
|
||||
def kph_to_mph(kph) when is_number(kph) do
|
||||
kph / 1.60934
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts inches to mm
|
||||
"""
|
||||
def inches_to_mm(inches) when is_number(inches) do
|
||||
inches * 25.4
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts mm to inches
|
||||
"""
|
||||
def mm_to_inches(mm) when is_number(mm) do
|
||||
mm / 25.4
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -329,7 +329,8 @@ defmodule AprsmeWeb.MapLive.CallsignView do
|
|||
socket = assign(socket, visible_packets: updated_visible_packets)
|
||||
|
||||
# Live packets are always the most recent for their callsign
|
||||
packet_data = PacketUtils.build_packet_data(packet, true)
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
packet_data = PacketUtils.build_packet_data(packet, true, locale)
|
||||
|
||||
socket =
|
||||
if packet_data,
|
||||
|
|
@ -355,7 +356,9 @@ defmodule AprsmeWeb.MapLive.CallsignView do
|
|||
end
|
||||
|
||||
defp handle_replay_packet(packet, socket) do
|
||||
case PacketUtils.build_packet_data(packet) do
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
|
||||
case PacketUtils.build_packet_data(packet, false, locale) do
|
||||
nil -> handle_invalid_replay_packet(socket)
|
||||
packet_data -> handle_valid_replay_packet(packet, packet_data, socket)
|
||||
end
|
||||
|
|
@ -814,7 +817,8 @@ defmodule AprsmeWeb.MapLive.CallsignView do
|
|||
end
|
||||
|
||||
defp build_single_historical_packet_data(packet, index, callsign) do
|
||||
case PacketUtils.build_packet_data(packet) do
|
||||
# Note: We don't have access to locale here, so we'll use default "en"
|
||||
case PacketUtils.build_packet_data(packet, false, "en") do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
|
|
@ -847,7 +851,8 @@ defmodule AprsmeWeb.MapLive.CallsignView do
|
|||
|
||||
defp push_latest_marker(socket, latest_packet) do
|
||||
if latest_packet do
|
||||
packet_data = PacketUtils.build_packet_data(latest_packet, true)
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
packet_data = PacketUtils.build_packet_data(latest_packet, true, locale)
|
||||
|
||||
if packet_data,
|
||||
do: {push_event(socket, "new_packet", packet_data), true},
|
||||
|
|
@ -992,7 +997,8 @@ defmodule AprsmeWeb.MapLive.CallsignView do
|
|||
if Map.get(socket.assigns, :latest_marker_pushed, false) do
|
||||
socket
|
||||
else
|
||||
packet_data = PacketUtils.build_packet_data(packet, true)
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
packet_data = PacketUtils.build_packet_data(packet, true, locale)
|
||||
if packet_data, do: push_event(socket, "new_packet", packet_data), else: socket
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -144,9 +144,11 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end)
|
||||
|> Map.new()
|
||||
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
|
||||
visible_packets_list =
|
||||
filtered_packets
|
||||
|> Enum.map(fn {_callsign, packet} -> PacketUtils.build_packet_data(packet) end)
|
||||
|> Enum.map(fn {_callsign, packet} -> PacketUtils.build_packet_data(packet, false, locale) end)
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
socket = assign(socket, visible_packets: filtered_packets)
|
||||
|
|
@ -398,7 +400,8 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
new_visible_packets = Map.put(socket.assigns.visible_packets, callsign_key, packet)
|
||||
|
||||
# Live packets are always the most recent for their callsign
|
||||
marker_data = PacketUtils.build_packet_data(packet, true)
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
marker_data = PacketUtils.build_packet_data(packet, true, locale)
|
||||
|
||||
socket =
|
||||
if marker_data do
|
||||
|
|
@ -1046,7 +1049,8 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
# Only show as red dot if it's not the most recent position
|
||||
is_most_recent = index == 0
|
||||
|
||||
packet_data = PacketUtils.build_packet_data(packet, is_most_recent)
|
||||
# Note: We don't have access to locale here, so we'll use default "en"
|
||||
packet_data = PacketUtils.build_packet_data(packet, is_most_recent, "en")
|
||||
|
||||
if packet_data do
|
||||
packet_data
|
||||
|
|
|
|||
|
|
@ -137,24 +137,25 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
|
|||
Map.get(data_extended, to_string(key)) || "N/A"
|
||||
end
|
||||
|
||||
def build_packet_data(packet, is_most_recent_for_callsign) when is_boolean(is_most_recent_for_callsign) do
|
||||
def build_packet_data(packet, is_most_recent_for_callsign, locale \\ "en")
|
||||
when is_boolean(is_most_recent_for_callsign) do
|
||||
{lat, lon, data_extended} = MapHelpers.get_coordinates(packet)
|
||||
callsign = generate_callsign(packet)
|
||||
|
||||
if lat && lon && callsign != "" && callsign != nil do
|
||||
packet_data = build_packet_map(packet, lat, lon, data_extended)
|
||||
packet_data = build_packet_map(packet, lat, lon, data_extended, locale)
|
||||
Map.put(packet_data, "is_most_recent_for_callsign", is_most_recent_for_callsign)
|
||||
end
|
||||
end
|
||||
|
||||
def build_packet_data(packet) do
|
||||
build_packet_data(packet, false)
|
||||
build_packet_data(packet, false, "en")
|
||||
end
|
||||
|
||||
defp build_packet_map(packet, lat, lon, data_extended) do
|
||||
defp build_packet_map(packet, lat, lon, data_extended, locale) do
|
||||
data_extended = data_extended || %{}
|
||||
packet_info = extract_packet_info(packet, data_extended)
|
||||
popup = build_popup_content(packet, packet_info, lat, lon)
|
||||
popup = build_popup_content(packet, packet_info, lat, lon, locale)
|
||||
|
||||
build_packet_result(packet, packet_info, lat, lon, popup)
|
||||
end
|
||||
|
|
@ -171,9 +172,9 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
|
|||
}
|
||||
end
|
||||
|
||||
defp build_popup_content(packet, packet_info, lat, lon) do
|
||||
defp build_popup_content(packet, packet_info, lat, lon, locale) do
|
||||
if packet_info.is_weather_packet do
|
||||
build_weather_popup_html(packet, packet_info.callsign)
|
||||
build_weather_popup_html(packet, packet_info.callsign, locale)
|
||||
else
|
||||
build_standard_popup_html(packet_info, lat, lon)
|
||||
end
|
||||
|
|
@ -197,11 +198,27 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
|
|||
|> IO.iodata_to_binary()
|
||||
end
|
||||
|
||||
defp build_weather_popup_html(packet, sender) do
|
||||
defp build_weather_popup_html(packet, sender, locale) do
|
||||
received_at = get_received_at(packet)
|
||||
timestamp_dt = TimeHelpers.to_datetime(received_at)
|
||||
cache_buster = System.system_time(:millisecond)
|
||||
|
||||
# Get weather data and convert units based on locale
|
||||
temperature_raw = get_weather_field(packet, :temperature)
|
||||
wind_speed_raw = get_weather_field(packet, :wind_speed)
|
||||
wind_gust_raw = get_weather_field(packet, :wind_gust)
|
||||
rain_1h_raw = get_weather_field(packet, :rain_1h)
|
||||
rain_24h_raw = get_weather_field(packet, :rain_24h)
|
||||
rain_since_midnight_raw = get_weather_field(packet, :rain_since_midnight)
|
||||
|
||||
# Convert units if the values are numbers
|
||||
{temperature, temp_unit} = convert_temperature(temperature_raw, locale)
|
||||
{wind_speed, wind_unit} = convert_wind_speed(wind_speed_raw, locale)
|
||||
{wind_gust, gust_unit} = convert_wind_speed(wind_gust_raw, locale)
|
||||
{rain_1h, rain_unit} = convert_rain(rain_1h_raw, locale)
|
||||
{rain_24h, rain_unit} = convert_rain(rain_24h_raw, locale)
|
||||
{rain_since_midnight, rain_unit} = convert_rain(rain_since_midnight_raw, locale)
|
||||
|
||||
%{
|
||||
callsign: sender,
|
||||
comment: nil,
|
||||
|
|
@ -209,15 +226,19 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
|
|||
cache_buster: cache_buster,
|
||||
weather: true,
|
||||
weather_link: true,
|
||||
temperature: get_weather_field(packet, :temperature),
|
||||
temperature: temperature,
|
||||
temp_unit: temp_unit,
|
||||
humidity: get_weather_field(packet, :humidity),
|
||||
wind_direction: get_weather_field(packet, :wind_direction),
|
||||
wind_speed: get_weather_field(packet, :wind_speed),
|
||||
wind_gust: get_weather_field(packet, :wind_gust),
|
||||
wind_speed: wind_speed,
|
||||
wind_unit: wind_unit,
|
||||
wind_gust: wind_gust,
|
||||
gust_unit: gust_unit,
|
||||
pressure: get_weather_field(packet, :pressure),
|
||||
rain_1h: get_weather_field(packet, :rain_1h),
|
||||
rain_24h: get_weather_field(packet, :rain_24h),
|
||||
rain_since_midnight: get_weather_field(packet, :rain_since_midnight)
|
||||
rain_1h: rain_1h,
|
||||
rain_24h: rain_24h,
|
||||
rain_since_midnight: rain_since_midnight,
|
||||
rain_unit: rain_unit
|
||||
}
|
||||
|> PopupComponent.popup()
|
||||
|> Safe.to_iodata()
|
||||
|
|
@ -259,4 +280,23 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
|
|||
true -> nil
|
||||
end
|
||||
end
|
||||
|
||||
# Weather unit conversion helpers
|
||||
defp convert_temperature(value, locale) when is_number(value) do
|
||||
AprsmeWeb.WeatherUnits.format_temperature(value, locale)
|
||||
end
|
||||
|
||||
defp convert_temperature(value, _locale), do: {value, "°F"}
|
||||
|
||||
defp convert_wind_speed(value, locale) when is_number(value) do
|
||||
AprsmeWeb.WeatherUnits.format_wind_speed(value, locale)
|
||||
end
|
||||
|
||||
defp convert_wind_speed(value, _locale), do: {value, "mph"}
|
||||
|
||||
defp convert_rain(value, locale) when is_number(value) do
|
||||
AprsmeWeb.WeatherUnits.format_rain(value, locale)
|
||||
end
|
||||
|
||||
defp convert_rain(value, _locale), do: {value, "in"}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ defmodule AprsmeWeb.MapLive.PopupComponent do
|
|||
import AprsmeWeb.TimeHelpers
|
||||
import Gettext
|
||||
|
||||
alias AprsmeWeb.WeatherUnits
|
||||
|
||||
def popup(assigns) do
|
||||
~H"""
|
||||
<div class="aprs-popup" data-timestamp={@cache_buster}>
|
||||
|
|
@ -36,16 +38,16 @@ defmodule AprsmeWeb.MapLive.PopupComponent do
|
|||
<% end %>
|
||||
<%= if @weather do %>
|
||||
<hr style="margin-top: 4px; margin-bottom: 4px;" />
|
||||
{gettext(AprsmeWeb.Gettext, "Temperature:")} {@temperature}°F<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Temperature:")} {@temperature}{@temp_unit}<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Humidity:")} {@humidity}%<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Wind:")} {@wind_direction}° {gettext(AprsmeWeb.Gettext, "at")} {@wind_speed} mph, {gettext(
|
||||
{gettext(AprsmeWeb.Gettext, "Wind:")} {@wind_direction}° {gettext(AprsmeWeb.Gettext, "at")} {@wind_speed} {@wind_unit}, {gettext(
|
||||
AprsmeWeb.Gettext,
|
||||
"gusts to"
|
||||
)} {@wind_gust} mph<br />
|
||||
)} {@wind_gust} {@gust_unit}<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Pressure:")} {@pressure} hPa<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Rain (1h):")} {@rain_1h} in.<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Rain (24h):")} {@rain_24h} in.<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Rain (since midnight):")} {@rain_since_midnight} in.<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Rain (1h):")} {@rain_1h} {@rain_unit}<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Rain (24h):")} {@rain_24h} {@rain_unit}<br />
|
||||
{gettext(AprsmeWeb.Gettext, "Rain (since midnight):")} {@rain_since_midnight} {@rain_unit}<br />
|
||||
<% end %>
|
||||
</div>
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
|
||||
alias Aprsme.Packets
|
||||
alias AprsmeWeb.MapLive.PacketUtils
|
||||
alias AprsmeWeb.WeatherUnits
|
||||
|
||||
@impl true
|
||||
def mount(%{"callsign" => callsign}, _session, socket) do
|
||||
|
|
@ -22,6 +23,10 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
{start_time, end_time} = default_time_range()
|
||||
weather_history = get_weather_history(normalized_callsign, start_time, end_time)
|
||||
|
||||
# Get locale from socket assigns (set by LocaleHook)
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
unit_labels = WeatherUnits.unit_labels(locale)
|
||||
|
||||
weather_history_json =
|
||||
weather_history
|
||||
|> Enum.map(fn pkt ->
|
||||
|
|
@ -30,36 +35,44 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
calc_dew_point(pkt.temperature, pkt.humidity)
|
||||
end
|
||||
|
||||
# Convert units based on locale
|
||||
{temp_value, _} = WeatherUnits.format_temperature(pkt.temperature, locale)
|
||||
{dew_value, _} = if dew_point, do: WeatherUnits.format_temperature(dew_point, locale), else: {nil, nil}
|
||||
{wind_speed_value, _} = WeatherUnits.format_wind_speed(pkt.wind_speed, locale)
|
||||
{rain_1h_value, _} = WeatherUnits.format_rain(pkt.rain_1h, locale)
|
||||
{rain_24h_value, _} = WeatherUnits.format_rain(pkt.rain_24h, locale)
|
||||
{rain_since_midnight_value, _} = WeatherUnits.format_rain(pkt.rain_since_midnight, locale)
|
||||
|
||||
%{
|
||||
timestamp: pkt.received_at,
|
||||
temperature: pkt.temperature,
|
||||
dew_point: dew_point,
|
||||
temperature: temp_value,
|
||||
dew_point: dew_value,
|
||||
humidity: pkt.humidity,
|
||||
pressure: pkt.pressure,
|
||||
wind_direction: pkt.wind_direction,
|
||||
wind_speed: pkt.wind_speed,
|
||||
rain_1h: pkt.rain_1h,
|
||||
rain_24h: pkt.rain_24h,
|
||||
rain_since_midnight: pkt.rain_since_midnight,
|
||||
wind_speed: wind_speed_value,
|
||||
rain_1h: rain_1h_value,
|
||||
rain_24h: rain_24h_value,
|
||||
rain_since_midnight: rain_since_midnight_value,
|
||||
luminosity: pkt.luminosity
|
||||
}
|
||||
end)
|
||||
|> Jason.encode!()
|
||||
|
||||
# Add chart labels for translation
|
||||
# Add chart labels for translation with locale-aware units
|
||||
chart_labels = %{
|
||||
temp_title: gettext("Temperature & Dew Point (°F)"),
|
||||
temp_label: gettext("Temperature (°F)"),
|
||||
dew_label: gettext("Dew Point (°F)"),
|
||||
temp_title: gettext("Temperature & Dew Point (%{unit})", unit: unit_labels.temperature),
|
||||
temp_label: gettext("Temperature (%{unit})", unit: unit_labels.temperature),
|
||||
dew_label: gettext("Dew Point (%{unit})", unit: unit_labels.temperature),
|
||||
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)"),
|
||||
wind_title: gettext("Wind (%{unit})", unit: unit_labels.wind_speed),
|
||||
wind_label: gettext("Wind Speed (%{unit})", unit: unit_labels.wind_speed),
|
||||
gust_label: gettext("Wind Gust (%{unit})", unit: unit_labels.wind_speed),
|
||||
time: gettext("Time"),
|
||||
degf: gettext("°F"),
|
||||
degf: unit_labels.temperature,
|
||||
percent: gettext("%"),
|
||||
mb: gettext("mb")
|
||||
}
|
||||
|
|
@ -83,6 +96,9 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
{start_time, end_time} = default_time_range()
|
||||
weather_history = get_weather_history(socket.assigns.callsign, start_time, end_time)
|
||||
|
||||
# Get locale from socket assigns
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
|
||||
weather_history_json =
|
||||
weather_history
|
||||
|> Enum.map(fn pkt ->
|
||||
|
|
@ -91,17 +107,25 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
calc_dew_point(pkt.temperature, pkt.humidity)
|
||||
end
|
||||
|
||||
# Convert units based on locale
|
||||
{temp_value, _} = WeatherUnits.format_temperature(pkt.temperature, locale)
|
||||
{dew_value, _} = if dew_point, do: WeatherUnits.format_temperature(dew_point, locale), else: {nil, nil}
|
||||
{wind_speed_value, _} = WeatherUnits.format_wind_speed(pkt.wind_speed, locale)
|
||||
{rain_1h_value, _} = WeatherUnits.format_rain(pkt.rain_1h, locale)
|
||||
{rain_24h_value, _} = WeatherUnits.format_rain(pkt.rain_24h, locale)
|
||||
{rain_since_midnight_value, _} = WeatherUnits.format_rain(pkt.rain_since_midnight, locale)
|
||||
|
||||
%{
|
||||
timestamp: pkt.received_at,
|
||||
temperature: pkt.temperature,
|
||||
dew_point: dew_point,
|
||||
temperature: temp_value,
|
||||
dew_point: dew_value,
|
||||
humidity: pkt.humidity,
|
||||
pressure: pkt.pressure,
|
||||
wind_direction: pkt.wind_direction,
|
||||
wind_speed: pkt.wind_speed,
|
||||
rain_1h: pkt.rain_1h,
|
||||
rain_24h: pkt.rain_24h,
|
||||
rain_since_midnight: pkt.rain_since_midnight,
|
||||
wind_speed: wind_speed_value,
|
||||
rain_1h: rain_1h_value,
|
||||
rain_24h: rain_24h_value,
|
||||
rain_since_midnight: rain_since_midnight_value,
|
||||
luminosity: pkt.luminosity
|
||||
}
|
||||
end)
|
||||
|
|
@ -128,6 +152,9 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
{start_time, end_time} = default_time_range()
|
||||
weather_history = get_weather_history(socket.assigns.callsign, start_time, end_time)
|
||||
|
||||
# Get locale from socket assigns
|
||||
locale = Map.get(socket.assigns, :locale, "en")
|
||||
|
||||
weather_history_json =
|
||||
weather_history
|
||||
|> Enum.map(fn pkt ->
|
||||
|
|
@ -136,17 +163,25 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
calc_dew_point(pkt.temperature, pkt.humidity)
|
||||
end
|
||||
|
||||
# Convert units based on locale
|
||||
{temp_value, _} = WeatherUnits.format_temperature(pkt.temperature, locale)
|
||||
{dew_value, _} = if dew_point, do: WeatherUnits.format_temperature(dew_point, locale), else: {nil, nil}
|
||||
{wind_speed_value, _} = WeatherUnits.format_wind_speed(pkt.wind_speed, locale)
|
||||
{rain_1h_value, _} = WeatherUnits.format_rain(pkt.rain_1h, locale)
|
||||
{rain_24h_value, _} = WeatherUnits.format_rain(pkt.rain_24h, locale)
|
||||
{rain_since_midnight_value, _} = WeatherUnits.format_rain(pkt.rain_since_midnight, locale)
|
||||
|
||||
%{
|
||||
timestamp: pkt.received_at,
|
||||
temperature: pkt.temperature,
|
||||
dew_point: dew_point,
|
||||
temperature: temp_value,
|
||||
dew_point: dew_value,
|
||||
humidity: pkt.humidity,
|
||||
pressure: pkt.pressure,
|
||||
wind_direction: pkt.wind_direction,
|
||||
wind_speed: pkt.wind_speed,
|
||||
rain_1h: pkt.rain_1h,
|
||||
rain_24h: pkt.rain_24h,
|
||||
rain_since_midnight: pkt.rain_since_midnight,
|
||||
wind_speed: wind_speed_value,
|
||||
rain_1h: rain_1h_value,
|
||||
rain_24h: rain_24h_value,
|
||||
rain_since_midnight: rain_since_midnight_value,
|
||||
luminosity: pkt.luminosity
|
||||
}
|
||||
end)
|
||||
|
|
@ -220,4 +255,44 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
|
|||
_ -> value
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Formats weather values with appropriate units based on locale.
|
||||
"""
|
||||
def format_weather_value(packet, key, locale) do
|
||||
value = PacketUtils.get_weather_field(packet, key)
|
||||
|
||||
case {key, value} do
|
||||
{:temperature, value} when is_number(value) ->
|
||||
{converted_value, unit} = WeatherUnits.format_temperature(value, locale)
|
||||
"#{converted_value}#{unit}"
|
||||
|
||||
{:wind_speed, value} when is_number(value) ->
|
||||
{converted_value, unit} = WeatherUnits.format_wind_speed(value, locale)
|
||||
"#{converted_value} #{unit}"
|
||||
|
||||
{:wind_gust, value} when is_number(value) ->
|
||||
{converted_value, unit} = WeatherUnits.format_wind_speed(value, locale)
|
||||
"#{converted_value} #{unit}"
|
||||
|
||||
{:rain_1h, value} when is_number(value) ->
|
||||
{converted_value, unit} = WeatherUnits.format_rain(value, locale)
|
||||
"#{converted_value} #{unit}"
|
||||
|
||||
{:rain_24h, value} when is_number(value) ->
|
||||
{converted_value, unit} = WeatherUnits.format_rain(value, locale)
|
||||
"#{converted_value} #{unit}"
|
||||
|
||||
{:rain_since_midnight, value} when is_number(value) ->
|
||||
{converted_value, unit} = WeatherUnits.format_rain(value, locale)
|
||||
"#{converted_value} #{unit}"
|
||||
|
||||
_ ->
|
||||
case value do
|
||||
"N/A" -> "N/A"
|
||||
nil -> "N/A"
|
||||
_ -> "#{value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@
|
|||
<div>
|
||||
<dt class="text-xs font-medium opacity-70">{gettext("Temperature")}</dt>
|
||||
<dd class="mt-1 text-sm font-semibold">
|
||||
{PacketUtils.get_weather_field(@weather_packet, :temperature)}°F
|
||||
{format_weather_value(@weather_packet, :temperature, @locale)}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -108,16 +108,17 @@
|
|||
<div>
|
||||
<dt class="text-xs font-medium opacity-70">{gettext("Wind")}</dt>
|
||||
<dd class="mt-1 text-sm font-semibold">
|
||||
{PacketUtils.get_weather_field(@weather_packet, :wind_direction)}° @ {PacketUtils.get_weather_field(
|
||||
{PacketUtils.get_weather_field(@weather_packet, :wind_direction)}° @ {format_weather_value(
|
||||
@weather_packet,
|
||||
:wind_speed
|
||||
)} mph
|
||||
:wind_speed,
|
||||
@locale
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium opacity-70">{gettext("Gusts")}</dt>
|
||||
<dd class="mt-1 text-sm font-semibold">
|
||||
{PacketUtils.get_weather_field(@weather_packet, :wind_gust)} mph
|
||||
{format_weather_value(@weather_packet, :wind_gust, @locale)}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -129,19 +130,19 @@
|
|||
<div>
|
||||
<dt class="text-xs font-medium opacity-70">{gettext("Rain (1h)")}</dt>
|
||||
<dd class="mt-1 text-sm font-semibold">
|
||||
{get_weather_field_zero(@weather_packet, :rain_1h)} in
|
||||
{format_weather_value(@weather_packet, :rain_1h, @locale)}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium opacity-70">{gettext("Rain (24h)")}</dt>
|
||||
<dd class="mt-1 text-sm font-semibold">
|
||||
{get_weather_field_zero(@weather_packet, :rain_24h)} in
|
||||
{format_weather_value(@weather_packet, :rain_24h, @locale)}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-xs font-medium opacity-70">{gettext("Rain (since midnight)")}</dt>
|
||||
<dd class="mt-1 text-sm font-semibold">
|
||||
{get_weather_field_zero(@weather_packet, :rain_since_midnight)} in
|
||||
{format_weather_value(@weather_packet, :rain_since_midnight, @locale)}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
|
|
|
|||
98
lib/aprsme_web/weather_units.ex
Normal file
98
lib/aprsme_web/weather_units.ex
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
defmodule AprsmeWeb.WeatherUnits do
|
||||
@moduledoc """
|
||||
Handles weather unit conversions and formatting based on locale.
|
||||
Supports imperial (US) and metric (international) units.
|
||||
"""
|
||||
|
||||
alias Aprsme.Convert
|
||||
|
||||
@doc """
|
||||
Determines the unit system based on locale.
|
||||
Returns :imperial for US, :metric for most other countries.
|
||||
"""
|
||||
def unit_system(locale) when is_binary(locale) do
|
||||
case locale do
|
||||
# Default to imperial for English
|
||||
"en" -> :imperial
|
||||
# Spanish-speaking countries mostly use metric
|
||||
"es" -> :metric
|
||||
# Germany uses metric
|
||||
"de" -> :metric
|
||||
# France uses metric
|
||||
"fr" -> :metric
|
||||
# Default to metric for unknown locales
|
||||
_ -> :metric
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts temperature from Fahrenheit to Celsius if needed.
|
||||
Returns the temperature in the appropriate unit for the locale.
|
||||
"""
|
||||
def format_temperature(temp, locale) when is_number(temp) do
|
||||
case unit_system(locale) do
|
||||
:imperial -> {temp, "°F"}
|
||||
:metric -> {Convert.f_to_c(temp), "°C"}
|
||||
end
|
||||
end
|
||||
|
||||
def format_temperature(temp, _locale), do: {temp, "°F"}
|
||||
|
||||
@doc """
|
||||
Converts wind speed from mph to km/h if needed.
|
||||
Returns the wind speed in the appropriate unit for the locale.
|
||||
"""
|
||||
def format_wind_speed(speed, locale) when is_number(speed) do
|
||||
case unit_system(locale) do
|
||||
:imperial -> {speed, "mph"}
|
||||
:metric -> {Convert.mph_to_kph(speed), "km/h"}
|
||||
end
|
||||
end
|
||||
|
||||
def format_wind_speed(speed, _locale), do: {speed, "mph"}
|
||||
|
||||
@doc """
|
||||
Converts rain from inches to mm if needed.
|
||||
Returns the rain amount in the appropriate unit for the locale.
|
||||
"""
|
||||
def format_rain(rain, locale) when is_number(rain) do
|
||||
case unit_system(locale) do
|
||||
:imperial -> {rain, "in"}
|
||||
:metric -> {Convert.inches_to_mm(rain), "mm"}
|
||||
end
|
||||
end
|
||||
|
||||
def format_rain(rain, _locale), do: {rain, "in"}
|
||||
|
||||
@doc """
|
||||
Formats pressure (keeps hPa for all locales as it's standard).
|
||||
"""
|
||||
def format_pressure(pressure, _locale) when is_number(pressure) do
|
||||
{pressure, "hPa"}
|
||||
end
|
||||
|
||||
def format_pressure(pressure, _locale), do: {pressure, "hPa"}
|
||||
|
||||
@doc """
|
||||
Gets the appropriate unit labels for the locale.
|
||||
"""
|
||||
def unit_labels(locale) do
|
||||
case unit_system(locale) do
|
||||
:imperial ->
|
||||
%{
|
||||
temperature: "°F",
|
||||
wind_speed: "mph",
|
||||
rain: "in",
|
||||
pressure: "hPa"
|
||||
}
|
||||
|
||||
:metric ->
|
||||
%{
|
||||
temperature: "°C",
|
||||
wind_speed: "km/h",
|
||||
rain: "mm",
|
||||
pressure: "hPa"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue