Commit c3e9a27

HPCesia <me@hpcesia.com>
2026-09-15 17:29:46
widget: locale switcher
1 parent 92f6e13
Changed files (3)
src/components/widgets/LocaleSwitcher.astro
@@ -0,0 +1,46 @@
+---
+import {
+  getIntlayer,
+  getLocaleLang,
+  getLocaleName,
+  type Locale,
+} from "intlayer";
+
+import { SITE_CONFIG } from "@/config.ts";
+import { getPathWithoutLocale, getRelativeLocaleUrl } from "@/utils/url.ts";
+
+const locale = Astro.currentLocale;
+const globalI18n = getIntlayer("global", locale);
+const currentPath = Astro.url.pathname;
+const getLocaleUrl = (target: Locale) =>
+  getRelativeLocaleUrl(getPathWithoutLocale(currentPath), target) +
+  Astro.url.search;
+---
+<select
+  class="locale-switcher"
+  aria-label={globalI18n.language}
+  onchange="location.href = this.value"
+>
+  {
+    SITE_CONFIG.locales.map((target) => (
+      <option
+        value={getLocaleUrl(target)}
+        selected={target === locale}
+        lang={target}
+      >
+        {getLocaleName(getLocaleLang(target), target)}
+      </option>
+    ))
+  }
+</select>
+
+<style lang="scss" is:global>
+select.locale-switcher {
+  background-color: transparent;
+  border: none;
+  border-bottom: 0.05rem solid;
+  color: inherit;
+  font-family: inherit;
+  font-size: 1rem;
+}
+</style>
src/components/Header.astro
@@ -4,6 +4,8 @@ import { getIntlayer } from "intlayer";
 import { SITE_CONFIG } from "@/config.ts";
 import { getRelativeLocaleUrl, url } from "@/utils/url.ts";
 
+import LocaleSwitcher from "@/components/widgets/LocaleSwitcher.astro";
+
 const locale = Astro.currentLocale!;
 const globalI18n = getIntlayer("global", locale);
 ---
@@ -16,6 +18,7 @@ const globalI18n = getIntlayer("global", locale);
       <li><a href={getRelativeLocaleUrl("/about", locale)}>{globalI18n.about}</a></li>
       <li><a href={url("/feed.atom")}>{globalI18n.feed}</a></li>
     </ul>
+    <LocaleSwitcher />
   </nav>
 </header>
 
@@ -53,12 +56,18 @@ header.header {
     }
   }
 
-  nav > ul {
+  nav {
     display: flex;
-    margin-block: 0;
+    align-items: baseline;
+    gap: 0.75rem;
+
+    > ul {
+      display: flex;
+      margin-block: 0;
 
-    > :not(:last-child) {
-      margin-inline-end: 0.5rem;
+      > :not(:last-child) {
+        margin-inline-end: 0.5rem;
+      }
     }
   }
 }
src/globalI18n.content.ts
@@ -6,6 +6,7 @@ interface Content {
   about: string;
   recentPosts: string;
   feed: string;
+  language: string;
   description: string;
 }
 
@@ -28,6 +29,10 @@ export default {
       en: "Feed",
       "zh-CN": "订阅",
     }),
+    language: t({
+      en: "Language",
+      "zh-CN": "语言",
+    }),
     description: t(SITE_CONFIG.description),
   },
   schema: undefined,