feat(i18n): localize gwx output (wind, humidity, dew point, visibility, pressure, day names)

This commit is contained in:
custard
2026-08-28 10:30:13 +04:00
parent 2940e6f3c4
commit 495e5cb5d7
3 changed files with 105 additions and 64 deletions
@@ -1055,16 +1055,19 @@ class GlobalWxCommand(BaseCommand):
# Add feels like if significantly different
if abs(feels_like - temp) >= 5:
weather += f" (feels {feels_like}{temp_symbol})"
feels_str = self.translate('commands.gwx.feels_like', value=feels_like, unit=temp_symbol)
weather += f" {feels_str}"
# Add wind info (always show if >= 3 mph, show gusts if significant)
if wind_speed >= 3:
weather += f" {wind_direction}{wind_speed}"
if wind_gusts > wind_speed + 3:
weather += f"G{wind_gusts}"
gust_str = self.translate('commands.gwx.gust', value=wind_gusts)
weather += gust_str
# Add humidity
weather += f" {humidity}%RH"
humidity_str = self.translate('commands.gwx.humidity', value=humidity)
weather += f" {humidity_str}"
# Add additional conditions if space allows
conditions = []
@@ -1072,7 +1075,8 @@ class GlobalWxCommand(BaseCommand):
# Add dew point
if dewpoint is not None:
dewpoint_val = int(dewpoint)
conditions.append(f"💧{dewpoint_val}{temp_symbol}")
dew_str = self.translate('commands.gwx.dew_point', value=dewpoint_val, unit=temp_symbol)
conditions.append(dew_str)
# Add visibility (already converted to miles above)
if visibility_mi is not None and visibility_mi > 0:
@@ -1080,12 +1084,14 @@ class GlobalWxCommand(BaseCommand):
visibility_display = int(visibility_mi)
if visibility_display > 20:
visibility_display = 20
conditions.append(f"👁️{visibility_display}mi")
vis_str = self.translate('commands.gwx.visibility', value=visibility_display)
conditions.append(vis_str)
# Add pressure (convert from hPa to display format)
if pressure is not None:
pressure_hpa = int(pressure)
conditions.append(f"📊{pressure_hpa}hPa")
press_str = self.translate('commands.gwx.pressure', value=pressure_hpa)
conditions.append(press_str)
# Add conditions to weather string if space allows
# Reserve space for forecast data (high/low and tomorrow)
@@ -1177,7 +1183,8 @@ class GlobalWxCommand(BaseCommand):
if len(daily.get('wind_gusts_10m_max', [])) > 1:
wind_gusts = int(daily['wind_gusts_10m_max'][1])
if wind_gusts > wind_speed + 3:
wind_info += f"G{wind_gusts}"
gust_str = self.translate('commands.gwx.gust', value=wind_gusts)
wind_info += gust_str
# Get precipitation probability and amount
precip_info = ""
@@ -1231,13 +1238,13 @@ class GlobalWxCommand(BaseCommand):
# Map day names to 1-2 letter abbreviations
day_abbrev_map = {
'Monday': 'M',
'Tuesday': 'T',
'Wednesday': 'W',
'Thursday': 'Th',
'Friday': 'F',
'Saturday': 'Sa',
'Sunday': 'Su'
'Monday': self.translate('commands.gwx.day_abbrev.Monday'),
'Tuesday': self.translate('commands.gwx.day_abbrev.Tuesday'),
'Wednesday': self.translate('commands.gwx.day_abbrev.Wednesday'),
'Thursday': self.translate('commands.gwx.day_abbrev.Thursday'),
'Friday': self.translate('commands.gwx.day_abbrev.Friday'),
'Saturday': self.translate('commands.gwx.day_abbrev.Saturday'),
'Sunday': self.translate('commands.gwx.day_abbrev.Sunday')
}
parts = []
@@ -1360,20 +1367,24 @@ class GlobalWxCommand(BaseCommand):
if degrees is None:
return ""
directions = [
(0, "⬆️N"), (22.5, "↗️NE"), (45, "↗️NE"), (67.5, "➡️E"),
(90, "➡️E"), (112.5, "↘️SE"), (135, "↘️SE"), (157.5, "⬇️S"),
(180, "⬇️S"), (202.5, "↙️SW"), (225, "↙️SW"), (247.5, "⬅️W"),
(270, "⬅️W"), (292.5, "↖️NW"), (315, "↖️NW"), (337.5, "⬆️N"),
(360, "⬆️N")
dir_emojis = [
(0, "⬆️", "N"), (22.5, "↗️", "NE"), (45, "↗️", "NE"), (67.5, "➡️", "E"),
(90, "➡️", "E"), (112.5, "↘️", "SE"), (135, "↘️", "SE"), (157.5, "⬇️", "S"),
(180, "⬇️", "S"), (202.5, "↙️", "SW"), (225, "↙️", "SW"), (247.5, "⬅️", "W"),
(270, "⬅️", "W"), (292.5, "↖️", "NW"), (315, "↖️", "NW"), (337.5, "⬆️", "N"),
(360, "⬆️", "N")
]
# Find closest direction
for i in range(len(directions) - 1):
if directions[i][0] <= degrees < directions[i + 1][0]:
return directions[i][1]
for i in range(len(dir_emojis) - 1):
if dir_emojis[i][0] <= degrees < dir_emojis[i + 1][0]:
emoji, key = dir_emojis[i][1], dir_emojis[i][2]
translated = self.translate(f"services.weather_service.wind_directions.{key}")
return f"{emoji}{translated}"
return "⬆️N" # Default to North
emoji, key = dir_emojis[-1][1], dir_emojis[-1][2]
translated = self.translate(f"services.weather_service.wind_directions.{key}")
return f"{emoji}{translated}"
def _get_weather_description(self, code: int) -> str:
"""Convert WMO weather code to description.
+33 -18
View File
@@ -232,24 +232,39 @@
"mqtt_weather_stale": "MQTT weather data is too old",
"mqtt_weather_payload_error": "MQTT weather payload error: {detail}"
},
"gwx": {
"description": "Get weather information for any global location (usage: gwx Tokyo)",
"help": "Usage: gwx <location> - Get weather for any global location (city, country, or coordinates)",
"usage": "Usage: gwx <location> - Example: gwx Tokyo or gwx Paris, France",
"error_fetching": "Error fetching weather data",
"error_fetching_api": "Error fetching weather data from Open-Meteo",
"no_location": "Could not find location '{location}'",
"error": "Error getting weather data: {error}",
"tomorrow_not_available": "Tomorrow's forecast not available",
"tomorrow_error": "Error formatting tomorrow's forecast",
"multiday_not_available": "{num_days}-day forecast not available",
"multiday_error": "Error formatting {num_days}-day forecast",
"mqtt_forecast_not_supported": "Extended forecast is not available for MQTT weather sources",
"mqtt_weather_no_subscriber": "MQTT weather subscriber is not active (enable [MqttWeather] and custom.mqtt_weather.* topics)",
"mqtt_weather_no_data": "No MQTT weather message received for this topic yet",
"mqtt_weather_stale": "MQTT weather data is too old",
"mqtt_weather_payload_error": "MQTT weather payload error: {detail}",
"periods": {
"gwx": {
"description": "Get weather for any worldwide location (use: gwx Tokyo)",
"help": "Usage: gwx <location> - Weather for any location worldwide (city, country, or coordinates)",
"usage": "Usage: gwx <location> - Example: gwx Tokyo or gwx Paris, France",
"error_fetching": "Error fetching weather data",
"error_fetching_api": "Error fetching weather data from Open-Meteo",
"no_location": "Could not find location '{location}'",
"error": "Error fetching weather data: {error}",
"tomorrow_not_available": "Tomorrow forecast not available",
"tomorrow_error": "Error formatting tomorrow forecast",
"multiday_not_available": "{num_days}-day forecast not available",
"multiday_error": "Error formatting {num_days}-day forecast",
"mqtt_forecast_not_supported": "Extended forecast not supported for MQTT weather sources",
"mqtt_weather_no_subscriber": "MQTT weather subscriber not active",
"mqtt_weather_no_data": "MQTT weather message not yet received",
"mqtt_weather_stale": "MQTT weather data is stale",
"mqtt_weather_payload_error": "MQTT weather payload error: {detail}",
"feels_like": "(feels {value}{unit})",
"humidity": "{value}%RH",
"dew_point": "💧{value}{unit}",
"visibility": "👁️{value}mi",
"pressure": "📊{value}hPa",
"gust": "G{value}",
"day_abbrev": {
"Monday": "M",
"Tuesday": "T",
"Wednesday": "W",
"Thursday": "Th",
"Friday": "F",
"Saturday": "Sa",
"Sunday": "Su"
},
"periods": {
"today": "Today",
"tonight": "Tonight",
"tomorrow": "Tomorrow"
+37 -22
View File
@@ -84,28 +84,43 @@
"mqtt_weather_stale": "Данные MQTT-погоды слишком старые",
"mqtt_weather_payload_error": "Ошибка полезной нагрузки MQTT-погоды: {detail}"
},
"gwx": {
"description": "Получить погоду для любой точки мира (использование: gwx Tokyo)",
"help": "Использование: gwx <локация> - Погода для любой точки мира (город, страна или координаты)",
"usage": "Использование: gwx <локация> - Пример: gwx Tokyo или gwx Paris, France",
"error_fetching": "Ошибка получения данных погоды",
"error_fetching_api": "Ошибка получения данных погоды из Open-Meteo",
"no_location": "Не удалось найти локацию '{location}'",
"error": "Ошибка получения данных погоды: {error}",
"tomorrow_not_available": "Прогноз на завтра недоступен",
"tomorrow_error": "Ошибка форматирования прогноза на завтра",
"multiday_not_available": "Прогноз на {num_days} дней недоступен",
"multiday_error": "Ошибка форматирования прогноза на {num_days} дней",
"mqtt_forecast_not_supported": "Расширенный прогноз недоступен для MQTT-источников погоды",
"mqtt_weather_no_subscriber": "MQTT-подписчик погоды не активен",
"mqtt_weather_no_data": "Сообщение MQTT-погоды ещё не получено",
"mqtt_weather_stale": "Данные MQTT-погоды устарели",
"mqtt_weather_payload_error": "Ошибка полезной нагрузки MQTT: {detail}",
"periods": {
"today": "Сегодня",
"tonight": "Сегодня ночью",
"tomorrow": "Завтра"
},
"gwx": {
"description": "Получить погоду для любой точки мира (использование: gwx Tokyo)",
"help": "Использование: gwx <локация> - Погода для любой точки мира (город, страна или координаты)",
"usage": "Использование: gwx <локация> - Пример: gwx Tokyo или gwx Paris, France",
"error_fetching": "Ошибка получения данных погоды",
"error_fetching_api": "Ошибка получения данных погоды из Open-Meteo",
"no_location": "Не удалось найти локацию '{location}'",
"error": "Ошибка получения данных погоды: {error}",
"tomorrow_not_available": "Прогноз на завтра недоступен",
"tomorrow_error": "Ошибка форматирования прогноза на завтра",
"multiday_not_available": "Прогноз на {num_days} дней недоступен",
"multiday_error": "Ошибка форматирования прогноза на {num_days} дней",
"mqtt_forecast_not_supported": "Расширенный прогноз недоступен для MQTT-источников погоды",
"mqtt_weather_no_subscriber": "MQTT-подписчик погоды не активен",
"mqtt_weather_no_data": "Сообщение MQTT-погоды ещё не получено",
"mqtt_weather_stale": "Данные MQTT-погоды устарели",
"mqtt_weather_payload_error": "Ошибка полезной нагрузки MQTT: {detail}",
"feels_like": "(ош.{value}{unit})",
"humidity": "{value}%",
"dew_point": "💧{value}{unit}",
"visibility": "👁️{value}миль",
"pressure": "📊{value}гПа",
"gust": "G{value}",
"day_abbrev": {
"Monday": "Пн",
"Tuesday": "Вт",
"Wednesday": "Ср",
"Thursday": "Чт",
"Friday": "Пт",
"Saturday": "Сб",
"Sunday": "Вс"
},
"periods": {
"today": "Сегодня",
"tonight": "Сегодня ночью",
"tomorrow": "Завтра"
},
"weather_descriptions": {
"0": "Ясно",
"1": "Преимущественно ясно",