From 495e5cb5d72512e75dbdd9ef0a8f9c5bfa2ac359 Mon Sep 17 00:00:00 2001 From: custard Date: Fri, 28 Aug 2026 10:30:13 +0400 Subject: [PATCH] feat(i18n): localize gwx output (wind, humidity, dew point, visibility, pressure, day names) --- .../commands/alternatives/wx_international.py | 59 +++++++++++-------- translations/en.json | 51 ++++++++++------ translations/ru.json | 59 ++++++++++++------- 3 files changed, 105 insertions(+), 64 deletions(-) diff --git a/modules/commands/alternatives/wx_international.py b/modules/commands/alternatives/wx_international.py index ab223f6..8d5fa0b 100644 --- a/modules/commands/alternatives/wx_international.py +++ b/modules/commands/alternatives/wx_international.py @@ -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. diff --git a/translations/en.json b/translations/en.json index 84e78aa..716ad07 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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 - Get weather for any global location (city, country, or coordinates)", - "usage": "Usage: gwx - 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 - Weather for any location worldwide (city, country, or coordinates)", + "usage": "Usage: gwx - 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" diff --git a/translations/ru.json b/translations/ru.json index ced6094..f132239 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -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": "Преимущественно ясно",