fix(mail): retry a failed delivery instead of losing the message
A transport error stamped the message as sent, so it was never tried again and nobody could tell it had not arrived. Delivery is now tracked: a message that could not be sent keeps its place and is tried again after a configurable delay, and once the attempts run out it is recorded as failed with its error - never as sent. A message that cannot be built at all (no recipient, an address no server would accept, a template that does not render) is given up on at once, because every future run would fail the same way, and the rest of the batch still goes out.
A batch is now claimed before it is read, so two workers - the cron of a busy site firing again before the previous run finished - cannot pick the same message, and a claim nobody released within the timeout goes back into circulation. The claim needs no row locks, so it works on the older MySQL versions as well.
EmailSender is a service with its dependencies injected rather than a static method reaching into the container, and reports what the run did. The queue lives behind EmailQueueInterface. The table is defined once in MailSchema, shared by the installer, the new mail:upgrade-schema command and the tests; mail:cleanup removes delivered messages, never the failed ones.
Also fixes MailRenderer dying with a TypeError when no translator was registered yet, which is what a process rendering a message outside the usual bootstrap runs into.