Split off comments in translatable string enum value constructor
Marco Ricci

Marco Ricci commited on 2025-01-13 22:44:58
Zeige 1 geänderte Dateien mit 468 Einfügungen und 304 Löschungen.


Now that the constructor for translatable string enum values is
homogeneous, split the constructor into a curried two-step function
call.  This should hopefully look much better visually separated when
rendered in the API documentation than the current solution, because the
rendering normalizes the multiline strings into single-line strings with
escape sequences.

(This commit only splits the arguments into the two-step function call;
the arguments have not yet been trimmed or normalized.)
... ...
@@ -9,6 +9,7 @@ from __future__ import annotations
9 9
 import contextlib
10 10
 import datetime
11 11
 import enum
12
+import functools
12 13
 import gettext
13 14
 import inspect
14 15
 import os
... ...
@@ -16,7 +17,7 @@ import string
16 17
 import sys
17 18
 import textwrap
18 19
 import types
19
-from typing import TYPE_CHECKING, NamedTuple, TextIO, Union, cast
20
+from typing import TYPE_CHECKING, NamedTuple, Protocol, TextIO, Union, cast
20 21
 
21 22
 from typing_extensions import TypeAlias, override
22 23
 
... ...
@@ -213,11 +214,11 @@ class DebugTranslations(gettext.NullTranslations):
213 214
 
214 215
 
215 216
 class TranslatableString(NamedTuple):
216
-    singular: str
217
-    plural: str
218 217
     l10n_context: str
219
-    translator_comments: str
220
-    flags: frozenset[str]
218
+    singular: str
219
+    plural: str = ''
220
+    flags: frozenset[str] = frozenset()
221
+    translator_comments: str = ''
221 222
 
222 223
     @staticmethod
223 224
     def _maybe_rewrap(
... ...
@@ -341,23 +342,10 @@ class TranslatableString(NamedTuple):
341 342
         return self._replace(flags=all_flags)
342 343
 
343 344
 
344
-def _prepare_translatable(
345
-    msg: str,
346
-    comments: str = '',
347
-    context: str = '',
348
-    plural_msg: str = '',
349
-    *,
350
-    flags: Iterable[str] = (),
351
-) -> TranslatableString:
352
-    return translatable(
353
-        context, msg, plural=plural, comments=comments, flags=flags
354
-    )
355
-
356
-
357 345
 def translatable(
358 346
     context: str,
359 347
     single: str,
360
-    /,
348
+    # /,
361 349
     flags: Iterable[str] = (),
362 350
     plural: str = '',
363 351
     comments: str = '',
... ...
@@ -457,27 +445,60 @@ class TranslatedString:
457 445
         return self
458 446
 
459 447
 
448
+class _TranslatedStringConstructor(Protocol):
449
+    def __call__(
450
+        self,
451
+        context: str,
452
+        single: str,
453
+        # /,
454
+        flags: Iterable[str] = (),
455
+        plural: str = '',
456
+        comments: str = '',
457
+    ) -> TranslatableString: ...
458
+
459
+
460
+def _Commented(  # noqa: N802
461
+    comments: str = '',
462
+    # /
463
+) -> _TranslatedStringConstructor:
464
+    """A "decorator" for readably constructing commented enum values.
465
+
466
+    This is geared towards the quirks of the API documentation extractor
467
+    `mkdocstrings-python`/`griffe`, which reformat and trim enum value
468
+    declarations in somewhat weird ways.  Chains of function calls are
469
+    preserved, though, so use this to our advantage to suggest
470
+    a specific formatting.
471
+
472
+    This is not necessarily good code style, and it is
473
+    (quasi-)unnecessarily heavyweight.
474
+
475
+    """  # noqa: DOC201
476
+    return functools.partial(translatable, comments=comments)
477
+
478
+
460 479
 class Label(enum.Enum):
461
-    DEPRECATION_WARNING_LABEL = _prepare_translatable(
480
+    DEPRECATION_WARNING_LABEL = _Commented(
462 481
         comments=r"""
463 482
         TRANSLATORS: This is a short label that will be prepended to
464 483
         a warning message, e.g., "Deprecation warning: A subcommand will
465 484
         be required in v1.0."
466 485
         """,
486
+    )(
467 487
         context='Label :: Diagnostics :: Marker',
468
-        msg='Deprecation warning',
488
+        single='Deprecation warning',
469 489
     )
470
-    WARNING_LABEL = _prepare_translatable(
490
+    WARNING_LABEL = _Commented(
471 491
         comments=r"""
472 492
         TRANSLATORS: This is a short label that will be prepended to
473 493
         a warning message, e.g., "Warning: An empty service name is not
474 494
         supported by vault(1)."
475 495
         """,
496
+    )(
476 497
         context='Label :: Diagnostics :: Marker',
477
-        msg='Warning',
498
+        single='Warning',
478 499
     )
479 500
     CANNOT_UPDATE_SETTINGS_METAVAR_SETTINGS_TYPE_GLOBAL = (
480
-        _prepare_translatable(
501
+        _Commented(
481 502
             comments=r"""
482 503
             TRANSLATORS: This is one of two values of the settings_type
483 504
             metavar used in the CANNOT_UPDATE_SETTINGS_NO_SETTINGS
... ...
@@ -485,12 +506,13 @@ class Label(enum.Enum):
485 506
             reads: "Cannot update the global settings without any given
486 507
             settings."
487 508
             """,
509
+        )(
488 510
             context='Label :: Error message :: Metavar',
489
-            msg='global settings',
511
+            single='global settings',
490 512
         )
491 513
     )
492 514
     CANNOT_UPDATE_SETTINGS_METAVAR_SETTINGS_TYPE_SERVICE = (
493
-        _prepare_translatable(
515
+        _Commented(
494 516
             comments=r"""
495 517
             TRANSLATORS: This is one of two values of the settings_type
496 518
             metavar used in the CANNOT_UPDATE_SETTINGS_NO_SETTINGS
... ...
@@ -498,98 +520,108 @@ class Label(enum.Enum):
498 520
             reads: "Cannot update the service-specific settings without
499 521
             any given settings."
500 522
             """,
523
+        )(
501 524
             context='Label :: Error message :: Metavar',
502
-            msg='service-specific settings',
525
+            single='service-specific settings',
503 526
         )
504 527
     )
505
-    DERIVEPASSPHRASE_01 = _prepare_translatable(
528
+    DERIVEPASSPHRASE_01 = _Commented(
506 529
         comments=r"""
507 530
         TRANSLATORS: This is the first paragraph of the command help
508 531
         text, but it also appears (in truncated form, if necessary) as
509 532
         one-line help text for this command.  The translation should
510 533
         thus be as meaningful as possible even if truncated.
511 534
         """,
535
+    )(
512 536
         context='Label :: Help text :: Explanation',
513
-        msg="""
537
+        single="""
514 538
         Derive a strong passphrase, deterministically, from a master secret.
515 539
         """,
516 540
     )
517
-    DERIVEPASSPHRASE_02 = _prepare_translatable(
541
+    DERIVEPASSPHRASE_02 = _Commented(
518 542
         comments='',
543
+    )(
519 544
         context='Label :: Help text :: Explanation',
520
-        msg="""
545
+        single="""
521 546
         The currently implemented subcommands are "vault" (for the
522 547
         scheme used by vault) and "export" (for exporting foreign
523 548
         configuration data).  See the respective `--help` output for
524 549
         instructions.  If no subcommand is given, we default to "vault".
525 550
         """,
526 551
     )
527
-    DERIVEPASSPHRASE_03 = _prepare_translatable(
552
+    DERIVEPASSPHRASE_03 = _Commented(
528 553
         comments='',
554
+    )(
529 555
         context='Label :: Help text :: Explanation',
530
-        msg="""
556
+        single="""
531 557
         Deprecation notice: Defaulting to "vault" is deprecated.
532 558
         Starting in v1.0, the subcommand must be specified explicitly.
533 559
         """,
534 560
     )
535
-    DERIVEPASSPHRASE_EPILOG_01 = _prepare_translatable(
561
+    DERIVEPASSPHRASE_EPILOG_01 = _Commented(
536 562
         comments='',
563
+    )(
537 564
         context='Label :: Help text :: Explanation',
538
-        msg=r"""
565
+        single=r"""
539 566
         Configuration is stored in a directory according to the
540 567
         `DERIVEPASSPHRASE_PATH` variable, which defaults to
541 568
         `~/.derivepassphrase` on UNIX-like systems and
542 569
         `C:\Users\<user>\AppData\Roaming\Derivepassphrase` on Windows.
543 570
         """,
544 571
     )
545
-    DERIVEPASSPHRASE_EXPORT_01 = _prepare_translatable(
572
+    DERIVEPASSPHRASE_EXPORT_01 = _Commented(
546 573
         comments=r"""
547 574
         TRANSLATORS: This is the first paragraph of the command help
548 575
         text, but it also appears (in truncated form, if necessary) as
549 576
         one-line help text for this command.  The translation should
550 577
         thus be as meaningful as possible even if truncated.
551 578
         """,
579
+    )(
552 580
         context='Label :: Help text :: Explanation',
553
-        msg="""
581
+        single="""
554 582
         Export a foreign configuration to standard output.
555 583
         """,
556 584
     )
557
-    DERIVEPASSPHRASE_EXPORT_02 = _prepare_translatable(
585
+    DERIVEPASSPHRASE_EXPORT_02 = _Commented(
558 586
         comments='',
587
+    )(
559 588
         context='Label :: Help text :: Explanation',
560
-        msg="""
589
+        single="""
561 590
         The only available subcommand is "vault", which implements the
562 591
         vault-native configuration scheme.  If no subcommand is given,
563 592
         we default to "vault".
564 593
         """,
565 594
     )
566 595
     DERIVEPASSPHRASE_EXPORT_03 = DERIVEPASSPHRASE_03
567
-    DERIVEPASSPHRASE_EXPORT_VAULT_01 = _prepare_translatable(
596
+    DERIVEPASSPHRASE_EXPORT_VAULT_01 = _Commented(
568 597
         comments=r"""
569 598
         TRANSLATORS: This is the first paragraph of the command help
570 599
         text, but it also appears (in truncated form, if necessary) as
571 600
         one-line help text for this command.  The translation should
572 601
         thus be as meaningful as possible even if truncated.
573 602
         """,
603
+    )(
574 604
         context='Label :: Help text :: Explanation',
575
-        msg="""
605
+        single="""
576 606
         Export a vault-native configuration to standard output.
577 607
         """,
578 608
     )
579
-    DERIVEPASSPHRASE_EXPORT_VAULT_02 = _prepare_translatable(
609
+    DERIVEPASSPHRASE_EXPORT_VAULT_02 = _Commented(
580 610
         comments='',
611
+    )(
581 612
         context='Label :: Help text :: Explanation',
582
-        msg="""
613
+        single="""
583 614
         Depending on the configuration format, {path_metavar!s} may
584 615
         either be a file or a directory.  We support the vault "v0.2",
585 616
         "v0.3" and "storeroom" formats.
586 617
         """,
587 618
         flags='python-brace-format',
588 619
     )
589
-    DERIVEPASSPHRASE_EXPORT_VAULT_03 = _prepare_translatable(
620
+    DERIVEPASSPHRASE_EXPORT_VAULT_03 = _Commented(
590 621
         comments='',
622
+    )(
591 623
         context='Label :: Help text :: Explanation',
592
-        msg="""
624
+        single="""
593 625
         If {path_metavar!s} is explicitly given as `VAULT_PATH`, then
594 626
         use the `VAULT_PATH` environment variable to determine the
595 627
         correct path.  (Use `./VAULT_PATH` or similar to indicate
... ...
@@ -597,32 +629,35 @@ class Label(enum.Enum):
597 629
         """,
598 630
         flags='python-brace-format',
599 631
     )
600
-    DERIVEPASSPHRASE_VAULT_01 = _prepare_translatable(
632
+    DERIVEPASSPHRASE_VAULT_01 = _Commented(
601 633
         comments=r"""
602 634
         TRANSLATORS: This is the first paragraph of the command help
603 635
         text, but it also appears (in truncated form, if necessary) as
604 636
         one-line help text for this command.  The translation should
605 637
         thus be as meaningful as possible even if truncated.
606 638
         """,
639
+    )(
607 640
         context='Label :: Help text :: Explanation',
608
-        msg="""
641
+        single="""
609 642
         Derive a passphrase using the vault derivation scheme.
610 643
         """,
611 644
     )
612
-    DERIVEPASSPHRASE_VAULT_02 = _prepare_translatable(
645
+    DERIVEPASSPHRASE_VAULT_02 = _Commented(
613 646
         comments='',
647
+    )(
614 648
         context='Label :: Help text :: Explanation',
615
-        msg="""
649
+        single="""
616 650
         If operating on global settings, or importing/exporting
617 651
         settings, then {service_metavar!s} must be omitted.  Otherwise
618 652
         it is required.
619 653
         """,
620 654
         flags='python-brace-format',
621 655
     )
622
-    DERIVEPASSPHRASE_VAULT_EPILOG_01 = _prepare_translatable(
656
+    DERIVEPASSPHRASE_VAULT_EPILOG_01 = _Commented(
623 657
         comments='',
658
+    )(
624 659
         context='Label :: Help text :: Explanation',
625
-        msg="""
660
+        single="""
626 661
         WARNING: There is NO WAY to retrieve the generated passphrases
627 662
         if the master passphrase, the SSH key, or the exact passphrase
628 663
         settings are lost, short of trying out all possible
... ...
@@ -630,236 +665,265 @@ class Label(enum.Enum):
630 665
         backups of the settings and the SSH key, if any.
631 666
         """,
632 667
     )
633
-    DERIVEPASSPHRASE_VAULT_EPILOG_02 = _prepare_translatable(
668
+    DERIVEPASSPHRASE_VAULT_EPILOG_02 = _Commented(
634 669
         comments='',
670
+    )(
635 671
         context='Label :: Help text :: Explanation',
636
-        msg="""
672
+        single="""
637 673
         The configuration is NOT encrypted, and you are STRONGLY
638 674
         discouraged from using a stored passphrase.
639 675
         """,
640 676
     )
641
-    DEPRECATED_COMMAND_LABEL = _prepare_translatable(
677
+    DEPRECATED_COMMAND_LABEL = _Commented(
642 678
         comments=r"""
643 679
         TRANSLATORS: We use this format string to indicate, at the
644 680
         beginning of a command's help text, that this command is
645 681
         deprecated.
646 682
         """,
683
+    )(
647 684
         context='Label :: Help text :: Marker',
648
-        msg='(Deprecated) {text}',
685
+        single='(Deprecated) {text}',
649 686
         flags='python-brace-format',
650 687
     )
651
-    DEBUG_OPTION_HELP_TEXT = _prepare_translatable(
688
+    DEBUG_OPTION_HELP_TEXT = _Commented(
652 689
         comments='',
690
+    )(
653 691
         context='Label :: Help text :: One-line description',
654
-        msg='also emit debug information (implies --verbose)',
692
+        single='also emit debug information (implies --verbose)',
655 693
     )
656
-    EXPORT_VAULT_FORMAT_HELP_TEXT = _prepare_translatable(
694
+    EXPORT_VAULT_FORMAT_HELP_TEXT = _Commented(
657 695
         comments=r"""
658 696
         TRANSLATORS: The defaults_hint is
659 697
         Label.EXPORT_VAULT_FORMAT_DEFAULTS_HELP_TEXT, the metavar is
660 698
         Label.EXPORT_VAULT_FORMAT_METAVAR_FMT.
661 699
         """,
700
+    )(
662 701
         context='Label :: Help text :: One-line description',
663
-        msg=r"""
702
+        single=r"""
664 703
         try the following storage format {metavar!s}; may be
665 704
         specified multiple times, formats will be tried in order
666 705
         {defaults_hint!s}
667 706
         """,
668 707
         flags='python-brace-format',
669 708
     )
670
-    EXPORT_VAULT_FORMAT_DEFAULTS_HELP_TEXT = _prepare_translatable(
709
+    EXPORT_VAULT_FORMAT_DEFAULTS_HELP_TEXT = _Commented(
671 710
         comments=r"""
672 711
         TRANSLATORS: See EXPORT_VAULT_FORMAT_HELP_TEXT.  The format
673 712
         names/labels "v0.3", "v0.2" and "storeroom" should not be
674 713
         translated.
675 714
         """,
715
+    )(
676 716
         context='Label :: Help text :: One-line description',
677
-        msg=r"""
717
+        single=r"""
678 718
         (default: v0.3, v0.2, storeroom)
679 719
         """,
680 720
     )
681
-    EXPORT_VAULT_KEY_HELP_TEXT = _prepare_translatable(
721
+    EXPORT_VAULT_KEY_HELP_TEXT = _Commented(
682 722
         comments=r"""
683 723
         TRANSLATORS: The defaults_hint is
684 724
         Label.EXPORT_VAULT_KEY_DEFAULTS_HELP_TEXT, the metavar is
685 725
         Label.EXPORT_VAULT_KEY_METAVAR_K.
686 726
         """,
727
+    )(
687 728
         context='Label :: Help text :: One-line description',
688
-        msg=r"""
729
+        single=r"""
689 730
         use {metavar!s} as the storage master key {defaults_hint!s}
690 731
         """,
691 732
         flags='python-brace-format',
692 733
     )
693
-    EXPORT_VAULT_KEY_DEFAULTS_HELP_TEXT = _prepare_translatable(
734
+    EXPORT_VAULT_KEY_DEFAULTS_HELP_TEXT = _Commented(
694 735
         comments=r"""
695 736
         TRANSLATORS: See EXPORT_VAULT_KEY_HELP_TEXT.
696 737
         """,
738
+    )(
697 739
         context='Label :: Help text :: One-line description',
698
-        msg=r"""
740
+        single=r"""
699 741
         (default: check the `VAULT_KEY`, `LOGNAME`, `USER`, or
700 742
         `USERNAME` environment variables)
701 743
         """,
702 744
     )
703
-    HELP_OPTION_HELP_TEXT = _prepare_translatable(
745
+    HELP_OPTION_HELP_TEXT = _Commented(
704 746
         comments='',
747
+    )(
705 748
         context='Label :: Help text :: One-line description',
706
-        msg='show this help text, then exit',
749
+        single='show this help text, then exit',
707 750
     )
708
-    QUIET_OPTION_HELP_TEXT = _prepare_translatable(
751
+    QUIET_OPTION_HELP_TEXT = _Commented(
709 752
         comments='',
753
+    )(
710 754
         context='Label :: Help text :: One-line description',
711
-        msg='suppress even warnings, emit only errors',
755
+        single='suppress even warnings, emit only errors',
712 756
     )
713
-    VERBOSE_OPTION_HELP_TEXT = _prepare_translatable(
757
+    VERBOSE_OPTION_HELP_TEXT = _Commented(
714 758
         comments='',
759
+    )(
715 760
         context='Label :: Help text :: One-line description',
716
-        msg='emit extra/progress information to standard error',
761
+        single='emit extra/progress information to standard error',
717 762
     )
718
-    VERSION_OPTION_HELP_TEXT = _prepare_translatable(
763
+    VERSION_OPTION_HELP_TEXT = _Commented(
719 764
         comments='',
765
+    )(
720 766
         context='Label :: Help text :: One-line description',
721
-        msg='show applicable version information, then exit',
767
+        single='show applicable version information, then exit',
722 768
     )
723 769
 
724
-    DERIVEPASSPHRASE_VAULT_PHRASE_HELP_TEXT = _prepare_translatable(
770
+    DERIVEPASSPHRASE_VAULT_PHRASE_HELP_TEXT = _Commented(
725 771
         comments='',
772
+    )(
726 773
         context='Label :: Help text :: One-line description',
727
-        msg='prompt for a master passphrase',
774
+        single='prompt for a master passphrase',
728 775
     )
729
-    DERIVEPASSPHRASE_VAULT_KEY_HELP_TEXT = _prepare_translatable(
776
+    DERIVEPASSPHRASE_VAULT_KEY_HELP_TEXT = _Commented(
730 777
         comments='',
778
+    )(
731 779
         context='Label :: Help text :: One-line description',
732
-        msg='select a suitable SSH key from the SSH agent',
780
+        single='select a suitable SSH key from the SSH agent',
733 781
     )
734
-    DERIVEPASSPHRASE_VAULT_LENGTH_HELP_TEXT = _prepare_translatable(
782
+    DERIVEPASSPHRASE_VAULT_LENGTH_HELP_TEXT = _Commented(
735 783
         comments=r"""
736 784
         TRANSLATORS: The metavar is
737 785
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
738 786
         """,
787
+    )(
739 788
         context='Label :: Help text :: One-line description',
740
-        msg='ensure a passphrase length of {metavar!s} characters',
789
+        single='ensure a passphrase length of {metavar!s} characters',
741 790
         flags='python-brace-format',
742 791
     )
743
-    DERIVEPASSPHRASE_VAULT_REPEAT_HELP_TEXT = _prepare_translatable(
792
+    DERIVEPASSPHRASE_VAULT_REPEAT_HELP_TEXT = _Commented(
744 793
         comments=r"""
745 794
         TRANSLATORS: The metavar is
746 795
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
747 796
         """,
797
+    )(
748 798
         context='Label :: Help text :: One-line description',
749
-        msg='forbid any run of {metavar!s} identical characters',
799
+        single='forbid any run of {metavar!s} identical characters',
750 800
         flags='python-brace-format',
751 801
     )
752
-    DERIVEPASSPHRASE_VAULT_LOWER_HELP_TEXT = _prepare_translatable(
802
+    DERIVEPASSPHRASE_VAULT_LOWER_HELP_TEXT = _Commented(
753 803
         comments=r"""
754 804
         TRANSLATORS: The metavar is
755 805
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
756 806
         """,
807
+    )(
757 808
         context='Label :: Help text :: One-line description',
758
-        msg='ensure at least {metavar!s} lowercase characters',
809
+        single='ensure at least {metavar!s} lowercase characters',
759 810
         flags='python-brace-format',
760 811
     )
761
-    DERIVEPASSPHRASE_VAULT_UPPER_HELP_TEXT = _prepare_translatable(
812
+    DERIVEPASSPHRASE_VAULT_UPPER_HELP_TEXT = _Commented(
762 813
         comments=r"""
763 814
         TRANSLATORS: The metavar is
764 815
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
765 816
         """,
817
+    )(
766 818
         context='Label :: Help text :: One-line description',
767
-        msg='ensure at least {metavar!s} uppercase characters',
819
+        single='ensure at least {metavar!s} uppercase characters',
768 820
         flags='python-brace-format',
769 821
     )
770
-    DERIVEPASSPHRASE_VAULT_NUMBER_HELP_TEXT = _prepare_translatable(
822
+    DERIVEPASSPHRASE_VAULT_NUMBER_HELP_TEXT = _Commented(
771 823
         comments=r"""
772 824
         TRANSLATORS: The metavar is
773 825
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
774 826
         """,
827
+    )(
775 828
         context='Label :: Help text :: One-line description',
776
-        msg='ensure at least {metavar!s} digits',
829
+        single='ensure at least {metavar!s} digits',
777 830
         flags='python-brace-format',
778 831
     )
779
-    DERIVEPASSPHRASE_VAULT_SPACE_HELP_TEXT = _prepare_translatable(
832
+    DERIVEPASSPHRASE_VAULT_SPACE_HELP_TEXT = _Commented(
780 833
         comments=r"""
781 834
         TRANSLATORS: The metavar is
782 835
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
783 836
         """,
837
+    )(
784 838
         context='Label :: Help text :: One-line description',
785
-        msg='ensure at least {metavar!s} spaces',
839
+        single='ensure at least {metavar!s} spaces',
786 840
         flags='python-brace-format',
787 841
     )
788
-    DERIVEPASSPHRASE_VAULT_DASH_HELP_TEXT = _prepare_translatable(
842
+    DERIVEPASSPHRASE_VAULT_DASH_HELP_TEXT = _Commented(
789 843
         comments=r"""
790 844
         TRANSLATORS: The metavar is
791 845
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
792 846
         """,
847
+    )(
793 848
         context='Label :: Help text :: One-line description',
794
-        msg='ensure at least {metavar!s} "-" or "_" characters',
849
+        single='ensure at least {metavar!s} "-" or "_" characters',
795 850
         flags='python-brace-format',
796 851
     )
797
-    DERIVEPASSPHRASE_VAULT_SYMBOL_HELP_TEXT = _prepare_translatable(
852
+    DERIVEPASSPHRASE_VAULT_SYMBOL_HELP_TEXT = _Commented(
798 853
         comments=r"""
799 854
         TRANSLATORS: The metavar is
800 855
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
801 856
         """,
857
+    )(
802 858
         context='Label :: Help text :: One-line description',
803
-        msg='ensure at least {metavar!s} symbol characters',
859
+        single='ensure at least {metavar!s} symbol characters',
804 860
         flags='python-brace-format',
805 861
     )
806 862
 
807
-    DERIVEPASSPHRASE_VAULT_NOTES_HELP_TEXT = _prepare_translatable(
863
+    DERIVEPASSPHRASE_VAULT_NOTES_HELP_TEXT = _Commented(
808 864
         comments='',
865
+    )(
809 866
         context='Label :: Help text :: One-line description',
810
-        msg='spawn an editor to edit notes for {service_metavar!s}',
867
+        single='spawn an editor to edit notes for {service_metavar!s}',
811 868
         flags='python-brace-format',
812 869
     )
813
-    DERIVEPASSPHRASE_VAULT_CONFIG_HELP_TEXT = _prepare_translatable(
870
+    DERIVEPASSPHRASE_VAULT_CONFIG_HELP_TEXT = _Commented(
814 871
         comments='',
872
+    )(
815 873
         context='Label :: Help text :: One-line description',
816
-        msg='save the given settings for {service_metavar!s}, or global',
874
+        single='save the given settings for {service_metavar!s}, or global',
817 875
         flags='python-brace-format',
818 876
     )
819
-    DERIVEPASSPHRASE_VAULT_DELETE_HELP_TEXT = _prepare_translatable(
877
+    DERIVEPASSPHRASE_VAULT_DELETE_HELP_TEXT = _Commented(
820 878
         comments='',
879
+    )(
821 880
         context='Label :: Help text :: One-line description',
822
-        msg='delete the settings for {service_metavar!s}',
881
+        single='delete the settings for {service_metavar!s}',
823 882
         flags='python-brace-format',
824 883
     )
825
-    DERIVEPASSPHRASE_VAULT_DELETE_GLOBALS_HELP_TEXT = _prepare_translatable(
884
+    DERIVEPASSPHRASE_VAULT_DELETE_GLOBALS_HELP_TEXT = _Commented(
826 885
         comments='',
886
+    )(
827 887
         context='Label :: Help text :: One-line description',
828
-        msg='delete the global settings',
888
+        single='delete the global settings',
829 889
     )
830
-    DERIVEPASSPHRASE_VAULT_DELETE_ALL_HELP_TEXT = _prepare_translatable(
890
+    DERIVEPASSPHRASE_VAULT_DELETE_ALL_HELP_TEXT = _Commented(
831 891
         comments='',
892
+    )(
832 893
         context='Label :: Help text :: One-line description',
833
-        msg='delete all settings',
894
+        single='delete all settings',
834 895
     )
835
-    DERIVEPASSPHRASE_VAULT_EXPORT_HELP_TEXT = _prepare_translatable(
896
+    DERIVEPASSPHRASE_VAULT_EXPORT_HELP_TEXT = _Commented(
836 897
         comments="""
837 898
         TRANSLATORS: The metavar is
838 899
         Label.STORAGE_MANAGEMENT_METAVAR_SERVICE.
839 900
         """,
901
+    )(
840 902
         context='Label :: Help text :: One-line description',
841
-        msg='export all saved settings to {metavar!s}',
903
+        single='export all saved settings to {metavar!s}',
842 904
         flags='python-brace-format',
843 905
     )
844
-    DERIVEPASSPHRASE_VAULT_IMPORT_HELP_TEXT = _prepare_translatable(
906
+    DERIVEPASSPHRASE_VAULT_IMPORT_HELP_TEXT = _Commented(
845 907
         comments="""
846 908
         TRANSLATORS: The metavar is
847 909
         Label.STORAGE_MANAGEMENT_METAVAR_SERVICE.
848 910
         """,
911
+    )(
849 912
         context='Label :: Help text :: One-line description',
850
-        msg='import saved settings from {metavar!s}',
913
+        single='import saved settings from {metavar!s}',
851 914
         flags='python-brace-format',
852 915
     )
853
-    DERIVEPASSPHRASE_VAULT_OVERWRITE_HELP_TEXT = _prepare_translatable(
916
+    DERIVEPASSPHRASE_VAULT_OVERWRITE_HELP_TEXT = _Commented(
854 917
         comments="""
855 918
         TRANSLATORS: The corresponding option is displayed as
856 919
         "--overwrite-existing / --merge-existing", so you may want to
857 920
         hint that the default (merge) is the second of those options.
858 921
         """,
922
+    )(
859 923
         context='Label :: Help text :: One-line description',
860
-        msg='overwrite or merge (default) the existing configuration',
924
+        single='overwrite or merge (default) the existing configuration',
861 925
     )
862
-    DERIVEPASSPHRASE_VAULT_UNSET_HELP_TEXT = _prepare_translatable(
926
+    DERIVEPASSPHRASE_VAULT_UNSET_HELP_TEXT = _Commented(
863 927
         comments="""
864 928
         TRANSLATORS: The corresponding option is displayed as
865 929
         "--unset=phrase|key|...|symbol", so the "given setting" is
... ...
@@ -867,169 +931,192 @@ class Label(enum.Enum):
867 931
         respectively.  "with --config" here means that the user must
868 932
         also specify "--config" for this option to have any effect.
869 933
         """,
934
+    )(
870 935
         context='Label :: Help text :: One-line description',
871
-        msg="""
936
+        single="""
872 937
         with --config, also unsets the given setting; may be specified
873 938
         multiple times
874 939
         """,
875 940
     )
876
-    DERIVEPASSPHRASE_VAULT_EXPORT_AS_HELP_TEXT = _prepare_translatable(
941
+    DERIVEPASSPHRASE_VAULT_EXPORT_AS_HELP_TEXT = _Commented(
877 942
         comments="""
878 943
         TRANSLATORS: The corresponding option is displayed as
879 944
         "--export-as=json|sh", so json refers to the JSON format
880 945
         (default) and sh refers to the POSIX sh format.
881 946
         """,
947
+    )(
882 948
         context='Label :: Help text :: One-line description',
883
-        msg='when exporting, export as JSON (default) or POSIX sh',
949
+        single='when exporting, export as JSON (default) or POSIX sh',
884 950
     )
885 951
 
886
-    EXPORT_VAULT_FORMAT_METAVAR_FMT = _prepare_translatable(
952
+    EXPORT_VAULT_FORMAT_METAVAR_FMT = _Commented(
887 953
         comments='',
954
+    )(
888 955
         context='Label :: Help text :: Metavar :: export vault',
889
-        msg='FMT',
956
+        single='FMT',
890 957
     )
891
-    EXPORT_VAULT_KEY_METAVAR_K = _prepare_translatable(
958
+    EXPORT_VAULT_KEY_METAVAR_K = _Commented(
892 959
         comments=r"""
893 960
         TRANSLATORS: See Label.EXPORT_VAULT_KEY_HELP_TEXT.
894 961
         """,
962
+    )(
895 963
         context='Label :: Help text :: Metavar :: export vault',
896
-        msg='K',
964
+        single='K',
897 965
     )
898
-    EXPORT_VAULT_METAVAR_PATH = _prepare_translatable(
966
+    EXPORT_VAULT_METAVAR_PATH = _Commented(
899 967
         comments=r"""
900 968
         TRANSLATORS: Used as "path_metavar" in
901 969
         Label.DERIVEPASSPHRASE_EXPORT_VAULT_02 and others.
902 970
         """,
971
+    )(
903 972
         context='Label :: Help text :: Metavar :: export vault',
904
-        msg='PATH',
973
+        single='PATH',
905 974
     )
906
-    PASSPHRASE_GENERATION_METAVAR_NUMBER = _prepare_translatable(
975
+    PASSPHRASE_GENERATION_METAVAR_NUMBER = _Commented(
907 976
         comments=r"""
908 977
         TRANSLATORS: This metavar is also used in a matching epilog.
909 978
         """,
979
+    )(
910 980
         context='Label :: Help text :: Metavar :: vault',
911
-        msg='NUMBER',
981
+        single='NUMBER',
912 982
     )
913
-    STORAGE_MANAGEMENT_METAVAR_PATH = _prepare_translatable(
983
+    STORAGE_MANAGEMENT_METAVAR_PATH = _Commented(
914 984
         comments=r"""
915 985
         TRANSLATORS: This metavar is also used in multiple one-line help
916 986
         texts.
917 987
         """,
988
+    )(
918 989
         context='Label :: Help text :: Metavar :: vault',
919
-        msg='PATH',
990
+        single='PATH',
920 991
     )
921
-    VAULT_METAVAR_SERVICE = _prepare_translatable(
992
+    VAULT_METAVAR_SERVICE = _Commented(
922 993
         comments=r"""
923 994
         TRANSLATORS: This metavar is also used in multiple one-line help
924 995
         texts, as "service_metavar".
925 996
         """,
997
+    )(
926 998
         context='Label :: Help text :: Metavar :: vault',
927
-        msg='SERVICE',
999
+        single='SERVICE',
928 1000
     )
929
-    CONFIGURATION_EPILOG = _prepare_translatable(
1001
+    CONFIGURATION_EPILOG = _Commented(
930 1002
         comments='',
1003
+    )(
931 1004
         context='Label :: Help text :: Explanation',
932
-        msg='Use $VISUAL or $EDITOR to configure the spawned editor.',
1005
+        single='Use $VISUAL or $EDITOR to configure the spawned editor.',
933 1006
     )
934
-    PASSPHRASE_GENERATION_EPILOG = _prepare_translatable(
1007
+    PASSPHRASE_GENERATION_EPILOG = _Commented(
935 1008
         comments=r"""
936 1009
         TRANSLATORS: The metavar is
937 1010
         Label.PASSPHRASE_GENERATION_METAVAR_NUMBER.
938 1011
         """,
1012
+    )(
939 1013
         context='Label :: Help text :: Explanation',
940
-        msg=r"""
1014
+        single=r"""
941 1015
         Use {metavar!s}=0 to exclude a character type from the output.
942 1016
         """,
943 1017
         flags='python-brace-format',
944 1018
     )
945
-    STORAGE_MANAGEMENT_EPILOG = _prepare_translatable(
1019
+    STORAGE_MANAGEMENT_EPILOG = _Commented(
946 1020
         comments=r"""
947 1021
         TRANSLATORS: The metavar is
948 1022
         Label.STORAGE_MANAGEMENT_METAVAR_PATH.
949 1023
         """,
1024
+    )(
950 1025
         context='Label :: Help text :: Explanation',
951
-        msg=r"""
1026
+        single=r"""
952 1027
         Using "-" as {metavar!s} for standard input/standard output
953 1028
         is supported.
954 1029
         """,
955 1030
         flags='python-brace-format',
956 1031
     )
957
-    COMMANDS_LABEL = _prepare_translatable(
1032
+    COMMANDS_LABEL = _Commented(
958 1033
         comments='',
1034
+    )(
959 1035
         context='Label :: Help text :: Option group name',
960
-        msg='Commands',
1036
+        single='Commands',
961 1037
     )
962
-    COMPATIBILITY_OPTION_LABEL = _prepare_translatable(
1038
+    COMPATIBILITY_OPTION_LABEL = _Commented(
963 1039
         comments='',
1040
+    )(
964 1041
         context='Label :: Help text :: Option group name',
965
-        msg='Compatibility and extension options',
1042
+        single='Compatibility and extension options',
966 1043
     )
967
-    CONFIGURATION_LABEL = _prepare_translatable(
1044
+    CONFIGURATION_LABEL = _Commented(
968 1045
         comments='',
1046
+    )(
969 1047
         context='Label :: Help text :: Option group name',
970
-        msg='Configuration',
1048
+        single='Configuration',
971 1049
     )
972
-    LOGGING_LABEL = _prepare_translatable(
1050
+    LOGGING_LABEL = _Commented(
973 1051
         comments='',
1052
+    )(
974 1053
         context='Label :: Help text :: Option group name',
975
-        msg='Logging',
1054
+        single='Logging',
976 1055
     )
977
-    OPTIONS_LABEL = _prepare_translatable(
1056
+    OPTIONS_LABEL = _Commented(
978 1057
         comments='',
1058
+    )(
979 1059
         context='Label :: Help text :: Option group name',
980
-        msg='Options',
1060
+        single='Options',
981 1061
     )
982
-    OTHER_OPTIONS_LABEL = _prepare_translatable(
1062
+    OTHER_OPTIONS_LABEL = _Commented(
983 1063
         comments='',
1064
+    )(
984 1065
         context='Label :: Help text :: Option group name',
985
-        msg='Other options',
1066
+        single='Other options',
986 1067
     )
987
-    PASSPHRASE_GENERATION_LABEL = _prepare_translatable(
1068
+    PASSPHRASE_GENERATION_LABEL = _Commented(
988 1069
         comments='',
1070
+    )(
989 1071
         context='Label :: Help text :: Option group name',
990
-        msg='Passphrase generation',
1072
+        single='Passphrase generation',
991 1073
     )
992
-    STORAGE_MANAGEMENT_LABEL = _prepare_translatable(
1074
+    STORAGE_MANAGEMENT_LABEL = _Commented(
993 1075
         comments='',
1076
+    )(
994 1077
         context='Label :: Help text :: Option group name',
995
-        msg='Storage management',
1078
+        single='Storage management',
996 1079
     )
997
-    VERSION_INFO_TEXT = _prepare_translatable(
998
-        msg=r"""
999
-        {PROG_NAME!s} {__version__}
1000
-        """,  # noqa: RUF027
1080
+    VERSION_INFO_TEXT = _Commented(
1001 1081
         comments='',
1082
+    )(
1002 1083
         context='Label :: Info Message',
1084
+        single=r"""
1085
+        {PROG_NAME!s} {__version__}
1086
+        """,  # noqa: RUF027
1003 1087
         flags='python-brace-format',
1004 1088
     )
1005
-    CONFIRM_THIS_CHOICE_PROMPT_TEXT = _prepare_translatable(
1089
+    CONFIRM_THIS_CHOICE_PROMPT_TEXT = _Commented(
1006 1090
         comments=r"""
1007 1091
         TRANSLATORS: There is no support for "yes" or "no" in other
1008 1092
         languages than English, so it is advised that your translation
1009 1093
         makes it clear that only the strings "y", "yes", "n" or "no" are
1010 1094
         supported, even if the prompt becomes a bit longer.
1011 1095
         """,
1096
+    )(
1012 1097
         context='Label :: Interactive prompt',
1013
-        msg='Confirm this choice? (y/N)',
1098
+        single='Confirm this choice? (y/N)',
1014 1099
     )
1015
-    SUITABLE_SSH_KEYS_LABEL = _prepare_translatable(
1100
+    SUITABLE_SSH_KEYS_LABEL = _Commented(
1016 1101
         comments=r"""
1017 1102
         TRANSLATORS: This label is the heading of the list of suitable
1018 1103
         SSH keys.
1019 1104
         """,
1105
+    )(
1020 1106
         context='Label :: Interactive prompt',
1021
-        msg='Suitable SSH keys:',
1107
+        single='Suitable SSH keys:',
1022 1108
     )
1023
-    YOUR_SELECTION_PROMPT_TEXT = _prepare_translatable(
1109
+    YOUR_SELECTION_PROMPT_TEXT = _Commented(
1024 1110
         comments='',
1111
+    )(
1025 1112
         context='Label :: Interactive prompt',
1026
-        msg='Your selection? (1-{n}, leave empty to abort)',
1113
+        single='Your selection? (1-{n}, leave empty to abort)',
1027 1114
         flags='python-brace-format',
1028 1115
     )
1029 1116
 
1030 1117
 
1031 1118
 class DebugMsgTemplate(enum.Enum):
1032
-    BUCKET_ITEM_FOUND = _prepare_translatable(
1119
+    BUCKET_ITEM_FOUND = _Commented(
1033 1120
         comments=r"""
1034 1121
         TRANSLATORS: This message is emitted by the vault configuration
1035 1122
         exporter for "storeroom"-type configuration directories.  The
... ...
@@ -1038,19 +1125,21 @@ class DebugMsgTemplate(enum.Enum):
1038 1125
         after decrypting the whole bucket.  (We ensure the path and
1039 1126
         value are printable as-is.)
1040 1127
         """,
1128
+    )(
1041 1129
         context='Debug message',
1042
-        msg='Found bucket item: {path} -> {value}',
1130
+        single='Found bucket item: {path} -> {value}',
1043 1131
         flags='python-brace-format',
1044 1132
     )
1045
-    DECRYPT_BUCKET_ITEM_INFO = _prepare_translatable(
1133
+    DECRYPT_BUCKET_ITEM_INFO = _Commented(
1046 1134
         comments=r"""
1047 1135
         TRANSLATORS: "AES256-CBC" and "PKCS#7" are, in essence, names of
1048 1136
         formats, and should not be translated.  "IV" means
1049 1137
         "initialization vector", and is specifically a cryptographic
1050 1138
         term, as are "plaintext" and "ciphertext".
1051 1139
         """,
1140
+    )(
1052 1141
         context='Debug message',
1053
-        msg="""
1142
+        single="""
1054 1143
         Decrypt bucket item contents:
1055 1144
 
1056 1145
           \b
... ...
@@ -1062,10 +1151,11 @@ class DebugMsgTemplate(enum.Enum):
1062 1151
         """,
1063 1152
         flags='python-brace-format',
1064 1153
     )
1065
-    DECRYPT_BUCKET_ITEM_KEY_INFO = _prepare_translatable(
1154
+    DECRYPT_BUCKET_ITEM_KEY_INFO = _Commented(
1066 1155
         comments='',
1156
+    )(
1067 1157
         context='Debug message',
1068
-        msg="""
1158
+        single="""
1069 1159
         Decrypt bucket item:
1070 1160
 
1071 1161
           \b
... ...
@@ -1075,7 +1165,7 @@ class DebugMsgTemplate(enum.Enum):
1075 1165
         """,
1076 1166
         flags='python-brace-format',
1077 1167
     )
1078
-    DECRYPT_BUCKET_ITEM_MAC_INFO = _prepare_translatable(
1168
+    DECRYPT_BUCKET_ITEM_MAC_INFO = _Commented(
1079 1169
         comments=r"""
1080 1170
         TRANSLATORS: The MAC stands for "message authentication code",
1081 1171
         which guarantees the authenticity of the message to anyone who
... ...
@@ -1085,8 +1175,9 @@ class DebugMsgTemplate(enum.Enum):
1085 1175
         asking for debug output, after all.  Please use your judgement
1086 1176
         as to whether to translate this term or not, expanded or not.
1087 1177
         """,
1178
+    )(
1088 1179
         context='Debug message',
1089
-        msg="""
1180
+        single="""
1090 1181
         Decrypt bucket item contents:
1091 1182
 
1092 1183
           \b
... ...
@@ -1097,15 +1188,16 @@ class DebugMsgTemplate(enum.Enum):
1097 1188
         """,
1098 1189
         flags='python-brace-format',
1099 1190
     )
1100
-    DECRYPT_BUCKET_ITEM_SESSION_KEYS_INFO = _prepare_translatable(
1191
+    DECRYPT_BUCKET_ITEM_SESSION_KEYS_INFO = _Commented(
1101 1192
         comments=r"""
1102 1193
         TRANSLATORS: "AES256-CBC" and "PKCS#7" are, in essence, names of
1103 1194
         formats, and should not be translated.  "IV" means
1104 1195
         "initialization vector", and is specifically a cryptographic
1105 1196
         term, as are "plaintext" and "ciphertext".
1106 1197
         """,
1198
+    )(
1107 1199
         context='Debug message',
1108
-        msg="""
1200
+        single="""
1109 1201
         Decrypt bucket item session keys:
1110 1202
 
1111 1203
           \b
... ...
@@ -1118,7 +1210,7 @@ class DebugMsgTemplate(enum.Enum):
1118 1210
         """,
1119 1211
         flags='python-brace-format',
1120 1212
     )
1121
-    DECRYPT_BUCKET_ITEM_SESSION_KEYS_MAC_INFO = _prepare_translatable(
1213
+    DECRYPT_BUCKET_ITEM_SESSION_KEYS_MAC_INFO = _Commented(
1122 1214
         comments=r"""
1123 1215
         TRANSLATORS: The MAC stands for "message authentication code",
1124 1216
         which guarantees the authenticity of the message to anyone who
... ...
@@ -1128,8 +1220,9 @@ class DebugMsgTemplate(enum.Enum):
1128 1220
         asking for debug output, after all.  Please use your judgement
1129 1221
         as to whether to translate this term or not, expanded or not.
1130 1222
         """,
1223
+    )(
1131 1224
         context='Debug message',
1132
-        msg="""
1225
+        single="""
1133 1226
         Decrypt bucket item session keys:
1134 1227
 
1135 1228
           \b
... ...
@@ -1140,10 +1233,11 @@ class DebugMsgTemplate(enum.Enum):
1140 1233
         """,
1141 1234
         flags='python-brace-format',
1142 1235
     )
1143
-    DERIVED_MASTER_KEYS_KEYS = _prepare_translatable(
1236
+    DERIVED_MASTER_KEYS_KEYS = _Commented(
1144 1237
         comments='',
1238
+    )(
1145 1239
         context='Debug message',
1146
-        msg="""
1240
+        single="""
1147 1241
         Derived master keys' keys:
1148 1242
 
1149 1243
           \b
... ...
@@ -1155,7 +1249,7 @@ class DebugMsgTemplate(enum.Enum):
1155 1249
         """,  # noqa: E501
1156 1250
         flags='python-brace-format',
1157 1251
     )
1158
-    DIRECTORY_CONTENTS_CHECK_OK = _prepare_translatable(
1252
+    DIRECTORY_CONTENTS_CHECK_OK = _Commented(
1159 1253
         comments=r"""
1160 1254
         TRANSLATORS: This message is emitted by the vault configuration
1161 1255
         exporter for "storeroom"-type configuration directories, while
... ...
@@ -1166,11 +1260,12 @@ class DebugMsgTemplate(enum.Enum):
1166 1260
         actually confirm the claim.  (We would have already thrown an
1167 1261
         error here otherwise.)
1168 1262
         """,
1263
+    )(
1169 1264
         context='Debug message',
1170
-        msg='Directory contents check OK: {path} -> {contents}',
1265
+        single='Directory contents check OK: {path} -> {contents}',
1171 1266
         flags='python-brace-format',
1172 1267
     )
1173
-    MASTER_KEYS_DATA_MAC_INFO = _prepare_translatable(
1268
+    MASTER_KEYS_DATA_MAC_INFO = _Commented(
1174 1269
         comments=r"""
1175 1270
         TRANSLATORS: The MAC stands for "message authentication code",
1176 1271
         which guarantees the authenticity of the message to anyone who
... ...
@@ -1180,8 +1275,9 @@ class DebugMsgTemplate(enum.Enum):
1180 1275
         asking for debug output, after all.  Please use your judgement
1181 1276
         as to whether to translate this term or not, expanded or not.
1182 1277
         """,
1278
+    )(
1183 1279
         context='Debug message',
1184
-        msg="""
1280
+        single="""
1185 1281
         Master keys data:
1186 1282
 
1187 1283
           \b
... ...
@@ -1192,7 +1288,7 @@ class DebugMsgTemplate(enum.Enum):
1192 1288
         """,
1193 1289
         flags='python-brace-format',
1194 1290
     )
1195
-    POSTPONING_DIRECTORY_CONTENTS_CHECK = _prepare_translatable(
1291
+    POSTPONING_DIRECTORY_CONTENTS_CHECK = _Commented(
1196 1292
         comments=r"""
1197 1293
         TRANSLATORS: This message is emitted by the vault configuration
1198 1294
         exporter for "storeroom"-type configuration directories, while
... ...
@@ -1203,11 +1299,12 @@ class DebugMsgTemplate(enum.Enum):
1203 1299
         message, we merely indicate that we saved the "claimed" list for
1204 1300
         this directory for later.
1205 1301
         """,
1302
+    )(
1206 1303
         context='Debug message',
1207
-        msg='Postponing directory contents check: {path} -> {contents}',
1304
+        single='Postponing directory contents check: {path} -> {contents}',
1208 1305
         flags='python-brace-format',
1209 1306
     )
1210
-    SETTING_CONFIG_STRUCTURE_CONTENTS = _prepare_translatable(
1307
+    SETTING_CONFIG_STRUCTURE_CONTENTS = _Commented(
1211 1308
         comments=r"""
1212 1309
         TRANSLATORS: This message is emitted by the vault configuration
1213 1310
         exporter for "storeroom"-type configuration directories, while
... ...
@@ -1215,11 +1312,12 @@ class DebugMsgTemplate(enum.Enum):
1215 1312
         the item's "path".  We confirm that we set the entry at the
1216 1313
         given path to the given value.
1217 1314
         """,
1315
+    )(
1218 1316
         context='Debug message',
1219
-        msg='Setting contents: {path} -> {value}',
1317
+        single='Setting contents: {path} -> {value}',
1220 1318
         flags='python-brace-format',
1221 1319
     )
1222
-    SETTING_CONFIG_STRUCTURE_CONTENTS_EMPTY_DIRECTORY = _prepare_translatable(
1320
+    SETTING_CONFIG_STRUCTURE_CONTENTS_EMPTY_DIRECTORY = _Commented(
1223 1321
         comments=r"""
1224 1322
         TRANSLATORS: This message is emitted by the vault configuration
1225 1323
         exporter for "storeroom"-type configuration directories, while
... ...
@@ -1227,11 +1325,12 @@ class DebugMsgTemplate(enum.Enum):
1227 1325
         the item's "path".  We confirm that we set up a currently empty
1228 1326
         directory at the given path.
1229 1327
         """,
1328
+    )(
1230 1329
         context='Debug message',
1231
-        msg='Setting contents (empty directory): {path}',
1330
+        single='Setting contents (empty directory): {path}',
1232 1331
         flags='python-brace-format',
1233 1332
     )
1234
-    VAULT_NATIVE_EVP_BYTESTOKEY_INIT = _prepare_translatable(
1333
+    VAULT_NATIVE_EVP_BYTESTOKEY_INIT = _Commented(
1235 1334
         comments=r"""
1236 1335
         TRANSLATORS: This message is emitted by the vault configuration
1237 1336
         exporter for "native"-type configuration directories: in v0.2,
... ...
@@ -1239,8 +1338,9 @@ class DebugMsgTemplate(enum.Enum):
1239 1338
         OpenSSL must be reimplemented from scratch.  The terms "salt"
1240 1339
         and "IV" (initialization vector) are cryptographic terms.
1241 1340
         """,
1341
+    )(
1242 1342
         context='Debug message',
1243
-        msg="""
1343
+        single="""
1244 1344
         evp_bytestokey_md5 (initialization):
1245 1345
 
1246 1346
           \b
... ...
@@ -1253,7 +1353,7 @@ class DebugMsgTemplate(enum.Enum):
1253 1353
         """,
1254 1354
         flags='python-brace-format',
1255 1355
     )
1256
-    VAULT_NATIVE_EVP_BYTESTOKEY_RESULT = _prepare_translatable(
1356
+    VAULT_NATIVE_EVP_BYTESTOKEY_RESULT = _Commented(
1257 1357
         comments=r"""
1258 1358
         TRANSLATORS: This message is emitted by the vault configuration
1259 1359
         exporter for "native"-type configuration directories: in v0.2,
... ...
@@ -1263,8 +1363,9 @@ class DebugMsgTemplate(enum.Enum):
1263 1363
         This function reports on the updated buffer length and contents
1264 1364
         after executing one round of hashing.
1265 1365
         """,
1366
+    )(
1266 1367
         context='Debug message',
1267
-        msg="""
1368
+        single="""
1268 1369
         evp_bytestokey_md5 (result):
1269 1370
 
1270 1371
           \b
... ...
@@ -1273,7 +1374,7 @@ class DebugMsgTemplate(enum.Enum):
1273 1374
         """,
1274 1375
         flags='python-brace-format',
1275 1376
     )
1276
-    VAULT_NATIVE_EVP_BYTESTOKEY_ROUND = _prepare_translatable(
1377
+    VAULT_NATIVE_EVP_BYTESTOKEY_ROUND = _Commented(
1277 1378
         comments=r"""
1278 1379
         TRANSLATORS: This message is emitted by the vault configuration
1279 1380
         exporter for "native"-type configuration directories: in v0.2,
... ...
@@ -1283,8 +1384,9 @@ class DebugMsgTemplate(enum.Enum):
1283 1384
         This function reports on the updated buffer length and contents
1284 1385
         after executing one round of hashing.
1285 1386
         """,
1387
+    )(
1286 1388
         context='Debug message',
1287
-        msg="""
1389
+        single="""
1288 1390
         evp_bytestokey_md5 (round update):
1289 1391
 
1290 1392
           \b
... ...
@@ -1293,7 +1395,7 @@ class DebugMsgTemplate(enum.Enum):
1293 1395
         """,
1294 1396
         flags='python-brace-format',
1295 1397
     )
1296
-    VAULT_NATIVE_CHECKING_MAC_DETAILS = _prepare_translatable(
1398
+    VAULT_NATIVE_CHECKING_MAC_DETAILS = _Commented(
1297 1399
         comments=r"""
1298 1400
         TRANSLATORS: This message is emitted by the vault configuration
1299 1401
         exporter for "native"-type configuration directories.  It is
... ...
@@ -1301,8 +1403,9 @@ class DebugMsgTemplate(enum.Enum):
1301 1403
         commentary there concerning the terms and thoughts on
1302 1404
         translating them.
1303 1405
         """,
1406
+    )(
1304 1407
         context='Debug message',
1305
-        msg="""
1408
+        single="""
1306 1409
         MAC details:
1307 1410
 
1308 1411
           \b
... ...
@@ -1311,17 +1414,18 @@ class DebugMsgTemplate(enum.Enum):
1311 1414
         """,
1312 1415
         flags='python-brace-format',
1313 1416
     )
1314
-    VAULT_NATIVE_PADDED_PLAINTEXT = _prepare_translatable(
1417
+    VAULT_NATIVE_PADDED_PLAINTEXT = _Commented(
1315 1418
         comments=r"""
1316 1419
         TRANSLATORS: This message is emitted by the vault configuration
1317 1420
         exporter for "native"-type configuration directories.  "padding"
1318 1421
         and "plaintext" are cryptographic terms.
1319 1422
         """,
1423
+    )(
1320 1424
         context='Debug message',
1321
-        msg='Padded plaintext: {contents}',
1425
+        single='Padded plaintext: {contents}',
1322 1426
         flags='python-brace-format',
1323 1427
     )
1324
-    VAULT_NATIVE_PARSE_BUFFER = _prepare_translatable(
1428
+    VAULT_NATIVE_PARSE_BUFFER = _Commented(
1325 1429
         comments=r"""
1326 1430
         TRANSLATORS: This message is emitted by the vault configuration
1327 1431
         exporter for "native"-type configuration directories.  It is
... ...
@@ -1329,8 +1433,9 @@ class DebugMsgTemplate(enum.Enum):
1329 1433
         commentary there concerning the terms and thoughts on
1330 1434
         translating them.
1331 1435
         """,
1436
+    )(
1332 1437
         context='Debug message',
1333
-        msg="""
1438
+        single="""
1334 1439
         Buffer: {contents}
1335 1440
 
1336 1441
           \b
... ...
@@ -1340,20 +1445,22 @@ class DebugMsgTemplate(enum.Enum):
1340 1445
         """,
1341 1446
         flags='python-brace-format',
1342 1447
     )
1343
-    VAULT_NATIVE_PLAINTEXT = _prepare_translatable(
1448
+    VAULT_NATIVE_PLAINTEXT = _Commented(
1344 1449
         comments=r"""
1345 1450
         TRANSLATORS: This message is emitted by the vault configuration
1346 1451
         exporter for "native"-type configuration directories.
1347 1452
         "plaintext" is a cryptographic term.
1348 1453
         """,
1454
+    )(
1349 1455
         context='Debug message',
1350
-        msg='Plaintext: {contents}',
1456
+        single='Plaintext: {contents}',
1351 1457
         flags='python-brace-format',
1352 1458
     )
1353
-    VAULT_NATIVE_PBKDF2_CALL = _prepare_translatable(
1459
+    VAULT_NATIVE_PBKDF2_CALL = _Commented(
1354 1460
         comments='',
1461
+    )(
1355 1462
         context='Debug message',
1356
-        msg="""
1463
+        single="""
1357 1464
         Master key derivation:
1358 1465
 
1359 1466
           \b
... ...
@@ -1363,7 +1470,7 @@ class DebugMsgTemplate(enum.Enum):
1363 1470
         """,  # noqa: E501
1364 1471
         flags='python-brace-format',
1365 1472
     )
1366
-    VAULT_NATIVE_V02_PAYLOAD_MAC_POSTPROCESSING = _prepare_translatable(
1473
+    VAULT_NATIVE_V02_PAYLOAD_MAC_POSTPROCESSING = _Commented(
1367 1474
         comments=r"""
1368 1475
         TRANSLATORS: This message is emitted by the vault configuration
1369 1476
         exporter for "native"-type configuration directories.  It is
... ...
@@ -1371,20 +1478,21 @@ class DebugMsgTemplate(enum.Enum):
1371 1478
         debug message PARSING_NATIVE_PARSE_BUFFER; see the commentary
1372 1479
         there concerning the terms and thoughts on translating them.
1373 1480
         """,
1374
-        msg="""
1481
+    )(
1482
+        context='Debug message',
1483
+        single="""
1375 1484
         Postprocessing buffer (v0.2):
1376 1485
 
1377 1486
           \b
1378 1487
           Payload: {payload} (decoded from base64)
1379 1488
           MAC: {mac} (decoded from hex)
1380 1489
         """,
1381
-        context='Debug message',
1382 1490
         flags='python-brace-format',
1383 1491
     )
1384 1492
 
1385 1493
 
1386 1494
 class InfoMsgTemplate(enum.Enum):
1387
-    ASSEMBLING_CONFIG_STRUCTURE = _prepare_translatable(
1495
+    ASSEMBLING_CONFIG_STRUCTURE = _Commented(
1388 1496
         comments=r"""
1389 1497
         TRANSLATORS: This message is emitted by the vault configuration
1390 1498
         exporter for "storeroom"-type configuration directories.  The
... ...
@@ -1395,20 +1503,22 @@ class InfoMsgTemplate(enum.Enum):
1395 1503
         existing directory tree to rely on, but rather must build it
1396 1504
         on-the-fly), hence the term "assembling".
1397 1505
         """,
1506
+    )(
1398 1507
         context='Info message',
1399
-        msg='Assembling config structure',
1508
+        single='Assembling config structure',
1400 1509
     )
1401
-    CANNOT_LOAD_AS_VAULT_CONFIG = _prepare_translatable(
1510
+    CANNOT_LOAD_AS_VAULT_CONFIG = _Commented(
1402 1511
         comments=r"""
1403 1512
         TRANSLATORS: "fmt" is a string such as "v0.2" or "storeroom",
1404 1513
         indicating the format which we tried to load the vault
1405 1514
         configuration as.
1406 1515
         """,
1516
+    )(
1407 1517
         context='Info message',
1408
-        msg='Cannot load {path!r} as a {fmt!s} vault configuration.',
1518
+        single='Cannot load {path!r} as a {fmt!s} vault configuration.',
1409 1519
         flags='python-brace-format',
1410 1520
     )
1411
-    CHECKING_CONFIG_STRUCTURE_CONSISTENCY = _prepare_translatable(
1521
+    CHECKING_CONFIG_STRUCTURE_CONSISTENCY = _Commented(
1412 1522
         comments=r"""
1413 1523
         TRANSLATORS: This message is emitted by the vault configuration
1414 1524
         exporter for "storeroom"-type configuration directories.  Having
... ...
@@ -1416,10 +1526,11 @@ class InfoMsgTemplate(enum.Enum):
1416 1526
         paths and contents, we then check if the assembled structure is
1417 1527
         internally consistent.
1418 1528
         """,
1529
+    )(
1419 1530
         context='Info message',
1420
-        msg='Checking config structure consistency',
1531
+        single='Checking config structure consistency',
1421 1532
     )
1422
-    DECRYPTING_BUCKET = _prepare_translatable(
1533
+    DECRYPTING_BUCKET = _Commented(
1423 1534
         comments=r"""
1424 1535
         TRANSLATORS: This message is emitted by the vault configuration
1425 1536
         exporter for "storeroom"-type configuration directories.  The
... ...
@@ -1428,21 +1539,23 @@ class InfoMsgTemplate(enum.Enum):
1428 1539
         numbered in hexadecimal, and typically there are 32 buckets, so
1429 1540
         2-digit hex numbers.
1430 1541
         """,
1542
+    )(
1431 1543
         context='Info message',
1432
-        msg='Decrypting bucket {bucket_number}',
1544
+        single='Decrypting bucket {bucket_number}',
1433 1545
         flags='python-brace-format',
1434 1546
     )
1435
-    PARSING_MASTER_KEYS_DATA = _prepare_translatable(
1547
+    PARSING_MASTER_KEYS_DATA = _Commented(
1436 1548
         comments=r"""
1437 1549
         TRANSLATORS: This message is emitted by the vault configuration
1438 1550
         exporter for "storeroom"-type configuration directories.
1439 1551
         `.keys` is a filename, from which data about the master keys for
1440 1552
         this configuration are loaded.
1441 1553
         """,
1554
+    )(
1442 1555
         context='Info message',
1443
-        msg='Parsing master keys data from .keys',
1556
+        single='Parsing master keys data from .keys',
1444 1557
     )
1445
-    PIP_INSTALL_EXTRA = _prepare_translatable(
1558
+    PIP_INSTALL_EXTRA = _Commented(
1446 1559
         comments=r"""
1447 1560
         TRANSLATORS: This message immediately follows an error message
1448 1561
         about a missing library that needs to be installed.  The Python
... ...
@@ -1452,35 +1565,42 @@ class InfoMsgTemplate(enum.Enum):
1452 1565
         would then let the installer take care of the missing libraries
1453 1566
         automatically, hence this suggestion to PyPI users.
1454 1567
         """,
1568
+    )(
1455 1569
         context='Info message',
1456
-        msg='(For users installing from PyPI, see the {extra_name!r} extra.)',
1570
+        single="""
1571
+        (For users installing from PyPI, see the {extra_name!r} extra.)
1572
+        """,
1457 1573
         flags='python-brace-format',
1458 1574
     )
1459
-    SUCCESSFULLY_MIGRATED = _prepare_translatable(
1575
+    SUCCESSFULLY_MIGRATED = _Commented(
1460 1576
         comments=r"""
1461 1577
         TRANSLATORS: This info message immediately follows the "Using
1462 1578
         deprecated v0.1-style ..." deprecation warning.
1463 1579
         """,
1580
+    )(
1464 1581
         context='Info message',
1465
-        msg='Successfully migrated to {path!r}.',
1582
+        single='Successfully migrated to {path!r}.',
1466 1583
         flags='python-brace-format',
1467 1584
     )
1468
-    VAULT_NATIVE_CHECKING_MAC = _prepare_translatable(
1585
+    VAULT_NATIVE_CHECKING_MAC = _Commented(
1469 1586
         comments='',
1587
+    )(
1470 1588
         context='Info message',
1471
-        msg='Checking MAC',
1589
+        single='Checking MAC',
1472 1590
     )
1473
-    VAULT_NATIVE_DECRYPTING_CONTENTS = _prepare_translatable(
1591
+    VAULT_NATIVE_DECRYPTING_CONTENTS = _Commented(
1474 1592
         comments='',
1593
+    )(
1475 1594
         context='Info message',
1476
-        msg='Decrypting contents',
1595
+        single='Decrypting contents',
1477 1596
     )
1478
-    VAULT_NATIVE_DERIVING_KEYS = _prepare_translatable(
1597
+    VAULT_NATIVE_DERIVING_KEYS = _Commented(
1479 1598
         comments='',
1599
+    )(
1480 1600
         context='Info message',
1481
-        msg='Deriving an encryption and signing key',
1601
+        single='Deriving an encryption and signing key',
1482 1602
     )
1483
-    VAULT_NATIVE_PARSING_IV_PAYLOAD_MAC = _prepare_translatable(
1603
+    VAULT_NATIVE_PARSING_IV_PAYLOAD_MAC = _Commented(
1484 1604
         comments=r"""
1485 1605
         TRANSLATORS: This message is emitted by the vault configuration
1486 1606
         exporter for "native"-type configuration directories.  "IV"
... ...
@@ -1492,26 +1612,29 @@ class InfoMsgTemplate(enum.Enum):
1492 1612
         all.  Please use your judgement as to whether to translate this
1493 1613
         term or not, expanded or not.
1494 1614
         """,
1615
+    )(
1495 1616
         context='Info message',
1496
-        msg='Parsing IV, payload and MAC from the file contents',
1617
+        single='Parsing IV, payload and MAC from the file contents',
1497 1618
     )
1498 1619
 
1499 1620
 
1500 1621
 class WarnMsgTemplate(enum.Enum):
1501
-    EMPTY_SERVICE_NOT_SUPPORTED = _prepare_translatable(
1622
+    EMPTY_SERVICE_NOT_SUPPORTED = _Commented(
1502 1623
         comments='',
1624
+    )(
1503 1625
         context='Warning message',
1504
-        msg="""
1626
+        single="""
1505 1627
         An empty {service_metavar!s} is not supported by vault(1).
1506 1628
         For compatibility, this will be treated as if SERVICE was not
1507 1629
         supplied, i.e., it will error out, or operate on global settings.
1508 1630
         """,
1509 1631
         flags='python-brace-format',
1510 1632
     )
1511
-    EMPTY_SERVICE_SETTINGS_INACCESSIBLE = _prepare_translatable(
1633
+    EMPTY_SERVICE_SETTINGS_INACCESSIBLE = _Commented(
1512 1634
         comments='',
1635
+    )(
1513 1636
         context='Warning message',
1514
-        msg="""
1637
+        single="""
1515 1638
         An empty {service_metavar!s} is not supported by vault(1).
1516 1639
         The empty-string service settings will be inaccessible and
1517 1640
         ineffective.  To ensure that vault(1) and {PROG_NAME!s} see the
... ...
@@ -1519,24 +1642,26 @@ class WarnMsgTemplate(enum.Enum):
1519 1642
         """,
1520 1643
         flags='python-brace-format',
1521 1644
     )
1522
-    FAILED_TO_MIGRATE_CONFIG = _prepare_translatable(
1645
+    FAILED_TO_MIGRATE_CONFIG = _Commented(
1523 1646
         comments=r"""
1524 1647
         TRANSLATORS: "error" is supplied by the operating system
1525 1648
         (errno/strerror).
1526 1649
         """,
1650
+    )(
1527 1651
         context='Warning message',
1528
-        msg='Failed to migrate to {path!r}: {error!s}: {filename!r}.',
1652
+        single='Failed to migrate to {path!r}: {error!s}: {filename!r}.',
1529 1653
         flags='python-brace-format',
1530 1654
     )
1531
-    GLOBAL_PASSPHRASE_INEFFECTIVE = _prepare_translatable(
1655
+    GLOBAL_PASSPHRASE_INEFFECTIVE = _Commented(
1532 1656
         comments='',
1657
+    )(
1533 1658
         context='Warning message',
1534
-        msg=r"""
1659
+        single=r"""
1535 1660
         Setting a global passphrase is ineffective
1536 1661
         because a key is also set.
1537 1662
         """,
1538 1663
     )
1539
-    PASSPHRASE_NOT_NORMALIZED = _prepare_translatable(
1664
+    PASSPHRASE_NOT_NORMALIZED = _Commented(
1540 1665
         comments=r"""
1541 1666
         TRANSLATORS: The key is a (vault) configuration key, in JSONPath
1542 1667
         syntax, typically "$.global" for the global passphrase or
... ...
@@ -1549,8 +1674,9 @@ class WarnMsgTemplate(enum.Enum):
1549 1674
         any other appropriate way to mark up emphasis of the word
1550 1675
         "displays".
1551 1676
         """,
1677
+    )(
1552 1678
         context='Warning message',
1553
-        msg=r"""
1679
+        single=r"""
1554 1680
         The {key!s} passphrase is not {form!s}-normalized.  Its
1555 1681
         serialization as a byte string may not be what you expect it to
1556 1682
         be, even if it *displays* correctly.  Please make sure to
... ...
@@ -1558,10 +1684,11 @@ class WarnMsgTemplate(enum.Enum):
1558 1684
         """,
1559 1685
         flags='python-brace-format',
1560 1686
     )
1561
-    SERVICE_NAME_INCOMPLETABLE = _prepare_translatable(
1687
+    SERVICE_NAME_INCOMPLETABLE = _Commented(
1562 1688
         comments='',
1689
+    )(
1563 1690
         context='Warning message',
1564
-        msg="""
1691
+        single="""
1565 1692
         The service name {service!r} contains an ASCII control
1566 1693
         character, which is not supported by our shell completion code.
1567 1694
         This service name will therefore not be available for completion
... ...
@@ -1571,49 +1698,56 @@ class WarnMsgTemplate(enum.Enum):
1571 1698
         """,
1572 1699
         flags='python-brace-format',
1573 1700
     )
1574
-    SERVICE_PASSPHRASE_INEFFECTIVE = _prepare_translatable(
1701
+    SERVICE_PASSPHRASE_INEFFECTIVE = _Commented(
1575 1702
         comments=r"""
1576 1703
         TRANSLATORS: The key that is set need not necessarily be set at
1577 1704
         the service level; it may be a global key as well.
1578 1705
         """,
1706
+    )(
1579 1707
         context='Warning message',
1580
-        msg=r"""
1708
+        single=r"""
1581 1709
         Setting a service passphrase is ineffective because a key is
1582 1710
         also set: {service!s}.
1583 1711
         """,
1584 1712
         flags='python-brace-format',
1585 1713
     )
1586
-    STEP_REMOVE_INEFFECTIVE_VALUE = _prepare_translatable(
1714
+    STEP_REMOVE_INEFFECTIVE_VALUE = _Commented(
1587 1715
         comments='',
1716
+    )(
1588 1717
         context='Warning message',
1589
-        msg='Removing ineffective setting {path!s} = {old!s}.',
1718
+        single='Removing ineffective setting {path!s} = {old!s}.',
1590 1719
         flags='python-brace-format',
1591 1720
     )
1592
-    STEP_REPLACE_INVALID_VALUE = _prepare_translatable(
1721
+    STEP_REPLACE_INVALID_VALUE = _Commented(
1593 1722
         comments='',
1723
+    )(
1594 1724
         context='Warning message',
1595
-        msg='Replacing invalid value {old!s} for key {path!s} with {new!s}.',
1725
+        single="""
1726
+        Replacing invalid value {old!s} for key {path!s} with {new!s}.
1727
+        """,
1596 1728
         flags='python-brace-format',
1597 1729
     )
1598
-    V01_STYLE_CONFIG = _prepare_translatable(
1730
+    V01_STYLE_CONFIG = _Commented(
1599 1731
         comments='',
1732
+    )(
1600 1733
         context='Warning message :: Deprecation',
1601
-        msg=r"""
1734
+        single=r"""
1602 1735
         Using deprecated v0.1-style config file {old!r}, instead of
1603 1736
         v0.2-style {new!r}.  Support for v0.1-style config filenames
1604 1737
         will be removed in v1.0.
1605 1738
         """,
1606 1739
         flags='python-brace-format',
1607 1740
     )
1608
-    V10_SUBCOMMAND_REQUIRED = _prepare_translatable(
1741
+    V10_SUBCOMMAND_REQUIRED = _Commented(
1609 1742
         comments=r"""
1610 1743
         TRANSLATORS: This deprecation warning may be issued at any
1611 1744
         level, i.e. we may actually be talking about subcommands, or
1612 1745
         sub-subcommands, or sub-sub-subcommands, etc., which is what the
1613 1746
         "here" is supposed to indicate.
1614 1747
         """,
1748
+    )(
1615 1749
         context='Warning message :: Deprecation',
1616
-        msg="""
1750
+        single="""
1617 1751
         A subcommand will be required here in v1.0.  See --help for
1618 1752
         available subcommands.  Defaulting to subcommand "vault".
1619 1753
         """,
... ...
@@ -1621,82 +1755,90 @@ class WarnMsgTemplate(enum.Enum):
1621 1755
 
1622 1756
 
1623 1757
 class ErrMsgTemplate(enum.Enum):
1624
-    AGENT_REFUSED_LIST_KEYS = _prepare_translatable(
1758
+    AGENT_REFUSED_LIST_KEYS = _Commented(
1625 1759
         comments=r"""
1626 1760
         TRANSLATORS: "loaded keys" being keys loaded into the agent.
1627 1761
         """,
1762
+    )(
1628 1763
         context='Error message',
1629
-        msg="""
1764
+        single="""
1630 1765
         The SSH agent failed to or refused to supply a list of loaded keys.
1631 1766
         """,
1632 1767
     )
1633
-    AGENT_REFUSED_SIGNATURE = _prepare_translatable(
1768
+    AGENT_REFUSED_SIGNATURE = _Commented(
1634 1769
         comments=r"""
1635 1770
         TRANSLATORS: The message to be signed is the vault UUID, but
1636 1771
         there's no space to explain that here, so ideally the error
1637 1772
         message does not go into detail.
1638 1773
         """,
1774
+    )(
1639 1775
         context='Error message',
1640
-        msg="""
1776
+        single="""
1641 1777
         The SSH agent failed to or refused to issue a signature with the
1642 1778
         selected key, necessary for deriving a service passphrase.
1643 1779
         """,
1644 1780
     )
1645
-    CANNOT_CONNECT_TO_AGENT = _prepare_translatable(
1781
+    CANNOT_CONNECT_TO_AGENT = _Commented(
1646 1782
         comments=r"""
1647 1783
         TRANSLATORS: "error" is supplied by the operating system
1648 1784
         (errno/strerror).
1649 1785
         """,
1786
+    )(
1650 1787
         context='Error message',
1651
-        msg='Cannot connect to the SSH agent: {error!s}: {filename!r}.',
1788
+        single='Cannot connect to the SSH agent: {error!s}: {filename!r}.',
1652 1789
         flags='python-brace-format',
1653 1790
     )
1654
-    CANNOT_DECODEIMPORT_VAULT_SETTINGS = _prepare_translatable(
1791
+    CANNOT_DECODEIMPORT_VAULT_SETTINGS = _Commented(
1655 1792
         comments=r"""
1656 1793
         TRANSLATORS: "error" is supplied by the operating system
1657 1794
         (errno/strerror).
1658 1795
         """,
1796
+    )(
1659 1797
         context='Error message',
1660
-        msg='Cannot import vault settings: cannot decode JSON: {error!s}.',
1798
+        single='Cannot import vault settings: cannot decode JSON: {error!s}.',
1661 1799
         flags='python-brace-format',
1662 1800
     )
1663
-    CANNOT_EXPORT_VAULT_SETTINGS = _prepare_translatable(
1801
+    CANNOT_EXPORT_VAULT_SETTINGS = _Commented(
1664 1802
         comments=r"""
1665 1803
         TRANSLATORS: "error" is supplied by the operating system
1666 1804
         (errno/strerror).
1667 1805
         """,
1806
+    )(
1668 1807
         context='Error message',
1669
-        msg='Cannot export vault settings: {error!s}: {filename!r}.',
1808
+        single='Cannot export vault settings: {error!s}: {filename!r}.',
1670 1809
         flags='python-brace-format',
1671 1810
     )
1672
-    CANNOT_IMPORT_VAULT_SETTINGS = _prepare_translatable(
1811
+    CANNOT_IMPORT_VAULT_SETTINGS = _Commented(
1673 1812
         comments=r"""
1674 1813
         TRANSLATORS: "error" is supplied by the operating system
1675 1814
         (errno/strerror).
1676 1815
         """,
1816
+    )(
1677 1817
         context='Error message',
1678
-        msg='Cannot import vault settings: {error!s}: {filename!r}.',
1818
+        single='Cannot import vault settings: {error!s}: {filename!r}.',
1679 1819
         flags='python-brace-format',
1680 1820
     )
1681
-    CANNOT_LOAD_USER_CONFIG = _prepare_translatable(
1821
+    CANNOT_LOAD_USER_CONFIG = _Commented(
1682 1822
         comments=r"""
1683 1823
         TRANSLATORS: "error" is supplied by the operating system
1684 1824
         (errno/strerror).
1685 1825
         """,
1826
+    )(
1686 1827
         context='Error message',
1687
-        msg='Cannot load user config: {error!s}: {filename!r}.',
1828
+        single='Cannot load user config: {error!s}: {filename!r}.',
1688 1829
         flags='python-brace-format',
1689 1830
     )
1690
-    CANNOT_LOAD_VAULT_SETTINGS = _prepare_translatable(
1831
+    CANNOT_LOAD_VAULT_SETTINGS = _Commented(
1691 1832
         comments=r"""
1692 1833
         TRANSLATORS: "error" is supplied by the operating system
1693 1834
         (errno/strerror).
1694 1835
         """,
1836
+    )(
1695 1837
         context='Error message',
1696
-        msg='Cannot load vault settings: {error!s}: {filename!r}.',
1838
+        single='Cannot load vault settings: {error!s}: {filename!r}.',
1697 1839
         flags='python-brace-format',
1698 1840
     )
1699
-    CANNOT_PARSE_AS_VAULT_CONFIG = _prepare_translatable(
1841
+    CANNOT_PARSE_AS_VAULT_CONFIG = _Commented(
1700 1842
         comments=r"""
1701 1843
         TRANSLATORS: Unlike the "Cannot load {path!r} as a {fmt!s} vault
1702 1844
         configuration." message, *this* error message is emitted when we
... ...
@@ -1705,35 +1847,38 @@ class ErrMsgTemplate(enum.Enum):
1705 1847
         warning message potentially multiple times, and this error
1706 1848
         message at the very bottom.
1707 1849
         """,
1850
+    )(
1708 1851
         context='Error message',
1709
-        msg=r"""
1852
+        single=r"""
1710 1853
         Cannot parse {path!r} as a valid vault-native configuration
1711 1854
         file/directory.
1712 1855
         """,
1713 1856
         flags='python-brace-format',
1714 1857
     )
1715
-    CANNOT_PARSE_AS_VAULT_CONFIG_OSERROR = _prepare_translatable(
1858
+    CANNOT_PARSE_AS_VAULT_CONFIG_OSERROR = _Commented(
1716 1859
         comments=r"""
1717 1860
         TRANSLATORS: "error" is supplied by the operating system
1718 1861
         (errno/strerror).
1719 1862
         """,
1863
+    )(
1720 1864
         context='Error message',
1721
-        msg=r"""
1865
+        single=r"""
1722 1866
         Cannot parse {path!r} as a valid vault-native configuration
1723 1867
         file/directory: {error!s}: {filename!r}.
1724 1868
         """,
1725 1869
         flags='python-brace-format',
1726 1870
     )
1727
-    CANNOT_STORE_VAULT_SETTINGS = _prepare_translatable(
1871
+    CANNOT_STORE_VAULT_SETTINGS = _Commented(
1728 1872
         comments=r"""
1729 1873
         TRANSLATORS: "error" is supplied by the operating system
1730 1874
         (errno/strerror).
1731 1875
         """,
1876
+    )(
1732 1877
         context='Error message',
1733
-        msg='Cannot store vault settings: {error!s}: {filename!r}.',
1878
+        single='Cannot store vault settings: {error!s}: {filename!r}.',
1734 1879
         flags='python-brace-format',
1735 1880
     )
1736
-    CANNOT_UNDERSTAND_AGENT = _prepare_translatable(
1881
+    CANNOT_UNDERSTAND_AGENT = _Commented(
1737 1882
         comments=r"""
1738 1883
         TRANSLATORS: This error message is used whenever we cannot make
1739 1884
         any sense of a response from the SSH agent because the response
... ...
@@ -1743,13 +1888,14 @@ class ErrMsgTemplate(enum.Enum):
1743 1888
         requested operation failed, are handled with a different error
1744 1889
         message.
1745 1890
         """,
1891
+    )(
1746 1892
         context='Error message',
1747
-        msg="""
1893
+        single="""
1748 1894
         Cannot understand the SSH agent's response because it violates
1749 1895
         the communications protocol.
1750 1896
         """,
1751 1897
     )
1752
-    CANNOT_UPDATE_SETTINGS_NO_SETTINGS = _prepare_translatable(
1898
+    CANNOT_UPDATE_SETTINGS_NO_SETTINGS = _Commented(
1753 1899
         comments=r"""
1754 1900
         TRANSLATORS: The settings_type metavar contains translations for
1755 1901
         either "global settings" or "service-specific settings"; see the
... ...
@@ -1762,156 +1908,174 @@ class ErrMsgTemplate(enum.Enum):
1762 1908
         you see fit that achieves the desired translations of the first
1763 1909
         sentence.
1764 1910
         """,
1911
+    )(
1765 1912
         context='Error message',
1766
-        msg=r"""
1913
+        single=r"""
1767 1914
         Cannot update the {settings_type!s} without any given settings.
1768 1915
         You must specify at least one of --lower, ..., --symbol, or
1769 1916
         --phrase or --key.
1770 1917
         """,
1771 1918
         flags='python-brace-format',
1772 1919
     )
1773
-    INVALID_USER_CONFIG = _prepare_translatable(
1920
+    INVALID_USER_CONFIG = _Commented(
1774 1921
         comments=r"""
1775 1922
         TRANSLATORS: "error" is supplied by the operating system
1776 1923
         (errno/strerror).
1777 1924
         """,
1925
+    )(
1778 1926
         context='Error message',
1779
-        msg=r"""
1927
+        single=r"""
1780 1928
         The user configuration file is invalid.  {error!s}: {filename!r}.
1781 1929
         """,
1782 1930
         flags='python-brace-format',
1783 1931
     )
1784
-    INVALID_VAULT_CONFIG = _prepare_translatable(
1932
+    INVALID_VAULT_CONFIG = _Commented(
1785 1933
         comments=r"""
1786 1934
         TRANSLATORS: This error message is a reaction to a validator
1787 1935
         function saying *that* the configuration is not valid, but not
1788 1936
         *how* it is not valid.  The configuration file is principally
1789 1937
         parsable, however.
1790 1938
         """,
1939
+    )(
1791 1940
         context='Error message',
1792
-        msg='Invalid vault config: {config!r}.',
1941
+        single='Invalid vault config: {config!r}.',
1793 1942
         flags='python-brace-format',
1794 1943
     )
1795
-    MISSING_MODULE = _prepare_translatable(
1944
+    MISSING_MODULE = _Commented(
1796 1945
         comments='',
1946
+    )(
1797 1947
         context='Error message',
1798
-        msg='Cannot load the required Python module {module!r}.',
1948
+        single='Cannot load the required Python module {module!r}.',
1799 1949
         flags='python-brace-format',
1800 1950
     )
1801
-    NO_AF_UNIX = _prepare_translatable(
1951
+    NO_AF_UNIX = _Commented(
1802 1952
         comments='',
1953
+    )(
1803 1954
         context='Error message',
1804
-        msg=r"""
1955
+        single=r"""
1805 1956
         Cannot connect to an SSH agent because this Python version does
1806 1957
         not support UNIX domain sockets.
1807 1958
         """,
1808 1959
     )
1809
-    NO_KEY_OR_PHRASE = _prepare_translatable(
1960
+    NO_KEY_OR_PHRASE = _Commented(
1810 1961
         comments='',
1962
+    )(
1811 1963
         context='Error message',
1812
-        msg=r"""
1964
+        single=r"""
1813 1965
         No passphrase or key was given in the configuration.  In this
1814 1966
         case, the --phrase or --key argument is required.
1815 1967
         """,
1816 1968
     )
1817
-    NO_SSH_AGENT_FOUND = _prepare_translatable(
1969
+    NO_SSH_AGENT_FOUND = _Commented(
1818 1970
         comments='',
1971
+    )(
1819 1972
         context='Error message',
1820
-        msg="""
1973
+        single="""
1821 1974
         Cannot find any running SSH agent because SSH_AUTH_SOCK is not set.
1822 1975
         """,
1823 1976
     )
1824
-    NO_SUITABLE_SSH_KEYS = _prepare_translatable(
1977
+    NO_SUITABLE_SSH_KEYS = _Commented(
1825 1978
         comments='',
1979
+    )(
1826 1980
         context='Error message',
1827
-        msg="""
1981
+        single="""
1828 1982
         The SSH agent contains no keys suitable for {PROG_NAME!s}.
1829 1983
         """,  # noqa: RUF027
1830 1984
         flags='python-brace-format',
1831 1985
     )
1832
-    PARAMS_MUTUALLY_EXCLUSIVE = _prepare_translatable(
1986
+    PARAMS_MUTUALLY_EXCLUSIVE = _Commented(
1833 1987
         comments=r"""
1834 1988
         TRANSLATORS: The params are long-form command-line option names.
1835 1989
         Typical example: "--key is mutually exclusive with --phrase."
1836 1990
         """,
1991
+    )(
1837 1992
         context='Error message',
1838
-        msg='{param1!s} is mutually exclusive with {param2!s}.',
1993
+        single='{param1!s} is mutually exclusive with {param2!s}.',
1839 1994
         flags='python-brace-format',
1840 1995
     )
1841
-    PARAMS_NEEDS_SERVICE_OR_CONFIG = _prepare_translatable(
1996
+    PARAMS_NEEDS_SERVICE_OR_CONFIG = _Commented(
1842 1997
         comments=r"""
1843 1998
         TRANSLATORS: The param is a long-form command-line option name,
1844 1999
         the metavar is Label.VAULT_METAVAR_SERVICE.
1845 2000
         """,
2001
+    )(
1846 2002
         context='Error message',
1847
-        msg='{param!s} requires a {service_metavar!s} or --config.',
2003
+        single='{param!s} requires a {service_metavar!s} or --config.',
1848 2004
         flags='python-brace-format',
1849 2005
     )
1850
-    PARAMS_NEEDS_SERVICE = _prepare_translatable(
2006
+    PARAMS_NEEDS_SERVICE = _Commented(
1851 2007
         comments=r"""
1852 2008
         TRANSLATORS: The param is a long-form command-line option name,
1853 2009
         the metavar is Label.VAULT_METAVAR_SERVICE.
1854 2010
         """,
2011
+    )(
1855 2012
         context='Error message',
1856
-        msg='{param!s} requires a {service_metavar!s}.',
2013
+        single='{param!s} requires a {service_metavar!s}.',
1857 2014
         flags='python-brace-format',
1858 2015
     )
1859
-    PARAMS_NO_SERVICE = _prepare_translatable(
2016
+    PARAMS_NO_SERVICE = _Commented(
1860 2017
         comments=r"""
1861 2018
         TRANSLATORS: The param is a long-form command-line option name,
1862 2019
         the metavar is Label.VAULT_METAVAR_SERVICE.
1863 2020
         """,
2021
+    )(
1864 2022
         context='Error message',
1865
-        msg='{param!s} does not take a {service_metavar!s} argument.',
2023
+        single='{param!s} does not take a {service_metavar!s} argument.',
1866 2024
         flags='python-brace-format',
1867 2025
     )
1868
-    SERVICE_REQUIRED = _prepare_translatable(
2026
+    SERVICE_REQUIRED = _Commented(
1869 2027
         comments=r"""
1870 2028
         TRANSLATORS: The metavar is Label.VAULT_METAVAR_SERVICE.
1871 2029
         """,
2030
+    )(
1872 2031
         context='Error message',
1873
-        msg='Deriving a passphrase requires a {service_metavar!s}.',
2032
+        single='Deriving a passphrase requires a {service_metavar!s}.',
1874 2033
         flags='python-brace-format',
1875 2034
     )
1876
-    SET_AND_UNSET_SAME_SETTING = _prepare_translatable(
2035
+    SET_AND_UNSET_SAME_SETTING = _Commented(
1877 2036
         comments=r"""
1878 2037
         TRANSLATORS: The rephrasing "Attempted to unset and set the same
1879 2038
         setting (--unset={setting!s} --{setting!s}=...) at the same
1880 2039
         time." may or may not be more suitable as a basis for
1881 2040
         translation instead.
1882 2041
         """,
2042
+    )(
1883 2043
         context='Error message',
1884
-        msg='Attempted to unset and set --{setting!s} at the same time.',
2044
+        single='Attempted to unset and set --{setting!s} at the same time.',
1885 2045
         flags='python-brace-format',
1886 2046
     )
1887
-    SSH_KEY_NOT_LOADED = _prepare_translatable(
2047
+    SSH_KEY_NOT_LOADED = _Commented(
1888 2048
         comments='',
2049
+    )(
1889 2050
         context='Error message',
1890
-        msg='The requested SSH key is not loaded into the agent.',
2051
+        single='The requested SSH key is not loaded into the agent.',
1891 2052
     )
1892
-    USER_ABORTED_EDIT = _prepare_translatable(
2053
+    USER_ABORTED_EDIT = _Commented(
1893 2054
         comments=r"""
1894 2055
         TRANSLATORS: The user requested to edit the notes for a service,
1895 2056
         but aborted the request mid-editing.
1896 2057
         """,
2058
+    )(
1897 2059
         context='Error message',
1898
-        msg='Not saving any new notes: the user aborted the request.',
2060
+        single='Not saving any new notes: the user aborted the request.',
1899 2061
     )
1900
-    USER_ABORTED_PASSPHRASE = _prepare_translatable(
2062
+    USER_ABORTED_PASSPHRASE = _Commented(
1901 2063
         comments=r"""
1902 2064
         TRANSLATORS: The user was prompted for a master passphrase,
1903 2065
         but aborted the request.
1904 2066
         """,
2067
+    )(
1905 2068
         context='Error message',
1906
-        msg='No passphrase was given; the user aborted the request.',
2069
+        single='No passphrase was given; the user aborted the request.',
1907 2070
     )
1908
-    USER_ABORTED_SSH_KEY_SELECTION = _prepare_translatable(
2071
+    USER_ABORTED_SSH_KEY_SELECTION = _Commented(
1909 2072
         comments=r"""
1910 2073
         TRANSLATORS: The user was prompted to select a master SSH key,
1911 2074
         but aborted the request.
1912 2075
         """,
2076
+    )(
1913 2077
         context='Error message',
1914
-        msg='No SSH key was selected; the user aborted the request.',
2078
+        single='No SSH key was selected; the user aborted the request.',
1915 2079
     )
1916 2080
 
1917 2081
 
1918 2082