Add sitewide header language selector with route-preserving locale switching#236
Open
Copilot wants to merge 6 commits into
Open
Add sitewide header language selector with route-preserving locale switching#236Copilot wants to merge 6 commits into
Copilot wants to merge 6 commits into
Conversation
Copilot created this pull request from a session on behalf of
mimiflynn
May 22, 2026 19:31
View session
Copilot
AI
changed the title
feat: full site i18n for en-US, fr-CA, pt-BR
Fix exercise template SSR locale resolution to unblock Gatsby static builds
May 22, 2026
Copilot
AI
changed the title
Fix exercise template SSR locale resolution to unblock Gatsby static builds
Add sitewide header language selector with route-preserving locale switching
May 22, 2026
There was a problem hiding this comment.
Pull request overview
Adds a locale-aware i18n layer and exposes it via a sitewide header language selector that preserves the current route when switching languages.
Changes:
- Introduces
site/src/i18nhelpers (getLocaleFromPath,getLocalePath,getLocaleSwitchPath) plus aLocaleContextprovider/hook to supplylocale+ translated UI strings. - Updates header/nav and multiple components to use localized labels (exercise UI strings, footer message, summary card terms).
- Adds locale-specific content pages (fr-CA, pt-BR), plus styling updates for the new selector and related layout.
Reviewed changes
Copilot reviewed 31 out of 31 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| site/src/utils/language.js | Defaults getCurrentLanguage locales from i18n constants. |
| site/src/templates/page.jsx | Passes heroId from frontmatter into Hero. |
| site/src/templates/index.jsx | Passes heroId from frontmatter into Hero. |
| site/src/templates/exercise.jsx | Uses i18n strings for “Exercise” navigation labels. |
| site/src/styles/style.css | Adds header layout styles for nav + language selector. |
| site/src/styles/mobile.css | Adjusts header controls layout on mobile. |
| site/src/styles/global.css | Reformatting + global style adjustments (also touches link selectors). |
| site/src/i18n/strings.spec.js | Adds tests to validate per-locale string key consistency + fallback behavior. |
| site/src/i18n/strings.js | Adds translated UI strings for nav/footer/exercise/summary cards. |
| site/src/i18n/LocaleContext.jsx | Adds LocaleProvider/useLocale to expose locale + strings. |
| site/src/i18n/index.spec.js | Adds tests for locale detection/path rewriting helpers. |
| site/src/i18n/index.js | Implements locale constants, string lookup, and route rewrite helpers. |
| site/src/components/translations.jsx | Displays human-friendly locale names in translations list. |
| site/src/components/summary-card.jsx | Localizes SummaryCard term labels via LocaleContext. |
| site/src/components/site-map.jsx | Localizes “Level” headings via LocaleContext. |
| site/src/components/layout.jsx | Wraps app layout in LocaleProvider. |
| site/src/components/hero.jsx | Supports stable heroId-based hero class naming. |
| site/src/components/header.jsx | Adds locale dropdown and localized nav labels with route-preserving switching. |
| site/src/components/footer.jsx | Localizes footer text via LocaleContext. |
| site/src/components/exercises.jsx | Localizes exercise list labels via LocaleContext. |
| site/src/components/exercise-nav.jsx | Localizes “Level” headings via LocaleContext. |
| site/content/pt-BR/teach.mdx | Adds pt-BR localized Teach page content + heroId. |
| site/content/pt-BR/makerspace.mdx | Adds pt-BR localized Makerspace page content + heroId. |
| site/content/pt-BR/continue.mdx | Adds pt-BR localized Continue page content. |
| site/content/pt-BR/about.mdx | Adds pt-BR localized About page content + heroId. |
| site/content/fr-CA/teach.mdx | Adds fr-CA localized Teach page content + heroId. |
| site/content/fr-CA/makerspace.mdx | Adds fr-CA localized Makerspace page content + heroId. |
| site/content/fr-CA/continue.mdx | Adds fr-CA localized Continue page content. |
| site/content/fr-CA/about.mdx | Adds fr-CA localized About page content + heroId. |
| site/content/exercises/pt-BR/index.mdx | Adds heroId for pt-BR Learn index page. |
| site/content/exercises/fr-CA/index.mdx | Adds heroId for fr-CA Learn index page. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
55
to
57
| a:visted { | ||
| color: var(--accent); | ||
| } |
Comment on lines
+53
to
+61
| <label className="language-selector"> | ||
| <select value={locale} onChange={onLocaleChange}> | ||
| {SUPPORTED_LOCALES.map((supportedLocale) => ( | ||
| <option key={supportedLocale} value={supportedLocale}> | ||
| {LOCALE_NAMES[supportedLocale]} | ||
| </option> | ||
| ))} | ||
| </select> | ||
| </label> |
Comment on lines
+16
to
26
| const links = [ | ||
| { label: nav.learn, path: getLocalePath('/exercises', locale) }, | ||
| { label: nav.teach, path: getLocalePath('/teach', locale) }, | ||
| { label: nav.makerspace, path: getLocalePath('/makerspace', locale) }, | ||
| { label: nav.about, path: getLocalePath('/about', locale) }, | ||
| ]; | ||
|
|
||
| function menuLink({ label, path }) { | ||
| const classname = | ||
| path === location.pathname ? 'nav-link-current' : 'nav-link'; | ||
|
|
Comment on lines
+10
to
+14
| footer: { | ||
| message: | ||
| 'Thanks! We hope you found what you are looking for. Please feel free to contribute via', | ||
| github: 'Github', | ||
| }, |
Comment on lines
+38
to
+42
| footer: { | ||
| message: | ||
| "Merci\u00a0! Nous espérons que vous avez trouvé ce que vous cherchiez. N'hésitez pas à contribuer via", | ||
| github: 'Github', | ||
| }, |
Comment on lines
+66
to
+70
| footer: { | ||
| message: | ||
| 'Obrigado! Esperamos que você tenha encontrado o que procurava. Sinta-se à vontade para contribuir via', | ||
| github: 'Github', | ||
| }, |
Comment on lines
+4
to
+16
| const topLevelKeys = Object.keys(strings[DEFAULT_LOCALE]); | ||
|
|
||
| test('all locales define the same top-level sections', () => { | ||
| SUPPORTED_LOCALES.forEach((locale) => { | ||
| expect(Object.keys(strings[locale])).toEqual(topLevelKeys); | ||
| }); | ||
| }); | ||
|
|
||
| test('all locales define the same keys within each section', () => { | ||
| SUPPORTED_LOCALES.forEach((locale) => { | ||
| topLevelKeys.forEach((section) => { | ||
| expect(Object.keys(strings[locale][section])).toEqual( | ||
| Object.keys(strings[DEFAULT_LOCALE][section]) |
Comment on lines
+4
to
+16
| const topLevelKeys = Object.keys(strings[DEFAULT_LOCALE]); | ||
|
|
||
| test('all locales define the same top-level sections', () => { | ||
| SUPPORTED_LOCALES.forEach((locale) => { | ||
| expect(Object.keys(strings[locale])).toEqual(topLevelKeys); | ||
| }); | ||
| }); | ||
|
|
||
| test('all locales define the same keys within each section', () => { | ||
| SUPPORTED_LOCALES.forEach((locale) => { | ||
| topLevelKeys.forEach((section) => { | ||
| expect(Object.keys(strings[locale][section])).toEqual( | ||
| Object.keys(strings[DEFAULT_LOCALE][section]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a sitewide language selector to the top navigation so users can switch locale from any page. Locale changes now keep users on the equivalent route instead of sending them to locale roots.
Header navigation: language selector
header.jsxpowered bySUPPORTED_LOCALES/LOCALE_NAMES.LocaleContext.Locale routing: path rewrite helpers
getLocaleSwitchPath(pathname, locale)ini18n/index.jsfor bidirectional locale switching across:/about⇄/fr-CA/about)/exercises/en-US/python/E1/⇄/exercises/fr-CA/python/E1/)/exercises/⇄/exercises/<locale>/)getLocalePathto preserve exercise subpaths when localizing paths.Localization strings + UI styling
nav.languagelabel translations instrings.jsforen-US,fr-CA, andpt-BR.style.cssandmobile.css.Coverage updates
i18n/index.spec.jswith route-switching expectations for exercise and non-exercise paths, including default-locale edge behavior.