~vpzom/hitide

daa839ac94e81962ad17e5962e38ff8da935c05a — Colin Reeder 1 year, 1 month ago fcedb0f + a03e2f1
Merge branch 'lang-placeholder'
M Cargo.lock => Cargo.lock +2 -2
@@ 1132,7 1132,7 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "render"
version = "0.3.1"
source = "git+https://github.com/vpzomtrrfrt/render.rs?rev=ec18ff5f#ec18ff5f013e4d448ff59c5dffb2c11fb6668bc2"
source = "git+https://github.com/vpzomtrrfrt/render.rs?branch=for-hitide#19750310ed42eb978da944b0ee206731dad293c4"
dependencies = [
 "render_macros",
]


@@ 1140,7 1140,7 @@ dependencies = [
[[package]]
name = "render_macros"
version = "0.3.1"
source = "git+https://github.com/vpzomtrrfrt/render.rs?rev=ec18ff5f#ec18ff5f013e4d448ff59c5dffb2c11fb6668bc2"
source = "git+https://github.com/vpzomtrrfrt/render.rs?branch=for-hitide#19750310ed42eb978da944b0ee206731dad293c4"
dependencies = [
 "proc-macro-error",
 "proc-macro2",

M Cargo.toml => Cargo.toml +1 -1
@@ 11,7 11,7 @@ license = "AGPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
render = { git = "https://github.com/vpzomtrrfrt/render.rs", rev = "ec18ff5f" }
render = { git = "https://github.com/vpzomtrrfrt/render.rs", branch = "for-hitide" }
trout = "0.4.0"
hyper = "0.13.6"
hyper-tls = "0.4.1"

M res/lang/de.ftl => res/lang/de.ftl +16 -13
@@ 2,15 2,14 @@ about = Über
about_title = Über diese Instanz
about_what_is = Was ist lotide?
about_text1 = lotide ist ein Versuch, ein föderiertes Forum zu bauen. Nutzer können Communities erstellen um Links oder Textbeiträge zu teilen und diese mit anderen Nutzern diskutieren, auch mit welchen die auf anderen Servern registriert sind
about_text2 = Für mehr Informationen oder um den Sourcecode anzusehen, gehe zur
about_sourcehut = SourceHut Seite
about_text2 = Für mehr Informationen oder um den Sourcecode anzusehen, gehe zur { $part_sourcehut }
about_text2_part_sourcehut = SourceHut Seite
about_versions = Diese Instanz läuft auf hitide { $hitide_version } auf { $backend_name } { $backend_version }.
add = Hinzufügen
add_by_remote_id = Hinzugefügt von ID:
all = Alle
all_title = Das ganze Netzwerk
and_more = …und mehr
by = von
comment = Kommentar
comments = Kommentare
comment_attachment_prefix = Anhang:


@@ 40,13 39,14 @@ forgot_password_complete = Password erfolgreich zurückgesetzt.
forgot_password_email_prompt = Emailadresse:
forgot_password_info = Wenn dein Account eine Emailadresse gesetzt hat, kannst du dein Passwort hier zurücksetzen.
forgot_password_new_password_prompt = Neues Password:
home_follow_prompt1 = Warum nicht
home_follow_prompt2 = Folge einigen Communities?
home_follow_prompt = Warum nicht { $part_follow }?
home_follow_prompt_part_follow = Folge einigen Communities
liked_by = Geliked von:
likes = Likes
local = Lokal
login = Login
login_signup_link = Erstelle neuen Account
login_signup = Oder { $part_signup }
login_signup_part_signup = Erstelle neuen Account
lookup_nothing = Nichts gefunden.
lookup_title = Nachschlaten
moderators = Moderatoren


@@ 55,9 55,10 @@ no_cancel = Nein, abbrechen
nothing = Sieht so aus als wenn hier nichts ist.
nothing_yet = Sieht so aus als wenn hier nichts ist, (bis jetzt!).
notifications = Benachrichtigungen
on = Auf
on_your_post = Auf deinen Beitrag
or_start = Oder
notification_comment_reply = Antworten an { $part_your_comment } Auf { $part_post }
notification_comment_reply_part_your_comment = Dein Kommentar
notification_post_reply = { $part_comment } Auf deinen Beitrag { $part_post}:
notification_post_reply_part_comment = Kommentar
password_prompt = Password:
post_approve = Akzeptieren
post_approve_undo = Aus Community entfernen


@@ 74,13 75,16 @@ post_new_href_conflict = URL und Bild können nicht gleichzeitig angegeben werde
post_new_missing_content_type = Fehlender Content-Type für das hochgeladene Bild
post_new_image_prompt = Bild:
post_not_approved = Dieser Beitrag wurde noch nicht von der Community akzeptiert.
post_submitted = Abgesendet { $part_time }
post_submitted_by = Abgesendet { $part_time } von { $part_user }
post_submitted_by_to = Abgesendet { $part_time } von { $part_user } zu { $part_community }
post_submitted_to = Abgesendet { $part_time } zu { $part_community }
preview = Vorschau
register = Registrieren
remote = Entfernt
remove = Entfernen
reply = Antwort
reply_submit = Antworten
reply_to = Antworten an
save = Speichern
score = { $score } { $score ->
    [one] Like


@@ 91,8 95,9 @@ sort = Sortieren:
sort_hot = Heis
sort_new = Neu
submit = Absenden
submitted = Abgesendet
text_with_markdown = Text (Markdown unterstützt)
thing_comment = { $part_comment } Auf { $part_post }
thing_comment_part_comment = Kommentar
timeago_years =
    { $years } { $years ->
        [one] Jahr


@@ 131,7 136,6 @@ timeago_seconds =
timeago_now = Jetzt
timeago_future = in der Zukunft
title = Titel
to = zu
to_parent = Zeige Eltern
to_post = Kommentar auf
url = URL


@@ 151,7 155,6 @@ user_suspended_note = Der Nutzer wurde gesperrt.
username_prompt = Nutzername:
view_at_source = Zeige an Quelle
view_more_comments = Zeite mehr Kommentare
your_comment = Dein Kommentar
your_note = Persönliche Notiz
your_note_add = Persönliche Notiz hinzufügen
your_note_edit = Persönliche Notiz bearbeiten

M res/lang/en.ftl => res/lang/en.ftl +24 -23
@@ 1,10 1,9 @@
a_comment = a comment
about = About
about_title = About this instance
about_what_is = What is lotide?
about_text1 = lotide is an attempt to build a federated forum. Users can create communities to share links and text posts and discuss them with other users, include those registered on other servers through
about_text2 = For more information or to view the source code, check out the
about_sourcehut = SourceHut page
about_text1 = lotide is an attempt to build a federated forum. Users can create communities to share links and text posts and discuss them with other users, including those registered on other servers through { $part_activitypub }.
about_text2 = For more information or to view the source code, check out the { $part_sourcehut }.
about_text2_part_sourcehut = SourceHut page
about_versions = This instance is running hitide { $hitide_version } on { $backend_name } { $backend_version }.
action_flag = flag
add = Add


@@ 15,14 14,13 @@ administration_edit = Edit Instance Details
administration_edit_invitations_enabled = Invitations:
administration_edit_signup_allowed = Public Signups:
administration_invitation_creation_requirement = Invitations can be created by
administration_invitations_enabled = Invitations are currently
administration_signup_allowed = Signups are currently
administration_invitations_enabled = Invitations are currently { $part_enabled }
administration_signup_allowed = Signups are currently { $part_allowed }
all = All
all_title = The Whole Known Network
allowed_false = not allowed
allowed_true = allowed
and_more = …and more
by = by
comment = Comment
comments = Comments
comment_attachment_prefix = Attachment:


@@ 61,7 59,7 @@ enabled_false = disabled
fetch = Fetch
flag_comment_prompt = Add a comment:
flag_dismiss = Dismiss
flagged_by = Flagged by
flagged_by = Flagged by { $part_user }
flags = Flags
flags_title_community = Flags for this Community
flags_title_other = Flags


@@ 77,8 75,8 @@ forgot_password_complete = Password successfully reset.
forgot_password_email_prompt = Email Address:
forgot_password_info = If your account has an attached email address, you can reset your password here.
forgot_password_new_password_prompt = New Password:
home_follow_prompt1 = Why not
home_follow_prompt2 = follow some communities?
home_follow_prompt = Why not { $part_follow }?
home_follow_prompt_part_follow = follow some communities
invite_users = Invite Users
invitation_already_used = That invitation has already been used
liked_by = Liked by:


@@ 87,7 85,8 @@ local = Local
local_title = Posts from Local Communities
local_user_name_prompt = Local User Name:
login = Login
login_signup_link = create a new account
login_signup = Or { $part_signup }
login_signup_part_signup = create a new account
logout = Log Out
lookup_nothing = Nothing found.
lookup_title = Lookup


@@ 97,10 96,8 @@ moderation_dashboard_some = Moderation Dashboard (Pending Actions)
moderators = Moderators
modlog = Modlog
modlog_event_approve_post = Post Approved:
modlog_event_delete_post_1 = Deleted a post by
modlog_event_delete_post_2 = in
modlog_event_delete_comment_1 = Deleted a comment by
modlog_event_delete_comment_2 = on
modlog_event_delete_post = Deleted a post by { $part_user } in { $part_community }
modlog_event_delete_comment = Deleted a comment by { $part_user } in { $part_community }
modlog_event_reject_post = Post Rejected:
modlog_event_suspend_user = User Suspended:
modlog_event_unsuspend_user = User Unsuspended:


@@ 117,12 114,14 @@ not_site_admin = You are not an instance admin
nothing = Looks like there's nothing here.
nothing_yet = Looks like there's nothing here (yet!).
notifications = Notifications
notification_comment_mention_1 = You were mentioned in
notification_comment_mention = You were mentioned in { $part_comment } on { $part_post }:
notification_comment_mention_part_comment = a comment
notification_comment_reply = Reply to { $part_your_comment } on { $part_post }:
notification_comment_reply_part_your_comment = your comment
notification_post_mention = You were mentioned in a post:
on = on
on_your_post = on your post
notification_post_reply = { $part_comment } on your post { $part_post }:
notification_post_reply_part_comment = Comment
open_menu = Open Menu
or_start = Or
poll_new_closes_prompt = Closes in:
poll_new_multiple = Allow multiple choices
poll_new_options_prompt = Options:


@@ 153,6 152,10 @@ post_new_missing_content_type = Missing Content-Type for image upload
post_new_image_prompt = Image:
post_not_approved = This post has not been approved by the community.
post_rejected = This post has been rejected by the community.
post_submitted = Submitted { $part_time }
post_submitted_by = Submitted { $part_time } by { $part_user }
post_submitted_by_to = Submitted { $part_time } by { $part_user } to { $part_community }
post_submitted_to = Submitted { $part_time } to { $part_community }
post_timeframe = Posts from:
preview = Preview
profile = Profile


@@ 163,7 166,6 @@ remove = remove
remove_upvote = Remove upvote
reply = reply
reply_submit = Reply
reply_to = Reply to
requirement_none = all users
requirement_site_admin = site admins
save = Save


@@ 179,8 181,9 @@ sort_hot = hot
sort_new = new
sort_top = top
submit = Submit
submitted = Submitted
text_with_markdown = Text (markdown supported)
thing_comment = { $part_comment } on { $part_post }:
thing_comment_part_comment = Comment
time_input_minutes = minutes
time_input_hours = hours
time_input_days = days


@@ 228,7 231,6 @@ timeframe_month = past month
timeframe_week = past week
timeframe_year = past year
title = Title
to = to
to_parent = View Parent
to_post = Comment on
unknown = [unknown]


@@ 252,7 254,6 @@ user_suspended_note = This user has been suspended.
username_prompt = Username:
view_at_source = View at Source
view_more_comments = View More Comments
your_comment = your comment
your_note = Personal Note
your_note_add = Add Personal Note
your_note_edit = Edit Personal Note

M res/lang/eo.ftl => res/lang/eo.ftl +20 -19
@@ 1,16 1,15 @@
about = Pri
about_title = Pri ĉi tiu servilo
about_what_is = Kio estas lotide?
about_text1 = lotide estas provo konstrui federacian forumon. Uzantoj povas krei komunumojn por disdoni ligilojn kaj tekstpoŝtojn kaj diskuti ilin kun aliaj uzantoj, inkluzive de tiuj en aliaj serviloj per
about_text2 = Por pli da informo aŭ vidi la fontkodon, kontrolu la
about_sourcehut = SourceHut paĝon
about_text1 = lotide estas provo konstrui federacian forumon. Uzantoj povas krei komunumojn por disdoni ligilojn kaj tekstpoŝtojn kaj diskuti ilin kun aliaj uzantoj, inkluzive de tiuj en aliaj serviloj per { $part_activitypub }
about_text2 = Por pli da informo aŭ vidi la fontkodon, kontrolu la { $part_sourcehut }
about_text2_part_sourcehut = SourceHut paĝon
about_versions = Ĉi tiu servilo uzas hitide { $hitide_version } kun { $backend_name } { $backend_version }.
add = Aldoni
add_by_remote_id = Aldoni per ID:
all = Ĉiuj
all_title = La Tuta Konata Reto
and_more = …kaj pli
by = de
comment = Komento
comments = Komentoj
comment_attachment_prefix = Aldonaĵo:


@@ 43,15 42,16 @@ forgot_password_complete = Sukcese ŝanĝis pasvorton.
forgot_password_email_prompt = Retpoŝtadreso:
forgot_password_info = Se via konto havas ligitan retpoŝtadreson, vi povas restarigi vian pasvorton ĉi tie.
forgot_password_new_password_prompt = Nova Pasvorto:
home_follow_prompt1 = Kial ne
home_follow_prompt2 = aboni iujn komunumojn?
home_follow_prompt = Kial ne { $part_follow }?
home_follow_prompt_part_follow = aboni iujn komunumojn
liked_by = Ŝatata de:
likes = Ŝatantoj
local = Loka
local_title = Poŝtoj de Lokaj Komunumoj
local_user_name_prompt = Nomo de Loka Uzanto:
login = Ensaluti
login_signup_link = krei novan konton
login_signup = Aŭ { $part_signup }
login_signup_part_signup = krei novan konton
lookup_nothing = Nenio troveblas.
lookup_title = Serĉi
moderators = Kontrolantoj


@@ 61,9 61,10 @@ no_such_local_user = Neniu tia loka uzanto
nothing = Ŝajnas, ke estas nenio ĉi tie.
nothing_yet = Ŝajnas, ke estas nenio ĉi tie (ĝis nun!).
notifications = Sciigoj
on = sur
on_your_post = sur via poŝto
or_start = Aŭ
notification_comment_reply = Respondo al { $part_your_comment } sur { $part_post }
notification_comment_reply_part_your_comment = via komento
notification_post_reply = { $part_comment } sur via poŝto { $part_post }:
notification_post_reply_part_comment = Komento
password_prompt = Pasvorto:
posts_page_next = Vidi Pli
post_approve = Aprobi


@@ 83,13 84,16 @@ post_new_href_conflict = Ne rajtas doni kaj URL kaj Bildon
post_new_missing_content_type = Mankas Content-Type por bilda alŝuto
post_new_image_prompt = Bildo:
post_not_approved = Ĉi tiu poŝto ne estas aprobita per la komunumo.
post_submitted = Afiŝita { $part_time }
post_submitted_by = Afiŝita { $part_time } de { $part_user }
post_submitted_by_to = Afiŝita { $part_time } de { $part_user } al { $part_community }
post_submitted_to = Afiŝita { $part_time } al { $part_community }
preview = Antaŭprezenti
register = Registriĝi
remote = Fora
remove = forigi
reply = respondi
reply_submit = Respondi
reply_to = Respondo al
save = Konservi
score =
    { $score } { $score ->


@@ 101,8 105,9 @@ sort = Ordigi:
sort_hot = furora
sort_new = nova
submit = Sendi
submitted = Afiŝita
text_with_markdown = Teksto (markdown estas permesita)
thing_comment = { $part_comment } sur { $part_post }
thing_comment_part_comment = Komento
timeago_years =
    antaŭ { $years } { $years ->
        [one] jaro


@@ 141,7 146,6 @@ timeago_seconds =
timeago_now = nune
timeago_future = estontece
title = Titolo
to = al
to_parent = Vidi gepatron
to_post = Komento sur
url = URL


@@ 163,7 167,6 @@ user_suspended_note = Ĉi tiu konto estis ŝlosita.
username_prompt = Uzantnomo:
view_at_source = Vidi ĉe Fonto
view_more_comments = Vidi Pli da Komentoj
your_comment = via komento
your_note = Propra Noto
your_note_add = Aldoni Propra Noto
your_note_edit = Redakti Propra Noto


@@ 183,7 186,7 @@ community_delete_link = Forigi Komunumon
community_delete_title = Forigi Komunumon
community_flags_link = Raportoj
flag_comment_prompt = Aldoni komenton:
flagged_by = Raportita de
flagged_by = Raportita de { $part_user }
flags_title_community = Raportoj por ĉi tiu komunumo
flags_title_other = Raportoj
flags_title_site_admin = Raportoj por administrantoj de ĉi tiu retejo


@@ 210,7 213,7 @@ time_input_hours = horoj
time_input_days = tagoj
community_create_not_allowed = Vi ne rajtas krei komunumojn
create_invitation_result = Inviton kreis. Doni ĉi tiun ligilon por inviti iun (ĝi nur povas esti uzata unufoje):
modlog_event_delete_post_1 = Forigis poŝton de
modlog_event_delete_post = Forigis poŝton de { $part_user } en { $part_community }
no_such_invitation = Neniu tia invito
post_rejected = Ĉi tiu poŝto estas malakceptita de la komunumo.
create_invitation = Krei Inviton


@@ 223,9 226,7 @@ moderation_dashboard = Kontrolpanelo
moderation_dashboard_some = Kontrolpanelo (Pritraktotaj Agoj)
modlog = Kontrolprotokolo
modlog_event_approve_post = Poŝton Aprobis:
modlog_event_delete_post_2 = en
modlog_event_delete_comment_1 = Forigis komenton de
modlog_event_delete_comment_2 = sur
modlog_event_delete_comment = Forigis komenton de { $part_user } sur { $part_community }
modlog_event_reject_post = Poŝton Malakceptis:
modlog_event_suspend_user = Uzanton Ŝlosis:
modlog_event_unsuspend_user = Uzanton Malŝlosis:

M res/lang/fr.ftl => res/lang/fr.ftl +16 -13
@@ 2,15 2,14 @@ about = A propos
about_title = A propos de cette instance
about_what_is = QU'est ce que lotide ?
about_text1 = lotide est un essai de création de forum fédéré. Les utilisateurs peuvent créer des communautés et partager des liens ou du texte et en discuter avec d'autres utilisateurs, y compris ceux inscrits sur d'autres instances.
about_text2 = Pour plus d'information ou pour consulter le code source, rendez-vous sur la
about_sourcehut = page SourceHut 
about_text2 = Pour plus d'information ou pour consulter le code source, rendez-vous sur la { $part_sourcehut }
about_text2_part_sourcehut = page SourceHut 
about_versions = Cette instance fonctionne avec hitide { $hitide_version } sur { $backend_name } { $backend_version }.
add = Ajouter
add_by_remote_id = Ajouter par ID:
all = Tout
all_title = Tout le réseau connu
and_more = ...et plus
by = par
comment = Commentaire
comments = Commentaires
comment_attachment_prefix = Pièce jointe:


@@ 40,13 39,14 @@ forgot_password_complete = Le mot de passe a été changé avec succès
forgot_password_email_prompt = Addresse e-mail:
forgot_password_info = If your account has an attached email address, you can reset your password here.
forgot_password_new_password_prompt = Nouveau mot de passe :
home_follow_prompt1 = Pourquoi pas
home_follow_prompt2 = Suivre des communautés?
home_follow_prompt = Pourquoi pas { $part_follow }?
home_follow_prompt_part_follow = Suivre des communautés
liked_by = Aimé par:
likes = j'aime
local = Local
login = Identifiant
login_signup_link = créer un nouveau compte
login_signup = Ou { $part_signup }
login_signup_part_signup = créer un nouveau compte
lookup_nothing = Pas de résultat
lookup_title = Recherche
moderators = Modérateurs


@@ 55,9 55,10 @@ no_cancel = Non, annuler
nothing = Il semble qu'il n'y ait rien ici.
nothing_yet = Il semble qu'il n'y ait rien ici.(pour l'instant!).
notifications = Notifications
on = sur
on_your_post = sur votre post
or_start = Ou
notification_comment_reply = Répondre à { $part_your_comment } sur { $part_port }:
notification_comment_reply_part_your_comment = votre commentaire
notification_post_reply = { $part_comment } sur votre post { $part_post }:
notification_post_reply_part_comment = Commentaire
password_prompt = Mot de passe:
post_approve = Approuver
post_approve_undo = Enlever de la communauté


@@ 69,13 70,16 @@ post_new_href_conflict = Impossible de spifier une URL et une image
post_new_missing_content_type = Le type d'image n'a pas été spécifié
post_new_image_prompt = Image:
post_not_approved = Ce post n'a pas été approuver par la communauté.
post_submitted = envoyé { $part_time }
post_submitted_by = envoyé { $part_time } par { $part_user }
post_submitted_by_to = envoyé { $part_time } par { $part_user } à { $part_community }
post_submitted_to = envoyé { $part_time } à { $part_community }
preview = Prévisualiser
register = S'enregistrer
remote = Distant
remove = supprimer
reply = répondre
reply_submit = Répondre
reply_to = Répondre à
save = Sauvegarder
score = { $score } { $score ->
[one] j'aime


@@ 86,8 90,9 @@ sort = Tri:
sort_hot = tendance
sort_new = nouveau
submit = Envoyer
submitted = envoyé
text_with_markdown = Texte (markdown supporté)
thing_comment = { $part_comment } sur { $part_post }
thing_comment_part_comment = Commentaire
timeago_years =
    il y a { $years } { $years ->
        [one] an


@@ 126,7 131,6 @@ timeago_seconds =
timeago_now = maintenant
timeago_future = dans le futur
title = Titre
to = à
to_parent = Voir le parent
to_post = Commenter sur
url = URL


@@ 146,7 150,6 @@ user_suspended_note = Cet utilisateur a été suspendu.
username_prompt = Identifiant:
view_at_source = Voir la source
view_more_comments = Voir plus de commentaires
your_comment = votre commentaire
your_note = Note personnelle
your_note_add = Ajouter une note personnelle
your_note_edit = Editer la note personnelle

M src/components/mod.rs => src/components/mod.rs +221 -108
@@ 178,7 178,7 @@ pub struct CommunityLink<'community> {
    pub community: &'community RespMinimalCommunityInfo<'community>,
}
impl<'community> render::Render for CommunityLink<'community> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        let community = &self.community;

        if community.deleted {


@@ 284,7 284,7 @@ pub struct ContentView<'a, T: HavingContent + 'a> {
}

impl<'a, T: HavingContent + 'a> render::Render for ContentView<'a, T> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        match self.src.content_html() {
            Some(html) => {
                (render::rsx! { <div class={"contentView"}>{render::raw!(html)}</div> })


@@ 302,26 302,52 @@ impl<'a, T: HavingContent + 'a> render::Render for ContentView<'a, T> {
    }
}

#[render::component]
pub fn FlagItem<'a>(flag: &'a RespFlagInfo<'a>, in_community: bool, lang: &'a crate::Translator) {
    let RespFlagDetails::Post { post } = &flag.details;
pub struct FlagItem<'a> {
    pub flag: &'a RespFlagInfo<'a>,
    pub in_community: bool,
    pub lang: &'a crate::Translator,
}

    render::rsx! {
        <li class={"flagItem"}>
            <div class={"flaggedContent"}>
                <PostItemContent post={post} in_community no_user={false} lang />
            </div>
            {lang.tr(&lang::FLAGGED_BY)}{" "}<UserLink user={Some(&flag.flagger)} lang />
            {
                flag.content.as_ref().map(|content| {
                    render::rsx! {
                        <blockquote>
                            {content.content_text.as_ref()}
                        </blockquote>
                    }
                })
            }
        </li>
impl<'a> render::Render for FlagItem<'a> {
    fn render_into<W: std::fmt::Write + ?Sized>(self, w: &mut W) -> std::fmt::Result {
        let Self {
            flag,
            in_community,
            lang,
        } = self;

        let RespFlagDetails::Post { post } = &flag.details;

        render::rsx! {
            <li class={"flagItem"}>
                <div class={"flaggedContent"}>
                    <PostItemContent post={post} in_community no_user={false} lang />
                </div>
                {
                    lang::TrElements::new(
                        lang.tr(&lang::flagged_by(lang::LangPlaceholder(0))),
                        |id, w| {
                            match id {
                                0 => render::rsx! {
                                    <UserLink user={Some(&flag.flagger)} lang />
                                }.render_into(w),
                                _ => unreachable!(),
                            }
                        },
                    )
                }
                {
                    flag.content.as_ref().map(|content| {
                        render::rsx! {
                            <blockquote>
                                {content.content_text.as_ref()}
                            </blockquote>
                        }
                    })
                }
            </li>
        }
        .render_into(w)
    }
}



@@ 464,58 490,69 @@ pub fn PostItem<'a>(
    }
}

#[render::component]
pub fn PostItemContent<'a>(
pub struct PostItemContent<'a> {
    post: &'a RespPostListPost<'a>,
    in_community: bool,
    no_user: bool,
    lang: &'a crate::Translator,
) {
    let post_href = format!("/posts/{}", post.as_ref().as_ref().id);
}

    render::rsx! {
        <>
            <div class={"titleLine"}>
                <a href={post_href.clone()}>
                    {post.as_ref().as_ref().sensitive.then(|| hitide_icons::SENSITIVE.img(lang.tr(&lang::SENSITIVE)))}
                    {post.as_ref().as_ref().title.as_ref()}
                </a>
                {
                    post.as_ref().href.as_ref().map(|href| {
                        render::rsx! {
                            <em><a href={href.as_ref()}>{abbreviate_link(href)}{" ↗"}</a></em>
                        }
                    })
                }
            </div>
            <small>
                {lang.tr(&lang::SUBMITTED)}
                {" "}
                <TimeAgo since={chrono::DateTime::parse_from_rfc3339(&post.as_ref().created).unwrap()} lang />
                {
                    if no_user {
                        None
                    } else {
                        Some(render::rsx! {
                            <>
                                {" "}{lang.tr(&lang::BY)}{" "}<UserLink lang user={post.as_ref().author.as_ref()} />
                            </>
impl<'a> render::Render for PostItemContent<'a> {
    fn render_into<W: std::fmt::Write + ?Sized>(self, w: &mut W) -> std::fmt::Result {
        let Self {
            post,
            in_community,
            no_user,
            lang,
        } = self;

        let post_href = format!("/posts/{}", post.as_ref().as_ref().id);

        render::rsx! {
            <>
                <div class={"titleLine"}>
                    <a href={post_href.clone()}>
                        {post.as_ref().as_ref().sensitive.then(|| hitide_icons::SENSITIVE.img(lang.tr(&lang::SENSITIVE)))}
                        {post.as_ref().as_ref().title.as_ref()}
                    </a>
                    {
                        post.as_ref().href.as_ref().map(|href| {
                            render::rsx! {
                                <em><a href={href.as_ref()}>{abbreviate_link(href)}{" ↗"}</a></em>
                            }
                        })
                    }
                }
                {
                    if !in_community {
                        Some(render::rsx! {
                            <>{" "}{lang.tr(&lang::TO)}{" "}<CommunityLink community={&post.as_ref().community} /></>
                        })
                    } else {
                        None
                </div>
                <small>
                    {
                        lang::TrElements::new(
                            lang.tr(&match (no_user, in_community) {
                                (false, false) => lang::post_submitted_by_to(lang::LangPlaceholder(0), lang::LangPlaceholder(1), lang::LangPlaceholder(2)),
                                (false, true) => lang::post_submitted_by(lang::LangPlaceholder(0), lang::LangPlaceholder(1)),
                                (true, false) => lang::post_submitted_to(lang::LangPlaceholder(0), lang::LangPlaceholder(2)),
                                (true, true) => lang::post_submitted(lang::LangPlaceholder(0)),
                            }),
                            |id, w| {
                                match id {
                                    0 => render::rsx! {
                                        <TimeAgo since={chrono::DateTime::parse_from_rfc3339(&post.as_ref().created).unwrap()} lang />
                                    }.render_into(w),
                                    1 => render::rsx! {
                                        <UserLink lang user={post.as_ref().author.as_ref()} />
                                    }.render_into(w),
                                    2 => render::rsx! {
                                        <CommunityLink community={&post.as_ref().community} />
                                    }.render_into(w),
                                    _ => unreachable!(),
                                }
                            },
                        )
                    }
                }
                {" | "}
                <a href={post_href}>{lang.tr(&lang::post_comments_count(post.replies_count_total)).into_owned()}</a>
            </small>
        </>
                    {" | "}
                    <a href={post_href}>{lang.tr(&lang::post_comments_count(post.replies_count_total)).into_owned()}</a>
                </small>
            </>
        }.render_into(w)
    }
}



@@ 525,7 562,7 @@ pub struct ThingItem<'a> {
}

impl<'a> render::Render for ThingItem<'a> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        let lang = self.lang;

        match self.thing {


@@ 536,8 573,26 @@ impl<'a> render::Render for ThingItem<'a> {
                (render::rsx! {
                    <li>
                        <small>
                            <a href={format!("/comments/{}", comment.as_ref().id)}>{lang.tr(&lang::comment())}</a>
                            {" "}{lang.tr(&lang::on())}{" "}<a href={format!("/posts/{}", comment.post.id)}>{comment.post.title.as_ref()}</a>{":"}
                            {
                                lang::TrElements::new(
                                    lang.tr(&lang::thing_comment(lang::LangPlaceholder(0), lang::LangPlaceholder(1))),
                                    |id, w| {
                                        match id {
                                            0 => render::rsx! {
                                                <a href={format!("/comments/{}", comment.as_ref().id)}>
                                                    {lang.tr(&lang::thing_comment_part_comment())}
                                                </a>
                                            }.render_into(w),
                                            1 => render::rsx! {
                                                <a href={format!("/posts/{}", comment.post.id)}>
                                                    {comment.post.title.as_ref()}
                                                </a>
                                            }.render_into(w),
                                            _ => unreachable!(),
                                        }
                                    }
                                )
                            }
                        </small>
                        <ContentView src={comment} />
                    </li>


@@ 553,7 608,7 @@ pub struct UserLink<'a> {
}

impl<'user> render::Render for UserLink<'user> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        match self.user {
            None => "[unknown]".render_into(writer),
            Some(user) => {


@@ 756,7 811,7 @@ pub struct NotificationItem<'a> {
}

impl<'a> render::Render for NotificationItem<'a> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        let lang = self.lang;

        write!(writer, "<li class=\"notification-item")?;


@@ 772,8 827,26 @@ impl<'a> render::Render for NotificationItem<'a> {
                (render::rsx! {
                    <>
                        <div>
                            <a href={format!("/comments/{}", reply.as_ref().id)}>{lang.tr(&lang::comment())}</a>
                            {" "}{lang.tr(&lang::on_your_post())}{" "}<a href={format!("/posts/{}", post.as_ref().as_ref().id)}>{post.as_ref().as_ref().title.as_ref()}</a>{":"}
                            {
                                lang::TrElements::new(
                                    lang.tr(&lang::notification_post_reply(lang::LangPlaceholder(0), lang::LangPlaceholder(1))),
                                    |id, w| {
                                        match id {
                                            0 => render::rsx! {
                                                <a href={format!("/comments/{}", reply.as_ref().id)}>
                                                    {lang.tr(&lang::notification_post_reply_part_comment())}
                                                </a>
                                            }.render_into(w),
                                            1 => render::rsx! {
                                                <a href={format!("/posts/{}", post.as_ref().as_ref().id)}>
                                                    {post.as_ref().as_ref().title.as_ref()}
                                                </a>
                                            }.render_into(w),
                                            _ => unreachable!(),
                                        }
                                    }
                                )
                            }
                        </div>
                        <div class={"body"}>
                            <small>


@@ 806,11 879,26 @@ impl<'a> render::Render for NotificationItem<'a> {
                (render::rsx! {
                    <>
                        <div>
                            {lang.tr(&lang::reply_to())}
                            {" "}
                            <a href={format!("/comments/{}", comment.as_ref().id)}>{lang.tr(&lang::your_comment())}</a>
                            {" "}{lang.tr(&lang::on())}{" "}<a href={format!("/posts/{}", post.as_ref().as_ref().id)}>{post.as_ref().as_ref().title.as_ref()}</a>
                            {":"}
                            {
                                lang::TrElements::new(
                                    lang.tr(&lang::notification_comment_reply(lang::LangPlaceholder(0), lang::LangPlaceholder(1))),
                                    |id, w| {
                                        match id {
                                            0 => render::rsx! {
                                                <a href={format!("/comments/{}", comment.as_ref().id)}>
                                                    {lang.tr(&lang::notification_comment_reply_part_your_comment())}
                                                </a>
                                            }.render_into(w),
                                            1 => render::rsx! {
                                                <a href={format!("/posts/{}", post.as_ref().as_ref().id)}>
                                                    {post.as_ref().as_ref().title.as_ref()}
                                                </a>
                                            }.render_into(w),
                                            _ => unreachable!(),
                                        }
                                    }
                                )
                            }
                        </div>
                        <div class={"body"}>
                            <small>


@@ 827,13 915,26 @@ impl<'a> render::Render for NotificationItem<'a> {
                (render::rsx! {
                    <>
                        <div>
                            {lang.tr(&lang::notification_comment_mention_1())}{" "}
                            <a href={format!("/comments/{}", comment.as_ref().id)}>{lang.tr(&lang::a_comment())}</a>
                            {" "}{lang.tr(&lang::on())}{" "}
                            <a href={format!("/posts/{}", post.as_ref().as_ref().id)}>
                                {post.as_ref().as_ref().title.as_ref()}
                            </a>
                            {":"}
                            {
                                lang::TrElements::new(
                                    lang.tr(&lang::notification_comment_mention(lang::LangPlaceholder(0), lang::LangPlaceholder(1))),
                                    |id, w| {
                                        match id {
                                            0 => render::rsx! {
                                                <a href={format!("/comments/{}", comment.as_ref().id)}>
                                                    {lang.tr(&lang::notification_comment_mention_part_comment())}
                                                </a>
                                            }.render_into(w),
                                            1 => render::rsx! {
                                                <a href={format!("/posts/{}", post.as_ref().as_ref().id)}>
                                                    {post.as_ref().as_ref().title.as_ref()}
                                                </a>
                                            }.render_into(w),
                                            _ => unreachable!(),
                                        }
                                    },
                                )
                            }
                            <div class={"body"}>
                                <small>
                                    <cite><UserLink lang user={comment.author.as_ref()} /></cite>


@@ 858,7 959,7 @@ pub struct SiteModlogEventItem<'a> {
}

impl<'a> render::Render for SiteModlogEventItem<'a> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        let lang = self.lang;
        let event = &self.event;



@@ 873,31 974,43 @@ impl<'a> render::Render for SiteModlogEventItem<'a> {

        match &event.details {
            RespSiteModlogEventDetails::DeletePost { author, community } => {
                (render::rsx! {
                    <>
                        {lang.tr(&lang::MODLOG_EVENT_DELETE_POST_1)}
                        {" "}
                        <UserLink user={Some(author)} lang={&lang} />
                        {" "}
                        {lang.tr(&lang::MODLOG_EVENT_DELETE_POST_2)}
                        {" "}
                        <CommunityLink community />
                    </>
                })
                lang::TrElements::new(
                    lang.tr(&lang::modlog_event_delete_post(
                        lang::LangPlaceholder(0),
                        lang::LangPlaceholder(1),
                    )),
                    |id, w| match id {
                        0 => render::rsx! {
                            <UserLink user={Some(author)} lang={&lang} />
                        }
                        .render_into(w),
                        1 => render::rsx! {
                            <CommunityLink community />
                        }
                        .render_into(w),
                        _ => unreachable!(),
                    },
                )
                .render_into(writer)?;
            }
            RespSiteModlogEventDetails::DeleteComment { author, post } => {
                (render::rsx! {
                    <>
                        {lang.tr(&lang::MODLOG_EVENT_DELETE_COMMENT_1)}
                        {" "}
                        <UserLink user={Some(author)} lang={&lang} />
                        {" "}
                        {lang.tr(&lang::MODLOG_EVENT_DELETE_COMMENT_2)}
                        {" "}
                        <a href={format!("/posts/{}", post.id)}>{post.title.as_ref()}</a>
                    </>
                })
                lang::TrElements::new(
                    lang.tr(&lang::modlog_event_delete_comment(
                        lang::LangPlaceholder(0),
                        lang::LangPlaceholder(1),
                    )),
                    |id, w| match id {
                        0 => render::rsx! {
                            <UserLink user={Some(author)} lang={&lang} />
                        }
                        .render_into(w),
                        1 => render::rsx! {
                            <a href={format!("/posts/{}", post.id)}>{post.title.as_ref()}</a>
                        }
                        .render_into(w),
                        _ => unreachable!(),
                    },
                )
                .render_into(writer)?;
            }
            RespSiteModlogEventDetails::SuspendUser { user } => {


@@ 934,7 1047,7 @@ pub struct PollView<'a> {
    pub lang: &'a crate::Translator,
}
impl<'a> render::Render for PollView<'a> {
    fn render_into<W: std::fmt::Write>(self, writer: &mut W) -> std::fmt::Result {
    fn render_into<W: std::fmt::Write + ?Sized>(self, writer: &mut W) -> std::fmt::Result {
        let PollView { poll, action, lang } = &self;

        if poll.your_vote.is_some() || poll.is_closed {

M src/lang.rs => src/lang.rs +77 -0
@@ 1,4 1,5 @@
use std::borrow::Cow;
use std::convert::TryFrom;

pub struct Translator {
    bundle: fluent::bundle::FluentBundle<


@@ 56,6 57,82 @@ impl std::fmt::Debug for Translator {

pub struct LangKey<'a>(&'static str, Option<fluent::FluentArgs<'a>>);

pub const PLACEHOLDER_BASE: u32 = '\u{fba00}' as u32;
pub const PLACEHOLDER_MAX: u32 = PLACEHOLDER_BASE + (u8::MAX as u32);

#[derive(Clone, Copy, PartialEq, Debug)]
pub struct LangPlaceholder(pub u8);

impl fluent::types::FluentType for LangPlaceholder {
    fn duplicate(&self) -> Box<dyn fluent::types::FluentType + Send + 'static> {
        Box::new(*self)
    }

    fn as_string(&self, _intls: &intl_memoizer::IntlLangMemoizer) -> Cow<'static, str> {
        char::try_from(PLACEHOLDER_BASE + u32::from(self.0))
            .unwrap()
            .to_string()
            .into()
    }

    fn as_string_threadsafe(
        &self,
        _intls: &intl_memoizer::concurrent::IntlLangMemoizer,
    ) -> Cow<'static, str> {
        char::try_from(PLACEHOLDER_BASE + u32::from(self.0))
            .unwrap()
            .to_string()
            .into()
    }
}

impl From<LangPlaceholder> for fluent::FluentValue<'_> {
    fn from(src: LangPlaceholder) -> fluent::FluentValue<'static> {
        fluent::FluentValue::Custom(Box::new(src))
    }
}

pub struct TrElements<'a, F: (Fn(u8, &mut dyn std::fmt::Write) -> std::fmt::Result)> {
    src: Cow<'a, str>,
    render_placeholder: F,
}

impl<'a, F: (Fn(u8, &mut dyn std::fmt::Write) -> std::fmt::Result)> TrElements<'a, F> {
    pub fn new(src: Cow<'a, str>, render_placeholder: F) -> Self {
        Self {
            src,
            render_placeholder,
        }
    }
}

impl<'a, F: (Fn(u8, &mut dyn std::fmt::Write) -> std::fmt::Result)> render::Render
    for TrElements<'a, F>
{
    fn render_into<W: std::fmt::Write + ?Sized>(self, mut writer: &mut W) -> std::fmt::Result {
        let mut covered = 0;

        for (idx, chr) in self.src.char_indices() {
            let chr_value: u32 = chr.into();
            if chr_value >= PLACEHOLDER_BASE && chr_value <= PLACEHOLDER_MAX {
                if idx > covered {
                    self.src[covered..idx].render_into(writer)?;
                }

                (self.render_placeholder)((chr_value - PLACEHOLDER_BASE) as u8, &mut writer)?;

                covered = idx + chr.len_utf8();
            }
        }

        if covered < self.src.len() {
            self.src[covered..].render_into(writer)?;
        }

        Ok(())
    }
}

#[allow(unused)]
pub mod keys {
    use super::*;

M src/routes/administration.rs => src/routes/administration.rs +35 -12
@@ 5,6 5,7 @@ use super::{
use crate::components::{HTPage, MaybeFillOption, MaybeFillTextArea};
use crate::lang;
use crate::resp_types::RespInstanceInfo;
use render::Render;
use std::borrow::Cow;
use std::collections::HashMap;
use std::convert::TryInto;


@@ 53,20 54,42 @@ async fn page_administration(
            <a href={"/administration/edit"}>{lang.tr(&lang::administration_edit())}</a>
            <ul>
                <li>
                    {lang.tr(&lang::ADMINISTRATION_SIGNUP_ALLOWED)}{" "}
                    <strong>{lang.tr(if api_res.signup_allowed {
                        &lang::ALLOWED_TRUE
                    } else {
                        &lang::ALLOWED_FALSE
                    })}</strong>
                    {
                        lang::TrElements::new(
                            lang.tr(&lang::administration_signup_allowed(lang::LangPlaceholder(0))),
                            |id, w| {
                                match id {
                                    0 => render::rsx! {
                                        <strong>{lang.tr(if api_res.signup_allowed {
                                            &lang::ALLOWED_TRUE
                                        } else {
                                            &lang::ALLOWED_FALSE
                                        })}</strong>
                                    }.render_into(w),
                                    _ => unreachable!(),
                                }
                            }
                        )
                    }
                </li>
                <li>
                    {lang.tr(&lang::ADMINISTRATION_INVITATIONS_ENABLED)}{" "}
                    <strong>{lang.tr(if api_res.invitations_enabled {
                        &lang::ENABLED_TRUE
                    } else {
                        &lang::ENABLED_FALSE
                    })}</strong>
                    {
                        lang::TrElements::new(
                            lang.tr(&lang::administration_invitations_enabled(lang::LangPlaceholder(0))),
                            |id, w| {
                                match id {
                                    0 => render::rsx! {
                                        <strong>{lang.tr(if api_res.invitations_enabled {
                                            &lang::ENABLED_TRUE
                                        } else {
                                            &lang::ENABLED_FALSE
                                        })}</strong>
                                    }.render_into(w),
                                    _ => unreachable!(),
                                }
                            }
                        )
                    }
                    {
                        if api_res.invitations_enabled {
                            Some(render::rsx! {

M src/routes/mod.rs => src/routes/mod.rs +56 -9
@@ 1,3 1,4 @@
use render::Render;
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::HashMap;


@@ 172,13 173,34 @@ async fn page_about(
            </p>
            <h2>{lang.tr(&lang::about_what_is())}</h2>
            <p>
                {lang.tr(&lang::about_text1())}
                {" "}<a href={"https://activitypub.rocks"}>{"ActivityPub"}</a>{"."}
                {
                    lang::TrElements::new(
                        lang.tr(&lang::about_text1(lang::LangPlaceholder(0))),
                        |id, w| {
                            match id {
                                0 => render::rsx! {
                                    <a href={"https://activitypub.rocks"}>{"ActivityPub"}</a>
                                }.render_into(w),
                                _ => unreachable!(),
                            }
                        },
                    )
                }
            </p>
            <p>
                {lang.tr(&lang::about_text2())}
                {" "}
                <a href={"https://sr.ht/~vpzom/lotide/"}>{lang.tr(&lang::about_sourcehut())}</a>{"."}
                {
                    lang::TrElements::new(
                        lang.tr(&lang::about_text2(lang::LangPlaceholder(0))),
                        |id, w| {
                            match id {
                                0 => render::rsx! {
                                    <a href={"https://sr.ht/~vpzom/lotide/"}>{lang.tr(&lang::about_text2_part_sourcehut())}</a>
                                }.render_into(w),
                                _ => unreachable!(),
                            }
                        }
                    )
                }
            </p>
        </HTPage>
    }))


@@ 239,7 261,19 @@ async fn page_login_inner(
            </form>
            <br />
            <p>
                {lang.tr(&lang::or_start())}{" "}<a href={"/signup"}>{lang.tr(&lang::login_signup_link())}</a>
                {
                    lang::TrElements::new(
                        lang.tr(&lang::login_signup(lang::LangPlaceholder(0))),
                        |id, w| {
                            match id {
                                0 => render::rsx! {
                                    <a href={"/signup"}>{lang.tr(&lang::login_signup_part_signup())}</a>
                                }.render_into(w),
                                _ => unreachable!(),
                            }
                        }
                    )
                }
            </p>
            <p>
                <a href={"/forgot_password"}>{lang.tr(&lang::forgot_password())}</a>


@@ 1555,6 1589,9 @@ async fn page_home(
    let api_res = hyper::body::to_bytes(api_res.into_body()).await?;
    let api_res: RespList<RespPostListPost<'_>> = serde_json::from_slice(&api_res)?;

    let home_follow_prompt_src = lang::home_follow_prompt(lang::LangPlaceholder(0));
    let home_follow_prompt_src = lang.tr(&home_follow_prompt_src);

    Ok(html_response(render::html! {
        <HTPage base_data={&base_data} lang={&lang} title={"lotide"}>
            {


@@ 1563,9 1600,19 @@ async fn page_home(
                        <p>
                            {lang.tr(&lang::NOTHING)}
                            {" "}
                            {lang.tr(&lang::HOME_FOLLOW_PROMPT1)}
                            {" "}
                            <a href={"/communities"}>{lang.tr(&lang::HOME_FOLLOW_PROMPT2)}</a>
                            {
                                lang::TrElements::new(
                                    home_follow_prompt_src,
                                    |id, w| {
                                        match id {
                                            0 => render::rsx! {
                                                <a href={"/communities"}>{lang.tr(&lang::HOME_FOLLOW_PROMPT_PART_FOLLOW)}</a>
                                            }.render_into(w),
                                            _ => unreachable!(),
                                        }
                                    }
                                )
                            }
                        </p>
                    })
                } else {

M src/routes/posts.rs => src/routes/posts.rs +22 -4
@@ 14,6 14,7 @@ use crate::resp_types::{
    RespPostInfo,
};
use crate::util::author_is_me;
use render::Render;
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::HashMap;


@@ 150,6 151,8 @@ async fn page_post_inner(

    let title = post.as_ref().as_ref().title.as_ref();

    let created = chrono::DateTime::parse_from_rfc3339(&post.as_ref().created)?;

    Ok(html_response(render::html! {
        <HTPage base_data={&base_data} lang={&lang} title={title}>
            {


@@ 240,10 243,25 @@ async fn page_post_inner(
            </div>
            <br />
            <p>
                {lang.tr(&lang::submitted())}
                {" "}<TimeAgo since={chrono::DateTime::parse_from_rfc3339(&post.as_ref().created)?} lang={&lang} />
                {" "}{lang.tr(&lang::by())}{" "}<UserLink lang={&lang} user={post.as_ref().author.as_ref()} />
                {" "}{lang.tr(&lang::to())}{" "}<CommunityLink community={&post.as_ref().community} />
                {
                    lang::TrElements::new(
                        lang.tr(&lang::post_submitted_by_to(lang::LangPlaceholder(0), lang::LangPlaceholder(1), lang::LangPlaceholder(2))),
                        |id, w| {
                            match id {
                                0 => render::rsx! {
                                    <TimeAgo since={created} lang={&lang} />
                                }.render_into(w),
                                1 => render::rsx! {
                                    <UserLink lang={&lang} user={post.as_ref().author.as_ref()} />
                                }.render_into(w),
                                2 => render::rsx! {
                                    <CommunityLink community={&post.as_ref().community} />
                                }.render_into(w),
                                _ => unreachable!(),
                            }
                        }
                    )
                }
            </p>
            {
                post.as_ref().href.as_ref().map(|href| {