Changeset 4885eab in Klonkt
- Timestamp:
- 06/21/2026 03:05:14 PM (3 months ago)
- Branches:
- main
- Children:
- 5e61b17
- Parents:
- bbf9d1a
- Location:
- src
- Files:
-
- 27 edited
-
services/i18n.js (modified) (3 diffs)
-
views/pages/404.ejs (modified) (1 diff)
-
views/pages/account.ejs (modified) (10 diffs)
-
views/pages/archive.ejs (modified) (1 diff)
-
views/pages/artists-directory.ejs (modified) (5 diffs)
-
views/pages/auth-login.ejs (modified) (1 diff)
-
views/pages/auth-reset-request.ejs (modified) (3 diffs)
-
views/pages/auth-reset.ejs (modified) (2 diffs)
-
views/pages/changelog.ejs (modified) (1 diff)
-
views/pages/circle-feed.ejs (modified) (4 diffs)
-
views/pages/circle-post.ejs (modified) (3 diffs)
-
views/pages/download.ejs (modified) (3 diffs)
-
views/pages/epk.ejs (modified) (3 diffs)
-
views/pages/fan-gate.ejs (modified) (1 diff)
-
views/pages/favorites.ejs (modified) (1 diff)
-
views/pages/home.ejs (modified) (3 diffs)
-
views/pages/hub-home.ejs (modified) (5 diffs)
-
views/pages/linkbio.ejs (modified) (2 diffs)
-
views/pages/my-site.ejs (modified) (1 diff)
-
views/pages/newsletter.ejs (modified) (2 diffs)
-
views/pages/post-edit.ejs (modified) (27 diffs)
-
views/pages/prutter-conversation.ejs (modified) (3 diffs)
-
views/pages/prutter-inbox.ejs (modified) (2 diffs)
-
views/pages/tag.ejs (modified) (2 diffs)
-
views/pages/type.ejs (modified) (2 diffs)
-
views/pages/user.ejs (modified) (3 diffs)
-
views/pages/viewer-blocked.ejs (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/services/i18n.js
rbbf9d1a r4885eab 660 660 'ahelp.s_updates_h': 'Updates', 661 661 'ahelp.s_updates_b': 'Beheer → <strong>Updates</strong> (alleen god) toont de huidige versie en of er een nieuwere is. Met "Nu bijwerken" haal je de laatste versie binnen.', 662 // === Publieke + overige pagina's === 663 'pedit.title_new': 'Nieuwe post', 664 'pedit.title_edit': 'Post bewerken', 665 'pedit.f_title': 'Titel', 666 'pedit.f_slug': 'Slug (URL)', 667 'pedit.slug_placeholder': 'auto van titel als leeg', 668 'pedit.f_tags': 'Tags', 669 'pedit.tags_hint': '(komma-gescheiden)', 670 'pedit.f_excerpt': 'Samenvatting & Cirkel Preview', 671 'pedit.excerpt_hint': 'Wordt ook gebruikt als samenvatting in <strong>Cirkels</strong> (andere sites die je post tonen). Leeg laten = begin van de post.', 672 'pedit.s_cover': 'Cover', 673 'pedit.f_cover_url': 'Cover URL', 674 'pedit.cover_url_placeholder': '/media/… of https://… (of upload met de knop)', 675 'pedit.cover_upload_btn': 'Upload nieuwe cover', 676 'pedit.s_content': 'Content', 677 'pedit.content_hint': 'Sleep een afbeelding om in te voegen · selecteer tekst om op te maken', 678 'pedit.tb_done': 'Klaar', 679 'pedit.tb_done_title': 'Klaar met bewerken', 680 'pedit.tb_bold': 'Vet', 681 'pedit.tb_bold_title': 'Vet (Ctrl+B)', 682 'pedit.tb_italic': 'Cursief', 683 'pedit.tb_italic_title': 'Cursief (Ctrl+I)', 684 'pedit.tb_underline': 'Onderstreept', 685 'pedit.tb_h2': 'Kop', 686 'pedit.tb_h3': 'Subkop', 687 'pedit.tb_p': 'Paragraaf', 688 'pedit.tb_ul': 'Lijst', 689 'pedit.tb_ol': 'Genummerde lijst', 690 'pedit.tb_quote': 'Quote', 691 'pedit.tb_link': 'Link', 692 'pedit.tb_link_title': 'Link (Ctrl+K)', 693 'pedit.tb_code': 'Code', 694 'pedit.tb_code_title': 'Code (inline)', 695 'pedit.tb_image': 'Afbeelding', 696 'pedit.tb_image_title': 'Afbeelding invoegen', 697 'pedit.tb_track': 'Track', 698 'pedit.tb_track_title': 'Track invoegen', 699 'pedit.tb_playlist': 'Playlist', 700 'pedit.tb_playlist_title': 'Playlist invoegen', 701 'pedit.tb_embed': 'Media embedden', 702 'pedit.tb_embed_title': 'Embed (YouTube, Spotify, SoundCloud, Vimeo…)', 703 'pedit.tb_clear': 'Wis opmaak', 704 'pedit.tb_fullscreen': 'Volledig scherm', 705 'pedit.editor_aria': 'Content', 706 'pedit.editor_placeholder': 'Begin met schrijven…', 707 'pedit.tap_to_edit': 'Tik om te bewerken', 708 'pedit.tap_to_write': 'Tik om te schrijven…', 709 'pedit.chars': 'tekens', 710 'pedit.s_publication': 'Publicatie', 711 'pedit.f_status': 'Status', 712 'pedit.status_published': 'Gepubliceerd', 713 'pedit.status_draft': 'Concept', 714 'pedit.status_archived': 'Gearchiveerd', 715 'pedit.f_type': 'Type', 716 'pedit.type_post': 'Post', 717 'pedit.type_foto': 'Foto', 718 'pedit.type_video': 'Video', 719 'pedit.type_audio': 'Audio', 720 'pedit.pin_label': 'Vastpinnen bovenaan', 721 'pedit.pin_up': 'Hoger zetten', 722 'pedit.pin_down': 'Lager zetten', 723 'pedit.pin_top': 'bovenaan', 724 'pedit.pin_nth_suffix': 'e van boven', 725 'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 726 'pedit.fan_only_label': 'Alleen voor ingelogde fans', 727 'pedit.publish_at_label': 'Publiceren op (laat leeg = direct)', 728 'pedit.scheduled_for': 'Ingepland voor {d}', 729 'pedit.cancel': 'Annuleren', 730 'pedit.publish': 'Publish', 731 'pedit.save': 'Opslaan', 732 'pedit.js_link_prompt': 'Link URL (https://… of /pad)', 733 'pedit.js_uploading': 'Uploaden…', 734 'pedit.js_uploaded': 'Geüpload', 735 'pedit.js_inserted': 'Ingevoegd', 736 'pedit.js_failed': 'Mislukt', 737 'pedit.js_embed_prompt': 'Plak een media-URL om in te sluiten (YouTube, Spotify, SoundCloud, Vimeo, Apple Music, Bandcamp):', 738 'pedit.js_embed_invalid': 'Geef een volledige URL (https://…).', 739 'pedit.js_no_tracks_found': 'Geen tracks gevonden voor', 740 'pedit.js_no_tracks_yet': 'Nog geen tracks. Upload via Beheer naar Audio.', 741 'pedit.js_tracks_loading': 'Tracks laden…', 742 'pedit.js_tracks_load_fail': 'Kon tracks niet laden', 743 'pedit.js_playlist_editor_missing': 'Playlist editor niet geladen', 744 'pedit.js_playlist_existing': 'Bestaande playlists:', 745 'pedit.js_playlist_choose': 'Kies een nummer om in te voegen, leeg = nieuwe maken:', 746 'pedit.chip_track': 'Track', 747 'pedit.chip_album': 'Album:', 748 'pedit.chip_playlist': 'Playlist:', 749 'pedit.tp_title': 'Track invoegen', 750 'pedit.tp_close': 'Sluiten', 751 'pedit.tp_search_placeholder': 'Zoek op titel of artiest…', 752 'pedit.tp_list_aria': 'Tracks', 753 'acct.back_home': 'Terug naar home', 754 'acct.title': 'Account', 755 'acct.subtitle': 'Profiel en avatar.', 756 'acct.viewer_mode': 'Kijker-modus', 757 'acct.viewer_note': 'Dit is een demo-account. Je kunt alles bekijken, maar niets wijzigen — ook geen foto of bio.', 758 'acct.profile': 'Profiel', 759 'acct.avatar_change': 'Klik om je foto te wijzigen', 760 'acct.member_since': 'Lid sinds', 761 'acct.avatar_remove': 'Foto verwijderen', 762 'acct.email': 'E-mailadres', 763 'acct.email_ph': 'jij@email.nl', 764 'acct.bio': 'Bio', 765 'acct.bio_ph': 'Een korte regel over jezelf', 766 'acct.bio_empty': 'Geen bio', 767 'acct.save': 'Opslaan', 768 'acct.site': 'Site', 769 'acct.site_name': 'Site-naam', 770 'acct.site_name_hint': '— getoond in de kop van je site', 771 'acct.tagline': 'Tagline', 772 'acct.tagline_hint': '— korte oneliner (optioneel)', 773 'acct.site_save': 'Site opslaan', 774 'acct.password_change': 'Wachtwoord wijzigen', 775 'acct.password_current': 'Huidig wachtwoord', 776 'acct.password_new': 'Nieuw wachtwoord', 777 'acct.password_min': '(min 8 tekens)', 778 'acct.password_confirm': 'Bevestig nieuw wachtwoord', 779 'acct.google_login': 'Inloggen met Google', 780 'acct.google_linked': '✓ Je Google-account is gekoppeld — je kunt ook met Google inloggen.', 781 'acct.google_unlink': 'Google ontkoppelen', 782 'acct.google_link_hint': 'Koppel je Google-account zodat je voortaan ook met één klik via Google kunt inloggen (naast je wachtwoord).', 783 'acct.google_link': 'Koppel Google-account', 784 'acct.login': 'Inloggen', 785 'acct.login_google_only': 'Je bent ingelogd via Google ({email}). Er is geen apart wachtwoord.', 786 'news.this_artist': 'deze artiest', 787 'news.form_title': 'Blijf op de hoogte', 788 'news.form_sub_before': 'Schrijf je in voor de nieuwsbrief van ', 789 'news.form_sub_after': ' — nieuwe muziek, shows en updates, rechtstreeks in je inbox. Uitschrijven kan altijd met één klik.', 790 'news.email_ph': 'jouw@email.nl', 791 'news.subscribe': 'Inschrijven', 792 'news.check_title': 'Bijna klaar ✉', 793 'news.check_sub_before': 'We hebben een bevestigingsmail gestuurd naar ', 794 'news.check_sub_after': '. Klik op de link in die mail om je inschrijving te bevestigen.', 795 'news.your_address': 'je adres', 796 'news.done_title': 'Je bent ingeschreven ✓', 797 'news.done_sub_before': 'Bedankt — je staat op de lijst van ', 798 'news.done_sub_after': '.', 799 'news.confirmed_title': 'Inschrijving bevestigd ✓', 800 'news.confirmed_sub_before': 'Top! Je ontvangt voortaan de nieuwsbrief van ', 801 'news.confirmed_sub_after': '.', 802 'news.unsubbed_title': 'Uitgeschreven', 803 'news.unsubbed_sub': 'Je bent afgemeld. Je ontvangt geen nieuwsbrieven meer. Van gedachten veranderd? Je kunt je altijd opnieuw inschrijven.', 804 'news.invalid_title': 'Ongeldig e-mailadres', 805 'news.invalid_sub': 'Controleer je adres en probeer \'t opnieuw.', 806 'news.back': '← Terug', 807 'news.smtperror_title': 'Even niet gelukt', 808 'news.smtperror_sub': 'De bevestigingsmail kon niet verstuurd worden. Probeer \'t later nog eens.', 809 'news.badtoken_title': 'Link ongeldig of verlopen', 810 'news.badtoken_sub': 'Deze link werkt niet meer. Schrijf je eventueel opnieuw in.', 811 'news.to_subscribe': 'Naar inschrijven', 812 'news.error_title': 'Er ging iets mis', 813 'news.error_sub': 'Probeer \'t later nog eens.', 814 'dl.ready_title': 'Bedankt! ⬇', 815 'dl.ready_sub': 'Je download zou nu moeten starten. Gebeurt er niets?', 816 'dl.manual': 'Download handmatig starten', 817 'dl.download_btn': 'Download', 818 'dl.capture_sub': 'Laat je e-mailadres achter en je krijgt het bestand. Je komt dan ook op de nieuwsbrieflijst — uitschrijven kan altijd.', 819 'dl.email_ph': 'jouw@email.nl', 820 'epk.kicker': 'Perskit', 821 'epk.edit': 'Bewerken', 822 'epk.contact_booking': 'Contact / boeking', 823 'epk.view_site': 'Bekijk de site →', 824 'epk.press_photo': 'Persfoto', 825 'epk.most_played': 'Meest beluisterd', 826 'epk.recent': 'Recent', 827 'fgate.title': 'Alleen voor fans', 828 'fgate.sub': 'Dit is exclusief voor ingelogde fans. Log in om het te bekijken.', 829 'fgate.login': 'Inloggen / aanmelden', 830 'lbio.empty': 'Nog geen links ingesteld.', 831 'lbio.back_to_site': 'naar de site', 832 'myst.overview': 'Overzicht', 833 'myst.title': 'Mijn Klonkt Hub', 834 'myst.quick_links_aria': 'Beheer-snelkoppelingen', 835 'myst.new_post': 'Nieuwe post', 836 'myst.appearance': 'Uiterlijk', 837 'myst.comments': 'Reacties', 838 'myst.view_site': 'Bekijk mijn site', 839 'myst.account': 'Account', 840 'myst.posts': 'Posts', 841 'myst.published': 'Gepubliceerd', 842 'myst.draft_count_one': '{n} concept', 843 'myst.draft_count_many': '{n} concepten', 844 'myst.draft_badge': 'Concept', 845 'myst.untitled': '(zonder titel)', 846 'myst.edit': 'Bewerken', 847 'myst.view': 'Bekijk', 848 'arst.title': 'Wachtwoord resetten', 849 'arst.request_new': 'Vraag een nieuwe reset-link aan', 850 'arst.back_login': 'Terug naar inloggen', 851 'arst.set_for': 'Stel een nieuw wachtwoord in voor {username}.', 852 'arst.new_pw': 'Nieuw wachtwoord (min 8 tekens)', 853 'arst.confirm_pw': 'Bevestig nieuw wachtwoord', 854 'arst.submit': 'Wachtwoord instellen', 855 'arrq.title': 'Wachtwoord resetten', 856 'arrq.sent': 'Als er een account bestaat voor dat e-mailadres, is er een reset-link verstuurd.', 857 'arrq.no_mailserver': '<strong>Geen e-mailserver ingesteld</strong> — reset-link hieronder.', 858 'arrq.no_mail_cli': 'Geen e-mail ingesteld? De beheerder kan ook <code>npm run reset-admin</code> draaien op de server.', 859 'arrq.back_login': '← Terug naar inloggen', 860 'arrq.tagline': 'Vul je e-mail in; we sturen een reset-link.', 861 'arrq.email': 'E-mail', 862 'arrq.submit': 'Stuur reset-link', 863 'adir.home': 'Home', 864 'adir.logout': 'Uitloggen', 865 'adir.login': 'Inloggen', 866 'adir.title': 'Leden', 867 'adir.count_one': '{n} Klonkt', 868 'adir.count_many': "{n} Klonkt's", 869 'adir.search_ph': 'Zoek op naam…', 870 'adir.search_aria': 'Zoek leden', 871 'adir.search_btn': 'Zoeken', 872 'adir.clear': 'Wissen', 873 'adir.empty_q': 'Geen leden gevonden voor "{q}".', 874 'adir.empty': 'Nog geen leden.', 875 'adir.posts_one': '{n} post', 876 'adir.posts_many': '{n} posts', 877 'adir.pager_aria': 'Paginering', 878 'adir.prev': '← Vorige', 879 'adir.page_info': 'Pagina {page} van {pages}', 880 'adir.next': 'Volgende →', 881 'glog.admin_title': 'Dit is een beheerders-account', 882 'glog.admin_body': 'Je logde in met Google, maar dit e-mailadres hoort bij de beheerder van deze site. Inloggen met Google is alleen voor luisteraars/fans — beheerders gebruiken altijd hun wachtwoord (zo kan een Google-login nooit per ongeluk beheerrechten geven).', 883 'glog.admin_li1': '<strong>Ben je de beheerder?</strong> <a href="/auth/admin">Log in met je gebruikersnaam + wachtwoord</a>.', 884 'glog.admin_li2': '<strong>Wil je voortaan met Google inloggen?</strong> Log eerst in met wachtwoord en <strong>koppel je Google-account</strong> onder Account → daarna werkt Google-login ook voor jou.', 885 'glog.admin_li3': '<strong>Wil je als fan inloggen?</strong> Gebruik een <strong>ander Google-account</strong> (niet je beheerdersadres).', 886 'glog.session_title': 'Login afgebroken', 887 'glog.session_body': 'De Google-login is afgebroken of de sessie was verlopen. Probeer het gewoon opnieuw.', 888 'glog.email_title': 'Geen geverifieerd e-mailadres', 889 'glog.email_body': 'Je Google-account heeft geen geverifieerd e-mailadres. Verifieer je adres bij Google.', 890 'glog.linked_title': 'Adres al gekoppeld', 891 'glog.linked_body': 'Dit e-mailadres is al aan een ander Google-account gekoppeld. Gebruik dat account, of neem contact op met de beheerder.', 892 'glog.unavailable_title': 'Google-login niet beschikbaar', 893 'glog.unavailable_body': 'Inloggen met Google is op deze site (nog) niet ingesteld.', 894 'glog.failed_title': 'Google-login mislukt', 895 'glog.failed_body': 'Er ging iets mis bij het inloggen met Google. Probeer het opnieuw.', 896 'pusr.post_one': 'post', 897 'pusr.post_many': 'posts', 898 'pusr.on_this_site': 'op deze site', 899 'pusr.total': '{n} totaal', 900 'pusr.joined': 'lid sinds {date}', 901 'pusr.send_dm': 'Stuur DM', 902 'pusr.empty': 'Nog geen posts op deze site.', 903 'pusr.posts_heading': 'Posts', 904 'pusr.untitled': '(zonder titel)', 905 'phub.main_badge': 'Hoofdpagina', 906 'phub.view_page': 'Bekijk de pagina', 907 'phub.members': 'Leden', 908 'phub.post_one': 'post', 909 'phub.post_many': 'posts', 910 'phub.all_members': 'Alle {n} leden', 911 'phub.latest_posts': 'Laatste posts van onze leden.', 912 'cfeed.title': 'Cirkel', 913 'cfeed.sub': 'Wat er speelt bij de andere sites in mijn cirkel.', 914 'cfeed.count': '{n} sites in mijn cirkel', 915 'cfeed.empty': 'Nog niets in mijn cirkel.', 916 'cfeed.grid_view': 'Grid-weergave', 917 'cpost.back': 'Cirkel', 918 'cpost.via': 'via', 919 'cpost.read_more': 'Lees verder bij {source}', 920 'vblk.title': 'Kijker-modus', 921 'vblk.text_before': 'Dit is een alleen-lezen account. Je kunt alles bekijken, maar', 922 'vblk.text_strong': 'niets wijzigen', 923 'vblk.text_after': '— opslaan, uploaden, verwijderen en reageren zijn uitgeschakeld.', 924 'vblk.back': 'Terug', 925 'vblk.to_home': 'Naar de hoofdpagina', 926 'phome.empty_title': 'Hier is het nog stil.', 927 'phome.empty_sub': 'Nog geen posts. Spannend.', 928 'phome.write_first': 'Schrijf je eerste post', 929 'phome.grid_view': 'Grid-weergave', 930 'fav.title': 'Favorieten', 931 'fav.sub': "Posts die je hebt geliket. Tik op het ♥ bij een post om 'm hier te bewaren.", 932 'fav.empty': 'Je hebt nog geen favorieten. Open een post en tik op het hartje ♡.', 933 'chlog.back': 'Terug', 934 'chlog.title': 'Wijzigingen', 935 'chlog.app_version': 'App-versie', 936 'chlog.fed_proto': 'Federatie-proto', 937 'chlog.manage_updates': 'Updates beheren', 938 'e404.title': 'Pagina niet gevonden', 939 'e404.sub': 'Deze pagina bestaat niet (meer). Misschien is de link verouderd of verkeerd getypt.', 940 'e404.home': 'Naar de homepagina', 941 'e404.archive': 'Archief', 942 'ptype.eyebrow': 'Type', 943 'ptype.count_one': '{n} post', 944 'ptype.count_many': '{n} posts', 945 'ptype.empty': 'Nog geen posts van dit type.', 946 'ptype.untitled': '(zonder titel)', 947 'ptag.eyebrow': 'Tag', 948 'ptag.count_one': '{n} post', 949 'ptag.count_many': '{n} posts', 950 'ptag.empty': 'Nog geen posts met deze tag.', 951 'ptag.untitled': '(zonder titel)', 952 'parch.back': 'Terug', 953 'parch.title': 'Archief', 954 'parch.count_one': '{n} bericht', 955 'parch.count_many': '{n} berichten', 956 'parch.empty': 'Nog geen berichten.', 957 'prin.tagline': 'Je directe berichten op deze site.', 958 'prin.empty': 'Nog geen gesprekken. Open iemands profiel en klik op "DM sturen" om er een te beginnen.', 959 'prin.empty_conv': 'Leeg gesprek', 960 'prcv.back_aria': 'Terug naar inbox', 961 'prcv.inbox': 'Inbox', 962 'prcv.unknown': 'Onbekend', 963 'prcv.view_profile': 'Bekijk profiel', 964 'prcv.placeholder': 'Bericht…', 965 'prcv.send': 'Stuur', 662 966 }, 663 967 en: { … … 1303 1607 'ahelp.s_updates_h': 'Updates', 1304 1608 'ahelp.s_updates_b': 'Admin → <strong>Updates</strong> (god only) shows the current version and whether a newer one is available. With "Update now" you fetch the latest version.', 1609 'pedit.title_new': 'New post', 1610 'pedit.title_edit': 'Edit post', 1611 'pedit.f_title': 'Title', 1612 'pedit.f_slug': 'Slug (URL)', 1613 'pedit.slug_placeholder': 'auto from title if empty', 1614 'pedit.f_tags': 'Tags', 1615 'pedit.tags_hint': '(comma-separated)', 1616 'pedit.f_excerpt': 'Summary & Circle Preview', 1617 'pedit.excerpt_hint': 'Also used as the summary in <strong>Circles</strong> (other sites that show your post). Leave empty = start of the post.', 1618 'pedit.s_cover': 'Cover', 1619 'pedit.f_cover_url': 'Cover URL', 1620 'pedit.cover_url_placeholder': '/media/… or https://… (or upload with the button)', 1621 'pedit.cover_upload_btn': 'Upload new cover', 1622 'pedit.s_content': 'Content', 1623 'pedit.content_hint': 'Drag an image to insert · select text to format', 1624 'pedit.tb_done': 'Done', 1625 'pedit.tb_done_title': 'Done editing', 1626 'pedit.tb_bold': 'Bold', 1627 'pedit.tb_bold_title': 'Bold (Ctrl+B)', 1628 'pedit.tb_italic': 'Italic', 1629 'pedit.tb_italic_title': 'Italic (Ctrl+I)', 1630 'pedit.tb_underline': 'Underline', 1631 'pedit.tb_h2': 'Heading', 1632 'pedit.tb_h3': 'Subheading', 1633 'pedit.tb_p': 'Paragraph', 1634 'pedit.tb_ul': 'List', 1635 'pedit.tb_ol': 'Numbered list', 1636 'pedit.tb_quote': 'Quote', 1637 'pedit.tb_link': 'Link', 1638 'pedit.tb_link_title': 'Link (Ctrl+K)', 1639 'pedit.tb_code': 'Code', 1640 'pedit.tb_code_title': 'Code (inline)', 1641 'pedit.tb_image': 'Image', 1642 'pedit.tb_image_title': 'Insert image', 1643 'pedit.tb_track': 'Track', 1644 'pedit.tb_track_title': 'Insert track', 1645 'pedit.tb_playlist': 'Playlist', 1646 'pedit.tb_playlist_title': 'Insert playlist', 1647 'pedit.tb_embed': 'Embed media', 1648 'pedit.tb_embed_title': 'Embed (YouTube, Spotify, SoundCloud, Vimeo…)', 1649 'pedit.tb_clear': 'Clear formatting', 1650 'pedit.tb_fullscreen': 'Full screen', 1651 'pedit.editor_aria': 'Content', 1652 'pedit.editor_placeholder': 'Start writing…', 1653 'pedit.tap_to_edit': 'Tap to edit', 1654 'pedit.tap_to_write': 'Tap to write…', 1655 'pedit.chars': 'characters', 1656 'pedit.s_publication': 'Publication', 1657 'pedit.f_status': 'Status', 1658 'pedit.status_published': 'Published', 1659 'pedit.status_draft': 'Draft', 1660 'pedit.status_archived': 'Archived', 1661 'pedit.f_type': 'Type', 1662 'pedit.type_post': 'Post', 1663 'pedit.type_foto': 'Photo', 1664 'pedit.type_video': 'Video', 1665 'pedit.type_audio': 'Audio', 1666 'pedit.pin_label': 'Pin to top', 1667 'pedit.pin_up': 'Move up', 1668 'pedit.pin_down': 'Move down', 1669 'pedit.pin_top': 'at the top', 1670 'pedit.pin_nth_suffix': 'th from top', 1671 'pedit.noindex_label': 'noindex (hide from search engines)', 1672 'pedit.fan_only_label': 'Logged-in fans only', 1673 'pedit.publish_at_label': 'Publish at (leave empty = immediately)', 1674 'pedit.scheduled_for': 'Scheduled for {d}', 1675 'pedit.cancel': 'Cancel', 1676 'pedit.publish': 'Publish', 1677 'pedit.save': 'Save', 1678 'pedit.js_link_prompt': 'Link URL (https://… or /path)', 1679 'pedit.js_uploading': 'Uploading…', 1680 'pedit.js_uploaded': 'Uploaded', 1681 'pedit.js_inserted': 'Inserted', 1682 'pedit.js_failed': 'Failed', 1683 'pedit.js_embed_prompt': 'Paste a media URL to embed (YouTube, Spotify, SoundCloud, Vimeo, Apple Music, Bandcamp):', 1684 'pedit.js_embed_invalid': 'Enter a full URL (https://…).', 1685 'pedit.js_no_tracks_found': 'No tracks found for', 1686 'pedit.js_no_tracks_yet': 'No tracks yet. Upload via Admin then Audio.', 1687 'pedit.js_tracks_loading': 'Loading tracks…', 1688 'pedit.js_tracks_load_fail': 'Could not load tracks', 1689 'pedit.js_playlist_editor_missing': 'Playlist editor not loaded', 1690 'pedit.js_playlist_existing': 'Existing playlists:', 1691 'pedit.js_playlist_choose': 'Choose a number to insert, empty = create new:', 1692 'pedit.chip_track': 'Track', 1693 'pedit.chip_album': 'Album:', 1694 'pedit.chip_playlist': 'Playlist:', 1695 'pedit.tp_title': 'Insert track', 1696 'pedit.tp_close': 'Close', 1697 'pedit.tp_search_placeholder': 'Search by title or artist…', 1698 'pedit.tp_list_aria': 'Tracks', 1699 'acct.back_home': 'Back to home', 1700 'acct.title': 'Account', 1701 'acct.subtitle': 'Profile and avatar.', 1702 'acct.viewer_mode': 'Viewer mode', 1703 'acct.viewer_note': 'This is a demo account. You can view everything, but change nothing — not even your photo or bio.', 1704 'acct.profile': 'Profile', 1705 'acct.avatar_change': 'Click to change your photo', 1706 'acct.member_since': 'Member since', 1707 'acct.avatar_remove': 'Remove photo', 1708 'acct.email': 'Email address', 1709 'acct.email_ph': 'you@email.com', 1710 'acct.bio': 'Bio', 1711 'acct.bio_ph': 'A short line about yourself', 1712 'acct.bio_empty': 'No bio', 1713 'acct.save': 'Save', 1714 'acct.site': 'Site', 1715 'acct.site_name': 'Site name', 1716 'acct.site_name_hint': '— shown in your site\'s header', 1717 'acct.tagline': 'Tagline', 1718 'acct.tagline_hint': '— short one-liner (optional)', 1719 'acct.site_save': 'Save site', 1720 'acct.password_change': 'Change password', 1721 'acct.password_current': 'Current password', 1722 'acct.password_new': 'New password', 1723 'acct.password_min': '(min 8 characters)', 1724 'acct.password_confirm': 'Confirm new password', 1725 'acct.google_login': 'Sign in with Google', 1726 'acct.google_linked': '✓ Your Google account is linked — you can also sign in with Google.', 1727 'acct.google_unlink': 'Unlink Google', 1728 'acct.google_link_hint': 'Link your Google account so you can also sign in with one click via Google (alongside your password).', 1729 'acct.google_link': 'Link Google account', 1730 'acct.login': 'Sign in', 1731 'acct.login_google_only': 'You are signed in via Google ({email}). There is no separate password.', 1732 'news.this_artist': 'this artist', 1733 'news.form_title': 'Stay in the loop', 1734 'news.form_sub_before': 'Subscribe to the newsletter from ', 1735 'news.form_sub_after': ' — new music, shows and updates, straight to your inbox. You can unsubscribe anytime with one click.', 1736 'news.email_ph': 'you@email.com', 1737 'news.subscribe': 'Subscribe', 1738 'news.check_title': 'Almost done ✉', 1739 'news.check_sub_before': 'We\'ve sent a confirmation email to ', 1740 'news.check_sub_after': '. Click the link in that email to confirm your subscription.', 1741 'news.your_address': 'your address', 1742 'news.done_title': 'You\'re subscribed ✓', 1743 'news.done_sub_before': 'Thanks — you\'re on the list of ', 1744 'news.done_sub_after': '.', 1745 'news.confirmed_title': 'Subscription confirmed ✓', 1746 'news.confirmed_sub_before': 'Great! You\'ll now receive the newsletter from ', 1747 'news.confirmed_sub_after': '.', 1748 'news.unsubbed_title': 'Unsubscribed', 1749 'news.unsubbed_sub': 'You\'ve been unsubscribed. You won\'t receive any more newsletters. Changed your mind? You can always subscribe again.', 1750 'news.invalid_title': 'Invalid email address', 1751 'news.invalid_sub': 'Please check your address and try again.', 1752 'news.back': '← Back', 1753 'news.smtperror_title': 'That didn\'t work', 1754 'news.smtperror_sub': 'The confirmation email couldn\'t be sent. Please try again later.', 1755 'news.badtoken_title': 'Link invalid or expired', 1756 'news.badtoken_sub': 'This link no longer works. Feel free to subscribe again.', 1757 'news.to_subscribe': 'To subscribe', 1758 'news.error_title': 'Something went wrong', 1759 'news.error_sub': 'Please try again later.', 1760 'dl.ready_title': 'Thanks! ⬇', 1761 'dl.ready_sub': 'Your download should start now. Nothing happening?', 1762 'dl.manual': 'Start download manually', 1763 'dl.download_btn': 'Download', 1764 'dl.capture_sub': 'Leave your email address and you\'ll get the file. You\'ll also be added to the newsletter list — you can unsubscribe anytime.', 1765 'dl.email_ph': 'you@email.com', 1766 'epk.kicker': 'Press kit', 1767 'epk.edit': 'Edit', 1768 'epk.contact_booking': 'Contact / booking', 1769 'epk.view_site': 'View the site →', 1770 'epk.press_photo': 'Press photo', 1771 'epk.most_played': 'Most played', 1772 'epk.recent': 'Recent', 1773 'fgate.title': 'Fans only', 1774 'fgate.sub': 'This is exclusive to logged-in fans. Log in to view it.', 1775 'fgate.login': 'Log in / sign up', 1776 'lbio.empty': 'No links set up yet.', 1777 'lbio.back_to_site': 'back to the site', 1778 'myst.overview': 'Overview', 1779 'myst.title': 'My Klonkt Hub', 1780 'myst.quick_links_aria': 'Admin shortcuts', 1781 'myst.new_post': 'New post', 1782 'myst.appearance': 'Appearance', 1783 'myst.comments': 'Comments', 1784 'myst.view_site': 'View my site', 1785 'myst.account': 'Account', 1786 'myst.posts': 'Posts', 1787 'myst.published': 'Published', 1788 'myst.draft_count_one': '{n} draft', 1789 'myst.draft_count_many': '{n} drafts', 1790 'myst.draft_badge': 'Draft', 1791 'myst.untitled': '(untitled)', 1792 'myst.edit': 'Edit', 1793 'myst.view': 'View', 1794 'arst.title': 'Reset password', 1795 'arst.request_new': 'Request a new reset link', 1796 'arst.back_login': 'Back to login', 1797 'arst.set_for': 'Set a new password for {username}.', 1798 'arst.new_pw': 'New password (min 8 characters)', 1799 'arst.confirm_pw': 'Confirm new password', 1800 'arst.submit': 'Set password', 1801 'arrq.title': 'Reset password', 1802 'arrq.sent': 'If an account exists for that email address, a reset link has been sent.', 1803 'arrq.no_mailserver': '<strong>No mail server configured</strong> — reset link below.', 1804 'arrq.no_mail_cli': 'No email set up? The administrator can also run <code>npm run reset-admin</code> on the server.', 1805 'arrq.back_login': '← Back to login', 1806 'arrq.tagline': 'Enter your email and we\'ll send you a reset link.', 1807 'arrq.email': 'Email', 1808 'arrq.submit': 'Send reset link', 1809 'adir.home': 'Home', 1810 'adir.logout': 'Log out', 1811 'adir.login': 'Log in', 1812 'adir.title': 'Members', 1813 'adir.count_one': '{n} Klonkt', 1814 'adir.count_many': "{n} Klonkt's", 1815 'adir.search_ph': 'Search by name…', 1816 'adir.search_aria': 'Search members', 1817 'adir.search_btn': 'Search', 1818 'adir.clear': 'Clear', 1819 'adir.empty_q': 'No members found for "{q}".', 1820 'adir.empty': 'No members yet.', 1821 'adir.posts_one': '{n} post', 1822 'adir.posts_many': '{n} posts', 1823 'adir.pager_aria': 'Pagination', 1824 'adir.prev': '← Previous', 1825 'adir.page_info': 'Page {page} of {pages}', 1826 'adir.next': 'Next →', 1827 'glog.admin_title': 'This is an administrator account', 1828 'glog.admin_body': 'You signed in with Google, but this email address belongs to the administrator of this site. Google sign-in is only for listeners/fans — administrators always use their password (so a Google login can never accidentally grant admin rights).', 1829 'glog.admin_li1': '<strong>Are you the administrator?</strong> <a href="/auth/admin">Log in with your username + password</a>.', 1830 'glog.admin_li2': '<strong>Want to sign in with Google from now on?</strong> Log in with your password first and <strong>link your Google account</strong> under Account → after that Google sign-in works for you too.', 1831 'glog.admin_li3': '<strong>Want to sign in as a fan?</strong> Use a <strong>different Google account</strong> (not your administrator address).', 1832 'glog.session_title': 'Login interrupted', 1833 'glog.session_body': 'The Google login was interrupted or the session had expired. Just try again.', 1834 'glog.email_title': 'No verified email address', 1835 'glog.email_body': 'Your Google account has no verified email address. Verify your address with Google.', 1836 'glog.linked_title': 'Address already linked', 1837 'glog.linked_body': 'This email address is already linked to another Google account. Use that account, or contact the administrator.', 1838 'glog.unavailable_title': 'Google sign-in unavailable', 1839 'glog.unavailable_body': 'Google sign-in has not (yet) been set up on this site.', 1840 'glog.failed_title': 'Google sign-in failed', 1841 'glog.failed_body': 'Something went wrong signing in with Google. Please try again.', 1842 'pusr.post_one': 'post', 1843 'pusr.post_many': 'posts', 1844 'pusr.on_this_site': 'on this site', 1845 'pusr.total': '{n} total', 1846 'pusr.joined': 'joined {date}', 1847 'pusr.send_dm': 'Send DM', 1848 'pusr.empty': 'No posts on this site yet.', 1849 'pusr.posts_heading': 'Posts', 1850 'pusr.untitled': '(untitled)', 1851 'phub.main_badge': 'Main page', 1852 'phub.view_page': 'View the page', 1853 'phub.members': 'Members', 1854 'phub.post_one': 'post', 1855 'phub.post_many': 'posts', 1856 'phub.all_members': 'All {n} members', 1857 'phub.latest_posts': 'Latest posts from our members.', 1858 'cfeed.title': 'Circle', 1859 'cfeed.sub': "What's happening at the other sites in my circle.", 1860 'cfeed.count': '{n} sites in my circle', 1861 'cfeed.empty': 'Nothing in my circle yet.', 1862 'cfeed.grid_view': 'Grid view', 1863 'cpost.back': 'Circle', 1864 'cpost.via': 'via', 1865 'cpost.read_more': 'Read more at {source}', 1866 'vblk.title': 'Viewer mode', 1867 'vblk.text_before': 'This is a read-only account. You can view everything, but', 1868 'vblk.text_strong': 'cannot change anything', 1869 'vblk.text_after': '— saving, uploading, deleting and commenting are disabled.', 1870 'vblk.back': 'Back', 1871 'vblk.to_home': 'Go to the main page', 1872 'phome.empty_title': "It's quiet here for now.", 1873 'phome.empty_sub': 'No posts yet. Exciting.', 1874 'phome.write_first': 'Write your first post', 1875 'phome.grid_view': 'Grid view', 1876 'fav.title': 'Favorites', 1877 'fav.sub': 'Posts you have liked. Tap the ♥ on a post to save it here.', 1878 'fav.empty': 'You have no favorites yet. Open a post and tap the heart ♡.', 1879 'chlog.back': 'Back', 1880 'chlog.title': 'Changes', 1881 'chlog.app_version': 'App version', 1882 'chlog.fed_proto': 'Federation proto', 1883 'chlog.manage_updates': 'Manage updates', 1884 'e404.title': 'Page not found', 1885 'e404.sub': 'This page no longer exists. The link may be outdated or mistyped.', 1886 'e404.home': 'Go to the homepage', 1887 'e404.archive': 'Archive', 1888 'ptype.eyebrow': 'Type', 1889 'ptype.count_one': '{n} post', 1890 'ptype.count_many': '{n} posts', 1891 'ptype.empty': 'No posts of this type yet.', 1892 'ptype.untitled': '(untitled)', 1893 'ptag.eyebrow': 'Tag', 1894 'ptag.count_one': '{n} post', 1895 'ptag.count_many': '{n} posts', 1896 'ptag.empty': 'No posts with this tag yet.', 1897 'ptag.untitled': '(untitled)', 1898 'parch.back': 'Back', 1899 'parch.title': 'Archive', 1900 'parch.count_one': '{n} post', 1901 'parch.count_many': '{n} posts', 1902 'parch.empty': 'No posts yet.', 1903 'prin.tagline': 'Your direct messages on this site.', 1904 'prin.empty': 'No conversations yet. Open someone\'s profile and click "Send DM" to start one.', 1905 'prin.empty_conv': 'Empty conversation', 1906 'prcv.back_aria': 'Back to inbox', 1907 'prcv.inbox': 'Inbox', 1908 'prcv.unknown': 'Unknown', 1909 'prcv.view_profile': 'View profile', 1910 'prcv.placeholder': 'Message…', 1911 'prcv.send': 'Send', 1305 1912 }, 1306 1913 de: { … … 1946 2553 'ahelp.s_updates_h': 'Updates', 1947 2554 'ahelp.s_updates_b': 'Verwaltung → <strong>Updates</strong> (nur God) zeigt die aktuelle Version und ob eine neuere verfügbar ist. Mit „Jetzt aktualisieren“ holst du dir die neueste Version.', 2555 'pedit.title_new': 'Neuer Beitrag', 2556 'pedit.title_edit': 'Beitrag bearbeiten', 2557 'pedit.f_title': 'Titel', 2558 'pedit.f_slug': 'Slug (URL)', 2559 'pedit.slug_placeholder': 'automatisch aus Titel, wenn leer', 2560 'pedit.f_tags': 'Tags', 2561 'pedit.tags_hint': '(kommagetrennt)', 2562 'pedit.f_excerpt': 'Zusammenfassung & Zirkel-Vorschau', 2563 'pedit.excerpt_hint': 'Wird auch als Zusammenfassung in <strong>Zirkeln</strong> verwendet (andere Seiten, die deinen Beitrag zeigen). Leer lassen = Anfang des Beitrags.', 2564 'pedit.s_cover': 'Titelbild', 2565 'pedit.f_cover_url': 'Titelbild-URL', 2566 'pedit.cover_url_placeholder': '/media/… oder https://… (oder per Schaltfläche hochladen)', 2567 'pedit.cover_upload_btn': 'Neues Titelbild hochladen', 2568 'pedit.s_content': 'Inhalt', 2569 'pedit.content_hint': 'Bild ziehen zum Einfügen · Text markieren zum Formatieren', 2570 'pedit.tb_done': 'Fertig', 2571 'pedit.tb_done_title': 'Bearbeitung beenden', 2572 'pedit.tb_bold': 'Fett', 2573 'pedit.tb_bold_title': 'Fett (Strg+B)', 2574 'pedit.tb_italic': 'Kursiv', 2575 'pedit.tb_italic_title': 'Kursiv (Strg+I)', 2576 'pedit.tb_underline': 'Unterstrichen', 2577 'pedit.tb_h2': 'Überschrift', 2578 'pedit.tb_h3': 'Zwischenüberschrift', 2579 'pedit.tb_p': 'Absatz', 2580 'pedit.tb_ul': 'Liste', 2581 'pedit.tb_ol': 'Nummerierte Liste', 2582 'pedit.tb_quote': 'Zitat', 2583 'pedit.tb_link': 'Link', 2584 'pedit.tb_link_title': 'Link (Strg+K)', 2585 'pedit.tb_code': 'Code', 2586 'pedit.tb_code_title': 'Code (inline)', 2587 'pedit.tb_image': 'Bild', 2588 'pedit.tb_image_title': 'Bild einfügen', 2589 'pedit.tb_track': 'Track', 2590 'pedit.tb_track_title': 'Track einfügen', 2591 'pedit.tb_playlist': 'Playlist', 2592 'pedit.tb_playlist_title': 'Playlist einfügen', 2593 'pedit.tb_embed': 'Medien einbetten', 2594 'pedit.tb_embed_title': 'Einbetten (YouTube, Spotify, SoundCloud, Vimeo…)', 2595 'pedit.tb_clear': 'Formatierung entfernen', 2596 'pedit.tb_fullscreen': 'Vollbild', 2597 'pedit.editor_aria': 'Inhalt', 2598 'pedit.editor_placeholder': 'Schreib los…', 2599 'pedit.tap_to_edit': 'Zum Bearbeiten tippen', 2600 'pedit.tap_to_write': 'Zum Schreiben tippen…', 2601 'pedit.chars': 'Zeichen', 2602 'pedit.s_publication': 'Veröffentlichung', 2603 'pedit.f_status': 'Status', 2604 'pedit.status_published': 'Veröffentlicht', 2605 'pedit.status_draft': 'Entwurf', 2606 'pedit.status_archived': 'Archiviert', 2607 'pedit.f_type': 'Typ', 2608 'pedit.type_post': 'Beitrag', 2609 'pedit.type_foto': 'Foto', 2610 'pedit.type_video': 'Video', 2611 'pedit.type_audio': 'Audio', 2612 'pedit.pin_label': 'Oben anheften', 2613 'pedit.pin_up': 'Nach oben', 2614 'pedit.pin_down': 'Nach unten', 2615 'pedit.pin_top': 'ganz oben', 2616 'pedit.pin_nth_suffix': '. von oben', 2617 'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 2618 'pedit.fan_only_label': 'Nur für angemeldete Fans', 2619 'pedit.publish_at_label': 'Veröffentlichen am (leer lassen = sofort)', 2620 'pedit.scheduled_for': 'Geplant für {d}', 2621 'pedit.cancel': 'Abbrechen', 2622 'pedit.publish': 'Veröffentlichen', 2623 'pedit.save': 'Speichern', 2624 'pedit.js_link_prompt': 'Link-URL (https://… oder /pfad)', 2625 'pedit.js_uploading': 'Wird hochgeladen…', 2626 'pedit.js_uploaded': 'Hochgeladen', 2627 'pedit.js_inserted': 'Eingefügt', 2628 'pedit.js_failed': 'Fehlgeschlagen', 2629 'pedit.js_embed_prompt': 'Füge eine Medien-URL zum Einbetten ein (YouTube, Spotify, SoundCloud, Vimeo, Apple Music, Bandcamp):', 2630 'pedit.js_embed_invalid': 'Gib eine vollständige URL ein (https://…).', 2631 'pedit.js_no_tracks_found': 'Keine Tracks gefunden für', 2632 'pedit.js_no_tracks_yet': 'Noch keine Tracks. Lade welche über Verwaltung dann Audio hoch.', 2633 'pedit.js_tracks_loading': 'Tracks werden geladen…', 2634 'pedit.js_tracks_load_fail': 'Tracks konnten nicht geladen werden', 2635 'pedit.js_playlist_editor_missing': 'Playlist-Editor nicht geladen', 2636 'pedit.js_playlist_existing': 'Vorhandene Playlists:', 2637 'pedit.js_playlist_choose': 'Wähle eine Nummer zum Einfügen, leer = neue erstellen:', 2638 'pedit.chip_track': 'Track', 2639 'pedit.chip_album': 'Album:', 2640 'pedit.chip_playlist': 'Playlist:', 2641 'pedit.tp_title': 'Track einfügen', 2642 'pedit.tp_close': 'Schließen', 2643 'pedit.tp_search_placeholder': 'Nach Titel oder Künstler suchen…', 2644 'pedit.tp_list_aria': 'Tracks', 2645 'acct.back_home': 'Zurück zur Startseite', 2646 'acct.title': 'Konto', 2647 'acct.subtitle': 'Profil und Avatar.', 2648 'acct.viewer_mode': 'Betrachter-Modus', 2649 'acct.viewer_note': 'Dies ist ein Demo-Konto. Du kannst alles ansehen, aber nichts ändern — auch kein Foto oder keine Bio.', 2650 'acct.profile': 'Profil', 2651 'acct.avatar_change': 'Zum Ändern deines Fotos klicken', 2652 'acct.member_since': 'Mitglied seit', 2653 'acct.avatar_remove': 'Foto entfernen', 2654 'acct.email': 'E-Mail-Adresse', 2655 'acct.email_ph': 'du@email.de', 2656 'acct.bio': 'Bio', 2657 'acct.bio_ph': 'Eine kurze Zeile über dich', 2658 'acct.bio_empty': 'Keine Bio', 2659 'acct.save': 'Speichern', 2660 'acct.site': 'Seite', 2661 'acct.site_name': 'Seitenname', 2662 'acct.site_name_hint': '— wird in der Kopfzeile deiner Seite angezeigt', 2663 'acct.tagline': 'Slogan', 2664 'acct.tagline_hint': '— kurzer Einzeiler (optional)', 2665 'acct.site_save': 'Seite speichern', 2666 'acct.password_change': 'Passwort ändern', 2667 'acct.password_current': 'Aktuelles Passwort', 2668 'acct.password_new': 'Neues Passwort', 2669 'acct.password_min': '(mind. 8 Zeichen)', 2670 'acct.password_confirm': 'Neues Passwort bestätigen', 2671 'acct.google_login': 'Mit Google anmelden', 2672 'acct.google_linked': '✓ Dein Google-Konto ist verknüpft — du kannst dich auch mit Google anmelden.', 2673 'acct.google_unlink': 'Google trennen', 2674 'acct.google_link_hint': 'Verknüpfe dein Google-Konto, damit du dich künftig auch mit einem Klick über Google anmelden kannst (zusätzlich zu deinem Passwort).', 2675 'acct.google_link': 'Google-Konto verknüpfen', 2676 'acct.login': 'Anmelden', 2677 'acct.login_google_only': 'Du bist über Google angemeldet ({email}). Es gibt kein separates Passwort.', 2678 'news.this_artist': 'diese:r Künstler:in', 2679 'news.form_title': 'Bleib auf dem Laufenden', 2680 'news.form_sub_before': 'Abonniere den Newsletter von ', 2681 'news.form_sub_after': ' — neue Musik, Konzerte und Updates, direkt in dein Postfach. Abmelden ist jederzeit mit einem Klick möglich.', 2682 'news.email_ph': 'du@email.de', 2683 'news.subscribe': 'Abonnieren', 2684 'news.check_title': 'Fast geschafft ✉', 2685 'news.check_sub_before': 'Wir haben eine Bestätigungs-E-Mail gesendet an ', 2686 'news.check_sub_after': '. Klicke auf den Link in dieser E-Mail, um dein Abo zu bestätigen.', 2687 'news.your_address': 'deine Adresse', 2688 'news.done_title': 'Du bist angemeldet ✓', 2689 'news.done_sub_before': 'Danke — du stehst auf der Liste von ', 2690 'news.done_sub_after': '.', 2691 'news.confirmed_title': 'Abo bestätigt ✓', 2692 'news.confirmed_sub_before': 'Super! Du erhältst ab jetzt den Newsletter von ', 2693 'news.confirmed_sub_after': '.', 2694 'news.unsubbed_title': 'Abgemeldet', 2695 'news.unsubbed_sub': 'Du wurdest abgemeldet. Du erhältst keine Newsletter mehr. Anders überlegt? Du kannst dich jederzeit wieder anmelden.', 2696 'news.invalid_title': 'Ungültige E-Mail-Adresse', 2697 'news.invalid_sub': 'Bitte überprüfe deine Adresse und versuche es erneut.', 2698 'news.back': '← Zurück', 2699 'news.smtperror_title': 'Hat gerade nicht geklappt', 2700 'news.smtperror_sub': 'Die Bestätigungs-E-Mail konnte nicht gesendet werden. Bitte versuche es später noch einmal.', 2701 'news.badtoken_title': 'Link ungültig oder abgelaufen', 2702 'news.badtoken_sub': 'Dieser Link funktioniert nicht mehr. Melde dich bei Bedarf erneut an.', 2703 'news.to_subscribe': 'Zur Anmeldung', 2704 'news.error_title': 'Etwas ist schiefgelaufen', 2705 'news.error_sub': 'Bitte versuche es später noch einmal.', 2706 'dl.ready_title': 'Danke! ⬇', 2707 'dl.ready_sub': 'Dein Download sollte jetzt starten. Passiert nichts?', 2708 'dl.manual': 'Download manuell starten', 2709 'dl.download_btn': 'Download', 2710 'dl.capture_sub': 'Hinterlasse deine E-Mail-Adresse und du erhältst die Datei. Du kommst damit auch auf die Newsletter-Liste — abmelden ist jederzeit möglich.', 2711 'dl.email_ph': 'du@email.de', 2712 'epk.kicker': 'Pressekit', 2713 'epk.edit': 'Bearbeiten', 2714 'epk.contact_booking': 'Kontakt / Buchung', 2715 'epk.view_site': 'Zur Seite →', 2716 'epk.press_photo': 'Pressefoto', 2717 'epk.most_played': 'Meistgehört', 2718 'epk.recent': 'Aktuell', 2719 'fgate.title': 'Nur für Fans', 2720 'fgate.sub': 'Dies ist exklusiv für eingeloggte Fans. Melde dich an, um es anzusehen.', 2721 'fgate.login': 'Anmelden / Registrieren', 2722 'lbio.empty': 'Noch keine Links eingerichtet.', 2723 'lbio.back_to_site': 'zurück zur Seite', 2724 'myst.overview': 'Übersicht', 2725 'myst.title': 'Mein Klonkt Hub', 2726 'myst.quick_links_aria': 'Verwaltungs-Schnellzugriffe', 2727 'myst.new_post': 'Neuer Beitrag', 2728 'myst.appearance': 'Erscheinungsbild', 2729 'myst.comments': 'Kommentare', 2730 'myst.view_site': 'Meine Seite ansehen', 2731 'myst.account': 'Konto', 2732 'myst.posts': 'Beiträge', 2733 'myst.published': 'Veröffentlicht', 2734 'myst.draft_count_one': '{n} Entwurf', 2735 'myst.draft_count_many': '{n} Entwürfe', 2736 'myst.draft_badge': 'Entwurf', 2737 'myst.untitled': '(ohne Titel)', 2738 'myst.edit': 'Bearbeiten', 2739 'myst.view': 'Ansehen', 2740 'arst.title': 'Passwort zurücksetzen', 2741 'arst.request_new': 'Neuen Reset-Link anfordern', 2742 'arst.back_login': 'Zurück zur Anmeldung', 2743 'arst.set_for': 'Lege ein neues Passwort für {username} fest.', 2744 'arst.new_pw': 'Neues Passwort (mind. 8 Zeichen)', 2745 'arst.confirm_pw': 'Neues Passwort bestätigen', 2746 'arst.submit': 'Passwort festlegen', 2747 'arrq.title': 'Passwort zurücksetzen', 2748 'arrq.sent': 'Falls für diese E-Mail-Adresse ein Konto besteht, wurde ein Reset-Link gesendet.', 2749 'arrq.no_mailserver': '<strong>Kein Mailserver eingerichtet</strong> — Reset-Link unten.', 2750 'arrq.no_mail_cli': 'Keine E-Mail eingerichtet? Der Administrator kann auch <code>npm run reset-admin</code> auf dem Server ausführen.', 2751 'arrq.back_login': '← Zurück zur Anmeldung', 2752 'arrq.tagline': 'Gib deine E-Mail-Adresse ein; wir senden dir einen Reset-Link.', 2753 'arrq.email': 'E-Mail', 2754 'arrq.submit': 'Reset-Link senden', 2755 'adir.home': 'Startseite', 2756 'adir.logout': 'Abmelden', 2757 'adir.login': 'Anmelden', 2758 'adir.title': 'Mitglieder', 2759 'adir.count_one': '{n} Klonkt', 2760 'adir.count_many': "{n} Klonkt's", 2761 'adir.search_ph': 'Nach Namen suchen…', 2762 'adir.search_aria': 'Mitglieder suchen', 2763 'adir.search_btn': 'Suchen', 2764 'adir.clear': 'Löschen', 2765 'adir.empty_q': 'Keine Mitglieder gefunden für „{q}".', 2766 'adir.empty': 'Noch keine Mitglieder.', 2767 'adir.posts_one': '{n} Beitrag', 2768 'adir.posts_many': '{n} Beiträge', 2769 'adir.pager_aria': 'Seitennummerierung', 2770 'adir.prev': '← Zurück', 2771 'adir.page_info': 'Seite {page} von {pages}', 2772 'adir.next': 'Weiter →', 2773 'glog.admin_title': 'Dies ist ein Administrator-Konto', 2774 'glog.admin_body': 'Du hast dich mit Google angemeldet, aber diese E-Mail-Adresse gehört dem Administrator dieser Seite. Die Google-Anmeldung ist nur für Hörer/Fans — Administratoren verwenden immer ihr Passwort (so kann eine Google-Anmeldung niemals versehentlich Administratorrechte erteilen).', 2775 'glog.admin_li1': '<strong>Bist du der Administrator?</strong> <a href="/auth/admin">Melde dich mit Benutzername + Passwort an</a>.', 2776 'glog.admin_li2': '<strong>Möchtest du dich künftig mit Google anmelden?</strong> Melde dich zuerst mit Passwort an und <strong>verknüpfe dein Google-Konto</strong> unter Konto → danach funktioniert die Google-Anmeldung auch für dich.', 2777 'glog.admin_li3': '<strong>Möchtest du dich als Fan anmelden?</strong> Verwende ein <strong>anderes Google-Konto</strong> (nicht deine Administrator-Adresse).', 2778 'glog.session_title': 'Anmeldung abgebrochen', 2779 'glog.session_body': 'Die Google-Anmeldung wurde abgebrochen oder die Sitzung war abgelaufen. Versuche es einfach erneut.', 2780 'glog.email_title': 'Keine verifizierte E-Mail-Adresse', 2781 'glog.email_body': 'Dein Google-Konto hat keine verifizierte E-Mail-Adresse. Verifiziere deine Adresse bei Google.', 2782 'glog.linked_title': 'Adresse bereits verknüpft', 2783 'glog.linked_body': 'Diese E-Mail-Adresse ist bereits mit einem anderen Google-Konto verknüpft. Verwende dieses Konto oder wende dich an den Administrator.', 2784 'glog.unavailable_title': 'Google-Anmeldung nicht verfügbar', 2785 'glog.unavailable_body': 'Die Google-Anmeldung wurde auf dieser Seite (noch) nicht eingerichtet.', 2786 'glog.failed_title': 'Google-Anmeldung fehlgeschlagen', 2787 'glog.failed_body': 'Beim Anmelden mit Google ist etwas schiefgelaufen. Bitte versuche es erneut.', 2788 'pusr.post_one': 'Beitrag', 2789 'pusr.post_many': 'Beiträge', 2790 'pusr.on_this_site': 'auf dieser Seite', 2791 'pusr.total': '{n} insgesamt', 2792 'pusr.joined': 'dabei seit {date}', 2793 'pusr.send_dm': 'DM senden', 2794 'pusr.empty': 'Noch keine Beiträge auf dieser Seite.', 2795 'pusr.posts_heading': 'Beiträge', 2796 'pusr.untitled': '(ohne Titel)', 2797 'phub.main_badge': 'Hauptseite', 2798 'phub.view_page': 'Seite ansehen', 2799 'phub.members': 'Mitglieder', 2800 'phub.post_one': 'Beitrag', 2801 'phub.post_many': 'Beiträge', 2802 'phub.all_members': 'Alle {n} Mitglieder', 2803 'phub.latest_posts': 'Neueste Beiträge unserer Mitglieder.', 2804 'cfeed.title': 'Zirkel', 2805 'cfeed.sub': 'Was bei den anderen Seiten in meinem Zirkel passiert.', 2806 'cfeed.count': '{n} Seiten in meinem Zirkel', 2807 'cfeed.empty': 'Noch nichts in meinem Zirkel.', 2808 'cfeed.grid_view': 'Rasteransicht', 2809 'cpost.back': 'Zirkel', 2810 'cpost.via': 'via', 2811 'cpost.read_more': 'Weiterlesen bei {source}', 2812 'vblk.title': 'Betrachter-Modus', 2813 'vblk.text_before': 'Dies ist ein schreibgeschütztes Konto. Du kannst alles ansehen, aber', 2814 'vblk.text_strong': 'nichts ändern', 2815 'vblk.text_after': '— Speichern, Hochladen, Löschen und Kommentieren sind deaktiviert.', 2816 'vblk.back': 'Zurück', 2817 'vblk.to_home': 'Zur Hauptseite', 2818 'phome.empty_title': 'Hier ist es noch still.', 2819 'phome.empty_sub': 'Noch keine Beiträge. Spannend.', 2820 'phome.write_first': 'Schreibe deinen ersten Beitrag', 2821 'phome.grid_view': 'Rasteransicht', 2822 'fav.title': 'Favoriten', 2823 'fav.sub': 'Beiträge, die dir gefallen haben. Tippe auf das ♥ eines Beitrags, um ihn hier zu speichern.', 2824 'fav.empty': 'Du hast noch keine Favoriten. Öffne einen Beitrag und tippe auf das Herz ♡.', 2825 'chlog.back': 'Zurück', 2826 'chlog.title': 'Änderungen', 2827 'chlog.app_version': 'App-Version', 2828 'chlog.fed_proto': 'Föderations-Proto', 2829 'chlog.manage_updates': 'Updates verwalten', 2830 'e404.title': 'Seite nicht gefunden', 2831 'e404.sub': 'Diese Seite existiert nicht (mehr). Vielleicht ist der Link veraltet oder falsch eingegeben.', 2832 'e404.home': 'Zur Startseite', 2833 'e404.archive': 'Archiv', 2834 'ptype.eyebrow': 'Typ', 2835 'ptype.count_one': '{n} Beitrag', 2836 'ptype.count_many': '{n} Beiträge', 2837 'ptype.empty': 'Noch keine Beiträge dieses Typs.', 2838 'ptype.untitled': '(ohne Titel)', 2839 'ptag.eyebrow': 'Tag', 2840 'ptag.count_one': '{n} Beitrag', 2841 'ptag.count_many': '{n} Beiträge', 2842 'ptag.empty': 'Noch keine Beiträge mit diesem Tag.', 2843 'ptag.untitled': '(ohne Titel)', 2844 'parch.back': 'Zurück', 2845 'parch.title': 'Archiv', 2846 'parch.count_one': '{n} Beitrag', 2847 'parch.count_many': '{n} Beiträge', 2848 'parch.empty': 'Noch keine Beiträge.', 2849 'prin.tagline': 'Deine Direktnachrichten auf dieser Seite.', 2850 'prin.empty': 'Noch keine Unterhaltungen. Öffne das Profil einer Person und klicke auf „DM senden“, um eine zu beginnen.', 2851 'prin.empty_conv': 'Leere Unterhaltung', 2852 'prcv.back_aria': 'Zurück zum Posteingang', 2853 'prcv.inbox': 'Posteingang', 2854 'prcv.unknown': 'Unbekannt', 2855 'prcv.view_profile': 'Profil ansehen', 2856 'prcv.placeholder': 'Nachricht…', 2857 'prcv.send': 'Senden', 1948 2858 }, 1949 2859 }; -
src/views/pages/404.ejs
rbbf9d1a r4885eab 1 1 <section class="nf"> 2 2 <div class="nf-code">404</div> 3 <h1 class="nf-title"> Pagina niet gevonden</h1>4 <p class="nf-sub"> Deze pagina bestaat niet (meer). Misschien is de link verouderd of verkeerd getypt.</p>3 <h1 class="nf-title"><%= t('e404.title') %></h1> 4 <p class="nf-sub"><%= t('e404.sub') %></p> 5 5 <div class="nf-actions"> 6 <a class="nf-btn nf-btn-primary" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/">← Naar de homepagina</a>6 <a class="nf-btn nf-btn-primary" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/">← <%= t('e404.home') %></a> 7 7 <% if (typeof site !== 'undefined' && site && site.show_archive_link !== 0) { %> 8 <a class="nf-btn" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/archive"> Archief</a>8 <a class="nf-btn" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/archive"><%= t('e404.archive') %></a> 9 9 <% } %> 10 10 </div> -
src/views/pages/account.ejs
rbbf9d1a r4885eab 2 2 3 3 <header class="ax-header"> 4 <a href="/" class="ax-back" aria-label=" Terug naar home">←</a>4 <a href="/" class="ax-back" aria-label="<%= t('acct.back_home') %>">←</a> 5 5 <div> 6 <h1> Account</h1>7 <p class="ax-tagline"> Profiel en avatar.</p>6 <h1><%= t('acct.title') %></h1> 7 <p class="ax-tagline"><%= t('acct.subtitle') %></p> 8 8 </div> 9 9 </header> … … 16 16 <span class="ax-viewer-ico" aria-hidden="true">👁️</span> 17 17 <div> 18 <strong> Kijker-modus</strong>19 <span> Dit is een demo-account. Je kunt alles bekijken, maar niets wijzigen — ook geen foto of bio.</span>18 <strong><%= t('acct.viewer_mode') %></strong> 19 <span><%= t('acct.viewer_note') %></span> 20 20 </div> 21 21 </div> … … 24 24 <%# ── PROFILE ────────────────────────────────────────────── %> 25 25 <section class="ax-card"> 26 <div class="ax-card-title"> Profiel</div>26 <div class="ax-card-title"><%= t('acct.profile') %></div> 27 27 28 28 <div class="ax-profile-id"> 29 29 <% if (canMutate) { %> 30 30 <form action="/account/avatar" method="post" enctype="multipart/form-data" class="ax-avatar-form"> 31 <label class="ax-avatar-wrap" title=" Klik om je foto te wijzigen">31 <label class="ax-avatar-wrap" title="<%= t('acct.avatar_change') %>"> 32 32 <span class="ax-profile-avatar"> 33 33 <% if (account.avatar_url) { %> … … 56 56 <div class="ax-profile-role"><%= account.role %> · <%= account.email %></div> 57 57 <% if (account.created_at) { %> 58 <div class="ax-profile-joined"> Lid sinds<%= formatDate(account.created_at) %></div>58 <div class="ax-profile-joined"><%= t('acct.member_since') %> <%= formatDate(account.created_at) %></div> 59 59 <% } %> 60 60 <% if (account.avatar_url && canMutate) { %> 61 61 <form action="/account/avatar/remove" method="post" class="ax-avatar-remove"> 62 <button type="submit" class="ax-linkbtn"> Foto verwijderen</button>62 <button type="submit" class="ax-linkbtn"><%= t('acct.avatar_remove') %></button> 63 63 </form> 64 64 <% } %> … … 69 69 <form action="/account/profile" method="post" class="ax-form ax-form-bio"> 70 70 <label class="ax-field"> 71 <span> E-mailadres</span>72 <input type="email" name="email" value="<%= account.email || '' %>" maxlength="254" autocomplete="email" placeholder=" jij@email.nl">73 </label> 74 <label class="ax-field"> 75 <span> Bio</span>76 <textarea name="bio" rows="3" maxlength="500" placeholder=" Een korte regel over jezelf"><%= account.bio || '' %></textarea>71 <span><%= t('acct.email') %></span> 72 <input type="email" name="email" value="<%= account.email || '' %>" maxlength="254" autocomplete="email" placeholder="<%= t('acct.email_ph') %>"> 73 </label> 74 <label class="ax-field"> 75 <span><%= t('acct.bio') %></span> 76 <textarea name="bio" rows="3" maxlength="500" placeholder="<%= t('acct.bio_ph') %>"><%= account.bio || '' %></textarea> 77 77 </label> 78 78 <div class="ax-form-actions"> 79 <button type="submit" class="ax-btn ax-btn-primary"> Opslaan</button>79 <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.save') %></button> 80 80 </div> 81 81 </form> … … 83 83 <div class="ax-form ax-form-bio"> 84 84 <label class="ax-field"> 85 <span> Bio</span>86 <textarea rows="3" disabled placeholder=" Geen bio"><%= account.bio || '' %></textarea>85 <span><%= t('acct.bio') %></span> 86 <textarea rows="3" disabled placeholder="<%= t('acct.bio_empty') %>"><%= account.bio || '' %></textarea> 87 87 </label> 88 88 </div> … … 93 93 <% if (typeof editableSite !== 'undefined' && editableSite && canMutate) { %> 94 94 <section class="ax-card"> 95 <div class="ax-card-title"> Site</div>95 <div class="ax-card-title"><%= t('acct.site') %></div> 96 96 <form action="/account/site" method="post" class="ax-form"> 97 97 <label class="ax-field"> 98 <span> Site-naam <small>— getoond in de kop van je site</small></span>98 <span><%= t('acct.site_name') %> <small><%= t('acct.site_name_hint') %></small></span> 99 99 <input type="text" name="site_title" value="<%= editableSite.title || '' %>" maxlength="200" required> 100 100 </label> 101 101 <label class="ax-field"> 102 <span> Tagline <small>— korte oneliner (optioneel)</small></span>102 <span><%= t('acct.tagline') %> <small><%= t('acct.tagline_hint') %></small></span> 103 103 <input type="text" name="site_tagline" value="<%= editableSite.tagline || '' %>" maxlength="200"> 104 104 </label> 105 105 <div class="ax-form-actions"> 106 <button type="submit" class="ax-btn ax-btn-primary"> Site opslaan</button>106 <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.site_save') %></button> 107 107 </div> 108 108 </form> … … 115 115 <% if (hasPassword) { %> 116 116 <section class="ax-card"> 117 <div class="ax-card-title"> Wachtwoord wijzigen</div>117 <div class="ax-card-title"><%= t('acct.password_change') %></div> 118 118 <form action="/account/password" method="post" class="ax-form"> 119 119 <label class="ax-field"> 120 <span> Huidig wachtwoord</span>120 <span><%= t('acct.password_current') %></span> 121 121 <input type="password" name="current" required autocomplete="current-password"> 122 122 </label> 123 123 <div class="ax-row ax-row-2"> 124 124 <label class="ax-field"> 125 <span> Nieuw wachtwoord <small>(min 8 tekens)</small></span>125 <span><%= t('acct.password_new') %> <small><%= t('acct.password_min') %></small></span> 126 126 <input type="password" name="new_password" required minlength="8" autocomplete="new-password"> 127 127 </label> 128 128 <label class="ax-field"> 129 <span> Bevestig nieuw wachtwoord</span>129 <span><%= t('acct.password_confirm') %></span> 130 130 <input type="password" name="confirm" required minlength="8" autocomplete="new-password"> 131 131 </label> 132 132 </div> 133 133 <div class="ax-form-actions"> 134 <button type="submit" class="ax-btn ax-btn-primary"> Wachtwoord wijzigen</button>134 <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.password_change') %></button> 135 135 </div> 136 136 </form> … … 139 139 <% if (typeof googleAvailable !== 'undefined' && googleAvailable) { %> 140 140 <section class="ax-card"> 141 <div class="ax-card-title"> Inloggen met Google</div>141 <div class="ax-card-title"><%= t('acct.google_login') %></div> 142 142 <% if (typeof googleLinked !== 'undefined' && googleLinked) { %> 143 <p class="ax-tagline" style="margin:0 0 1rem"> ✓ Je Google-account is gekoppeld — je kunt ook met Google inloggen.</p>143 <p class="ax-tagline" style="margin:0 0 1rem"><%= t('acct.google_linked') %></p> 144 144 <form action="/account/google/unlink" method="post"> 145 <button type="submit" class="ax-btn"> Google ontkoppelen</button>145 <button type="submit" class="ax-btn"><%= t('acct.google_unlink') %></button> 146 146 </form> 147 147 <% } else { %> 148 <p class="ax-tagline" style="margin:0 0 1rem"> Koppel je Google-account zodat je voortaan ook met één klik via Google kunt inloggen (naast je wachtwoord).</p>149 <a href="/auth/google/link" class="ax-btn ax-btn-primary" data-full-load> Koppel Google-account</a>148 <p class="ax-tagline" style="margin:0 0 1rem"><%= t('acct.google_link_hint') %></p> 149 <a href="/auth/google/link" class="ax-btn ax-btn-primary" data-full-load><%= t('acct.google_link') %></a> 150 150 <% } %> 151 151 </section> … … 153 153 <% } else { %> 154 154 <section class="ax-card"> 155 <div class="ax-card-title"> Inloggen</div>156 <p class="ax-tagline" style="margin:0"> Je bent ingelogd via Google (<%= account.email %>). Er is geen apart wachtwoord.</p>155 <div class="ax-card-title"><%= t('acct.login') %></div> 156 <p class="ax-tagline" style="margin:0"><%= t('acct.login_google_only', { email: account.email }) %></p> 157 157 </section> 158 158 <% } %> -
src/views/pages/archive.ejs
rbbf9d1a r4885eab 9 9 <a href="<%= _archBase %>/" class="btn archive-back" 10 10 hx-get="<%= _archBase %>/?partial=1" hx-target="#pcms-main" 11 hx-push-url="<%= _archBase %>/" hx-indicator="#pcms-loading">← Terug</a>11 hx-push-url="<%= _archBase %>/" hx-indicator="#pcms-loading">← <%= t('parch.back') %></a> 12 12 </p> 13 <h1> Archief</h1>14 <p class="archive-meta"><%= totalPosts %> bericht<%= totalPosts !== 1 ? 'en' : ''%></p>13 <h1><%= t('parch.title') %></h1> 14 <p class="archive-meta"><%= totalPosts === 1 ? t('parch.count_one', { n: totalPosts }) : t('parch.count_many', { n: totalPosts }) %></p> 15 15 16 16 <% if (totalPosts === 0) { %> 17 <p class="empty-state"> Nog geen berichten.</p>17 <p class="empty-state"><%= t('parch.empty') %></p> 18 18 <% } %> 19 19 -
src/views/pages/artists-directory.ejs
rbbf9d1a r4885eab 15 15 <nav class="adir-nav"> 16 16 <div class="adir-nav-inner container"> 17 <a class="adir-nav-home" href="/"><span aria-hidden="true">←</span> <%= (typeof hubTitle !== 'undefined' && hubTitle) ? hubTitle : 'Home'%></a>17 <a class="adir-nav-home" href="/"><span aria-hidden="true">←</span> <%= (typeof hubTitle !== 'undefined' && hubTitle) ? hubTitle : t('adir.home') %></a> 18 18 <span class="adir-nav-spacer"></span> 19 19 <% if (user) { %> 20 20 <a class="adir-nav-link" href="/account"><%= user.username %></a> 21 <a class="adir-nav-link" href="/auth/logout"> Uitloggen</a>21 <a class="adir-nav-link" href="/auth/logout"><%= t('adir.logout') %></a> 22 22 <% } else { %> 23 <a class="adir-nav-link" href="/auth/login"> Inloggen</a>23 <a class="adir-nav-link" href="/auth/login"><%= t('adir.login') %></a> 24 24 <% } %> 25 25 </div> … … 27 27 28 28 <header class="adir-head container"> 29 <h1 class="adir-title"> Leden</h1>30 <p class="adir-sub"><%= t otal %> Klonkt<%= total === 1 ? '' : "'s"%></p>29 <h1 class="adir-title"><%= t('adir.title') %></h1> 30 <p class="adir-sub"><%= t(total === 1 ? 'adir.count_one' : 'adir.count_many', { n: total }) %></p> 31 31 32 32 <form class="adir-search" method="get" action="/leden" role="search"> 33 <input type="search" name="q" value="<%= q %>" placeholder=" Zoek op naam…"34 autocomplete="off" aria-label=" Zoek leden">35 <button type="submit" class="adir-search-btn" aria-label=" Zoeken">33 <input type="search" name="q" value="<%= q %>" placeholder="<%= t('adir.search_ph') %>" 34 autocomplete="off" aria-label="<%= t('adir.search_aria') %>"> 35 <button type="submit" class="adir-search-btn" aria-label="<%= t('adir.search_btn') %>"> 36 36 <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> 37 37 </button> 38 <% if (q) { %><a class="adir-search-clear" href="/leden"> Wissen</a><% } %>38 <% if (q) { %><a class="adir-search-clear" href="/leden"><%= t('adir.clear') %></a><% } %> 39 39 </form> 40 40 </header> … … 43 43 <% if (!artists.length) { %> 44 44 <p class="adir-empty"> 45 <% if (q) { %> Geen leden gevonden voor "<strong><%= q %></strong>".<% } else { %>Nog geen leden.<% } %>45 <% if (q) { %><%- t('adir.empty_q', { q: '<strong>' + q + '</strong>' }) %><% } else { %><%= t('adir.empty') %><% } %> 46 46 </p> 47 47 <% } else { %> … … 55 55 <span class="roster-name"><%= a.title %></span> 56 56 <% if (a.tagline) { %><span class="roster-tag"><%= a.tagline %></span><% } %> 57 <span class="roster-count"><%= a.post_count %> post<%= a.post_count === 1 ? '' : 's'%></span>57 <span class="roster-count"><%= t(a.post_count === 1 ? 'adir.posts_one' : 'adir.posts_many', { n: a.post_count }) %></span> 58 58 </a> 59 59 <% }); %> … … 61 61 62 62 <% if (pages > 1) { %> 63 <nav class="adir-pager" aria-label=" Paginering">63 <nav class="adir-pager" aria-label="<%= t('adir.pager_aria') %>"> 64 64 <% if (page > 1) { %> 65 <a class="adir-page" href="<%= _qs({ page: page - 1 }) %>" rel="prev"> ← Vorige</a>65 <a class="adir-page" href="<%= _qs({ page: page - 1 }) %>" rel="prev"><%= t('adir.prev') %></a> 66 66 <% } else { %> 67 <span class="adir-page is-disabled"> ← Vorige</span>67 <span class="adir-page is-disabled"><%= t('adir.prev') %></span> 68 68 <% } %> 69 <span class="adir-page-info"> Pagina <%= page %> van <%= pages%></span>69 <span class="adir-page-info"><%= t('adir.page_info', { page: page, pages: pages }) %></span> 70 70 <% if (page < pages) { %> 71 <a class="adir-page" href="<%= _qs({ page: page + 1 }) %>" rel="next"> Volgende →</a>71 <a class="adir-page" href="<%= _qs({ page: page + 1 }) %>" rel="next"><%= t('adir.next') %></a> 72 72 <% } else { %> 73 <span class="adir-page is-disabled"> Volgende →</span>73 <span class="adir-page is-disabled"><%= t('adir.next') %></span> 74 74 <% } %> 75 75 </nav> -
src/views/pages/auth-login.ejs
rbbf9d1a r4885eab 11 11 <% 12 12 var GMAP = { 13 admin: { icon: '🔐', title: 'Dit is een beheerders-account',14 body: 'Je logde in met Google, maar dit e-mailadres hoort bij de beheerder van deze site. Inloggen met Google is alleen voor luisteraars/fans — beheerders gebruiken altijd hun wachtwoord (zo kan een Google-login nooit per ongeluk beheerrechten geven).',15 list: [ '<strong>Ben je de beheerder?</strong> <a href="/auth/admin">Log in met je gebruikersnaam + wachtwoord</a>.', '<strong>Wil je voortaan met Google inloggen?</strong> Log eerst in met wachtwoord en <strong>koppel je Google-account</strong> onder Account → daarna werkt Google-login ook voor jou.', '<strong>Wil je als fan inloggen?</strong> Gebruik een <strong>ander Google-account</strong> (niet je beheerdersadres).'] },16 session: { icon: '⏱️', title: 'Login afgebroken', body: 'De Google-login is afgebroken of de sessie was verlopen. Probeer het gewoon opnieuw.'},17 email: { icon: '✉️', title: 'Geen geverifieerd e-mailadres', body: 'Je Google-account heeft geen geverifieerd e-mailadres. Verifieer je adres bij Google.'},18 linked: { icon: '🔗', title: 'Adres al gekoppeld', body: 'Dit e-mailadres is al aan een ander Google-account gekoppeld. Gebruik dat account, of neem contact op met de beheerder.'},19 unavailable: { icon: '🚫', title: 'Google-login niet beschikbaar', body: 'Inloggen met Google is op deze site (nog) niet ingesteld.'},20 failed: { icon: '⚠️', title: 'Google-login mislukt', body: 'Er ging iets mis bij het inloggen met Google. Probeer het opnieuw.'},13 admin: { icon: '🔐', title: t('glog.admin_title'), 14 body: t('glog.admin_body'), 15 list: [t('glog.admin_li1'), t('glog.admin_li2'), t('glog.admin_li3')] }, 16 session: { icon: '⏱️', title: t('glog.session_title'), body: t('glog.session_body') }, 17 email: { icon: '✉️', title: t('glog.email_title'), body: t('glog.email_body') }, 18 linked: { icon: '🔗', title: t('glog.linked_title'), body: t('glog.linked_body') }, 19 unavailable: { icon: '🚫', title: t('glog.unavailable_title'), body: t('glog.unavailable_body') }, 20 failed: { icon: '⚠️', title: t('glog.failed_title'), body: t('glog.failed_body') }, 21 21 }; 22 22 var _g = (typeof gerr !== 'undefined' && gerr && GMAP[gerr]) ? GMAP[gerr] : null; -
src/views/pages/auth-reset-request.ejs
rbbf9d1a r4885eab 1 1 <div class="container auth-page"> 2 <h1> Wachtwoord resetten</h1>2 <h1><%= t('arrq.title') %></h1> 3 3 4 4 <% if (sent) { %> 5 5 <div class="alert alert-success"> 6 Als er een account bestaat voor dat e-mailadres, is er een reset-link verstuurd.6 <%= t('arrq.sent') %> 7 7 </div> 8 8 <% if (devResetUrl) { %> 9 9 <div class="alert alert-info"> 10 < strong>Geen e-mailserver ingesteld</strong> — reset-link hieronder.10 <%- t('arrq.no_mailserver') %> 11 11 <p style="margin: 0.5rem 0 0; word-break: break-all;"> 12 12 <a href="<%= devResetUrl %>"><%= devResetUrl %></a> … … 15 15 <% } %> 16 16 <% if (typeof mailer !== 'undefined' && !mailer) { %> 17 <p class="auth-link-muted"> Geen e-mail ingesteld? De beheerder kan ook <code>npm run reset-admin</code> draaien op de server.</p>17 <p class="auth-link-muted"><%- t('arrq.no_mail_cli') %></p> 18 18 <% } %> 19 <p class="auth-link"><a href="/auth/login"> ← Terug naar inloggen</a></p>19 <p class="auth-link"><a href="/auth/login"><%= t('arrq.back_login') %></a></p> 20 20 <% } else { %> 21 <p class="auth-tagline"> Vul je e-mail in; we sturen een reset-link.</p>21 <p class="auth-tagline"><%= t('arrq.tagline') %></p> 22 22 23 23 <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %> … … 25 25 <form method="post" action="/auth/reset-request" class="auth-form"> 26 26 <label> 27 <span> E-mail</span>27 <span><%= t('arrq.email') %></span> 28 28 <input type="email" name="email" required autofocus autocomplete="email"> 29 29 </label> 30 <button type="submit" class="btn btn-primary"> Stuur reset-link</button>31 <p class="auth-link"><a href="/auth/login"> ← Terug naar inloggen</a></p>30 <button type="submit" class="btn btn-primary"><%= t('arrq.submit') %></button> 31 <p class="auth-link"><a href="/auth/login"><%= t('arrq.back_login') %></a></p> 32 32 </form> 33 33 <% } %> -
src/views/pages/auth-reset.ejs
rbbf9d1a r4885eab 1 1 <div class="container auth-page"> 2 <h1> Wachtwoord resetten</h1>2 <h1><%= t('arst.title') %></h1> 3 3 4 4 <% if (!token) { %> 5 5 <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %> 6 6 <p class="auth-link"> 7 <a href="/auth/reset-request"> Vraag een nieuwe reset-link aan</a>7 <a href="/auth/reset-request"><%= t('arst.request_new') %></a> 8 8 · 9 <a href="/auth/login"> Terug naar inloggen</a>9 <a href="/auth/login"><%= t('arst.back_login') %></a> 10 10 </p> 11 11 <% } else { %> 12 <p class="auth-tagline">Stel een nieuw wachtwoord in voor <strong><%= username %></strong>.</p> 12 <p class="auth-tagline"><%- t('arst.set_for', { username: '<strong>' + username + '</strong>' }) %></p> 13 <%# arst.set_for bevat {username} → rendered met <%- omdat username in <strong> staat %> 13 14 14 15 <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %> … … 16 17 <form method="post" action="/auth/reset/<%= token %>" class="auth-form"> 17 18 <label> 18 <span> Nieuw wachtwoord (min 8 tekens)</span>19 <span><%= t('arst.new_pw') %></span> 19 20 <input type="password" name="new_password" required minlength="8" autofocus autocomplete="new-password"> 20 21 </label> 21 22 <label> 22 <span> Bevestig nieuw wachtwoord</span>23 <span><%= t('arst.confirm_pw') %></span> 23 24 <input type="password" name="confirm" required minlength="8" autocomplete="new-password"> 24 25 </label> 25 <button type="submit" class="btn btn-primary"> Wachtwoord instellen</button>26 <button type="submit" class="btn btn-primary"><%= t('arst.submit') %></button> 26 27 </form> 27 28 <% } %> -
src/views/pages/changelog.ejs
rbbf9d1a r4885eab 1 1 <div class="container changelog-page"> 2 <p><a href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/" class="btn">← Terug</a></p>3 <h1> Wijzigingen</h1>2 <p><a href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/" class="btn">← <%= t('chlog.back') %></a></p> 3 <h1><%= t('chlog.title') %></h1> 4 4 <p class="cl-meta"> 5 App-versie<strong>v<%= appVersion %></strong>5 <%= t('chlog.app_version') %> <strong>v<%= appVersion %></strong> 6 6 <span class="cl-sep">·</span> 7 Federatie-proto<strong><%= proto %></strong>7 <%= t('chlog.fed_proto') %> <strong><%= proto %></strong> 8 8 <% if (typeof user !== 'undefined' && user && user.role === 'god') { %> 9 <span class="cl-sep">·</span> <a href="/admin/updates"> Updates beheren</a>9 <span class="cl-sep">·</span> <a href="/admin/updates"><%= t('chlog.manage_updates') %></a> 10 10 <% } %> 11 11 </p> -
src/views/pages/circle-feed.ejs
rbbf9d1a r4885eab 10 10 </svg> 11 11 </span> 12 <h1> Cirkel</h1>13 <p class="cirkel-sub"> Wat er speelt bij de andere sites in mijn cirkel.</p>12 <h1><%= t('cfeed.title') %></h1> 13 <p class="cirkel-sub"><%= t('cfeed.sub') %></p> 14 14 <% if (typeof sites !== 'undefined' && sites && sites.length) { %> 15 15 <% if (sites.length <= 5) { %> … … 24 24 <% } else { %> 25 25 <%# Meer dan 5: geen chips (zou een muur worden) — alleen het aantal. %> 26 <span class="cirkel-count"><%= sites.length %> sites in mijn cirkel</span>26 <span class="cirkel-count"><%= t('cfeed.count', { n: sites.length }) %></span> 27 27 <% } %> 28 28 <% } %> … … 31 31 <% if (!posts.length) { %> 32 32 <div class="container"> 33 <p class="cirkel-empty"> Nog niets in mijn cirkel.</p>33 <p class="cirkel-empty"><%= t('cfeed.empty') %></p> 34 34 </div> 35 35 <% } else { %> … … 50 50 51 51 <!-- ===== GRID (body[data-feed-view="grid"]) ===== --> 52 <section class="feed-grid container" aria-label=" Grid-weergave">52 <section class="feed-grid container" aria-label="<%= t('cfeed.grid_view') %>"> 53 53 <div class="grid-tiles"> 54 54 <% posts.forEach(function(post){ %> -
src/views/pages/circle-post.ejs
rbbf9d1a r4885eab 2 2 <p class="cirkel-post-back"> 3 3 <a href="/cirkel" hx-get="/cirkel?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" 4 hx-push-url="/cirkel" hx-indicator="#pcms-loading">← Cirkel</a>4 hx-push-url="/cirkel" hx-indicator="#pcms-loading">← <%= t('cpost.back') %></a> 5 5 </p> 6 6 … … 8 8 <div class="cirkel-post-src"> 9 9 <% if (post.sourceAvatar) { %><img class="cirkel-post-avatar" src="<%= post.sourceAvatar %>" alt=""><% } %> 10 <span> via10 <span><%= t('cpost.via') %> 11 11 <% if (post.sourceUrl) { %><a href="<%= post.sourceUrl %>" target="_blank" rel="noopener"><%= post.sourceName %></a> 12 12 <% } else { %><%= post.sourceName %><% } %> … … 37 37 <% if (post.originalUrl) { %> 38 38 <p class="cirkel-post-readmore"> 39 <a href="<%= post.originalUrl %>" target="_blank" rel="noopener"> Lees verder bij <%= post.sourceName%> →</a>39 <a href="<%= post.originalUrl %>" target="_blank" rel="noopener"><%= t('cpost.read_more', { source: post.sourceName }) %> →</a> 40 40 </p> 41 41 <% } %> -
src/views/pages/download.ejs
rbbf9d1a r4885eab 1 <% 2 var _ready_h1 = t('dl.ready_title'); 3 var _ready_sub = t('dl.ready_sub'); 4 var _manual = t('dl.manual'); 5 var _capture_sub = t('dl.capture_sub'); 6 var _email_ph = t('dl.email_ph'); 7 var _download_btn = t('dl.download_btn'); 8 %> 1 9 <% 2 10 var t = (typeof dlTrack !== 'undefined') ? dlTrack : {}; … … 15 23 16 24 <% if (st === 'ready') { %> 17 <h1 class="dl-h1"> Bedankt! ⬇</h1>18 <p class="dl-sub"> Je download zou nu moeten starten. Gebeurt er niets?</p>19 <p><a class="dl-go" href="<%= fileUrl %>"> Download handmatig starten</a></p>25 <h1 class="dl-h1"><%= _ready_h1 %></h1> 26 <p class="dl-sub"><%= _ready_sub %></p> 27 <p><a class="dl-go" href="<%= fileUrl %>"><%= _manual %></a></p> 20 28 <script> 21 29 // Auto-start de download (zelfde-origin attachment-link). … … 23 31 </script> 24 32 <% } else { %> 25 <h1 class="dl-h1"> Download<%= t.title %></h1>26 <p class="dl-sub"> Laat je e-mailadres achter en je krijgt het bestand. Je komt dan ook op de nieuwsbrieflijst — uitschrijven kan altijd.</p>33 <h1 class="dl-h1"><%= _download_btn %> <%= t.title %></h1> 34 <p class="dl-sub"><%= _capture_sub %></p> 27 35 <% if (typeof dlError !== 'undefined' && dlError) { %><p class="dl-err"><%= dlError %></p><% } %> 28 36 <form method="POST" action="<%= siteUrlBase %>/download/<%= t.id %>" class="dl-form"> 29 <input type="email" name="email" required placeholder=" jouw@email.nl" value="<%= (typeof dlPrefill!=='undefined')?dlPrefill:'' %>" autocomplete="email">30 <button type="submit" class="dl-go">⬇ Download</button>37 <input type="email" name="email" required placeholder="<%= _email_ph %>" value="<%= (typeof dlPrefill!=='undefined')?dlPrefill:'' %>" autocomplete="email"> 38 <button type="submit" class="dl-go">⬇ <%= _download_btn %></button> 31 39 </form> 32 40 <% } %> -
src/views/pages/epk.ejs
rbbf9d1a r4885eab 11 11 <% if (_photo) { %><div class="epk-photo"><img src="<%= _photo %>" alt="<%= site.title %>"></div><% } %> 12 12 <div class="epk-hero-text"> 13 <div class="epk-kicker"> Perskit<% if (typeof user !== 'undefined' && user && user.role === 'god') { %> <a class="epk-edit-link" href="/admin/epk">✎ Bewerken</a><% } %></div>13 <div class="epk-kicker"><%= t('epk.kicker') %><% if (typeof user !== 'undefined' && user && user.role === 'god') { %> <a class="epk-edit-link" href="/admin/epk">✎ <%= t('epk.edit') %></a><% } %></div> 14 14 <h1 class="epk-title"><%= site.title %></h1> 15 15 <% if (epkBio) { %><p class="epk-bio"><%= epkBio %></p><% } %> 16 16 <div class="epk-actions"> 17 17 <% if (_isMail) { %> 18 <a class="epk-btn epk-btn-primary" href="mailto:<%= epkContact %>">✉ Contact / boeking</a>18 <a class="epk-btn epk-btn-primary" href="mailto:<%= epkContact %>">✉ <%= t('epk.contact_booking') %></a> 19 19 <% } else if (_isUrl) { %> 20 <a class="epk-btn epk-btn-primary" href="<%= epkContact %>" target="_blank" rel="noopener"> Contact / boeking</a>20 <a class="epk-btn epk-btn-primary" href="<%= epkContact %>" target="_blank" rel="noopener"><%= t('epk.contact_booking') %></a> 21 21 <% } %> 22 <a class="epk-btn" href="<%= siteUrlBase %>/"> Bekijk de site →</a>23 <% if (_photo) { %><a class="epk-btn" href="<%= _photo %>" download>⬇ Persfoto</a><% } %>22 <a class="epk-btn" href="<%= siteUrlBase %>/"><%= t('epk.view_site') %></a> 23 <% if (_photo) { %><a class="epk-btn" href="<%= _photo %>" download>⬇ <%= t('epk.press_photo') %></a><% } %> 24 24 </div> 25 25 </div> … … 28 28 <% if (epkTracks && epkTracks.length) { %> 29 29 <div class="epk-block"> 30 <h2 class="epk-h2"> Meest beluisterd</h2>30 <h2 class="epk-h2"><%= t('epk.most_played') %></h2> 31 31 <ul class="epk-tracks"> 32 32 <% epkTracks.forEach(function(t, i){ %> … … 47 47 <% if (epkPosts && epkPosts.length) { %> 48 48 <div class="epk-block"> 49 <h2 class="epk-h2"> Recent</h2>49 <h2 class="epk-h2"><%= t('epk.recent') %></h2> 50 50 <ul class="epk-posts"> 51 51 <% epkPosts.forEach(function(p){ %> -
src/views/pages/fan-gate.ejs
rbbf9d1a r4885eab 9 9 <div class="fg-card"> 10 10 <div class="fg-lock">🔒</div> 11 <h1 class="fg-h1"> Alleen voor fans</h1>11 <h1 class="fg-h1"><%= t('fgate.title') %></h1> 12 12 <% if (typeof fgTitle !== 'undefined' && fgTitle) { %><p class="fg-which">"<%= fgTitle %>"</p><% } %> 13 <p class="fg-sub"> Dit is exclusief voor ingelogde fans. Log in om het te bekijken.</p>14 <p><a class="fg-btn" href="/auth/login?next=<%= encodeURIComponent(fgNext || '/') %>"> Inloggen / aanmelden</a></p>13 <p class="fg-sub"><%= t('fgate.sub') %></p> 14 <p><a class="fg-btn" href="/auth/login?next=<%= encodeURIComponent(fgNext || '/') %>"><%= t('fgate.login') %></a></p> 15 15 </div> 16 16 </section> -
src/views/pages/favorites.ejs
rbbf9d1a r4885eab 1 1 <div class="container favorites-page"> 2 <h1> Favorieten</h1>3 <p class="fav-sub"> Posts die je hebt geliket. Tik op het ♥ bij een post om 'm hier te bewaren.</p>2 <h1><%= t('fav.title') %></h1> 3 <p class="fav-sub"><%= t('fav.sub') %></p> 4 4 5 5 <% if (!posts || !posts.length) { %> 6 <p class="fav-empty"> Je hebt nog geen favorieten. Open een post en tik op het hartje ♡.</p>6 <p class="fav-empty"><%= t('fav.empty') %></p> 7 7 <% } else { %> 8 8 <ul class="post-list"> -
src/views/pages/home.ejs
rbbf9d1a r4885eab 23 23 <% if (allPosts.length === 0) { %> 24 24 <div class="empty"> 25 <h1> Hier is het nog stil.</h1>26 <p> Nog geen posts. Spannend.</p>25 <h1><%= t('phome.empty_title') %></h1> 26 <p><%= t('phome.empty_sub') %></p> 27 27 <% if (user && canMutate && permissions.canCreatePost(user, site)) { %> 28 28 <p style="margin-top:2rem"> … … 30 30 hx-get="<%= _base %>/posts/new?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" 31 31 hx-push-url="<%= _base %>/posts/new" hx-indicator="#pcms-loading"> 32 Schrijf je eerste post→32 <%= t('phome.write_first') %> → 33 33 </a> 34 34 </p> … … 47 47 <!-- ========== GRID (only visible when body[data-feed-view="grid"]) ========== --> 48 48 <% if (allPosts.length > 0) { %> 49 <section class="feed-grid container" aria-label=" Grid-weergave">49 <section class="feed-grid container" aria-label="<%= t('phome.grid_view') %>"> 50 50 <div class="grid-tiles"> 51 51 <% allPosts.forEach(function(post) { %> -
src/views/pages/hub-home.ejs
rbbf9d1a r4885eab 28 28 </span> 29 29 <span class="hub-main-info"> 30 <span class="hub-main-badge"> Hoofdpagina</span>30 <span class="hub-main-badge"><%= t('phub.main_badge') %></span> 31 31 <span class="hub-main-name"><%= mainSite.title %></span> 32 32 <% if (mainSite.tagline) { %><span class="hub-main-tag"><%= mainSite.tagline %></span><% } %> 33 33 </span> 34 <span class="hub-main-cta"> Bekijk de pagina<span aria-hidden="true">→</span></span>34 <span class="hub-main-cta"><%= t('phub.view_page') %> <span aria-hidden="true">→</span></span> 35 35 </a> 36 36 </section> … … 43 43 <section class="hub-sec"> 44 44 <h2 class="hub-sec-title"> 45 Leden45 <%= t('phub.members') %> 46 46 <% if (_hasMore) { %><span class="hub-sec-count"><%= _total %></span><% } %> 47 47 </h2> … … 57 57 <span class="roster-name"><%= a.title %></span> 58 58 <% if (a.tagline) { %><span class="roster-tag"><%= a.tagline %></span><% } %> 59 <span class="roster-count"><%= a.post_count %> post<%= a.post_count === 1 ? '' : 's'%></span>59 <span class="roster-count"><%= a.post_count %> <%= a.post_count === 1 ? t('phub.post_one') : t('phub.post_many') %></span> 60 60 </a> 61 61 <% }); %> … … 63 63 <% if (_hasMore) { %> 64 64 <div class="hub-more"> 65 <a class="hub-more-link" href="/leden"> Alle <%= _total %> leden<span aria-hidden="true">→</span></a>65 <a class="hub-more-link" href="/leden"><%= t('phub.all_members', { n: _total }) %> <span aria-hidden="true">→</span></a> 66 66 </div> 67 67 <% } %> … … 72 72 <% if (posts && posts.length) { %> 73 73 <section class="hub-sec"> 74 <h2 class="hub-sec-title"> Laatste posts van onze leden.</h2>74 <h2 class="hub-sec-title"><%= t('phub.latest_posts') %></h2> 75 75 <div class="hub-feed"> 76 76 <% posts.forEach(function(p){ %> -
src/views/pages/linkbio.ejs
rbbf9d1a r4885eab 13 13 14 14 <% if (!links.length) { %> 15 <p class="lb-empty"> Nog geen links ingesteld.</p>15 <p class="lb-empty"><%= t('lbio.empty') %></p> 16 16 <% } else { %> 17 17 <div class="lb-links"> … … 25 25 <% } %> 26 26 27 <p class="lb-foot"><a href="<%= siteUrlBase %>/">← naar de site</a></p>27 <p class="lb-foot"><a href="<%= siteUrlBase %>/">← <%= t('lbio.back_to_site') %></a></p> 28 28 </section> 29 29 -
src/views/pages/my-site.ejs
rbbf9d1a r4885eab 1 1 <div class="container admin-page"> 2 <p><a href="/" class="btn">← Overzicht</a></p>3 <h1> Mijn Klonkt Hub</h1>2 <p><a href="/" class="btn">← <%= t('myst.overview') %></a></p> 3 <h1><%= t('myst.title') %></h1> 4 4 <p class="admin-tagline"><%= mySite.title %></p> 5 5 6 <nav class="admin-quick-links" aria-label=" Beheer-snelkoppelingen">7 <a href="/user/<%= mySite.slug %>/posts/new" class="btn">✍️ Nieuwe post</a>8 <a href="/admin/sites/<%= mySite.slug %>/edit" class="btn">🎨 Uiterlijk</a>9 <a href="/user/<%= mySite.slug %>/admin/comments" class="btn">💬 Reacties</a>10 <a href="/user/<%= mySite.slug %>" class="btn">👁️ Bekijk mijn site</a>11 <a href="/account" class="btn">👤 Account</a>6 <nav class="admin-quick-links" aria-label="<%= t('myst.quick_links_aria') %>"> 7 <a href="/user/<%= mySite.slug %>/posts/new" class="btn">✍️ <%= t('myst.new_post') %></a> 8 <a href="/admin/sites/<%= mySite.slug %>/edit" class="btn">🎨 <%= t('myst.appearance') %></a> 9 <a href="/user/<%= mySite.slug %>/admin/comments" class="btn">💬 <%= t('myst.comments') %></a> 10 <a href="/user/<%= mySite.slug %>" class="btn">👁️ <%= t('myst.view_site') %></a> 11 <a href="/account" class="btn">👤 <%= t('myst.account') %></a> 12 12 </nav> 13 13 14 14 <div class="admin-stats"> 15 <div class="stat-card"><div class="stat-num"><%= mine.posts %></div><div class="stat-label"> Posts</div></div>16 <div class="stat-card"><div class="stat-num"><%= mine.published %></div><div class="stat-label"> Gepubliceerd</div></div>15 <div class="stat-card"><div class="stat-num"><%= mine.posts %></div><div class="stat-label"><%= t('myst.posts') %></div></div> 16 <div class="stat-card"><div class="stat-num"><%= mine.published %></div><div class="stat-label"><%= t('myst.published') %></div></div> 17 17 </div> 18 18 19 19 <% if (typeof posts !== 'undefined' && posts && posts.length) { %> 20 20 <% var _drafts = posts.filter(function(p){ return p.isDraft; }).length; %> 21 <h2 class="ms-posts-title">Posts<% if (_drafts) { %> <span class="pl-draftcount"><%= _drafts %> concept<%= _drafts === 1 ? '' : 'en' %></span><% } %></h2> 21 <% 22 var _lblBadge = t('myst.draft_badge'); 23 var _lblUntitled = t('myst.untitled'); 24 var _lblEdit = t('myst.edit'); 25 var _lblView = t('myst.view'); 26 %> 27 <h2 class="ms-posts-title"><%= t('myst.posts') %><% if (_drafts) { %> <span class="pl-draftcount"><%= _drafts === 1 ? t('myst.draft_count_one', { n: _drafts }) : t('myst.draft_count_many', { n: _drafts }) %></span><% } %></h2> 22 28 <ul class="post-admin-list"> 23 29 <% posts.forEach(function(p) { %> 24 30 <li class="pal-row<%= p.isDraft ? ' is-draft' : '' %>"> 25 31 <a class="pal-title" href="<%= p.editUrl %>"> 26 <% if (p.isDraft) { %><span class="pal-badge"> Concept</span><% } %>27 <span class="pal-name"><%= p.title || '(zonder titel)'%></span>32 <% if (p.isDraft) { %><span class="pal-badge"><%= _lblBadge %></span><% } %> 33 <span class="pal-name"><%= p.title || _lblUntitled %></span> 28 34 </a> 29 35 <span class="pal-actions"> 30 <a class="pal-link" href="<%= p.editUrl %>"> Bewerken</a>31 <% if (!p.isDraft) { %><a class="pal-link" href="<%= p.viewUrl %>"> Bekijk</a><% } %>36 <a class="pal-link" href="<%= p.editUrl %>"><%= _lblEdit %></a> 37 <% if (!p.isDraft) { %><a class="pal-link" href="<%= p.viewUrl %>"><%= _lblView %></a><% } %> 32 38 </span> 33 39 </li> -
src/views/pages/newsletter.ejs
rbbf9d1a r4885eab 1 1 <% 2 2 var _site = site || {}; 3 var _title = _site.title || 'deze artiest';3 var _title = _site.title || t('news.this_artist'); 4 4 var st = (typeof nlState !== 'undefined') ? nlState : 'form'; 5 5 %> … … 7 7 <div class="nl-card"> 8 8 <% if (st === 'form') { %> 9 <h1 class="nl-h1"> Blijf op de hoogte</h1>10 <p class="nl-sub"> Schrijf je in voor de nieuwsbrief van <strong><%= _title %></strong> — nieuwe muziek, shows en updates, rechtstreeks in je inbox. Uitschrijven kan altijd met één klik.</p>9 <h1 class="nl-h1"><%= t('news.form_title') %></h1> 10 <p class="nl-sub"><%= t('news.form_sub_before') %><strong><%= _title %></strong><%= t('news.form_sub_after') %></p> 11 11 <form method="POST" action="<%= siteUrlBase %>/nieuwsbrief" class="nl-form"> 12 <input type="email" name="email" required placeholder=" jouw@email.nl" value="<%= (typeof nlPrefill !== 'undefined') ? nlPrefill : '' %>" autocomplete="email">13 <button type="submit" class="nl-btn"> Inschrijven</button>12 <input type="email" name="email" required placeholder="<%= t('news.email_ph') %>" value="<%= (typeof nlPrefill !== 'undefined') ? nlPrefill : '' %>" autocomplete="email"> 13 <button type="submit" class="nl-btn"><%= t('news.subscribe') %></button> 14 14 </form> 15 15 <% } else if (st === 'check') { %> 16 <h1 class="nl-h1"> Bijna klaar ✉</h1>17 <p class="nl-sub"> We hebben een bevestigingsmail gestuurd naar <strong><%= (typeof nlEmail!=='undefined')?nlEmail:'je adres' %></strong>. Klik op de link in die mail om je inschrijving te bevestigen.</p>16 <h1 class="nl-h1"><%= t('news.check_title') %></h1> 17 <p class="nl-sub"><%= t('news.check_sub_before') %><strong><%= (typeof nlEmail!=='undefined')?nlEmail:t('news.your_address') %></strong><%= t('news.check_sub_after') %></p> 18 18 <% } else if (st === 'done') { %> 19 <h1 class="nl-h1"> Je bent ingeschreven ✓</h1>20 <p class="nl-sub"> Bedankt — je staat op de lijst van <strong><%= _title %></strong>.</p>19 <h1 class="nl-h1"><%= t('news.done_title') %></h1> 20 <p class="nl-sub"><%= t('news.done_sub_before') %><strong><%= _title %></strong><%= t('news.done_sub_after') %></p> 21 21 <% } else if (st === 'confirmed') { %> 22 <h1 class="nl-h1"> Inschrijving bevestigd ✓</h1>23 <p class="nl-sub"> Top! Je ontvangt voortaan de nieuwsbrief van <strong><%= _title %></strong>.</p>22 <h1 class="nl-h1"><%= t('news.confirmed_title') %></h1> 23 <p class="nl-sub"><%= t('news.confirmed_sub_before') %><strong><%= _title %></strong><%= t('news.confirmed_sub_after') %></p> 24 24 <% } else if (st === 'unsubbed') { %> 25 <h1 class="nl-h1"> Uitgeschreven</h1>26 <p class="nl-sub"> Je bent afgemeld. Je ontvangt geen nieuwsbrieven meer. Van gedachten veranderd? Je kunt je altijd opnieuw inschrijven.</p>25 <h1 class="nl-h1"><%= t('news.unsubbed_title') %></h1> 26 <p class="nl-sub"><%= t('news.unsubbed_sub') %></p> 27 27 <% } else if (st === 'invalid') { %> 28 <h1 class="nl-h1"> Ongeldig e-mailadres</h1>29 <p class="nl-sub"> Controleer je adres en probeer 't opnieuw.</p>30 <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief"> ← Terug</a></p>28 <h1 class="nl-h1"><%= t('news.invalid_title') %></h1> 29 <p class="nl-sub"><%= t('news.invalid_sub') %></p> 30 <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief"><%= t('news.back') %></a></p> 31 31 <% } else if (st === 'smtperror') { %> 32 <h1 class="nl-h1"> Even niet gelukt</h1>33 <p class="nl-sub"> De bevestigingsmail kon niet verstuurd worden. Probeer 't later nog eens.</p>32 <h1 class="nl-h1"><%= t('news.smtperror_title') %></h1> 33 <p class="nl-sub"><%= t('news.smtperror_sub') %></p> 34 34 <% } else if (st === 'badtoken') { %> 35 <h1 class="nl-h1"> Link ongeldig of verlopen</h1>36 <p class="nl-sub"> Deze link werkt niet meer. Schrijf je eventueel opnieuw in.</p>37 <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief"> Naar inschrijven</a></p>35 <h1 class="nl-h1"><%= t('news.badtoken_title') %></h1> 36 <p class="nl-sub"><%= t('news.badtoken_sub') %></p> 37 <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief"><%= t('news.to_subscribe') %></a></p> 38 38 <% } else { %> 39 <h1 class="nl-h1"> Er ging iets mis</h1>40 <p class="nl-sub"> Probeer 't later nog eens.</p>39 <h1 class="nl-h1"><%= t('news.error_title') %></h1> 40 <p class="nl-sub"><%= t('news.error_sub') %></p> 41 41 <% } %> 42 42 </div> -
src/views/pages/post-edit.ejs
rbbf9d1a r4885eab 15 15 %> 16 16 <div class="container post-edit-page"> 17 <h1><%= isNew ? 'Nieuwe post' : 'Post bewerken'%></h1>17 <h1><%= isNew ? t('pedit.title_new') : t('pedit.title_edit') %></h1> 18 18 19 19 <form method="post" action="<%= _base %><%= isNew ? '/posts/create' : '/posts/' + post.slug + '/save' %>" class="post-edit-form"> … … 22 22 <section class="pe-card"> 23 23 <label class="pe-field"> 24 <span> Titel</span>24 <span><%= t('pedit.f_title') %></span> 25 25 <input type="text" name="title" value="<%= post.title %>" required> 26 26 </label> 27 27 <div class="pe-row pe-row-2"> 28 28 <label class="pe-field"> 29 <span> Slug (URL)</span>30 <input type="text" name="slug" value="<%= post.slug %>" placeholder=" auto van titel als leeg">29 <span><%= t('pedit.f_slug') %></span> 30 <input type="text" name="slug" value="<%= post.slug %>" placeholder="<%= t('pedit.slug_placeholder') %>"> 31 31 </label> 32 32 <label class="pe-field"> 33 <span> Tags <small>(komma-gescheiden)</small></span>33 <span><%= t('pedit.f_tags') %> <small><%= t('pedit.tags_hint') %></small></span> 34 34 <input type="text" name="tags" value="<%= Array.isArray(post.tags) ? post.tags.join(', ') : '' %>"> 35 35 </label> 36 36 </div> 37 37 <label class="pe-field"> 38 <span> Samenvatting & Cirkel Preview</span>38 <span><%= t('pedit.f_excerpt') %></span> 39 39 <textarea name="excerpt" rows="2"><%= post.excerpt %></textarea> 40 <small class="pe-hint" style="opacity:.7"> Wordt ook gebruikt als samenvatting in <strong>Cirkels</strong> (andere sites die je post tonen). Leeg laten = begin van de post.</small>40 <small class="pe-hint" style="opacity:.7"><%- t('pedit.excerpt_hint') %></small> 41 41 </label> 42 42 </section> … … 44 44 <%# ── COVER ────────────────────────────────────────────────── %> 45 45 <section class="pe-card"> 46 <div class="pe-section-title"> Cover</div>46 <div class="pe-section-title"><%= t('pedit.s_cover') %></div> 47 47 <div class="pe-cover-row"> 48 48 <div class="pe-cover-thumb" id="cover-preview-wrap" <%= _hasCover ? '' : 'data-empty' %>> … … 52 52 <div class="pe-cover-actions"> 53 53 <label class="pe-field"> 54 <span> Cover URL</span>54 <span><%= t('pedit.f_cover_url') %></span> 55 55 <input type="text" name="cover_image_url" id="cover-url-field" 56 56 value="<%= post.cover_image_url || '' %>" 57 57 inputmode="url" autocapitalize="none" spellcheck="false" 58 placeholder=" /media/… of https://… (of upload met de knop)">58 placeholder="<%= t('pedit.cover_url_placeholder') %>"> 59 59 </label> 60 60 <div class="pe-cover-upload"> 61 61 <button type="button" class="pe-btn pe-btn-secondary" id="cover-upload-trigger"> 62 📷 Upload nieuwe cover62 📷 <%= t('pedit.cover_upload_btn') %> 63 63 </button> 64 64 <input type="file" id="cover-upload-field" accept="image/jpeg,image/png,image/webp,image/gif" hidden> … … 72 72 <section class="pe-card pe-card-content"> 73 73 <div class="pe-section-title"> 74 Content75 <small class="pe-content-hint"> Sleep een afbeelding om in te voegen · selecteer tekst om op te maken</small>74 <%= t('pedit.s_content') %> 75 <small class="pe-content-hint"><%= t('pedit.content_hint') %></small> 76 76 </div> 77 77 <div class="pe-editor-frame"> 78 78 <div class="pe-toolbar" id="pe-toolbar" role="toolbar" aria-label="Format"> 79 <button type="button" id="pe-fs-done" class="pe-fs-done" title=" Klaar met bewerken">✓ Klaar</button>80 <button type="button" data-cmd="bold" title=" Vet (Ctrl+B)" aria-label="Vet">79 <button type="button" id="pe-fs-done" class="pe-fs-done" title="<%= t('pedit.tb_done_title') %>">✓ <%= t('pedit.tb_done') %></button> 80 <button type="button" data-cmd="bold" title="<%= t('pedit.tb_bold_title') %>" aria-label="<%= t('pedit.tb_bold') %>"> 81 81 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4h8a4 4 0 0 1 0 8H6z"/><path d="M6 12h9a4 4 0 0 1 0 8H6z"/></svg> 82 82 </button> 83 <button type="button" data-cmd="italic" title=" Cursief (Ctrl+I)" aria-label="Cursief">83 <button type="button" data-cmd="italic" title="<%= t('pedit.tb_italic_title') %>" aria-label="<%= t('pedit.tb_italic') %>"> 84 84 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg> 85 85 </button> 86 <button type="button" data-cmd="underline" title=" Onderstreept" aria-label="Onderstreept">86 <button type="button" data-cmd="underline" title="<%= t('pedit.tb_underline') %>" aria-label="<%= t('pedit.tb_underline') %>"> 87 87 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4v6a6 6 0 0 0 12 0V4"/><line x1="4" y1="20" x2="20" y2="20"/></svg> 88 88 </button> 89 89 <span class="pe-toolbar-sep" aria-hidden="true"></span> 90 <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title=" Kop">H2</button>91 <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title=" Subkop">H3</button>92 <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p" title=" Paragraaf">¶</button>90 <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="<%= t('pedit.tb_h2') %>">H2</button> 91 <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="<%= t('pedit.tb_h3') %>">H3</button> 92 <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p" title="<%= t('pedit.tb_p') %>">¶</button> 93 93 <span class="pe-toolbar-sep" aria-hidden="true"></span> 94 <button type="button" data-cmd="insertUnorderedList" title=" Lijst" aria-label="Lijst">94 <button type="button" data-cmd="insertUnorderedList" title="<%= t('pedit.tb_ul') %>" aria-label="<%= t('pedit.tb_ul') %>"> 95 95 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="9" y1="6" x2="20" y2="6"/><line x1="9" y1="12" x2="20" y2="12"/><line x1="9" y1="18" x2="20" y2="18"/><circle cx="4.5" cy="6" r="1.2"/><circle cx="4.5" cy="12" r="1.2"/><circle cx="4.5" cy="18" r="1.2"/></svg> 96 96 </button> 97 <button type="button" data-cmd="insertOrderedList" title=" Genummerde lijst" aria-label="Genummerde lijst">97 <button type="button" data-cmd="insertOrderedList" title="<%= t('pedit.tb_ol') %>" aria-label="<%= t('pedit.tb_ol') %>"> 98 98 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg> 99 99 </button> 100 <button type="button" data-cmd="formatBlock" data-arg="blockquote" title=" Quote" aria-label="Quote">100 <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="<%= t('pedit.tb_quote') %>" aria-label="<%= t('pedit.tb_quote') %>"> 101 101 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2H4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h2.5C6 17.5 4 19 3 19v2z"/><path d="M14 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2h-4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h2.5c-.5 4.5-2.5 6-3.5 6v2z"/></svg> 102 102 </button> 103 <button type="button" data-cmd="link-prompt" title=" Link (Ctrl+K)" aria-label="Link">103 <button type="button" data-cmd="link-prompt" title="<%= t('pedit.tb_link_title') %>" aria-label="<%= t('pedit.tb_link') %>"> 104 104 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg> 105 105 </button> 106 <button type="button" data-cmd="code-wrap" title=" Code (inline)" aria-label="Code">106 <button type="button" data-cmd="code-wrap" title="<%= t('pedit.tb_code_title') %>" aria-label="<%= t('pedit.tb_code') %>"> 107 107 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg> 108 108 </button> 109 109 <span class="pe-toolbar-sep" aria-hidden="true"></span> 110 <button type="button" id="insert-image-btn" title=" Afbeelding invoegen" aria-label="Afbeelding">110 <button type="button" id="insert-image-btn" title="<%= t('pedit.tb_image_title') %>" aria-label="<%= t('pedit.tb_image') %>"> 111 111 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg> 112 112 </button> 113 <button type="button" id="insert-track-btn" title=" Track invoegen" aria-label="Track">113 <button type="button" id="insert-track-btn" title="<%= t('pedit.tb_track_title') %>" aria-label="<%= t('pedit.tb_track') %>"> 114 114 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg> 115 115 </button> 116 <button type="button" id="insert-playlist-btn" title=" Playlist invoegen" aria-label="Playlist">116 <button type="button" id="insert-playlist-btn" title="<%= t('pedit.tb_playlist_title') %>" aria-label="<%= t('pedit.tb_playlist') %>"> 117 117 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="15" y2="18"/><polygon points="3 5 3 13 9 9"/></svg> 118 118 </button> 119 <button type="button" id="insert-embed-btn" title=" Embed (YouTube, Spotify, SoundCloud, Vimeo…)" aria-label="Media embedden">119 <button type="button" id="insert-embed-btn" title="<%= t('pedit.tb_embed_title') %>" aria-label="<%= t('pedit.tb_embed') %>"> 120 120 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><polygon points="10 9 15.5 12 10 15"/></svg> 121 121 </button> 122 122 <span class="pe-toolbar-spacer"></span> 123 <button type="button" data-cmd="removeFormat" title=" Wis opmaak" aria-label="Wis opmaak">123 <button type="button" data-cmd="removeFormat" title="<%= t('pedit.tb_clear') %>" aria-label="<%= t('pedit.tb_clear') %>"> 124 124 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M5 5l14 14" stroke-linecap="round"/></svg> 125 125 </button> 126 <button type="button" id="pe-fullscreen-btn" title=" Volledig scherm" aria-label="Volledig scherm" aria-pressed="false">126 <button type="button" id="pe-fullscreen-btn" title="<%= t('pedit.tb_fullscreen') %>" aria-label="<%= t('pedit.tb_fullscreen') %>" aria-pressed="false"> 127 127 <svg class="fs-icon-expand" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg> 128 128 <svg class="fs-icon-compress" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/><line x1="14" y1="10" x2="21" y2="3"/><line x1="3" y1="21" x2="10" y2="14"/></svg> … … 135 135 role="textbox" 136 136 aria-multiline="true" 137 aria-label=" Content"138 data-placeholder=" Begin met schrijven…"></div>137 aria-label="<%= t('pedit.editor_aria') %>" 138 data-placeholder="<%= t('pedit.editor_placeholder') %>"></div> 139 139 140 140 <%# Sticky "tik om te bewerken"-hint (alleen zichtbaar op touch + niet-fullscreen 141 141 via CSS). Puur visueel (pointer-events:none); de tik op het veld opent fullscreen. %> 142 <div class="pe-edit-hint" aria-hidden="true">✎ Tik om te bewerken</div>142 <div class="pe-edit-hint" aria-hidden="true">✎ <%= t('pedit.tap_to_edit') %></div> 143 143 144 144 <%# Hidden field is what actually submits to the server. The visible … … 153 153 <div class="pe-editor-status"> 154 154 <small id="content-upload-status" class="pe-status"></small> 155 <span class="pe-char-count"><span id="char-count">0</span> tekens</span>155 <span class="pe-char-count"><span id="char-count">0</span> <%= t('pedit.chars') %></span> 156 156 </div> 157 157 … … 162 162 <%# ── META ─────────────────────────────────────────────────── %> 163 163 <section class="pe-card"> 164 <div class="pe-section-title"> Publicatie</div>164 <div class="pe-section-title"><%= t('pedit.s_publication') %></div> 165 165 <div class="pe-row pe-row-2"> 166 166 <label class="pe-field"> 167 <span> Status</span>167 <span><%= t('pedit.f_status') %></span> 168 168 <% var _st = isNew ? 'published' : (post.status || 'draft'); %> 169 169 <select name="status"> 170 <option value="published" <%= _st === 'published' ? 'selected' : '' %>> Gepubliceerd</option>171 <option value="draft" <%= _st === 'draft' ? 'selected' : '' %>> Concept</option>172 <option value="archived" <%= _st === 'archived' ? 'selected' : '' %>> Gearchiveerd</option>170 <option value="published" <%= _st === 'published' ? 'selected' : '' %>><%= t('pedit.status_published') %></option> 171 <option value="draft" <%= _st === 'draft' ? 'selected' : '' %>><%= t('pedit.status_draft') %></option> 172 <option value="archived" <%= _st === 'archived' ? 'selected' : '' %>><%= t('pedit.status_archived') %></option> 173 173 </select> 174 174 </label> 175 175 <label class="pe-field"> 176 <span> Type</span>176 <span><%= t('pedit.f_type') %></span> 177 177 <select name="type"> 178 178 <% var ptype = post.type || 'post'; %> 179 <option value="post" <%= ptype === 'post' ? 'selected' : '' %>> Post</option>180 <option value="foto" <%= ptype === 'foto' ? 'selected' : '' %>> Foto</option>181 <option value="video" <%= ptype === 'video' ? 'selected' : '' %>> Video</option>182 <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>> Audio</option>179 <option value="post" <%= ptype === 'post' ? 'selected' : '' %>><%= t('pedit.type_post') %></option> 180 <option value="foto" <%= ptype === 'foto' ? 'selected' : '' %>><%= t('pedit.type_foto') %></option> 181 <option value="video" <%= ptype === 'video' ? 'selected' : '' %>><%= t('pedit.type_video') %></option> 182 <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>><%= t('pedit.type_audio') %></option> 183 183 </select> 184 184 </label> … … 188 188 <label class="pe-checkbox"> 189 189 <input type="checkbox" id="pin-toggle" <%= (Number(post.pinned) > 0) ? 'checked' : '' %>> 190 <span>📌 Vastpinnen bovenaan</span>190 <span>📌 <%= t('pedit.pin_label') %></span> 191 191 </label> 192 192 <input type="hidden" name="pinned" id="pin-rank" value="<%= post.pinned || 0 %>"> 193 193 <div class="pe-pin-pos" id="pin-pos" <%= (Number(post.pinned) > 0) ? '' : 'hidden' %>> 194 194 <span class="pe-pin-steps"> 195 <button type="button" class="pe-pin-btn" id="pin-up" aria-label=" Hoger zetten">▲</button>196 <button type="button" class="pe-pin-btn" id="pin-down" aria-label=" Lager zetten">▼</button>195 <button type="button" class="pe-pin-btn" id="pin-up" aria-label="<%= t('pedit.pin_up') %>">▲</button> 196 <button type="button" class="pe-pin-btn" id="pin-down" aria-label="<%= t('pedit.pin_down') %>">▼</button> 197 197 </span> 198 198 <span class="pe-pin-label" id="pin-label"></span> … … 201 201 <label class="pe-checkbox"> 202 202 <input type="checkbox" name="noindex" value="1" <%= post.noindex ? 'checked' : '' %>> 203 <span>🚫 noindex (verberg voor zoekmachines)</span>203 <span>🚫 <%= t('pedit.noindex_label') %></span> 204 204 </label> 205 205 <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %> 206 206 <label class="pe-checkbox"> 207 207 <input type="checkbox" name="fan_only" value="1" <%= post.fan_only ? 'checked' : '' %>> 208 <span>🔒 Alleen voor ingelogde fans</span>208 <span>🔒 <%= t('pedit.fan_only_label') %></span> 209 209 </label> 210 210 <div style="margin-top:8px"> 211 <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px">📅 Publiceren op (laat leeg = direct)</label>211 <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px">📅 <%= t('pedit.publish_at_label') %></label> 212 212 <input type="datetime-local" name="publish_at" value="<%= post.publish_at ? String(post.publish_at).replace(' ','T').slice(0,16) : '' %>" style="padding:8px 10px;border-radius:8px;border:1px solid var(--rule,rgba(128,128,128,.4));background:transparent;color:inherit"> 213 <% if (post.status === 'scheduled' && post.publish_at) { %><div style="font-size:12px;opacity:.7;margin-top:4px">⏳ Ingepland voor <%= post.publish_at%></div><% } %>213 <% if (post.status === 'scheduled' && post.publish_at) { %><div style="font-size:12px;opacity:.7;margin-top:4px">⏳ <%= t('pedit.scheduled_for', { d: post.publish_at }) %></div><% } %> 214 214 </div> 215 215 <% } %> … … 219 219 <%# ── ACTIONS (sticky at bottom) ──────────────────────────── %> 220 220 <div class="pe-actions"> 221 <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn"> Annuleren</a>221 <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn"><%= t('pedit.cancel') %></a> 222 222 <div class="pe-actions-spacer"></div> 223 223 <% if (!isNew && post.status !== 'published') { %> 224 <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 Publish</button>224 <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 <%= t('pedit.publish') %></button> 225 225 <% } %> 226 <button type="submit" class="pe-btn pe-btn-primary">💾 Opslaan</button>226 <button type="submit" class="pe-btn pe-btn-primary">💾 <%= t('pedit.save') %></button> 227 227 </div> 228 228 </form> … … 983 983 if (!coverField.files[0]) return; 984 984 coverStatus.classList.remove('is-error'); 985 coverStatus.textContent = ' Uploaden…';985 coverStatus.textContent = '<%= t('pedit.js_uploading') %>'; 986 986 try { 987 987 const j = await uploadImage(coverField.files[0]); 988 988 coverUrl.value = j.url; 989 989 showCoverPreview(j.url); 990 coverStatus.textContent = ' Geüpload✓';990 coverStatus.textContent = '<%= t('pedit.js_uploaded') %> ✓'; 991 991 setTimeout(() => { coverStatus.textContent = ''; }, 2000); 992 992 } catch (e) { 993 993 coverStatus.classList.add('is-error'); 994 coverStatus.textContent = ' Mislukt: ' + e.message;994 coverStatus.textContent = '<%= t('pedit.js_failed') %>: ' + e.message; 995 995 } 996 996 }); … … 1053 1053 // UUIDs are noisy — show a 6-char prefix for visual hint 1054 1054 const v = String(value || ''); 1055 return ' Track' + (v.length > 8 ? v.slice(0, 6) + '…' : v);1056 } 1057 if (kind === 'album') return ' Album:' + value;1058 if (kind === 'playlist') return ' Playlist:' + value;1055 return '<%= t('pedit.chip_track') %> ' + (v.length > 8 ? v.slice(0, 6) + '…' : v); 1056 } 1057 if (kind === 'album') return '<%= t('pedit.chip_album') %> ' + value; 1058 if (kind === 'playlist') return '<%= t('pedit.chip_playlist') %> ' + value; 1059 1059 if (kind === 'embed') { 1060 1060 const clean = String(value || '').replace(/^https?:\/\/(www\.)?/, ''); … … 1187 1187 } 1188 1188 function linkPrompt() { 1189 const url = window.prompt(' Link URL (https://… of /pad)');1189 const url = window.prompt('<%= t('pedit.js_link_prompt') %>'); 1190 1190 if (!url) return; 1191 1191 execCmd('createLink', url); … … 1288 1288 if (fsBtn) { 1289 1289 fsBtn.setAttribute('aria-pressed', on ? 'true' : 'false'); 1290 fsBtn.title = on ? ' Klaar' : 'Volledig scherm';1290 fsBtn.title = on ? '<%= t('pedit.tb_done') %>' : '<%= t('pedit.tb_fullscreen') %>'; 1291 1291 } 1292 1292 if (window.visualViewport) { … … 1342 1342 editor.setAttribute('contenteditable', 'false'); 1343 1343 editor.classList.add('pe-tap-to-edit'); 1344 editor.setAttribute('data-placeholder', ' Tik om te schrijven…');1344 editor.setAttribute('data-placeholder', '<%= t('pedit.tap_to_write') %>'); 1345 1345 editor.addEventListener('click', function () { 1346 1346 if (!isFs()) openFs(); … … 1390 1390 async function uploadAndInsertImage(file) { 1391 1391 contentStatus.classList.remove('is-error'); 1392 contentStatus.textContent = ' Uploaden…';1392 contentStatus.textContent = '<%= t('pedit.js_uploading') %>'; 1393 1393 try { 1394 1394 const j = await uploadImage(file); … … 1396 1396 editor.focus({ preventScroll: true }); 1397 1397 document.execCommand('insertHTML', false, img); 1398 contentStatus.textContent = ' Ingevoegd✓';1398 contentStatus.textContent = '<%= t('pedit.js_inserted') %> ✓'; 1399 1399 setTimeout(() => { contentStatus.textContent = ''; }, 2000); 1400 1400 updateCharCount(); 1401 1401 } catch (e) { 1402 1402 contentStatus.classList.add('is-error'); 1403 contentStatus.textContent = ' Mislukt: ' + e.message;1403 contentStatus.textContent = '<%= t('pedit.js_failed') %>: ' + e.message; 1404 1404 } 1405 1405 } … … 1459 1459 if (embedBtn) { 1460 1460 embedBtn.addEventListener('click', () => { 1461 const raw = window.prompt(' Plak een media-URL om in te sluiten\n(YouTube, Spotify, SoundCloud, Vimeo, Apple Music, Bandcamp):');1461 const raw = window.prompt('<%= t('pedit.js_embed_prompt') %>'); 1462 1462 if (!raw) return; 1463 1463 const url = raw.trim(); 1464 if (!/^https?:\/\//i.test(url)) { alert(' Geef een volledige URL (https://…).'); return; }1464 if (!/^https?:\/\//i.test(url)) { alert('<%= t('pedit.js_embed_invalid') %>'); return; } 1465 1465 insertChip('embed', url); 1466 1466 }); … … 1501 1501 if (!filtered.length) { 1502 1502 tpList.innerHTML = ''; 1503 tpEmpty.textContent = q ? ' Geen tracks gevonden voor "' + q + '"' : 'Nog geen tracks. Upload via Beheer → Audio.';1503 tpEmpty.textContent = q ? '<%= t('pedit.js_no_tracks_found') %> ' + q : '<%= t('pedit.js_no_tracks_yet') %>'; 1504 1504 tpList.appendChild(tpEmpty); 1505 1505 return; … … 1527 1527 async function loadTracks() { 1528 1528 if (Array.isArray(tpCache)) return tpCache; 1529 tpEmpty.textContent = ' Tracks laden…';1529 tpEmpty.textContent = '<%= t('pedit.js_tracks_loading') %>'; 1530 1530 try { 1531 1531 const r = await fetch('/admin/playlists/api/tracks', { credentials: 'same-origin' }); … … 1534 1534 } catch (e) { 1535 1535 tpCache = []; 1536 tpEmpty.textContent = ' Kon tracks niet laden: ' + e.message;1536 tpEmpty.textContent = '<%= t('pedit.js_tracks_load_fail') %>: ' + e.message; 1537 1537 } 1538 1538 return tpCache; … … 1596 1596 playlistBtn.addEventListener('click', async () => { 1597 1597 if (typeof window.openPlaylistEditor !== 'function') { 1598 alert(' Playlist editor niet geladen');1598 alert('<%= t('pedit.js_playlist_editor_missing') %>'); 1599 1599 return; 1600 1600 } … … 1604 1604 if (j.ok && Array.isArray(j.playlists) && j.playlists.length > 0) { 1605 1605 const choice = prompt( 1606 ' Bestaande playlists:\n\n' +1606 '<%= t('pedit.js_playlist_existing') %>\n\n' + 1607 1607 j.playlists.map((p, i) => `${i + 1}. ${p.title} (${p.track_count} tracks)`).join('\n') + 1608 '\n\n Kies een nummer om in te voegen, leeg = nieuwe maken:'1608 '\n\n<%= t('pedit.js_playlist_choose') %>' 1609 1609 ); 1610 1610 if (choice && /^\d+$/.test(choice.trim())) { … … 1644 1644 <div class="tp-sheet" role="dialog" aria-modal="true" aria-labelledby="tp-title"> 1645 1645 <header class="tp-header"> 1646 <h2 id="tp-title" class="tp-h2"> Track invoegen</h2>1647 <button type="button" class="tp-close" data-tp-close aria-label=" Sluiten">1646 <h2 id="tp-title" class="tp-h2"><%= t('pedit.tp_title') %></h2> 1647 <button type="button" class="tp-close" data-tp-close aria-label="<%= t('pedit.tp_close') %>"> 1648 1648 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> 1649 1649 </button> … … 1653 1653 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> 1654 1654 </span> 1655 <input type="search" class="tp-search" id="tp-search" placeholder=" Zoek op titel of artiest…" autocomplete="off" inputmode="search">1655 <input type="search" class="tp-search" id="tp-search" placeholder="<%= t('pedit.tp_search_placeholder') %>" autocomplete="off" inputmode="search"> 1656 1656 </div> 1657 <div class="tp-list" id="tp-list" role="listbox" aria-label=" Tracks">1658 <div class="tp-empty" id="tp-empty"> Tracks laden…</div>1657 <div class="tp-list" id="tp-list" role="listbox" aria-label="<%= t('pedit.tp_list_aria') %>"> 1658 <div class="tp-empty" id="tp-empty"><%= t('pedit.js_tracks_loading') %></div> 1659 1659 </div> 1660 1660 </div> … … 1682 1682 function descr(n) { 1683 1683 n = Number(n) || 0; 1684 if (n <= 1) return ' bovenaan';1685 return n + ' e van boven';1684 if (n <= 1) return '<%= t('pedit.pin_top') %>'; 1685 return n + '<%= t('pedit.pin_nth_suffix') %>'; 1686 1686 } 1687 1687 function render() { -
src/views/pages/prutter-conversation.ejs
rbbf9d1a r4885eab 1 1 <div class="prutter-conv-page" data-conversation-id="<%= conversation.id %>" data-me="<%= user.id %>"> 2 2 <header class="prutter-conv-header"> 3 <a class="prutter-back" href="<%= siteUrlBase %>/prutter" aria-label=" Terug naar inbox" title="Inbox">3 <a class="prutter-back" href="<%= siteUrlBase %>/prutter" aria-label="<%= t('prcv.back_aria') %>" title="<%= t('prcv.inbox') %>"> 4 4 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg> 5 5 </a> … … 12 12 </div> 13 13 <div class="prutter-conv-id"> 14 <span class="prutter-conv-name"><%= other ? other.username : 'Onbekend'%></span>14 <span class="prutter-conv-name"><%= other ? other.username : t('prcv.unknown') %></span> 15 15 <% if (other) { %> 16 <a class="prutter-conv-sub" href="/users/<%= encodeURIComponent(other.username) %>"> Bekijk profiel</a>16 <a class="prutter-conv-sub" href="/users/<%= encodeURIComponent(other.username) %>"><%= t('prcv.view_profile') %></a> 17 17 <% } %> 18 18 </div> … … 34 34 hx-target="#prutter-thread" 35 35 hx-swap="beforeend"> 36 <textarea name="content" rows="1" maxlength="2000" placeholder=" Bericht…" required></textarea>37 <button type="submit" class="btn btn-primary"> Stuur</button>36 <textarea name="content" rows="1" maxlength="2000" placeholder="<%= t('prcv.placeholder') %>" required></textarea> 37 <button type="submit" class="btn btn-primary"><%= t('prcv.send') %></button> 38 38 </form> 39 39 </div> -
src/views/pages/prutter-inbox.ejs
rbbf9d1a r4885eab 2 2 <header class="prutter-header"> 3 3 <h1>Prutter</h1> 4 <p class="prutter-tagline"> Your direct messages on this site.</p>4 <p class="prutter-tagline"><%= t('prin.tagline') %></p> 5 5 </header> 6 6 7 7 <% if (!conversations.length) { %> 8 8 <p class="prutter-empty"> 9 No conversations yet. Open someone's profile and click <em>Send DM</em> to start one.9 <%= t('prin.empty') %> 10 10 </p> 11 11 <% } else { %> … … 31 31 <div class="prutter-conv-preview"><%= c.last_message_preview %></div> 32 32 <% } else { %> 33 <div class="prutter-conv-preview muted"> Empty conversation</div>33 <div class="prutter-conv-preview muted"><%= t('prin.empty_conv') %></div> 34 34 <% } %> 35 35 </div> -
src/views/pages/tag.ejs
rbbf9d1a r4885eab 1 1 <div class="container tag-page"> 2 2 <header class="tag-page-header"> 3 <p class="tag-eyebrow"> Tag</p>3 <p class="tag-eyebrow"><%= t('ptag.eyebrow') %></p> 4 4 <h1>#<%= tag %></h1> 5 5 <p class="tag-meta"> 6 <%= posts.length %> post<%= posts.length === 1 ? '' : 's'%>6 <%= posts.length === 1 ? t('ptag.count_one', { n: posts.length }) : t('ptag.count_many', { n: posts.length }) %> 7 7 </p> 8 8 </header> 9 9 10 10 <% if (!posts.length) { %> 11 <p class="tag-empty"> No posts with this tag yet.</p>11 <p class="tag-empty"><%= t('ptag.empty') %></p> 12 12 <% } else { %> 13 13 <ol class="tag-results"> … … 26 26 hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" 27 27 hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading"> 28 <%= p.title || '(untitled)'%>28 <%= p.title || t('ptag.untitled') %> 29 29 </a> 30 30 </h2> -
src/views/pages/type.ejs
rbbf9d1a r4885eab 1 1 <div class="container type-page"> 2 2 <header class="type-page-header"> 3 <p class="type-eyebrow"> Type</p>3 <p class="type-eyebrow"><%= t('ptype.eyebrow') %></p> 4 4 <h1><%= type.charAt(0).toUpperCase() + type.slice(1) %></h1> 5 <p class="type-meta"><%= posts.length %> post<%= posts.length === 1 ? '' : 's'%></p>5 <p class="type-meta"><%= posts.length === 1 ? t('ptype.count_one', { n: posts.length }) : t('ptype.count_many', { n: posts.length }) %></p> 6 6 </header> 7 7 8 8 <% if (!posts.length) { %> 9 <p class="type-empty"> No posts of this type yet.</p>9 <p class="type-empty"><%= t('ptype.empty') %></p> 10 10 <% } else { %> 11 11 <ol class="type-results"> … … 24 24 hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" 25 25 hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading"> 26 <%= p.title || '(untitled)'%>26 <%= p.title || t('ptype.untitled') %> 27 27 </a> 28 28 </h2> -
src/views/pages/user.ejs
rbbf9d1a r4885eab 19 19 <% } %> 20 20 <p class="user-stats"> 21 <%= posts.length %> post<%= posts.length === 1 ? '' : 's' %> on this site21 <%= posts.length %> <%= posts.length === 1 ? t('pusr.post_one') : t('pusr.post_many') %> <%= t('pusr.on_this_site') %> 22 22 <% if (totalPosts > posts.length) { %> 23 · <%= t otalPosts %> total23 · <%= t('pusr.total', { n: totalPosts }) %> 24 24 <% } %> 25 25 <% if (author.created_at) { %> 26 · joined <%= formatDate(author.created_at) %>26 · <%= t('pusr.joined', { date: formatDate(author.created_at) }) %> 27 27 <% } %> 28 28 </p> 29 29 <% if (user && user.id !== author.id && site && site.enable_prutter && (typeof tenancy !== 'undefined' && tenancy === 'hub') && (typeof premiumUnlocked === 'undefined' || premiumUnlocked)) { %> 30 30 <p class="user-actions"> 31 <a href="<%= siteUrlBase %>/prutter/new?to=<%= encodeURIComponent(author.username) %>" class="btn btn-primary">💬 Send DM</a>31 <a href="<%= siteUrlBase %>/prutter/new?to=<%= encodeURIComponent(author.username) %>" class="btn btn-primary">💬 <%= t('pusr.send_dm') %></a> 32 32 </p> 33 33 <% } %> … … 36 36 37 37 <% if (!posts.length) { %> 38 <p class="user-empty"> No posts on this site yet.</p>38 <p class="user-empty"><%= t('pusr.empty') %></p> 39 39 <% } else { %> 40 <h2 class="user-posts-heading"> Posts</h2>40 <h2 class="user-posts-heading"><%= t('pusr.posts_heading') %></h2> 41 41 <ol class="user-posts"> 42 42 <% posts.forEach(function(p) { %> … … 54 54 hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" 55 55 hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading"> 56 <%= p.title || '(untitled)'%>56 <%= p.title || t('pusr.untitled') %> 57 57 </a> 58 58 </h3> -
src/views/pages/viewer-blocked.ejs
rbbf9d1a r4885eab 2 2 <div class="vb-card"> 3 3 <div class="vb-ico" aria-hidden="true">👁️</div> 4 <h1 class="vb-title"> Kijker-modus</h1>4 <h1 class="vb-title"><%= t('vblk.title') %></h1> 5 5 <p class="vb-text"> 6 Dit is een alleen-lezen account. Je kunt alles bekijken, maar 7 <strong>niets wijzigen</strong> — opslaan, uploaden, verwijderen en 8 reageren zijn uitgeschakeld. 6 <%= t('vblk.text_before') %> 7 <strong><%= t('vblk.text_strong') %></strong> <%= t('vblk.text_after') %> 9 8 </p> 10 9 <div class="vb-actions"> 11 <button type="button" class="vb-btn vb-btn-primary" onclick="history.back()">← Terug</button>12 <a class="vb-btn" href="/"> Naar de hoofdpagina</a>10 <button type="button" class="vb-btn vb-btn-primary" onclick="history.back()">← <%= t('vblk.back') %></button> 11 <a class="vb-btn" href="/"><%= t('vblk.to_home') %></a> 13 12 </div> 14 13 </div>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)