From 5a663ee67103403cebc17b85833a6f79006d0838 Mon Sep 17 00:00:00 2001 From: Guillaume Bernard Date: Mon, 2 Mar 2026 22:21:12 +0100 Subject: [PATCH] multi-choice: Fix i18n of navigation button tooltips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous approach built tooltip strings by formatting a translated category name (e.g. "Month") into a generic "Previous %1$s" template. This is a classic gettext concatenation anti-pattern: languages with grammatical gender (French, Spanish, German, …) need the adjective to agree with the noun, and word order can differ from English, so translators of the format string have no way to produce a correct result. Replace the format strings and their helper callbacks with two explicit GObject properties on GcalMultiChoice: prev-button-tooltip-text and next-button-tooltip-text. The buttons in the template now bind to these properties directly. Each widget instance in gcal-date-chooser.blp sets its own fully self-contained translatable string ("Previous month", "Next month", "Previous year", "Next year"), giving translators all the context they need to handle gender and word order correctly. Closes: https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1547 Part-of: --- po/POTFILES.in | 1 + src/gui/common/gcal-date-chooser.blp | 10 ++++ src/gui/event-editor/gcal-multi-choice.blp | 4 +- src/gui/event-editor/gcal-multi-choice.c | 58 +++++++++++++--------- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index b0ca00a5..afa169ee 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -31,6 +31,7 @@ src/gui/event-editor/gcal-date-time-chooser.c src/gui/event-editor/gcal-event-editor-dialog.blp src/gui/event-editor/gcal-event-editor-dialog.c src/gui/event-editor/gcal-multi-choice.c +src/gui/event-editor/gcal-multi-choice.blp src/gui/event-editor/gcal-organizer-row.blp src/gui/event-editor/gcal-organizer-row.c src/gui/event-editor/gcal-reminders-section.blp diff --git a/src/gui/common/gcal-date-chooser.blp b/src/gui/common/gcal-date-chooser.blp index eb36fd96..9cc3f2a4 100644 --- a/src/gui/common/gcal-date-chooser.blp +++ b/src/gui/common/gcal-date-chooser.blp @@ -13,6 +13,8 @@ template $GcalDateChooser: Adw.Bin { value: 0; wrap: true; category: _("Month"); + previous-button-tooltip: _("Previous Month"); + next-button-tooltip: _("Next Month"); visible: bind $get_split_choice_visible(template.show-heading, template.split-month-year) as ; notify::value => $split_multi_choice_changed() swapped; @@ -30,6 +32,8 @@ template $GcalDateChooser: Adw.Bin { animate: false; value: 0; category: _("Year"); + previous-button-tooltip: _("Previous Year"); + next-button-tooltip: _("Next Year"); notify::value => $split_multi_choice_changed() swapped; layout { @@ -49,6 +53,8 @@ template $GcalDateChooser: Adw.Bin { animate: false; value: 0; category: _("Month"); + previous-button-tooltip: _("Previous Month"); + next-button-tooltip: _("Next Month"); notify::value => $combined_multi_choice_changed() swapped; layout { @@ -83,6 +89,8 @@ Popover popover { animate: bind month_choice.animate bidirectional; value: bind month_choice.value bidirectional; category: bind month_choice.category bidirectional; + previous-button-tooltip: bind month_choice.previous-button-tooltip; + next-button-tooltip: bind month_choice.next-button-tooltip; } $GcalMultiChoice { @@ -91,6 +99,8 @@ Popover popover { animate: bind year_choice.animate bidirectional; value: bind year_choice.value bidirectional; category: bind year_choice.category bidirectional; + previous-button-tooltip: bind year_choice.previous-button-tooltip; + next-button-tooltip: bind year_choice.next-button-tooltip; } styles [ diff --git a/src/gui/event-editor/gcal-multi-choice.blp b/src/gui/event-editor/gcal-multi-choice.blp index da8e3799..da7ff301 100644 --- a/src/gui/event-editor/gcal-multi-choice.blp +++ b/src/gui/event-editor/gcal-multi-choice.blp @@ -9,7 +9,7 @@ template $GcalMultiChoice: Box { icon-name: "pan-start-symbolic"; clicked => $button_clicked_cb(); can-focus: false; - tooltip-text: bind $get_down_button_tooltip(template.category) as ; + tooltip-text: bind template.previous-button-tooltip; styles [ "back-button", @@ -55,7 +55,7 @@ template $GcalMultiChoice: Box { icon-name: "pan-end-symbolic"; clicked => $button_clicked_cb(); can-focus: false; - tooltip-text: bind $get_up_button_tooltip(template.category) as ; + tooltip-text: bind template.next-button-tooltip; styles [ "circular", diff --git a/src/gui/event-editor/gcal-multi-choice.c b/src/gui/event-editor/gcal-multi-choice.c index 9ab22637..f3e98246 100644 --- a/src/gui/event-editor/gcal-multi-choice.c +++ b/src/gui/event-editor/gcal-multi-choice.c @@ -24,8 +24,6 @@ #include "gcal-multi-choice.h" -#include - struct _GcalMultiChoice { GtkBox parent; @@ -43,6 +41,8 @@ struct _GcalMultiChoice gboolean wrap; gboolean animate; gchar *category; + gchar *prev_button_tooltip_text; + gchar *next_button_tooltip_text; GtkWidget **choices; gint n_choices; @@ -66,6 +66,8 @@ enum PROP_CHOICES, PROP_POPOVER, PROP_CATEGORY, + PROP_PREVIOUS_BUTTON_TOOLTIP, + PROP_NEXT_BUTTON_TOOLTIP, NUM_PROPERTIES }; @@ -163,26 +165,6 @@ set_value (GcalMultiChoice *self, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]); } -static gchar * -get_down_button_tooltip (GcalMultiChoice *self, - gchar *category) -{ - g_autofree gchar *formatted_tooltip = NULL; - - formatted_tooltip = g_strdup_printf (_("Previous %1$s"), category); - return g_steal_pointer (&formatted_tooltip); -} - -static gchar * -get_up_button_tooltip (GcalMultiChoice *self, - gchar *category) -{ - g_autofree gchar *formatted_tooltip = NULL; - - formatted_tooltip = g_strdup_printf (_("Next %1$s"), category); - return g_steal_pointer (&formatted_tooltip); -} - static void go_up (GcalMultiChoice *self) { @@ -388,6 +370,10 @@ gcal_multi_choice_dispose (GObject *object) g_free (self->choices); self->choices = NULL; + g_clear_pointer (&self->category, g_free); + g_clear_pointer (&self->prev_button_tooltip_text, g_free); + g_clear_pointer (&self->next_button_tooltip_text, g_free); + if (self->format_destroy) { self->format_destroy (self->format_data); @@ -447,6 +433,14 @@ gcal_multi_choice_get_property (GObject *object, g_value_set_string (value, gcal_multi_choice_get_category (self)); break; + case PROP_PREVIOUS_BUTTON_TOOLTIP: + g_value_set_string (value, self->prev_button_tooltip_text); + break; + + case PROP_NEXT_BUTTON_TOOLTIP: + g_value_set_string (value, self->next_button_tooltip_text); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -501,6 +495,16 @@ gcal_multi_choice_set_property (GObject *object, gcal_multi_choice_set_category (self, g_value_get_string (value)); break; + case PROP_PREVIOUS_BUTTON_TOOLTIP: + if (g_set_str (&self->prev_button_tooltip_text, g_value_get_string (value))) + g_object_notify_by_pspec (object, properties[PROP_PREVIOUS_BUTTON_TOOLTIP]); + break; + + case PROP_NEXT_BUTTON_TOOLTIP: + if (g_set_str (&self->next_button_tooltip_text, g_value_get_string (value))) + g_object_notify_by_pspec (object, properties[PROP_NEXT_BUTTON_TOOLTIP]); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -677,6 +681,14 @@ gcal_multi_choice_class_init (GcalMultiChoiceClass *class) g_param_spec_string ("category", "Category", "Category", "", G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + properties[PROP_PREVIOUS_BUTTON_TOOLTIP] = + g_param_spec_string ("previous-button-tooltip", NULL, NULL, + NULL, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + properties[PROP_NEXT_BUTTON_TOOLTIP] = + g_param_spec_string ("next-button-tooltip", NULL, NULL, + NULL, + G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); @@ -713,8 +725,6 @@ gcal_multi_choice_class_init (GcalMultiChoiceClass *class) gtk_widget_class_bind_template_callback (widget_class, button_toggled_cb); gtk_widget_class_bind_template_callback (widget_class, button_state_flags_changed_cb); gtk_widget_class_bind_template_callback (widget_class, key_pressed_cb); - gtk_widget_class_bind_template_callback (widget_class, get_down_button_tooltip); - gtk_widget_class_bind_template_callback (widget_class, get_up_button_tooltip); gtk_widget_class_set_css_name (widget_class, "navigator"); }