Switch the app to German as its only language
The app is used internally only and its settings page was German already. Mails, controller error messages (shown in the form), the settings section name and the mail sender are now written in German directly instead of going through IL10N without any translation files. Nextcloud's own mail footer is requested in German as well.
This commit is contained in:
1 parent
578b235f08
commit
1ffc0765ea
6 files changed
+77
-66
No files matched your search
@@ -6,19 +6,31 @@ namespace OCA\WorkflowDeckAutomation\Service;
|
||||
|
||||
use OCA\Deck\Db\Card;
|
||||
use OCA\WorkflowDeckAutomation\Db\Workflow;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\Mail\IEMailTemplate;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Util;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* The app is used internally only and speaks German, like its settings
|
||||
* page: texts are written in German directly instead of going through
|
||||
* IL10N with English source strings and a translation file.
|
||||
*/
|
||||
class NotificationMailer {
|
||||
private const APP_ID = 'workflow_deck_automation';
|
||||
|
||||
private const SENDER_NAME = 'Deck Workflow-Automatisierung';
|
||||
|
||||
/**
|
||||
* Language of Nextcloud's own footer text ("automatically sent email,
|
||||
* please do not reply"), which would otherwise follow the instance
|
||||
* default — in cron there is no request to take a language from.
|
||||
*/
|
||||
private const FOOTER_LANGUAGE = 'de';
|
||||
|
||||
/**
|
||||
* Descriptions are unbounded in Deck; keep the mail readable and well
|
||||
* clear of any MTA size limits.
|
||||
@@ -28,7 +40,6 @@ class NotificationMailer {
|
||||
public function __construct(
|
||||
private IMailer $mailer,
|
||||
private IURLGenerator $urlGenerator,
|
||||
private IFactory $l10nFactory,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
@@ -50,7 +61,6 @@ class NotificationMailer {
|
||||
}
|
||||
|
||||
$count = count($entries);
|
||||
$l10n = $this->l10nFor($recipient);
|
||||
|
||||
try {
|
||||
$template = $this->mailer->createEMailTemplate(self::APP_ID . '.CardsMoved', [
|
||||
@@ -58,20 +68,22 @@ class NotificationMailer {
|
||||
'cardTitles' => array_map(static fn (array $entry) => $entry['card']->getTitle(), $entries),
|
||||
]);
|
||||
$template->setSubject($count === 1
|
||||
? $l10n->t('Deck card moved: %s', [$entries[0]['card']->getTitle()])
|
||||
: $l10n->n('%n Deck card moved', '%n Deck cards moved', $count));
|
||||
? sprintf('Deck-Karte verschoben: %s', $entries[0]['card']->getTitle())
|
||||
: sprintf('%d Deck-Karten verschoben', $count));
|
||||
$template->addHeader();
|
||||
$template->addHeading($l10n->n('A card was moved automatically', '%n cards were moved automatically', $count), false);
|
||||
$template->addHeading($count === 1
|
||||
? 'Eine Karte wurde automatisch verschoben'
|
||||
: sprintf('%d Karten wurden automatisch verschoben', $count), false);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$this->addCard($template, $l10n, $entry['workflow'], $entry['card'], $entry['targets']);
|
||||
$this->addCard($template, $entry['workflow'], $entry['card'], $entry['targets']);
|
||||
}
|
||||
|
||||
$template->addFooter();
|
||||
$template->addFooter('', self::FOOTER_LANGUAGE);
|
||||
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setTo([$email => $recipient->getDisplayName()]);
|
||||
$message->setFrom([Util::getDefaultEmailAddress('noreply') => $l10n->t('Deck Workflow Automation')]);
|
||||
$message->setFrom([Util::getDefaultEmailAddress('noreply') => self::SENDER_NAME]);
|
||||
$message->useTemplate($template);
|
||||
|
||||
$failedRecipients = $this->mailer->send($message);
|
||||
@@ -91,21 +103,24 @@ class NotificationMailer {
|
||||
/**
|
||||
* @param array{board: string, sourceStack: string, targetStack: string} $targets
|
||||
*/
|
||||
private function addCard(IEMailTemplate $template, IL10N $l10n, Workflow $workflow, Card $card, array $targets): void {
|
||||
$template->addBodyText($l10n->t(
|
||||
'The card "%1$s" was moved because it is overdue (workflow "%2$s").',
|
||||
[$card->getTitle(), $workflow->getTitle()],
|
||||
));
|
||||
private function addCard(IEMailTemplate $template, Workflow $workflow, Card $card, array $targets): void {
|
||||
// Single-argument addBodyText() runs htmlspecialchars() itself, so
|
||||
// these board- and stack-titles (user input) are safe as-is —
|
||||
// unlike the description below, which passes both parts.
|
||||
$template->addBodyText($l10n->t(
|
||||
'Board "%1$s": moved from "%2$s" to "%3$s".',
|
||||
[$targets['board'], $targets['sourceStack'], $targets['targetStack']],
|
||||
// these card-, workflow-, board- and stack-titles (user input) are
|
||||
// safe as-is — unlike the description below, which passes both parts.
|
||||
$template->addBodyText(sprintf(
|
||||
'Die Karte „%1$s“ wurde verschoben, weil sie überfällig ist (Workflow „%2$s“).',
|
||||
$card->getTitle(),
|
||||
$workflow->getTitle(),
|
||||
));
|
||||
$this->addCardDescription($template, $l10n, $card);
|
||||
$template->addBodyText(sprintf(
|
||||
'Board „%1$s“: von „%2$s“ nach „%3$s“ verschoben.',
|
||||
$targets['board'],
|
||||
$targets['sourceStack'],
|
||||
$targets['targetStack'],
|
||||
));
|
||||
$this->addCardDescription($template, $card);
|
||||
$template->addBodyButton(
|
||||
$l10n->t('Open card'),
|
||||
'Karte öffnen',
|
||||
$this->urlGenerator->getAbsoluteURL('/apps/deck/board/' . $workflow->getBoardId() . '/card/' . $card->getId()),
|
||||
);
|
||||
}
|
||||
@@ -125,11 +140,10 @@ class NotificationMailer {
|
||||
return;
|
||||
}
|
||||
|
||||
$l10n = $this->l10nFor($user);
|
||||
$reason = match ($brokenTarget) {
|
||||
WorkflowRunner::TARGET_SOURCE_STACK => $l10n->t('its source stack no longer exists'),
|
||||
WorkflowRunner::TARGET_TARGET_STACK => $l10n->t('its target stack no longer exists'),
|
||||
default => $l10n->t('its board no longer exists or is no longer available to you'),
|
||||
WorkflowRunner::TARGET_SOURCE_STACK => 'sein Quell-Stapel nicht mehr existiert',
|
||||
WorkflowRunner::TARGET_TARGET_STACK => 'sein Ziel-Stapel nicht mehr existiert',
|
||||
default => 'sein Board nicht mehr existiert oder für dich nicht mehr verfügbar ist',
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -138,22 +152,23 @@ class NotificationMailer {
|
||||
$template = $this->mailer->createEMailTemplate(self::APP_ID . '.WorkflowDisabled', [
|
||||
'workflowTitle' => $workflow->getTitle(),
|
||||
]);
|
||||
$template->setSubject($l10n->t('Deck workflow deactivated: %s', [$workflow->getTitle()]));
|
||||
$template->setSubject(sprintf('Deck-Workflow deaktiviert: %s', $workflow->getTitle()));
|
||||
$template->addHeader();
|
||||
$template->addHeading($l10n->t('A workflow was deactivated'), false);
|
||||
$template->addBodyText($l10n->t(
|
||||
'The workflow "%1$s" was switched off automatically because %2$s. No cards are being moved by it any more.',
|
||||
[$workflow->getTitle(), $reason],
|
||||
$template->addHeading('Ein Workflow wurde deaktiviert', false);
|
||||
$template->addBodyText(sprintf(
|
||||
'Der Workflow „%1$s“ wurde automatisch ausgeschaltet, weil %2$s. Er verschiebt keine Karten mehr.',
|
||||
$workflow->getTitle(),
|
||||
$reason,
|
||||
));
|
||||
$template->addBodyText($l10n->t(
|
||||
'Delete the workflow or point it at an existing board and stack to reactivate it. You will not be reminded about this workflow again.',
|
||||
));
|
||||
$template->addBodyButton($l10n->t('Open settings'), $settingsLink);
|
||||
$template->addFooter();
|
||||
$template->addBodyText(
|
||||
'Lösche den Workflow oder stelle ein vorhandenes Board und vorhandene Stapel ein, um ihn wieder zu aktivieren. An diesen Workflow wirst du nicht noch einmal erinnert.',
|
||||
);
|
||||
$template->addBodyButton('Einstellungen öffnen', $settingsLink);
|
||||
$template->addFooter('', self::FOOTER_LANGUAGE);
|
||||
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setTo([$email => $user->getDisplayName()]);
|
||||
$message->setFrom([Util::getDefaultEmailAddress('noreply') => $l10n->t('Deck Workflow Automation')]);
|
||||
$message->setFrom([Util::getDefaultEmailAddress('noreply') => self::SENDER_NAME]);
|
||||
$message->useTemplate($template);
|
||||
|
||||
$failedRecipients = $this->mailer->send($message);
|
||||
@@ -170,15 +185,6 @@ class NotificationMailer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The recipient's own language. Cron has no request to take one from, so
|
||||
* an injected IL10N would always speak the instance default — wrong as
|
||||
* soon as a mail goes to someone other than whoever set that default.
|
||||
*/
|
||||
private function l10nFor(IUser $user): IL10N {
|
||||
return $this->l10nFactory->get(self::APP_ID, $this->l10nFactory->getUserLanguage($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the card's description, if it has one.
|
||||
*
|
||||
@@ -186,7 +192,7 @@ class NotificationMailer {
|
||||
* rendered: pulling a Markdown parser in just for the mail would be a
|
||||
* lot of machinery, and unrendered Markdown still reads fine.
|
||||
*/
|
||||
private function addCardDescription(IEMailTemplate $template, IL10N $l10n, Card $card): void {
|
||||
private function addCardDescription(IEMailTemplate $template, Card $card): void {
|
||||
$description = trim((string)$card->getDescription());
|
||||
if ($description === '') {
|
||||
return;
|
||||
@@ -196,7 +202,7 @@ class NotificationMailer {
|
||||
$description = mb_substr($description, 0, self::MAX_DESCRIPTION_LENGTH) . ' […]';
|
||||
}
|
||||
|
||||
$template->addBodyText($l10n->t('Card content:'));
|
||||
$template->addBodyText('Karteninhalt:');
|
||||
|
||||
// addBodyText() only runs htmlspecialchars() when it has to build the
|
||||
// plain-text part itself. Passing both parts means escaping is on us
|
||||
|
||||
Reference in new issue
Block a user