```
Create `themes/quotify/assets/css/components/language-switcher.css`:
```css
/* Language Switcher */
.language-switcher {
position: relative;
}
.language-switcher-toggle {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
background: transparent;
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-md);
font-size: var(--text-sm);
font-weight: 500;
color: var(--color-gray-700);
cursor: pointer;
transition: all var(--transition-fast);
}
.language-switcher-toggle:hover {
border-color: var(--color-gray-300);
background: var(--color-gray-50);
}
.language-switcher-toggle:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.language-switcher-icon {
width: 16px;
height: 16px;
transition: transform var(--transition-fast);
}
.language-switcher.open .language-switcher-icon {
transform: rotate(180deg);
}
.language-switcher-list {
position: absolute;
top: 100%;
right: 0;
z-index: 50;
min-width: 140px;
margin-top: var(--space-1);
padding: var(--space-1);
background: var(--color-white);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
list-style: none;
opacity: 0;
visibility: hidden;
transform: translateY(-8px);
transition: all var(--transition-fast);
}
.language-switcher.open .language-switcher-list {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.language-switcher-option {
display: block;
padding: var(--space-2) var(--space-3);
font-size: var(--text-sm);
color: var(--color-gray-700);
text-decoration: none;
border-radius: var(--radius-sm);
transition: all var(--transition-fast);
}
.language-switcher-option:hover {
background: var(--color-gray-50);
color: var(--color-gray-900);
}
.language-switcher-option.active {
background: rgba(0, 102, 102, 0.08);
color: var(--color-primary);
font-weight: 500;
}
.language-switcher-option:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: -2px;
}
/* Mobile: Slightly larger touch targets */
@media (max-width: 768px) {
.language-switcher-toggle {
padding: var(--space-2);
}
.language-switcher-label {
display: none;
}
.language-switcher-toggle::before {
content: '';
width: 20px;
height: 20px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
}
.language-switcher-option {
padding: var(--space-3) var(--space-4);
}
}
```
Add import to `themes/quotify/assets/css/app.css` after other component imports:
```css
@import 'components/language-switcher.css';
```
Add JavaScript for dropdown toggle to the partial (inline script at bottom):
```html
```
Files exist and contain expected content: dropdown HTML, CSS styles, JS toggle logicLanguage switcher partial created with accessible dropdown, CSS styling, and toggle JavaScriptTask 2: Integrate language switcher into headerthemes/quotify/partials/header.htm
Update `themes/quotify/partials/header.htm` to include the language switcher in the desktop navigation area, near the right side of the header.
Add the language switcher partial between the nav and the mobile menu toggle:
```twig
{% partial 'language-switcher' %}
```
Add CSS for header-actions positioning (add to header.htm inline styles or ensure it exists in header CSS):
The header-actions div should be positioned to the right, before the mobile menu toggle. If using flexbox on header-inner, it will naturally flow to the right.
Style addition (can be inline or in existing header CSS):
```css
.header-actions {
display: flex;
align-items: center;
gap: var(--space-3);
margin-left: auto;
}
@media (max-width: 768px) {
.header-actions {
order: 2; /* After logo, before menu toggle */
}
}
```
Header partial includes language-switcher partial with header-actions wrapperLanguage switcher integrated into site header, visible on all pagesTask 3: Register components and add hreflang to all layouts
themes/quotify/layouts/default.htm
themes/quotify/layouts/dashboard.htm
themes/quotify/layouts/empty.htm
Update all three layouts to:
1. Register localePicker and alternateHrefLangElements components
2. Add hreflang output in head section
For each layout, add component registration in the configuration section:
```ini
[localePicker]
[alternateHrefLangElements]
```
In the `` section, after the meta tags and before stylesheets, add:
```twig
{% component 'alternateHrefLangElements' %}
```
This generates:
```html
```
For default.htm - add both components
For dashboard.htm - add both components
For empty.htm - check if it exists, add both components if present
The localePicker component is needed so the language-switcher partial can access its properties (locales, activeLocale, etc.).
All layouts register localePicker and alternateHrefLangElements components, and output hreflang in headAll layouts have LocalePicker for switcher functionality and hreflang tags for SEO
1. Visual: Language switcher dropdown visible in header on all pages
2. Functional: Click switcher, dropdown opens with language options
3. Functional: Click a language option, page reloads in that language
4. Accessibility: Switcher focusable, Escape closes dropdown
5. SEO: View page source, confirm hreflang tags present for en, pl, de
6. Mobile: On mobile viewport, switcher shows icon only (label hidden)
7. Styling: Matches design palette (teal primary, proper spacing)
- Language switcher partial exists with dropdown UI
- Header displays language switcher near right side
- Dropdown shows all enabled locales (English, Polski, Deutsch)
- Clicking language triggers localePicker::onSwitchLocale
- All layouts register localePicker and alternateHrefLangElements components
- hreflang tags present in page source for all enabled locales
- Responsive design: icon-only on mobile