|
1 | 1 | --- |
2 | | -const locale = Astro.locals.starlightRoute?.locale; |
3 | | -const currentLang = locale || "en"; |
4 | | -
|
5 | | -// Compute localized URL for each language |
6 | | -function getLocalizedHref(targetCode: string): string { |
7 | | - const pathname = Astro.url.pathname; |
8 | | - if (targetCode === "en") { |
9 | | - // Remove locale prefix: /ReadAny/zh/support/foo/ -> /ReadAny/support/foo/ |
10 | | - return pathname.replace(/^\/ReadAny\/[a-z]{2}\//, "/ReadAny/"); |
11 | | - } else { |
12 | | - // Add locale prefix: /ReadAny/support/foo/ -> /ReadAny/zh/support/foo/ |
13 | | - // If already has a locale prefix, replace it |
14 | | - if (/^\/ReadAny\/[a-z]{2}\//.test(pathname)) { |
15 | | - return pathname.replace(/^\/ReadAny\/[a-z]{2}\//, `/ReadAny/${targetCode}/`); |
16 | | - } |
17 | | - return pathname.replace(/^\/ReadAny\//, `/ReadAny/${targetCode}/`); |
18 | | - } |
19 | | -} |
| 2 | +import Default from '@astrojs/starlight/components/LanguageSelect.astro'; |
20 | 3 | --- |
21 | 4 |
|
22 | | -<div class="lang-select-container"> |
23 | | - <button |
24 | | - class="lang-toggle-trigger" |
25 | | - aria-label="Switch language" |
26 | | - title="Switch language" |
27 | | - > |
28 | | - <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
29 | | - <circle cx="12" cy="12" r="10"></circle> |
30 | | - <line x1="2" y1="12" x2="22" y2="12"></line> |
31 | | - <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path> |
32 | | - </svg> |
33 | | - <span class="lang-label">{currentLang === 'zh' ? '中文' : 'EN'}</span> |
34 | | - </button> |
35 | | - <div class="lang-menu"> |
36 | | - {Object.entries(languages).map(([code, name]) => ( |
37 | | - <a |
38 | | - href={getLocalizedHref(code)} |
39 | | - class:list={['lang-item', { active: currentLang === code }]} |
40 | | - > |
41 | | - <span>{name}</span> |
42 | | - {currentLang === code && ( |
43 | | - <svg class="check-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
44 | | - <polyline points="20 6 9 17 4 12"></polyline> |
45 | | - </svg> |
46 | | - )} |
47 | | - </a> |
48 | | - ))} |
49 | | - </div> |
50 | | -</div> |
51 | | - |
52 | | -<style> |
53 | | - .lang-select-container { |
54 | | - position: relative; |
55 | | - display: inline-flex; |
56 | | - flex-shrink: 0; |
57 | | - } |
58 | | - |
59 | | - .lang-toggle-trigger { |
60 | | - display: flex; |
61 | | - align-items: center; |
62 | | - justify-content: center; |
63 | | - gap: 4px; |
64 | | - height: 32px; |
65 | | - padding: 0 10px; |
66 | | - border: 1px solid var(--sl-color-border); |
67 | | - background: var(--sl-color-bg); |
68 | | - color: var(--sl-color-text); |
69 | | - cursor: pointer; |
70 | | - border-radius: 8px; |
71 | | - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); |
72 | | - box-shadow: 0 1px 2px rgba(0,0,0,0.05); |
73 | | - font-size: 13px; |
74 | | - font-weight: 500; |
75 | | - white-space: nowrap; |
76 | | - flex-shrink: 0; |
77 | | - } |
78 | | - |
79 | | - .lang-toggle-trigger:hover { |
80 | | - border-color: var(--sl-color-accent); |
81 | | - background: var(--sl-color-bg-inline-code); |
82 | | - transform: translateY(-1px); |
83 | | - box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
84 | | - } |
85 | | - |
86 | | - .lang-label { |
87 | | - font-size: 12px; |
88 | | - font-weight: 600; |
89 | | - } |
90 | | - |
91 | | - .lang-menu { |
92 | | - position: absolute; |
93 | | - top: calc(100% + 8px); |
94 | | - right: 0; |
95 | | - width: 120px; |
96 | | - background: var(--sl-color-bg-sidebar); |
97 | | - border: 1px solid var(--sl-color-border); |
98 | | - border-radius: 10px; |
99 | | - padding: 6px; |
100 | | - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); |
101 | | - opacity: 0; |
102 | | - visibility: hidden; |
103 | | - transform: translateY(-10px) scale(0.95); |
104 | | - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); |
105 | | - z-index: 100; |
106 | | - } |
107 | | - |
108 | | - .lang-select-container:hover .lang-menu, |
109 | | - .lang-select-container:focus-within .lang-menu { |
110 | | - opacity: 1; |
111 | | - visibility: visible; |
112 | | - transform: translateY(0) scale(1); |
113 | | - } |
114 | | - |
115 | | - .lang-item { |
116 | | - display: flex; |
117 | | - align-items: center; |
118 | | - justify-content: space-between; |
119 | | - width: 100%; |
120 | | - padding: 8px 10px; |
121 | | - border: none; |
122 | | - background: transparent; |
123 | | - color: var(--sl-color-text); |
124 | | - font-size: 13px; |
125 | | - font-weight: 500; |
126 | | - cursor: pointer; |
127 | | - border-radius: 6px; |
128 | | - transition: all 0.15s ease; |
129 | | - text-decoration: none; |
130 | | - } |
131 | | - |
132 | | - .lang-item:hover { |
133 | | - background: var(--sl-color-bg-inline-code); |
134 | | - color: var(--sl-color-text-accent); |
135 | | - } |
136 | | - |
137 | | - .lang-item.active { |
138 | | - background: color-mix(in srgb, var(--sl-color-accent) 10%, transparent); |
139 | | - color: var(--sl-color-text-accent); |
140 | | - } |
141 | | - |
142 | | - .check-icon { |
143 | | - color: var(--sl-color-text-accent); |
144 | | - } |
145 | | -</style> |
| 5 | +<Default><slot /></Default> |
0 commit comments