-
Notifications
You must be signed in to change notification settings - Fork 99
Add translation support with language selector UI #177
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
base: main
Are you sure you want to change the base?
Conversation
… and UI components Co-authored-by: sinedied <593151+sinedied@users.noreply.github.com>
Co-authored-by: sinedied <593151+sinedied@users.noreply.github.com>
… fr_FR, etc.) Co-authored-by: sinedied <593151+sinedied@users.noreply.github.com>
|
|
||
| async loadLanguages(repoPath: string) { | ||
| try { | ||
| const catalog = await loadCatalog(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workshop rendered MUST NEVER load the catalog. It should only rely on the loaded workshop information
@copilot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 508fb66. The workshop component now uses only the workshop metadata (translations array and language field) and path information to build language options, without loading the catalog.
Co-authored-by: sinedied <593151+sinedied@users.noreply.github.com>
|
Why building a component using div for the language selection? Use the native HTML5 element for dropdown |
…ependency from workshop component Co-authored-by: sinedied <593151+sinedied@users.noreply.github.com>
Both issues fixed in 508fb66:
Screenshots: |
packages/website/src/app/shared/components/language-selector.component.ts
Fixed
Show fixed
Hide fixed
…, and error handling Co-authored-by: sinedied <593151+sinedied@users.noreply.github.com>
| const selectedOption = select.options[select.selectedIndex]; | ||
| const url = selectedOption.getAttribute('data-url'); | ||
| if (url) { | ||
| window.location.href = url; |
Check failure
Code scanning / CodeQL
DOM text reinterpreted as HTML High
DOM text
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general terms, the fix is to stop blindly trusting the string read from data-url and validate it before using it as a navigation target. Rather than assigning any arbitrary DOM-provided value to window.location.href, we should ensure it is a syntactically valid URL and, ideally, restrict it to safe schemes (e.g., http/https) or to the same origin. This prevents DOM text (or attributes) from being used to trigger navigation to malformed or potentially dangerous locations.
The best targeted fix here is to parse the url string with the standard URL constructor, check that it uses an allowed protocol and resolves either to the same origin or to http/https, and only then assign it to window.location.href. If parsing fails or the checks fail, we log an error and do not navigate. This preserves existing behavior for legitimate URLs, while blocking malicious or malformed values. No new imports are needed; the URL class is available in the browser environment where this Angular component runs.
Concretely, in packages/website/src/app/shared/components/language-selector.component.ts, we will replace the body of onLanguageChange after reading url with logic that:
- Returns early with an error if
urlis missing. - Tries to construct a
URLobject, usingwindow.location.originas the base if the URL is relative. - Checks that the resulting
URLhas protocolhttp:orhttps:(and optionally could be tightened to same-origin; here we’ll demonstrate safe-scheme enforcement). - Sets
window.location.hrefto the validated URL string if checks pass; otherwise logs an error and does not navigate.
-
Copy modified lines R84-R85 -
Copy modified line R87 -
Copy modified lines R89-R105
| @@ -81,10 +81,27 @@ | ||
| const select = event.target as HTMLSelectElement; | ||
| const selectedOption = select.options[select.selectedIndex]; | ||
| const url = selectedOption.getAttribute('data-url'); | ||
| if (url) { | ||
| window.location.href = url; | ||
| } else { | ||
|
|
||
| if (!url) { | ||
| console.error('Language selector: Invalid URL for selected language'); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| // Support both absolute and relative URLs by providing a base. | ||
| const targetUrl = new URL(url, window.location.origin); | ||
|
|
||
| // Only allow safe protocols to avoid javascript:, data:, etc. | ||
| if (targetUrl.protocol === 'http:' || targetUrl.protocol === 'https:') { | ||
| window.location.href = targetUrl.toString(); | ||
| } else { | ||
| console.error( | ||
| 'Language selector: Blocked navigation to URL with unsafe protocol', | ||
| targetUrl.toString() | ||
| ); | ||
| } | ||
| } catch (e) { | ||
| console.error('Language selector: Failed to parse URL for selected language', e); | ||
| } | ||
| } | ||
| } |


Implements workshop translation UI allowing users to view and switch between available translations. Translations are defined via metadata (
translations: [fr, es_ES]) and/or auto-discovered from/translations/*.{lang}.mdfiles.Changes
Metadata & Detection
languageandtranslationsfields to FrontMatterData/.*?\.([a-zA-Z]{2}(?:_[A-Z]{2})?)\.md$/(handles bothfrandfr_FR)findTranslations()to check both filesystem and metadata translations arraylanguageover filename detectionUI Components
LanguageSelectorComponentusing native HTML5<select>element with proper accessibilityHeaderComponenton right side of navbarWorkshop Navigation
Example
Workshop metadata:
Translation files:
Screenshots
Language selector (native HTML5 select):

Language selector expanded:

French translation:

Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.