Starter templates
Astro Multilingual Starter
A starting point for developers looking to build a multilingual website with Astro and the open-source Rosey translation workflow, using Editable Regions in CloudCannon.

A multilingual starter template for building an Astro site with CloudCannon. The site is written in English and translated into French and German with Rosey, with translations edited inline in the Visual Editor via the Rosey CloudCannon Connector.
See a demo site.
Features
- Multilingual (English, French, German) with Rosey and the Rosey CloudCannon Connector
- Inline translation editing, with split-by-directory collections for content that differs per language
- Visual editing with Editable Regions (text, image, array, source, and component regions)
- Page building with reusable components
- Blog with pagination and tags
- Tailwind CSS v4 with CSS-first configuration
- SEO controls, including translated page titles and descriptions
- Pagefind search
Getting Started
Click Use this template to make your own copy of the repository.
Prerequisites
- Node.js 22 or newer. Node.js 24 is recommended — the CloudCannon CLI requires it.
Local Development
Clone the repository to your local machine.
Start the development server.
npm install
npm run dev
CloudCannon Setup
This site is pre-configured for CloudCannon. Connect your repository and CloudCannon will detect the configuration in .cloudcannon/initial-site-settings.json and build your site automatically. The editing experience is defined in cloudcannon.config.yml, which you can modify to control your editors' experience.
Editing Locally with CloudCannon
Run CloudCannon against your local files with the CloudCannon CLI
dev server. This is the fastest way to iterate on cloudcannon.config.yml, inputs, structures, and
editable regions — you see the editing experience without committing and pushing first.
Install the CLI and log in (requires Node.js 24+):
npm install --global @cloudcannon/cli cloudcannon loginBuild the site, so the dev server has output to serve:
npm run buildStart CloudCannon locally, pointing it at the build output:
cloudcannon dev dist
The dev server runs on port 10101 by default and opens CloudCannon in your browser, pointed at the
files in this repo. Content edits sync to disk as you make them; re-run npm run build after changing
components or templates to refresh the preview.
npm run build produces the English site only — Rosey runs in .cloudcannon/postbuild, after the
Astro build. To preview the translated site locally, run the commands in that file after npm run build,
then serve the result with cloudcannon dev dist. Note that it moves dist to _untranslated_site,
so re-run npm run build before your next plain preview.
Before you commit configuration changes, validate them:
cloudcannon validate
The dev server is a development tool only — editors never access it. See Build your editing experience locally for the full workflow.
AI Agent Skills
If you build with an AI coding agent (Claude Code, Cursor, Copilot, and others), install CloudCannon's agent skills. They teach your agent how CloudCannon configuration, editable regions, and snippets actually work, so it stops guessing.
npx skills add cloudcannon/agent-skills --all
To see what's on offer before installing anything, or to install a subset:
npx skills add cloudcannon/agent-skills --list
npx skills add cloudcannon/agent-skills --skill <names>
Other useful flags and commands:
| Command | What it does |
|---|---|
--all | Install every skill for every detected agent, without prompts |
-l, --list | List the skills in the repository without installing |
-g, --global | Install for your user account instead of just this project |
npx skills ls | List the skills you have installed, project and global |
npx skills update | Update installed skills to their latest versions |
npx skills remove | Remove installed skills |
npx skills experimental_install | Restore the exact skills recorded in skills-lock.json |
Skills install to .agents/skills/, with agent-specific directories such as .claude/skills/ and agent/skills/
pointing at them. Those directories are gitignored, but skills-lock.json is committed — so a teammate can restore
the same set and versions you used.
In Claude Code you can install them as a plugin instead:
/plugin marketplace add CloudCannon/agent-skills
/plugin install agent-skills@cloudcannon
Editable Regions
This starter demonstrates several types of Editable Region:
- Text (
data-editable="text") for editing front matter text values inline - Image (
data-editable="image") for editing front matter image values - Array (
data-editable="array") for page-building with reorderable content blocks - Source (
data-editable="source") for making standalone.astropages editable - Component (
<editable-component>) for live re-rendering of Astro components
Components that need live re-rendering are registered in src/scripts/register-components.ts and loaded conditionally when the site is open in CloudCannon's Visual Editor.
Source Editables
The About page (src/content/pages/about.astro) demonstrates source editables — a pattern where content lives directly in an Astro template rather than in Markdown front matter. Source editable regions use data-editable="source", data-path="path/to/file.astro", and data-key attributes. CloudCannon writes changes straight back to the .astro file.
This is useful for standalone pages (like About or Contact) where a developer wants full control over the markup while still giving editors visual editing access — and where page building with components is not desired. No accompanying Markdown file or front matter schema is needed. A thin routing wrapper in src/pages/about.astro handles Astro's file-based routing.
Components
Three page-building components are included:
- Hero — heading, subheading, image, and optional button
- LeftRight — side-by-side text and image, with optional flip and button
- TextBlock — heading and rich text content
Content
- Pages are in
src/content/pages/as Markdown with structured front matter, and support a component-based page-building workflow. Developers can also add standalone pages paired with a routing file insrc/pages/(likesrc/content/pages/about.astro), and decide which parts of those pages are editable in CloudCannon. - Blog posts are in
src/content/blog/as MDX files - Data files (site settings, navigation) are in
data/
Multilingual
The site is built in English, then Rosey produces the French and German copies after the Astro build. Every language is served under a prefix — /en/, /fr/, /de/ — and / is a redirect page Rosey generates.
The pipeline lives in .cloudcannon/postbuild: Pagefind, then rosey generate (scans the built HTML for translation keys into rosey/base.json), then write-locales (syncs those keys into rosey/locales/*.json), then rosey build (writes the translated site).
CloudCannon requires CLOUDCANNON_SYNC_PATHS=/rosey/ so the files generated during the build are committed back to the repo. It's preset in .cloudcannon/initial-site-settings.json for new sites; existing sites need it added in their site settings. Without it, translations are lost on every build.
Two ways content gets translated
| Used for | Edited in | |
|---|---|---|
Rosey keys (data-rosey) | Shared UI, headings, nav, footer, page titles/descriptions | rosey/locales/*.json — via the visual editor or the Locales collection |
| Split-by-directory collections | Blog post bodies, where a whole article differs per language | src/content/blog_fr/, blog_de/ — ordinary content files |
Locale config is centralised in src/utils/locales.ts (collection name, date locale, display label per language). localizePath() prefixes in-site links.
Split-by-directory pages set data-rosey-root to the English-equivalent path via a roseyRoot prop, so /fr/blog/x/ shares keys with /blog/x/ rather than creating fr/-prefixed duplicates.
They also pass hideLocaleSwitcher, which sets data-rcc-exclude to every locale on the snapshot boundary so the RCC skips its locale switcher. Post bodies carry no Rosey keys, so switching locale in the Visual Editor would offer nothing to translate and read as a bug. It's set in Post.astro so both the default-language and locale post routes are covered.
Mixed pages
The blog listing keeps its switcher, because half of it is Rosey-keyed. Switching locale in the Visual Editor translates the blog:title heading and the tag chips, but leaves post titles in the default language — the switcher is a client-side swap of [data-rosey] elements, and those titles come from blog_<locale> files that were never part of this page's build.
That's the expected split, and adding a key to the title would make it worse: /en/blog/ and /fr/blog/ are both natively built, so one key is captured from both, collapses to a single entry keeping the default-language original, and Rosey then overwrites the correct titles on /fr/blog/.
/fr/blog/ is a real Astro route, not a Rosey-generated page — it appears in rosey/base.urls.json, which is produced by scanning the Astro output before rosey build runs. It has no source file of its own, though: its heading and head text come from the English pages/blog.md entry plus Rosey keys, which is why that route passes roseySeo. Nothing on it is editable in CloudCannon directly.
Head/SEO text
Rosey scans <head>, but untagged head text is copied to translated pages unchanged. Layout.astro takes a roseySeo prop that keys <title> and the meta description as {roseyRoot}:page_title / :page_description. Routes serving many pages from one template pass roseyTitleKey / roseyDescriptionKey explicitly instead.
It's opt-in per page on purpose: split-by-directory post pages must not have head keys, since their titles already come from translated frontmatter and a Rosey value would override it.
Head text isn't on the page, so it can't be edited inline — it's only reachable through the Locales collection.
Tags
Tag slugs stay English because they're the URL (/fr/tags/markdown/); only the label is translated, under a shared tags:<slug> key so each label is translated once for every chip and heading that uses it. src/pages/[locale]/tags/[tag]/[...page].astro builds tag pages per language so each lists that language's posts.
Adding a language
- Add the code to
localesinsrc/utils/locales.ts - Create
src/content/blog_<code>/, mirroringsrc/content/blog/ - Register the collection in
src/content.config.ts - In
cloudcannon.config.yml: add ablog_<code>collection withurl: '/<code>/blog/[full_slug]/', adata_config.locales_<code>entry, and the collection tocollection_groups - Add the code to
--localesin.cloudcannon/postbuild
Learn More
- CloudCannon documentation — configuration, editing, and build reference
- CloudCannon CLI reference —
cloudcannon dev,validate, and more - CloudCannon agent skills — skills for working on CloudCannon sites with AI agents
- Rosey and the Rosey CloudCannon Connector — the translation pipeline this starter uses
Project Structure
├── .cloudcannon/ # CloudCannon schemas, postbuild, editor guide
├── cloudcannon.config.yml # CloudCannon configuration
├── data/ # Site-wide data files
├── public/ # Static assets
├── rosey/ # Translation keys (base.json) and locale files
└── src/
├── components/ # Astro components
├── content/ # Content collections (pages, blog, blog_fr, blog_de)
├── layouts/ # Page layouts
├── pages/ # Astro page routes ([locale]/ for translated content)
├── scripts/ # Component registration for visual editing
├── styles/ # Global CSS (Tailwind v4)
└── utils/ # Locale configuration