Skip to content

Commit 9f2ba93

Browse files
committed
Fix translation delivery, add five locales, drop emoji from defaults
Bundled translations could never load: the Domain Path header was missing, so WordPress 6.7+ resolved the lookup to the plugin root instead of languages/ and cached a permanent no-op. Verified before and after on a real page load. The twelve zodiac sign names rendered into a visible tooltip with no translatable source. They also double as matching keys in the date-range comparison, so they are left as identifiers and a new get_zodiac_label() translates them at render time - wrapping them in place would have broken sign detection. The activity and notification defaults existed twice with different text: the admin class has no emoji, the cron fallbacks did. Since the admin class only loads under is_admin(), cron emitted the emoji variant until an owner saved the Activity tab. Both now use the same text, with the emoji removed here and in the three email templates. The widget's emoji option (none/cake/party/balloon) is a feature and is unchanged. Also wrapped the activity/notification defaults (property initializers cannot call __(), so they moved to a static getter), 3 screen-reader strings in bb-core.js that had no PHP twin, and the admin-summary line that concatenated ' (Turning ' . age . ')'. Added de_DE, fr_FR, es_ES, pt_BR and it_IT. POT grew 116 -> 229.
1 parent 3667a27 commit 9f2ba93

21 files changed

Lines changed: 6614 additions & 319 deletions

.wbcom-i18n.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"domain": "buddypress-birthdays",
3+
"potFile": "languages/buddypress-birthdays.pot",
4+
"languagesDir": "languages",
5+
"engine": "claude",
6+
"model": "haiku",
7+
"batch": 400,
8+
"makeJson": true,
9+
"protect": [
10+
"Wbcom",
11+
"BuddyPress",
12+
"BuddyBoss",
13+
"WordPress",
14+
"Pro",
15+
"REST",
16+
"API",
17+
"URL",
18+
"HTML",
19+
"CSV",
20+
"AI",
21+
"ID",
22+
"xProfile",
23+
"GDPR",
24+
"BP Follow"
25+
],
26+
"locales": [
27+
{
28+
"locale": "de_DE",
29+
"name": "German",
30+
"register": "informal (du)"
31+
},
32+
{
33+
"locale": "fr_FR",
34+
"name": "French",
35+
"register": "informal (tu)"
36+
},
37+
{
38+
"locale": "es_ES",
39+
"name": "Spanish",
40+
"register": "informal (tú)"
41+
},
42+
{
43+
"locale": "pt_BR",
44+
"name": "Portuguese (Brazil)",
45+
"register": "informal (você)"
46+
},
47+
{
48+
"locale": "it_IT",
49+
"name": "Italian",
50+
"register": "informal (tu)"
51+
}
52+
]
53+
}

admin/class-bp-birthdays-admin.php

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
* This class is retained ONLY for the option contract that the new panel
2222
* delegates to:
2323
* - sanitize_settings() — the canonical sanitizer for all 13 subkeys.
24-
* - get_settings() — settings getter with $defaults applied.
25-
* - $defaults — the single source of truth for defaults.
24+
* - get_settings() — settings getter with the defaults applied.
25+
* - get_defaults() — the single source of truth for defaults.
2626
*
2727
* Its menu/enqueue/render methods (add_admin_menu, register_settings,
2828
* enqueue_admin_assets, render_settings_page and the per-tab renderers)
@@ -39,30 +39,43 @@ class BP_Birthdays_Admin {
3939
const OPTION_NAME = 'bp_birthdays_settings';
4040

4141
/**
42-
* Default settings.
42+
* Default settings — the single source of truth for every subkey.
4343
*
44-
* @var array
44+
* Built by a method rather than a property initializer because two of the
45+
* defaults (activity_message, notification_text) are member-facing copy
46+
* that must pass through __(). PHP does not allow a function call in a
47+
* property default, and resolving a label before the textdomain loads on
48+
* init:10 would return English anyway (docs/standards/i18n.md trap 4).
49+
* Every caller reaches these through get_settings(), which runs at render
50+
* time, so the textdomain is always loaded by then.
51+
*
52+
* @since 2.5.0
53+
*
54+
* @return array Default settings.
4555
*/
46-
private $defaults = array(
47-
// General.
48-
'default_field_id' => '',
49-
'cache_duration' => 30,
50-
// Email Notifications (content is managed in BP Emails).
51-
'email_enabled' => false,
52-
'email_send_time' => '09:00',
53-
'admin_email_enabled' => false,
54-
'admin_email' => '',
55-
// Activity Feed.
56-
'activity_enabled' => false,
57-
'activity_message' => "Today is {name}'s birthday! Send your wishes!",
58-
// BP Notifications.
59-
'notification_enabled' => false,
60-
'notification_friends_only' => false,
61-
'notification_text' => "It's {name}'s birthday today!",
62-
// Display Extras.
63-
'confetti_enabled' => false,
64-
'zodiac_enabled' => false,
65-
);
56+
public static function get_defaults() {
57+
return array(
58+
// General.
59+
'default_field_id' => '',
60+
'cache_duration' => 30,
61+
// Email Notifications (content is managed in BP Emails).
62+
'email_enabled' => false,
63+
'email_send_time' => '09:00',
64+
'admin_email_enabled' => false,
65+
'admin_email' => '',
66+
// Activity Feed. {name}/{age}/{profile_url} are placeholders the
67+
// site owner can re-order, so the whole sentence is one unit.
68+
'activity_enabled' => false,
69+
'activity_message' => __( "Today is {name}'s birthday! Send your wishes!", 'buddypress-birthdays' ),
70+
// BP Notifications.
71+
'notification_enabled' => false,
72+
'notification_friends_only' => false,
73+
'notification_text' => __( "It's {name}'s birthday today!", 'buddypress-birthdays' ),
74+
// Display Extras.
75+
'confetti_enabled' => false,
76+
'zodiac_enabled' => false,
77+
);
78+
}
6679

6780
/**
6881
* Instance of this class.
@@ -210,8 +223,7 @@ public function sanitize_settings( $input ) {
210223
*/
211224
public static function get_settings( $key = null ) {
212225
$settings = get_option( self::OPTION_NAME, array() );
213-
$instance = self::get_instance();
214-
$settings = wp_parse_args( $settings, $instance->defaults );
226+
$settings = wp_parse_args( $settings, self::get_defaults() );
215227

216228
if ( null !== $key ) {
217229
return isset( $settings[ $key ] ) ? $settings[ $key ] : null;

assets/inc/buddypress-birthdays-widget.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ public function get_name_to_display( $user = null ) {
12121212
}
12131213

12141214
if ( ! isset( $user_info->user_login ) ) {
1215-
return 'N/A';
1215+
return __( 'N/A', 'buddypress-birthdays' );
12161216
}
12171217

12181218
if ( ( ! empty( $user_info->user_firstname ) || ! empty( $user_info->user_lastname ) ) ) {

assets/js/bb-core.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,7 @@
6060

6161
if (!href || href === '#') {
6262
e.preventDefault();
63-
// Use localized string if available, fallback to English.
64-
const errorMsg = (typeof bbBirthdays !== 'undefined' && bbBirthdays.strings && bbBirthdays.strings.wishes_error)
65-
? bbBirthdays.strings.wishes_error
66-
: 'Unable to send wishes at this time.';
67-
this.showMessage(errorMsg, 'error');
63+
this.showMessage(this.s('wishes_error', 'Unable to send wishes at this time.'), 'error');
6864
return;
6965
}
7066

@@ -177,22 +173,34 @@
177173
}
178174
},
179175

176+
// Read a localized string, falling back to the English text. Every key
177+
// used here MUST be seeded in the bbBirthdays.strings array in
178+
// core-init.php — a key read but not seeded renders English on every
179+
// locale forever. The fallback only covers the case where the localize
180+
// script did not run at all.
181+
s: function(key, fallback) {
182+
return (typeof bbBirthdays !== 'undefined' && bbBirthdays.strings && bbBirthdays.strings[key])
183+
? bbBirthdays.strings[key]
184+
: fallback;
185+
},
186+
180187
initAccessibility: function() {
181188
// Add ARIA labels for better accessibility
189+
const sendWishesLabel = this.s('send_wishes_aria', 'Send birthday wishes');
182190
this.cache.$sendWishesButtons.each(function() {
183191
const $button = $(this);
184192
if (!$button.attr('aria-label')) {
185-
$button.attr('aria-label', 'Send birthday wishes');
193+
$button.attr('aria-label', sendWishesLabel);
186194
}
187195
$button.attr('role', 'button');
188196
});
189197

190198
// Add role attributes where needed
191199
this.cache.$birthdayLists.attr('role', 'list');
192200
this.cache.$birthdayLists.find('.bp-birthday-item').attr('role', 'listitem');
193-
201+
194202
// Add landmark roles
195-
this.cache.$birthdayWidgets.attr('role', 'complementary').attr('aria-label', 'Birthday notifications');
203+
this.cache.$birthdayWidgets.attr('role', 'complementary').attr('aria-label', this.s('widget_aria_label', 'Birthday notifications'));
196204
},
197205

198206
initSpecialEffects: function() {
@@ -553,7 +561,7 @@
553561
if ('Notification' in window && Notification.permission === 'default') {
554562
Notification.requestPermission().then(permission => {
555563
if (permission === 'granted') {
556-
this.showMessage('Birthday notifications enabled!', 'success');
564+
this.showMessage(this.s('notifications_on', 'Birthday notifications enabled!'), 'success');
557565
}
558566
});
559567
}

audit/qa-coverage.json

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"schema_version": "v1",
3+
"plugin": {
4+
"slug": "buddypress-birthdays",
5+
"version": "2.4.1"
6+
},
7+
"generated": {
8+
"at": "2026-07-03T08:57:28.848Z",
9+
"manifest_at": "2026-06-05T00:00:00+05:30",
10+
"manifest_branch": "2.5.0"
11+
},
12+
"categories": {
13+
"rest": {
14+
"total": 0,
15+
"covered": 0,
16+
"uncovered": 0,
17+
"skipped": 0,
18+
"covered_items": [],
19+
"uncovered_items": []
20+
},
21+
"ajax": {
22+
"total": 1,
23+
"covered": 0,
24+
"uncovered": 1,
25+
"skipped": 0,
26+
"covered_items": [],
27+
"uncovered_items": [
28+
{
29+
"action": "bb_birthdays_action",
30+
"stub_command": "bin/qa-stub-gen.php ajax bb_birthdays_action"
31+
}
32+
]
33+
},
34+
"hooks_fired": {
35+
"total": 0,
36+
"covered": 0,
37+
"uncovered": 0,
38+
"skipped": 5,
39+
"covered_items": [],
40+
"uncovered_items": [],
41+
"_note": "Tracks only hooks with non-empty consumed_by. Skipped hooks are pure extensibility surface."
42+
},
43+
"cron": {
44+
"total": 2,
45+
"covered": 0,
46+
"uncovered": 2,
47+
"skipped": 0,
48+
"covered_items": [],
49+
"uncovered_items": [
50+
{
51+
"hook": "bb_cleanup_old_wishes",
52+
"stub_command": "bin/qa-stub-gen.php cron bb_cleanup_old_wishes"
53+
},
54+
{
55+
"hook": "bp_birthdays_daily_check",
56+
"stub_command": "bin/qa-stub-gen.php cron bp_birthdays_daily_check"
57+
}
58+
]
59+
},
60+
"wp_cli": {
61+
"total": 0,
62+
"covered": 0,
63+
"uncovered": 0,
64+
"skipped": 0,
65+
"covered_items": [],
66+
"uncovered_items": []
67+
}
68+
},
69+
"summary": {
70+
"categories_checked": 5,
71+
"items_total": 3,
72+
"items_covered": 0,
73+
"items_uncovered": 3,
74+
"items_skipped": 5,
75+
"coverage_percent": 0
76+
},
77+
"drift": {
78+
"previous_uncovered": null,
79+
"current_uncovered": 3,
80+
"delta": null
81+
}
82+
}

buddypress-birthdays.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Author: Wbcom Designs
88
* Author URI: https://wbcomdesigns.com/
99
* Text Domain: buddypress-birthdays
10+
* Domain Path: /languages
1011
* License: GPLv3
1112
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
1213
* Requires at least: 5.3

0 commit comments

Comments
 (0)