Summary
Add support for multilingual blog posts in Prose, allowing bloggers to publish the same content in multiple languages with automatic linking between translations and a language switcher for readers.
Motivation
I write my blog posts in both Spanish and English. Right now I just publish them as two separate posts, and it works. But it always felt like something that could be handled more gracefully at the platform level. Things like linking translations together, showing readers that a post is available in another language, or even setting the correct <html lang> attribute for accessibility and SEO.
I started thinking about how this could work within Prose's philosophy (simple, file-based, no build steps) and after digging into the codebase, I realized it could fit quite naturally without breaking anything or adding complexity for users who don't need it. Or at least it looks that way to me.
So I figured: why not propose it? If this aligns with the project's interests, I'd be happy to work on the implementation myself.
The gap today
Currently, there's no built-in way to:
- Indicate a post's language
- Link translations of the same post together
- Show readers that content is available in other languages
- Set the correct
<html lang> attribute for accessibility and SEO
Proposed Design
Filename Convention
Translations are linked by filename convention. A post my-post.md and its Spanish translation my-post.es.md are automatically recognized as variants of the same content:
my-post.md → slug: my-post → lang: en (blog default)
my-post.es.md → slug: my-post.es → lang: es
my-post.pt.md → slug: my-post.pt → lang: pt
Language codes are validated against ISO 639-1 (two-letter codes like en, es, pt, fr, de, ja, zh, etc.), so regular posts with dots in their names (e.g., my-project.v2.md) won't be misidentified as translations.
Explicit Frontmatter Override
Users can explicitly set a post's language in frontmatter, overriding the filename convention:
---
title: Mi publicación
lang: es
---
Blog-Level Configuration
The blog's default language and language switcher position are configured in _readme.md:
---
title: My Blog
lang: pt
lang_switcher: both
---
lang: The blog's default language (default: en). Posts without a language suffix are assumed to be in this language.
lang_switcher: Where to show the language switcher on posts (header, footer, or both).
URL Structure
Translated posts keep flat URLs consistent with Prose's existing behavior:
- Primary:
https://user.prose.sh/my-post
- Spanish:
https://user.prose.sh/my-post.es
- Portuguese:
https://user.prose.sh/my-post.pt
No routing changes are required.
Language Switcher
When viewing a post that has translations, readers see a small navigation element showing available languages:
The current language is displayed as plain text; others are clickable links. The position (header, footer, or both) is configurable per-blog.
Blog Listing Behavior
To avoid cluttering the blog listing with duplicate entries, only the primary-language version of each post is shown. Posts with available translations display a small indicator (e.g., a globe or language count) next to their title.
HTML Semantics
The <html lang> attribute is set dynamically based on the post's language, improving accessibility for screen readers and SEO for search engines.
Technical Approach
I've reviewed the codebase and tried to find the least invasive path to make this work. Here's what I have in mind:
No Database Migration Required
Language metadata can be stored in the existing PostData JSON column (data field in the posts table), so no schema migration would be needed. The PostData struct simply gets a new Lang field.
Translation Discovery
Translations would be found via a database query matching posts by the same user with slugs that share a common base (e.g., my-post matches my-post, my-post.es, my-post.pt).
Backward Compatibility
This is something I paid special attention to (existing blogs should be completely unaffected in theory):
- The
<html lang> attribute defaults to "en" when no language is specified
- No changes to existing URLs or upload workflow
- Language switcher only appears when translations actually exist
- Zero impact for users who don't use the feature
User Workflow
# Write posts in multiple languages
echo "---
title: Hello World
date: 2025-03-01
---
Welcome to my blog!" > hello.md
echo "---
title: Hola Mundo
date: 2025-03-01
---
¡Bienvenidos a mi blog!" > hello.es.md
# Upload both
scp hello.md hello.es.md prose.sh:/
# Both are now live:
# https://user.prose.sh/hello (English)
# https://user.prose.sh/hello.es (Spanish, with link back to English)
Open Questions
A few things I'd love your input on before diving into implementation:
- I'd like to align with the team on whether this feature is worth implementing for Prose.
- Language display names: Should the switcher show codes (
EN) or full names (English/Español)? Codes are more compact; names are more user-friendly.
- Discovery feed: Should the
/read discovery page also group translations, or show all posts independently?
- RSS feeds: Should translated posts appear in the main RSS feed, or should there be per-language feeds?
Thanks for reading! I'm excited about this one (it's a feature I'd use every time I write on my blog). If the team thinks it's a good fit for Prose, I'm ready to put in the work and submit a PR. Happy to discuss any aspect of the design or adjust the approach based on your feedback.
Summary
Add support for multilingual blog posts in Prose, allowing bloggers to publish the same content in multiple languages with automatic linking between translations and a language switcher for readers.
Motivation
I write my blog posts in both Spanish and English. Right now I just publish them as two separate posts, and it works. But it always felt like something that could be handled more gracefully at the platform level. Things like linking translations together, showing readers that a post is available in another language, or even setting the correct
<html lang>attribute for accessibility and SEO.I started thinking about how this could work within Prose's philosophy (simple, file-based, no build steps) and after digging into the codebase, I realized it could fit quite naturally without breaking anything or adding complexity for users who don't need it. Or at least it looks that way to me.
So I figured: why not propose it? If this aligns with the project's interests, I'd be happy to work on the implementation myself.
The gap today
Currently, there's no built-in way to:
<html lang>attribute for accessibility and SEOProposed Design
Filename Convention
Translations are linked by filename convention. A post
my-post.mdand its Spanish translationmy-post.es.mdare automatically recognized as variants of the same content:Language codes are validated against ISO 639-1 (two-letter codes like
en,es,pt,fr,de,ja,zh, etc.), so regular posts with dots in their names (e.g.,my-project.v2.md) won't be misidentified as translations.Explicit Frontmatter Override
Users can explicitly set a post's language in frontmatter, overriding the filename convention:
Blog-Level Configuration
The blog's default language and language switcher position are configured in
_readme.md:lang: The blog's default language (default:en). Posts without a language suffix are assumed to be in this language.lang_switcher: Where to show the language switcher on posts (header,footer, orboth).URL Structure
Translated posts keep flat URLs consistent with Prose's existing behavior:
https://user.prose.sh/my-posthttps://user.prose.sh/my-post.eshttps://user.prose.sh/my-post.ptNo routing changes are required.
Language Switcher
When viewing a post that has translations, readers see a small navigation element showing available languages:
The current language is displayed as plain text; others are clickable links. The position (header, footer, or both) is configurable per-blog.
Blog Listing Behavior
To avoid cluttering the blog listing with duplicate entries, only the primary-language version of each post is shown. Posts with available translations display a small indicator (e.g., a globe or language count) next to their title.
HTML Semantics
The
<html lang>attribute is set dynamically based on the post's language, improving accessibility for screen readers and SEO for search engines.Technical Approach
I've reviewed the codebase and tried to find the least invasive path to make this work. Here's what I have in mind:
No Database Migration Required
Language metadata can be stored in the existing
PostDataJSON column (datafield in thepoststable), so no schema migration would be needed. ThePostDatastruct simply gets a newLangfield.Translation Discovery
Translations would be found via a database query matching posts by the same user with slugs that share a common base (e.g.,
my-postmatchesmy-post,my-post.es,my-post.pt).Backward Compatibility
This is something I paid special attention to (existing blogs should be completely unaffected in theory):
<html lang>attribute defaults to"en"when no language is specifiedUser Workflow
Open Questions
A few things I'd love your input on before diving into implementation:
EN) or full names (English/Español)? Codes are more compact; names are more user-friendly./readdiscovery page also group translations, or show all posts independently?Thanks for reading! I'm excited about this one (it's a feature I'd use every time I write on my blog). If the team thinks it's a good fit for Prose, I'm ready to put in the work and submit a PR. Happy to discuss any aspect of the design or adjust the approach based on your feedback.