Skip to main content

Two things a translator cannot fix (FluentCommunity 2.9.0, FluentPlayer Pro 1.4.0)

Posting this as a translator again. Same setup as before: I maintain the German
catalogues for FluentCommunity, FluentCommunity Pro, FluentMessaging and
FluentPlayer, a little over 5,000 strings, and I check every string against the
rendered page rather than against the return value of __().

Two findings from going through 2.9.0. The first is a small UI slip, the second
is a string that cannot be translated correctly in any language, and it affects
two of your products with the same code.


1. A preference row that renders with no control at all

Where: member profile, notification preferences.

The table has one row per event. Four of the five rows offer an email checkbox,
a push checkbox, or both. The fifth offers neither, and looks like a bug to the
member.

The row is co_comment – Someone also comments on a post I commented on. It
is push-only by design, and that design is consistent in both layers:

// app/Services/NotificationPref.php:31-36
const NOTIFICATION_EVENTS = [
    'comment'    => ['mail' => 'com_my_post_mail',  'push' => 'com_my_post_push'],
    'reply'      => ['mail' => 'reply_my_com_mail', 'push' => 'reply_my_com_push'],
    'mention'    => ['mail' => 'mention_mail',      'push' => 'mention_push'],
    'co_comment' => ['push' => 'co_com_push'],
    'digest'     => ['mail' => 'digest_mail']
];
// app.js, NotificationPref component, computed channelRows
{ key: "co_comment",
  label: this.$t("Someone also comments on a post I commented on"),
  push: "co_com_push" }        // no mail key, unlike the other four

The template renders the email cell only when row.mail is set, which is right.
The push column, however, is hidden as a whole when push is not available:

showPush() { return this.push_available }

and push_available comes from PushNotificationModule::isAvailable(), which
requires FluentNotify to be installed and configured, on top of
push_enabled being yes in FluentCommunity:

public static function isAvailable()
{
    $pushEnabledInCommunity = Arr::get(Utility::getPushNotificationSettings(), 'push_enabled') === 'yes';

    return self::isFluentNotifyActive() && $pushEnabledInCommunity;
}

So on every site that has not set up FluentNotify – which is every site right
after updating to 2.9.0, since push_enabled already defaults to yes – that
row appears with its label and two empty cells. Nothing to click, no
explanation.

Suggestion: drop rows from channelRows that have no visible channel left.
Something like

visibleChannelRows() {
    return this.channelRows.filter(row => row.mail || (row.push && this.showPush))
}

and render that instead. If you would rather keep the row visible as a teaser
for push, a short hint in the empty cell would do the same job and would tell
the member what is missing.

I mention it here because from the outside it reads as a translation problem –
"the German label is there but the checkbox is gone" – and it is the kind of
thing a site owner reports to the translator first.


2. The expired-license message is assembled from hardcoded English

Where: FluentLicensing::getExpireMessage(), in both
fluent-player-pro and fluent-community-pro.

// fluent-player-pro/app/Services/PluginManager/FluentLicensing.php:316-322
$expired = $expiresAt
    ? __('expired at', 'fluent-player-pro') . ' ' . gmdate('d M Y', $expiresAt)
    : __('expired', 'fluent-player-pro');

return '<p>Your ' . $this->getConfig('plugin_title') . ' ' . __('license has been', 'fluent-player-pro') . ' <b>' . $expired . '</b>, Please ' .
    '<a href="' . esc_url($renewUrl) . '"><b>' . __('Click Here to Renew Your License', 'fluent-player-pro') . '</b></a>' . '</p>';

fluent-community-pro/app/Services/PluginManager/FluentLicensing.php:316 is the
same line with a different text domain.

Three separate problems in one sentence:

  • Your and , Please are not translatable. Whatever the fragments become,
    the sentence stays half English.
  • The word order is fixed by concatenation. German puts the date and the
    participle in a different place than English does; there is no arrangement of
    license has been + expired at <date> that produces a correct German
    sentence, because the pieces cannot move past each other.
  • gmdate('d M Y') always renders English month abbreviations, regardless of
    locale. date_i18n() is the drop-in replacement.

This is not hypothetical, and it is not only about German. Your own bundled
Spanish pack translates the fragments faithfully:

'license has been' => 'la licencia ha sido',
'expired at'       => 'expirado el',

which renders as:

Your FluentCommunity Pro la licencia ha sido expirado el 12 Jan 2026,
Please Click aquΓ­ para renovar tu licencia

Every translator who touches these four fragments produces something like that,
because there is no other option available to them.

Suggestion: one string per case, with placeholders, so the translator owns
the whole sentence:

$renewLink = '<a href="' . esc_url($renewUrl) . '"><b>' .
    __('Renew your license', 'fluent-player-pro') . '</b></a>';

if ($expiresAt) {
    // translators: 1: product name, 2: expiry date, 3: link that says "Renew your license"
    $message = sprintf(
        __('Your %1$s license expired on %2$s. Please %3$s.', 'fluent-player-pro'),
        $this->getConfig('plugin_title'),
        date_i18n(get_option('date_format'), $expiresAt),
        $renewLink
    );
} else {
    // translators: 1: product name, 2: link that says "Renew your license"
    $message = sprintf(
        __('Your %1$s license has expired. Please %2$s.', 'fluent-player-pro'),
        $this->getConfig('plugin_title'),
        $renewLink
    );
}

That is four fragments replaced by two full sentences and one link label, and it
costs the existing translations nothing worth keeping.

One thing worth checking while you are in that file: the FluentCommunity Pro
version calls strtotime($licenseData['expires']) unguarded, where the
FluentPlayer Pro version has learned to handle an empty expires for lifetime
licenses. A lifetime FluentCommunity Pro license would render 01 Jan 1970.


Happy to send a PR for either of these if that is easier for you.

Shahjahan Jewel

Forwarded both to the team. Will be fixed/improved in the next version.

Julian Brinke

Wo kann ich Deine Übersetzungen bekommen?

Moin Julian Brinke, ich werde ein ΓΆffentliches GitHub-Repo dafΓΌr einrichten.

Ich lâse das mit einem WordPress-Plugin, weil bestimmte Strings nicht direkt übersetzbar sind und meine Übersetzung statt der aus der WordPress-Community angezeigt werden soll.

Merkmale: Du-Anrede, Space wird als Forum ΓΌbersetzt.

Alles übersetzt, KI-gestützt ein eigenes Glossar angelegt, damit die Übersetzung konsistent bleibt. Viele Texte verbessert und verstÀndlicher formuliert.

Julian Brinke

Peter Claus LamprechtΒ mega - Danke

weißt Du ob sowas oder in Ànlich auch schon für FluentCart gibt?

Julian Brinke, ich pflege eine Übersetzung für FluentCart, da gibt es aber noch ein paar Dinge, die nicht übersetzbar sind; Bug-Reports sind draußen. Zwei Varianten: Du- und Sie-Form. Da zâgere ich, das freizugeben, weil das teilweise Texte mit rechtlicher Bedeutung sind.

Julian Brinke

Peter Claus LamprechtΒ verstehe,

hatte mal fΓΌr ein Projekt alles in Du-Form nur die Rechtstexte in Sie- Form…

ist meiner Meinung nach die sauberste aber nicht die einfachste LΓΆsung :)

Julian Brinke

Peter Claus LamprechtΒ Ich liebe es
Du mit großem D - einfach geil
Danke!!!!

Julian Brinke, vielen Dank. Wenn Dir etwas auffΓ€llt oder Du WΓΌnsche hast, melde Dich gern.

Julian Brinke, ich habe noch ein Update nachgelegt (1.5.0). Damit kannst Du direkt im WordPress-Backend kΓΌnftige Updates nachladen.

Julian Brinke

sehr schΓΆn