This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository enlightenment-module-forecasts.
View the commit online.
commit 3fd6968e8f7235ac14aa9ed60e74ce60b7ef656d
Author: Alastair Poole <[email protected]>
AuthorDate: Mon May 25 12:56:33 2026 +0100
forecasts: location respects language.
---
po/fr.po | 11 +-
src/e_mod_config.c | 1 +
src/e_mod_main.c | 295 +++++++++++++++++++++++++++++++++++++++++++-------
src/forecast_places.c | 5 +-
src/forecast_places.h | 1 +
5 files changed, 269 insertions(+), 44 deletions(-)
diff --git a/po/fr.po b/po/fr.po
index c83747b..5e5e812 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -85,6 +85,15 @@ msgstr "Configuration"
msgid "%s: Current Conditions"
msgstr "%s : conditions actuelles"
+msgid "Current Conditions"
+msgstr "Conditions actuelles"
+
+msgid "<small>Updated: %s</>"
+msgstr "<small>Mis à jour : %s</>"
+
+msgid "Temperature"
+msgstr "Température"
+
#: src/e_mod_main.c:1014
msgid "Wind Chill"
msgstr "Ressenti"
@@ -123,7 +132,7 @@ msgstr "Lever / coucher de soleil"
#: src/e_mod_main.c:1071
msgid "Today"
-msgstr "Ajourd'hui"
+msgstr "Aujourd'hui"
#: src/e_mod_main.c:1073
msgid "Tomorrow"
diff --git a/src/e_mod_config.c b/src/e_mod_config.c
index ef371d8..a9be0f5 100644
--- a/src/e_mod_config.c
+++ b/src/e_mod_config.c
@@ -162,6 +162,7 @@ _location_select(E_Config_Dialog_Data *cfdata, Places_Location *location)
cfdata->lat = strdup(location->lat);
cfdata->lon = strdup(location->lon);
cfdata->timezone = strdup(location->timezone);
+ cfdata->location_id = location->country_id;
if (!cfdata->location || !cfdata->lat || !cfdata->lon || !cfdata->timezone)
{
diff --git a/src/e_mod_main.c b/src/e_mod_main.c
index 089df72..8072655 100644
--- a/src/e_mod_main.c
+++ b/src/e_mod_main.c
@@ -5,6 +5,7 @@
#include <e.h>
#include "e_mod_main.h"
#include <json-c/json.h>
+#include <locale.h>
#include <limits.h>
char *strptime(const char *s, const char *format, struct tm *tm);
@@ -85,8 +86,8 @@ struct _Instance
struct
{
- char day[4];
- char date[12];
+ char day[16];
+ char date[32];
int low, high;
char code[128];
char desc[256];
@@ -135,6 +136,10 @@ static Evas_Object *_forecasts_popup_icon_create(Evas *evas, const char *code);
static void _forecasts_popup_destroy(Instance *inst);
static void _forecasts_popup_del_cb(void *obj);
static Eina_Bool _forecasts_time_local_get(const char *timezone, time_t t, struct tm *tm_out);
+static const char *_forecasts_location_text_get(Config_Item *ci, const char *msgid,
+ char *buf, size_t len);
+static size_t _forecasts_location_strftime(Config_Item *ci, char *buf, size_t len,
+ const char *format, const struct tm *tm);
static void _forecasts_location_time_text_get(Instance *inst, char *buf, size_t len,
Eina_Bool include_date);
static time_t _forecasts_timestamp_utc(const char *timestamp);
@@ -708,6 +713,204 @@ _forecasts_timezone_restore(char *old_tz, Eina_Bool had_old_tz)
free(old_tz);
}
+static const char *
+_forecasts_country_locale_get(int country_id)
+{
+ static const struct
+ {
+ int country_id;
+ const char *locale;
+ } locales[] =
+ {
+ { 11, "de_AT" }, { 18, "fr_BE" }, { 26, "pt_BR" },
+ { 28, "bg_BG" }, { 35, "en_CA" }, { 40, "zh_CN" },
+ { 47, "hr_HR" }, { 51, "cs_CZ" }, { 67, "fi_FI" },
+ { 68, "fr_FR" }, { 69, "fr_GF" }, { 70, "fr_PF" },
+ { 74, "de_DE" }, { 77, "el_GR" }, { 89, "hu_HU" },
+ { 97, "he_IL" }, { 98, "it_IT" }, { 100, "ja_JP" },
+ { 106, "ko_KR" }, { 117, "lt_LT" }, { 142, "nl_NL" },
+ { 149, "nb_NO" }, { 158, "pl_PL" }, { 159, "pt_PT" },
+ { 164, "ru_RU" }, { 175, "sr_RS" }, { 179, "sk_SK" },
+ { 180, "sl_SI" }, { 186, "es_ES" }, { 191, "sv_SE" },
+ { 192, "de_CH" }, { 203, "tr_TR" }, { 208, "uk_UA" },
+ { 210, "en_GB" }, { 211, "en_US" }, { 232, "fr_FR" },
+ { 233, "fr_FR" }, { 238, "sv_AX" }
+ };
+ unsigned int i;
+
+ for (i = 0; i < sizeof(locales) / sizeof(locales[0]); i++)
+ if (locales[i].country_id == country_id)
+ return locales[i].locale;
+
+ return NULL;
+}
+
+static const char *
+_forecasts_timezone_locale_get(const char *timezone)
+{
+ static const struct
+ {
+ const char *timezone;
+ const char *locale;
+ } locales[] =
+ {
+ { "America/New_York", "en_US" },
+ { "America/Los_Angeles", "en_US" },
+ { "America/Chicago", "en_US" },
+ { "America/Denver", "en_US" },
+ { "America/Sao_Paulo", "pt_BR" },
+ { "Asia/Seoul", "ko_KR" },
+ { "Asia/Shanghai", "zh_CN" },
+ { "Asia/Tokyo", "ja_JP" },
+ { "Europe/Amsterdam", "nl_NL" },
+ { "Europe/Berlin", "de_DE" },
+ { "Europe/Helsinki", "fi_FI" },
+ { "Europe/Lisbon", "pt_PT" },
+ { "Europe/London", "en_GB" },
+ { "Europe/Madrid", "es_ES" },
+ { "Europe/Moscow", "ru_RU" },
+ { "Europe/Oslo", "nb_NO" },
+ { "Europe/Paris", "fr_FR" },
+ { "Europe/Rome", "it_IT" },
+ { "Europe/Stockholm", "sv_SE" },
+ { "Europe/Warsaw", "pl_PL" }
+ };
+ unsigned int i;
+
+ if (!timezone) return NULL;
+
+ for (i = 0; i < sizeof(locales) / sizeof(locales[0]); i++)
+ if (!strcmp(locales[i].timezone, timezone))
+ return locales[i].locale;
+
+ return NULL;
+}
+
+static const char *
+_forecasts_location_locale_get(Config_Item *ci)
+{
+ const char *locale = NULL;
+
+ if (!ci) return NULL;
+
+ locale = _forecasts_country_locale_get(ci->location_id);
+ if (!locale)
+ locale = _forecasts_timezone_locale_get(ci->timezone);
+
+ return locale;
+}
+
+static Eina_Bool
+_forecasts_setlocale_try(int category, const char *locale)
+{
+ char buf[64];
+
+ if (!locale) return EINA_FALSE;
+
+ snprintf(buf, sizeof(buf), "%s.UTF-8", locale);
+ if (setlocale(category, buf)) return EINA_TRUE;
+
+ snprintf(buf, sizeof(buf), "%s.utf8", locale);
+ if (setlocale(category, buf)) return EINA_TRUE;
+
+ return !!setlocale(category, locale);
+}
+
+static const char *
+_forecasts_location_text_get(Config_Item *ci, const char *msgid, char *buf, size_t len)
+{
+ const char *locale;
+
+ if (!buf || !len) return "";
+ snprintf(buf, len, "%s", msgid ? D_(msgid) : "");
+
+#ifdef ENABLE_NLS
+ if (!msgid) return buf;
+
+ locale = _forecasts_location_locale_get(ci);
+ if (locale)
+ {
+ const char *old_locale, *translated;
+ const char *old_language;
+ char *old_locale_dup, *old_language_dup;
+ Eina_Bool had_old_language;
+
+ old_locale = setlocale(LC_MESSAGES, NULL);
+ old_language = getenv("LANGUAGE");
+ had_old_language = !!old_language;
+ old_locale_dup = old_locale ? strdup(old_locale) : NULL;
+ old_language_dup = old_language ? strdup(old_language) : NULL;
+ if ((!old_locale || old_locale_dup) && (!had_old_language || old_language_dup))
+ {
+ setenv("LANGUAGE", locale, 1);
+ if (_forecasts_setlocale_try(LC_MESSAGES, locale))
+ {
+ translated = dgettext(PACKAGE, msgid);
+ if (translated)
+ snprintf(buf, len, "%s", translated);
+ }
+ if (old_locale_dup)
+ {
+ setlocale(LC_MESSAGES, old_locale_dup);
+ free(old_locale_dup);
+ old_locale_dup = NULL;
+ }
+ if (had_old_language)
+ setenv("LANGUAGE", old_language_dup, 1);
+ else
+ unsetenv("LANGUAGE");
+ }
+ free(old_locale_dup);
+ free(old_language_dup);
+ }
+#else
+ (void)ci;
+#endif
+
+ return buf;
+}
+
+static locale_t
+_forecasts_time_locale_new(Config_Item *ci)
+{
+ const char *locale = NULL;
+ char buf[64];
+ locale_t loc;
+
+ locale = _forecasts_location_locale_get(ci);
+ if (!locale) return (locale_t)0;
+
+ snprintf(buf, sizeof(buf), "%s.UTF-8", locale);
+ loc = newlocale(LC_TIME_MASK, buf, (locale_t)0);
+ if (loc) return loc;
+
+ snprintf(buf, sizeof(buf), "%s.utf8", locale);
+ loc = newlocale(LC_TIME_MASK, buf, (locale_t)0);
+ if (loc) return loc;
+
+ return newlocale(LC_TIME_MASK, locale, (locale_t)0);
+}
+
+static size_t
+_forecasts_location_strftime(Config_Item *ci, char *buf, size_t len,
+ const char *format, const struct tm *tm)
+{
+ locale_t loc;
+ size_t ret;
+
+ if (!buf || !len || !format || !tm) return 0;
+
+ loc = _forecasts_time_locale_new(ci);
+ if (loc)
+ {
+ ret = strftime_l(buf, len, format, tm, loc);
+ freelocale(loc);
+ if (ret > 0) return ret;
+ }
+
+ return strftime(buf, len, format, tm);
+}
+
static Eina_Bool
_forecasts_time_local_get(const char *timezone, time_t t, struct tm *tm_out)
{
@@ -734,14 +937,18 @@ _forecasts_location_time_text_get(Instance *inst, char *buf, size_t len,
if (!buf || len == 0) return;
- snprintf(buf, len, "%s", D_("N/A"));
- if (!inst) return;
+ if (!inst)
+ {
+ snprintf(buf, len, "%s", D_("N/A"));
+ return;
+ }
+ _forecasts_location_text_get(inst->ci, "N/A", buf, len);
now = time(NULL);
if (!_forecasts_time_local_get(inst->ci->timezone, now, &tm_now)) return;
if (include_date)
- strftime(buf, len, "%a %d %b %H:%M", &tm_now);
+ _forecasts_location_strftime(inst->ci, buf, len, "%a %d %b %H:%M", &tm_now);
else
strftime(buf, len, "%H:%M", &tm_now);
}
@@ -1048,16 +1255,6 @@ _forecasts_parse(void *data)
int icon_score[FORECASTS];
Eina_Bool condition_set = EINA_FALSE;
Eina_Bool condition_fallback_set = EINA_FALSE;
- static char *weekdays[7] =
- {
- "Sun",
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat",
- };
json_object *o_root = NULL;
if (!inst->buffer) return 0;
@@ -1131,9 +1328,12 @@ _forecasts_parse(void *data)
if (idx >= FORECASTS) break;
else if (idx < FORECASTS)
{
- strftime(inst->forecast[idx].date, 12, "%d %b %Y", &tm_data);
- strncpy(inst->forecast[idx].day, weekdays[tm_data.tm_wday], 3);
- inst->forecast[idx].day[3] = '\0';
+ _forecasts_location_strftime(inst->ci, inst->forecast[idx].date,
+ sizeof(inst->forecast[idx].date),
+ "%d %b %Y", &tm_data);
+ _forecasts_location_strftime(inst->ci, inst->forecast[idx].day,
+ sizeof(inst->forecast[idx].day),
+ "%a", &tm_data);
}
o_data = json_object_object_get(o_index, "data");
@@ -1282,17 +1482,17 @@ _forecasts_display_set(Instance *inst, int ok EINA_UNUSED)
edje_object_part_text_set(inst->forecasts->forecasts_obj, name, inst->forecast[i].desc);
snprintf(name, sizeof(name), "e.text.day%d.high", i);
- if (inst->forecast[i].high > TEMP_MIN_OUT_OF_RANGE && inst->forecast[i].high < TEMP_MAX_OUT_OF_RANGE)
+ if (inst->forecast[i].high > TEMP_MIN_OUT_OF_RANGE && inst->forecast[i].high < TEMP_MAX_OUT_OF_RANGE)
snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[i].high, inst->units.temp);
- else
- snprintf(buf, sizeof(buf), "N/A");
+ else
+ _forecasts_location_text_get(inst->ci, "N/A", buf, sizeof(buf));
edje_object_part_text_set(inst->forecasts->forecasts_obj, name, buf);
snprintf(name, sizeof(name), "e.text.day%d.low", i);
- if (inst->forecast[i].low > TEMP_MIN_OUT_OF_RANGE && inst->forecast[i].low < TEMP_MAX_OUT_OF_RANGE)
+ if (inst->forecast[i].low > TEMP_MIN_OUT_OF_RANGE && inst->forecast[i].low < TEMP_MAX_OUT_OF_RANGE)
snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[i].low, inst->units.temp);
- else
- snprintf(buf, sizeof(buf), "N/A");
+ else
+ _forecasts_location_text_get(inst->ci, "N/A", buf, sizeof(buf));
edje_object_part_text_set(inst->forecasts->forecasts_obj, name, buf);
snprintf(name, sizeof(name), "e.swallow.day%d.icon", i);
@@ -1368,10 +1568,11 @@ _forecasts_lb_add(Evas_Object *base, const char *txt)
}
static Evas_Object *
-_forecasts_day_hi_lo_add(Evas_Object *base, const char *day, int lo, int hi, char unit, const char *code)
+_forecasts_day_hi_lo_add(Evas_Object *base, Config_Item *ci, const char *day,
+ int lo, int hi, char unit, const char *code)
{
Evas_Object *o, *tb;
- char buf[256];
+ char buf[256], label[64];
tb = o = elm_table_add(base);
elm_table_padding_set(o, ELM_SCALE_SIZE(2), ELM_SCALE_SIZE(2));
@@ -1395,7 +1596,8 @@ _forecasts_day_hi_lo_add(Evas_Object *base, const char *day, int lo, int hi, cha
if ((hi > TEMP_MIN_OUT_OF_RANGE) && (hi < TEMP_MAX_OUT_OF_RANGE))
snprintf(buf, sizeof(buf), "%i°%c", hi, unit);
else
- snprintf(buf, sizeof(buf), D_("N/A"));
+ snprintf(buf, sizeof(buf), "%s", _forecasts_location_text_get(ci, "N/A",
+ label, sizeof(label)));
o = _forecasts_lb_add(base, buf);
evas_object_size_hint_align_set(o, 0.0, 0.5);
elm_table_pack(tb, o, 1, 1, 1, 1);
@@ -1403,7 +1605,8 @@ _forecasts_day_hi_lo_add(Evas_Object *base, const char *day, int lo, int hi, cha
if ((lo > TEMP_MIN_OUT_OF_RANGE) && (lo < TEMP_MAX_OUT_OF_RANGE))
snprintf(buf, sizeof(buf), "%i°%c", lo, unit);
else
- snprintf(buf, sizeof(buf), D_("N/A"));
+ snprintf(buf, sizeof(buf), "%s", _forecasts_location_text_get(ci, "N/A",
+ label, sizeof(label)));
o = _forecasts_lb_add(base, buf);
evas_object_size_hint_align_set(o, 0.0, 0.5);
elm_table_pack(tb, o, 1, 2, 1, 1);
@@ -1416,7 +1619,7 @@ _forecasts_popup_content_create(Instance *inst)
{
Evas_Object *base, *bx, *hbx, *fr, *tb, *lb, *sp;
Evas_Object *rec, *ic, *im;
- char buf[256], tmp[32];
+ char buf[256], tmp[32], label[128], format[128];
char icon_code[FORECASTS][128];
int row = 0, row0, rownum;
Evas_Coord w, h;
@@ -1435,7 +1638,8 @@ _forecasts_popup_content_create(Instance *inst)
fr = elm_frame_add(base);
evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(fr, EVAS_HINT_FILL, EVAS_HINT_FILL);
- elm_object_text_set(fr, D_("Current Conditions"));
+ elm_object_text_set(fr, _forecasts_location_text_get(inst->ci, "Current Conditions",
+ label, sizeof(label)));
evas_object_show(fr);
elm_box_pack_end(bx, fr);
@@ -1469,10 +1673,12 @@ _forecasts_popup_content_create(Instance *inst)
elm_table_pack(tb, im, 0, row, 3, 1);
evas_object_show(im);
- snprintf(tmp, sizeof(tmp), D_("N/A"));
+ _forecasts_location_text_get(inst->ci, "N/A", tmp, sizeof(tmp));
if (inst->update_time)
_forecasts_timestamp_local(inst->update_time, inst->ci->timezone, tmp, sizeof(tmp));
- snprintf(buf, sizeof(buf), D_("<small>Updated: %s</>"), tmp);
+ _forecasts_location_text_get(inst->ci, "<small>Updated: %s</>",
+ format, sizeof(format));
+ snprintf(buf, sizeof(buf), format, tmp);
lb = _forecasts_lb_add(base, buf);
evas_object_size_hint_align_set(lb, 1.0, 0.0);
elm_table_pack(tb, lb, 0, row++, 3, 1);
@@ -1489,7 +1695,8 @@ _forecasts_popup_content_create(Instance *inst)
row0 = row;
- lb = _forecasts_lb_add(base, D_("Temperature"));
+ lb = _forecasts_lb_add(base, _forecasts_location_text_get(inst->ci, "Temperature",
+ label, sizeof(label)));
evas_object_size_hint_align_set(lb, 1.0, 0.5);
elm_table_pack(tb, lb, 0, row, 1, 1);
snprintf(buf, sizeof(buf), "%d°%c", inst->condition.temp, inst->units.temp);
@@ -1497,18 +1704,20 @@ _forecasts_popup_content_create(Instance *inst)
evas_object_size_hint_align_set(lb, 0.0, 0.5);
elm_table_pack(tb, lb, 2, row++, 1, 1);
- lb = _forecasts_lb_add(base, D_("Wind Chill"));
+ lb = _forecasts_lb_add(base, _forecasts_location_text_get(inst->ci, "Wind Chill",
+ label, sizeof(label)));
evas_object_size_hint_align_set(lb, 1.0, 0.5);
elm_table_pack(tb, lb, 0, row, 1, 1);
if (inst->details.wind.can_chill)
snprintf(buf, sizeof(buf), "%d°%c", inst->details.wind.chill, inst->units.temp);
else
- snprintf(buf, sizeof(buf), "N/A");
+ _forecasts_location_text_get(inst->ci, "N/A", buf, sizeof(buf));
lb = _forecasts_lb_add(base, buf);
evas_object_size_hint_align_set(lb, 0.0, 0.5);
elm_table_pack(tb, lb, 2, row++, 1, 1);
- lb = _forecasts_lb_add(base, D_("Wind Speed"));
+ lb = _forecasts_lb_add(base, _forecasts_location_text_get(inst->ci, "Wind Speed",
+ label, sizeof(label)));
evas_object_size_hint_align_set(lb, 1.0, 0.5);
elm_table_pack(tb, lb, 0, row, 1, 1);
snprintf(buf, sizeof(buf), "%d %s", inst->details.wind.speed, inst->units.speed);
@@ -1516,7 +1725,8 @@ _forecasts_popup_content_create(Instance *inst)
evas_object_size_hint_align_set(lb, 0.0, 0.5);
elm_table_pack(tb, lb, 2, row++, 1, 1);
- lb = _forecasts_lb_add(base, D_("Humidity"));
+ lb = _forecasts_lb_add(base, _forecasts_location_text_get(inst->ci, "Humidity",
+ label, sizeof(label)));
evas_object_size_hint_align_set(lb, 1.0, 0.5);
elm_table_pack(tb, lb, 0, row, 1, 1);
snprintf(buf, sizeof(buf), "%d %%", inst->details.atmosphere.humidity);
@@ -1524,7 +1734,8 @@ _forecasts_popup_content_create(Instance *inst)
evas_object_size_hint_align_set(lb, 0.0, 0.5);
elm_table_pack(tb, lb, 2, row++, 1, 1);
- lb = _forecasts_lb_add(base, D_("Pressure"));
+ lb = _forecasts_lb_add(base, _forecasts_location_text_get(inst->ci, "Pressure",
+ label, sizeof(label)));
evas_object_size_hint_align_set(lb, 1.0, 0.5);
elm_table_pack(tb, lb, 0, row, 1, 1);
snprintf(buf, sizeof(buf), "%.2f %s", inst->details.atmosphere.pressure, inst->units.pressure);
@@ -1548,7 +1759,8 @@ _forecasts_popup_content_create(Instance *inst)
elm_box_pack_end(bx, hbx);
evas_object_show(hbx);
- fr = _forecasts_day_hi_lo_add(base, D_("Today"),
+ fr = _forecasts_day_hi_lo_add(base, inst->ci,
+ _forecasts_location_text_get(inst->ci, "Today", label, sizeof(label)),
inst->forecast[0].low,
inst->forecast[0].high,
inst->units.temp,
@@ -1556,7 +1768,8 @@ _forecasts_popup_content_create(Instance *inst)
elm_box_pack_end(hbx, fr);
evas_object_show(fr);
- fr = _forecasts_day_hi_lo_add(base, D_("Tomorrow"),
+ fr = _forecasts_day_hi_lo_add(base, inst->ci,
+ _forecasts_location_text_get(inst->ci, "Tomorrow", label, sizeof(label)),
inst->forecast[1].low,
inst->forecast[1].high,
inst->units.temp,
@@ -1585,7 +1798,7 @@ _forecasts_popup_content_create(Instance *inst)
for (i = 2; i < FORECASTS; i++)
{
- fr = _forecasts_day_hi_lo_add(base, inst->forecast[i].day,
+ fr = _forecasts_day_hi_lo_add(base, inst->ci, inst->forecast[i].day,
inst->forecast[i].low,
inst->forecast[i].high,
inst->units.temp,
diff --git a/src/forecast_places.c b/src/forecast_places.c
index bb4c6ba..533937e 100644
--- a/src/forecast_places.c
+++ b/src/forecast_places.c
@@ -104,7 +104,7 @@ _location_fields_parse(char *buf, char **fields)
}
static Places_Location *
-_location_new(char **fields, const char *country)
+_location_new(char **fields, const char *country, int country_id)
{
Places_Location *loc;
char *end;
@@ -118,6 +118,7 @@ _location_new(char **fields, const char *country)
loc = calloc(1, sizeof(Places_Location));
if (!loc) return NULL;
+ loc->country_id = country_id;
if (fields[1] && fields[1][0])
{
snprintf(loc->name, sizeof(loc->name), "%s, %s", fields[0], fields[1]);
@@ -177,7 +178,7 @@ forecast_places_by_category(int id)
memset(fields, 0, sizeof(fields));
if (!_location_fields_parse(buf, fields)) continue;
- loc = _location_new(fields, NULL);
+ loc = _location_new(fields, NULL, id);
if (loc)
list = eina_list_append(list, loc);
}
diff --git a/src/forecast_places.h b/src/forecast_places.h
index b1b30e5..f8e839f 100644
--- a/src/forecast_places.h
+++ b/src/forecast_places.h
@@ -18,6 +18,7 @@ struct Places_Location {
char lat[64];
char lon[64];
char timezone[128];
+ int country_id;
int population;
};
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.