Skip to content

Tutorial custom page title #12012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/0-introduction/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ description: >-
Learn the basics of Astro with a project-based tutorial. All the background
knowledge you need to get started!
i18nReady: true
head:
- tag: title
content: Build a blog tutorial | Docs
---
import Checklist from '~/components/Checklist.astro';
import Box from '~/components/tutorial/Box.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/1-setup/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ description: >-
Prepare your development environment, and create and deploy your first Astro
site
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Unit 1 - Setup | Docs'
Comment on lines +11 to +13
Copy link
Member

@sarah11918 sarah11918 Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick note that we want this done for all six of the tutorial units (The check in page only) to match this format! (Remove "Check in:" and replace it with "Build a blog tutorial:")

If you're reading this PR, you could be contributing those changes!

---
import Checklist from '~/components/Checklist.astro';
import Box from '~/components/tutorial/Box.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/2-pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ description: |-
Tutorial: Build your first Astro blog —
Create, style, and link to pages posts on your site
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Unit 2 - Pages | Docs'
---
import Checklist from '~/components/Checklist.astro';
import Box from '~/components/tutorial/Box.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/3-components/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ description: |-
Tutorial: Build your first Astro blog —
Build Astro components to reuse code for common elements across your website
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Unit 3 - Components | Docs'
---
import Box from '~/components/tutorial/Box.astro';
import Checklist from '~/components/Checklist.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/4-layouts/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ description: >-
Use Astro layouts to share common elements and styles across your pages and
posts
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Unit 4 - Layouts | Docs'
---
import Box from '~/components/tutorial/Box.astro';
import Checklist from '~/components/Checklist.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/5-astro-api/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ description: >-
Fetching and using data from project files to dynamically generate pages
content and routes
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Unit 5 - Astro API | Docs'
---
import Box from '~/components/tutorial/Box.astro';
import Checklist from '~/components/Checklist.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/6-islands/4.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ description: |-
Tutorial: Build your first Astro blog —
Convert your blog from file-based routing to content collections
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Make a content collection | Docs'
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
import Box from '~/components/tutorial/Box.astro';
Expand Down
3 changes: 3 additions & 0 deletions src/content/docs/en/tutorial/6-islands/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ description: |-
Tutorial: Build your first Astro blog —
Use Astro islands to bring frontend framework components into your Astro site
i18nReady: true
head:
- tag: title
content: 'Build a blog tutorial: Unit 6 - Astro Islands | Docs'
---
import Box from '~/components/tutorial/Box.astro';
import Checklist from '~/components/Checklist.astro';
Expand Down
1 change: 1 addition & 0 deletions src/content/i18n-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const AstroDocsI18nSchema = z
// Tutorial Navigation
'tutorial.trackerLabel': z.string(),
'tutorial.unit': z.string(),
'tutorial.title.prefix': z.string(),
// Tutorial
'tutorial.getReady': z.string(),
// Code snippet vocabulary
Expand Down
1 change: 1 addition & 0 deletions src/content/i18n/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ progress.done: Complete
# Tutorial Navigation
tutorial.trackerLabel: Tutorial Tracker
tutorial.unit: Unit
tutorial.title.prefix: 'Build a blog tutorial: {{title}}'
# Tutorial
tutorial.getReady: Get ready to…
# Code snippet vocabulary
Expand Down
1 change: 1 addition & 0 deletions src/content/i18n/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ progress.done: Terminer
# Tutorial Navigation
tutorial.trackerLabel: Suivi du tutoriel
tutorial.unit: Unité
tutorial.title.prefix: "Tutoriel de création d'un blog : {{title}}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only alternative that comes to my mind - if we want to shorten it - would be Tutoriel Créer un blog. But without quotes around the tutorial title (ie. Créer un blog) it feels a bit odd.
So, I think your translation is just fine!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the English one the same? Build a blog tutorial and "Build a blog" tutorial.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how it sounds for native English speakers while it sounds okay to me. However, in French I don't have the same feeling... maybe because of the words order...
I would write it Tutoriel « Créer un blog » : (capital letter for tutoriel because this is the beginning of the title, and for créer because we're quoting another title). Using French quotes seems odd in a <title> tag. I think HiDeoo's translation is more appropriate.

But that's only my opinion! So, I left this comment in case I was overthinking and that HiDeoo (or anaxite which is French too I believe?) had a different opinion on the matter! 😄

# Tutorial
tutorial.getReady: Préparez-vous à…
# Code snippet vocabulary
Expand Down
30 changes: 28 additions & 2 deletions src/routeData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,29 @@ export const onRequest = defineRouteMiddleware((context) => {
});

function updateHead(context: APIContext) {
const { head, isFallback, lang } = context.locals.starlightRoute;
const { head, entry, isFallback, lang, entryMeta } = context.locals.starlightRoute;

const title = head.find((item) => item.tag === 'title');
const frontmatterTitle = entry.data.head.find((item) => item.tag === 'title');

// Update the title of tutorial entry which do not provide a custom title in their frontmatter.
if (isTutorialEntry(entry) && title && !frontmatterTitle) {
// Check if a prefix translation exists for the page content language, without any possible
// fallback.
const isPrefixTranslated = context.locals.t.exists('tutorial.title.prefix', {
lngs: [entryMeta.lang],
});

if (isPrefixTranslated) {
// If a prefix translation exists, use it to format the title.
title.content = context.locals.t('tutorial.title.prefix', {
title: title.content,
// Explicitly use the language based on the page content, which can be different from the
// page language for fallback pages.
lngs: [entryMeta.lang],
});
}
}

const ogImageUrl = getOgImageUrl(context.url.pathname, !!isFallback);
const imageSrc = ogImageUrl ?? '/default-og-image.png';
Expand Down Expand Up @@ -40,7 +62,7 @@ function updateHead(context: APIContext) {
function updateTutorialPagination(starlightRoute: StarlightRouteData) {
const { entry, locale, pagination } = starlightRoute;

if (entry.id.split('/')[1] !== 'tutorial') return;
if (!isTutorialEntry(entry)) return;

const tutorialPages = getTutorialPages(pages, locale!);
const i = tutorialPages.findIndex((p) => p.id === entry.id);
Expand Down Expand Up @@ -71,3 +93,7 @@ function updateTutorialPagination(starlightRoute: StarlightRouteData) {
};
}
}

function isTutorialEntry(entry: StarlightRouteData['entry']) {
return entry.id.split('/')[1] === 'tutorial';
}