Speak German dates nicely using Piper

Unfortunately, the current German Piper voice models

  • don’t have a built-in date conversion
  • can’t speak ordinal numbers
  • and we don’t (yet?) have even rudimentary SSML support like we had in Larynx, a Piper predecessor.

This leads to German dates like 2025-04-15 or 15.04.2025 being spoken as 2025 Strich 04 Strich 15, or 15 Punkt 04 Punkt 2025. Ordinal numbers in dates like 15. April are spoken as 15 (sentence pause) April.

After a while, this tends to get on one’s nerves, especially when you’re still half-sleepy and your otherwise fine-tuned Good Morning wakeup routine can’t even speak today’s date or that of your next calendar appointment correctly. It also makes for a bad WAF… (PAF?)

So until we get better language models or at least a rudimentary SSML, I came up with some templating code that converts a datetime value like now() into a nicely speakable date string. This is for German, you might want to adapt it for your language. You can use it in scripts and automations.

Here is an example of what it does, using Developer Tools → Templates:

And here is the templating code from this example:

{% set datum = now() %}
{# This is how you can use an arbitrary date #}
{# set datum = now().fromisoformat('2025-12-24') #}

{% set weekday = datum.weekday() %} {# 0..6 = Mon..Sun #}
{% set day = datum.day %}
{% set month = datum.month %}
{% set year = datum.year %}

{# 0..6 = Monday .. Sunday #}
{% set weekdays = [
  'Montag',
  'Dienstag',
  'Mittwoch',
  'Donnerstag',
  'Freitag',
  'Samstag',
  'Sonntag'
  ] %}

{# 0..31 ordinal day names for TTS #}
{% set ordinal = [
  None,
  'erste',
  'zweite',
  'dritte',
  'vierte',
  'fünfte',
  'sechste',
  'siebte',
  'achte',
  'neunte',
  'zehnte',
  'elfte',
  'zwölfte',
  'dreizehnte',
  'vierzehnte',
  'fünfzehnte',
  'sechzehnte',
  'siebzehnte',
  'achtzehnte',
  'neunzehnte',
  'zwanzigste',
  'einundzwanzigste',
  'zweiundzwanzigste',
  'dreiundzwanzigste',
  'vierundzwanzigste',
  'fünfundzwanzigste',
  'sechsundzwanzigste',
  'siebenundzwanzigste',
  'achtundzwanzigste',
  'neunundzwanzigste',
  'dreißigste',
  'einunddreißigste'
  ] %}

{# month names #}
{% set months = [
  None,
  'Januar',
  'Februar',
  'März',
  'April',
  'Mai',
  'Juni',
  'Juli',
  'August',
  'September',
  'Oktober',
  'November',
  'Dezember'
  ] %}

{{ datum }}

{{ weekdays[weekday]}}, der {{ ordinal[day] }} {{ ordinal[month] | capitalize }} {{ year }}.
{{ weekdays[weekday]}}, der {{ ordinal[day] }} {{ months[month] | capitalize }} {{ year }}.
Am {{ weekdays[weekday]}}, dem {{ ordinal[day] }}n {{ ordinal[month] | capitalize }}n {{ year }} …
Am {{ weekdays[weekday]}}, dem {{ ordinal[day] }}n {{ months[month] }} {{ year }} …
Es ist {{ datum.strftime('%H:%M')}}.

Note: The current German language models already speak a time like 11:44 as Elf Uhr vierundvierzig.

It’s a lengthy workaround, but it makes for a much higher TTS acceptance, especially with family members and guests.

For easier re-use, we could maybe even make it into a macro, or a script. Suggestions welcome.