@hutusi/amytis 1.5.6 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -0
- package/CLAUDE.md +3 -2
- package/GEMINI.md +13 -6
- package/README.md +1 -1
- package/TODO.md +21 -76
- package/bun.lock +18 -3
- package/content/about.mdx +1 -0
- package/content/about.zh.mdx +21 -0
- package/content/flows/2026/02/20.md +16 -0
- package/content/links.mdx +42 -0
- package/content/links.zh.mdx +41 -0
- package/content/posts/2026-02-20-i18n-routing-considerations.mdx +150 -0
- package/content/posts/multimedia-showcase/index.mdx +261 -0
- package/content/privacy.mdx +32 -0
- package/content/privacy.zh.mdx +32 -0
- package/docs/ARCHITECTURE.md +11 -2
- package/docs/CONTRIBUTING.md +4 -2
- package/docs/deployment.md +9 -1
- package/eslint.config.mjs +2 -0
- package/package.json +5 -4
- package/public/next-image-export-optimizer-hashes.json +0 -3
- package/scripts/copy-assets.ts +1 -1
- package/site.config.ts +126 -44
- package/src/app/[slug]/page.tsx +0 -10
- package/src/app/archive/page.tsx +38 -10
- package/src/app/books/[slug]/page.tsx +18 -0
- package/src/app/flows/[year]/[month]/[day]/page.tsx +21 -4
- package/src/app/layout.tsx +48 -21
- package/src/app/page.tsx +135 -72
- package/src/app/posts/[slug]/page.tsx +6 -12
- package/src/app/search.json/route.ts +4 -0
- package/src/app/series/[slug]/page.tsx +18 -0
- package/src/app/subscribe/page.tsx +17 -0
- package/src/app/tags/[tag]/page.tsx +9 -26
- package/src/app/tags/page.tsx +3 -8
- package/src/components/AuthorCard.tsx +43 -0
- package/src/components/Comments.tsx +20 -4
- package/src/components/ExternalLinks.tsx +6 -2
- package/src/components/Footer.tsx +35 -26
- package/src/components/LanguageProvider.tsx +0 -5
- package/src/components/LanguageSwitch.tsx +117 -6
- package/src/components/LocaleSwitch.tsx +33 -0
- package/src/components/Navbar.tsx +31 -8
- package/src/components/PostNavigation.tsx +55 -0
- package/src/components/PostSidebar.tsx +172 -126
- package/src/components/ReadingProgressBar.tsx +6 -21
- package/src/components/RelatedPosts.tsx +1 -1
- package/src/components/Search.tsx +420 -70
- package/src/components/SelectedBooksSection.tsx +12 -6
- package/src/components/ShareBar.tsx +115 -0
- package/src/components/SimpleLayoutHeader.tsx +5 -14
- package/src/components/SubscribePage.tsx +298 -0
- package/src/components/TagContentTabs.tsx +103 -0
- package/src/components/TagPageHeader.tsx +7 -13
- package/src/components/TagSidebar.tsx +142 -0
- package/src/components/TagsIndexClient.tsx +156 -0
- package/src/hooks/useScrollY.ts +41 -0
- package/src/i18n/translations.ts +110 -2
- package/src/layouts/PostLayout.tsx +34 -7
- package/src/layouts/SimpleLayout.tsx +53 -15
- package/src/lib/markdown.ts +71 -15
- package/src/lib/search-utils.test.ts +163 -0
- package/src/lib/search-utils.ts +39 -0
- package/src/types/pagefind.d.ts +42 -0
- package/src/components/TableOfContents.tsx +0 -158
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.6.0] - 2026-02-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Search Engine Upgrade**: Migrated from Fuse.js to **Pagefind** for high-performance, static full-text search with extremely low client-side overhead.
|
|
12
|
+
- **Enhanced Search UI**:
|
|
13
|
+
- Full-content search with context highlighting and excerpts.
|
|
14
|
+
- Type-based filtering tabs (All, Post, Flow, Book).
|
|
15
|
+
- Recent searches history persisted in local storage.
|
|
16
|
+
- Interactive search tips panel with syntax hints.
|
|
17
|
+
- Advanced keyboard navigation (Tab-based focus trap, arrow keys, Alt+number shortcuts).
|
|
18
|
+
- Debounced input and visual loading states.
|
|
19
|
+
- Full-screen responsive layout for mobile devices.
|
|
20
|
+
- **Search Utilities**: New unit-tested utility library for processing search results and metadata.
|
|
21
|
+
- **Project Documentation**: Added a comprehensive `CHANGELOG.md` documenting the project's evolution from 1.0.0.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- **Documentation Overhaul**: Streamlined `TODO.md` roadmap and updated `GEMINI.md` and `ARCHITECTURE.md` to reflect the new search architecture.
|
|
25
|
+
- **i18n**: Fully localized search interface supporting both English and Chinese.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **Hydration**: Suppressed body-level hydration warnings caused by browser extensions.
|
|
29
|
+
- **Search Precision**: Improved title cleaning and date extraction for search results.
|
|
30
|
+
|
|
31
|
+
## [1.5.6] - 2026-02-19
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Scoped Publishing**: Transitioned to `@hutusi/amytis` for official release on npm and GitHub.
|
|
35
|
+
- **Trusted Publishing**: Implemented secure OIDC-based deployment for npm.
|
|
36
|
+
- **Engineering Quality**: Added `bun run validate` to integrate linting, testing, and building.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- **Refined DX**: Cleaned up all linting warnings and optimized interactive components with `requestAnimationFrame` cleanup.
|
|
40
|
+
- **Better Accessibility**: Improved `MarkdownRenderer` semantics by preserving native `<p>` tags.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- **Hydration & Stability**: Resolved all React 19 hydration mismatches in `LanguageProvider`, `Hero`, and `ThemeToggle`.
|
|
44
|
+
- **Content Fixes**: Corrected mangled LaTeX formulas and resolved duplicated frontmatter mapping keys.
|
|
45
|
+
|
|
46
|
+
## [1.5.0] - 2026-02-18
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- **Daily Notes (Flows)**: A new stream-style content format for micro-blogging and quick thoughts.
|
|
50
|
+
- **Full-text Search**: Migrated to **Pagefind** for high-performance static full-text indexing.
|
|
51
|
+
- **Metadata Inheritance**: Implemented logic for posts to inherit attributes from series metadata.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
- **Major Tech Upgrade**: Modernized architecture to **Next.js 16 (App Router)** and **React 19**.
|
|
55
|
+
- **UI Overhaul**: Redesigned homepage with horizontal scroll featured sections and distinct card styles.
|
|
56
|
+
- **Austere Elegance**: New design for Archive and Tags pages focusing on minimalism.
|
|
57
|
+
|
|
58
|
+
## [1.4.0] - 2026-02-10
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
- **Books Feature**: Support for structured, multi-chapter long-form content.
|
|
62
|
+
- **Reading Progress**: Integrated sticky progress bar and scroll tracking.
|
|
63
|
+
- **Import Tooling**: New CLI commands for PDF and image folder ingestion.
|
|
64
|
+
|
|
65
|
+
## [1.3.0] - 2026-02-05
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
- **Internationalization (i18n)**: Native support for English and Chinese with language switching.
|
|
69
|
+
- **Smart Reading Time**: Multilingual character counting for accurate estimates.
|
|
70
|
+
- **Author Pages**: Detailed statistics and contribution tracking.
|
|
71
|
+
|
|
72
|
+
## [1.2.0] - 2026-01-30
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
- **Series Management**: Robust support for manual ordering and folder-based structures.
|
|
76
|
+
- **Rich Code Blocks**: Syntax highlighting for 11+ languages with copy-to-clipboard.
|
|
77
|
+
|
|
78
|
+
## [1.1.0] - 2026-01-20
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
- **Testing Suite**: Integrated Vitest/Bun Test with 64+ automated tests.
|
|
82
|
+
- **Integrations**: Support for Giscus comments and multiple analytics providers (Umami/Plausible).
|
|
83
|
+
|
|
84
|
+
## [1.0.0] - 2026-01-12
|
|
85
|
+
|
|
86
|
+
### Added
|
|
87
|
+
- **Initial Release**: Launch of **Amytis**, a high-performance digital garden and blog engine.
|
|
88
|
+
- **Next.js Foundation**: Built on App Router for optimal static site generation (SSG).
|
|
89
|
+
- **Advanced Markdown**: Native support for GFM, Mermaid diagrams, and LaTeX math.
|
|
90
|
+
- **Refined Typography**: High-contrast typefaces and optimized readability.
|
|
91
|
+
- **Theming System**: Four built-in palettes (`default`, `blue`, `rose`, `amber`) with Dark Mode.
|
|
92
|
+
- **Content Organization**: Flexible flat-file and nested-folder post structures.
|
|
93
|
+
- **Smart Discovery**: Client-side search, tag clouds, and chronological archives.
|
|
94
|
+
- **SEO & Performance**: Automated Sitemap/RSS generation and optimized WebP delivery.
|
package/CLAUDE.md
CHANGED
|
@@ -22,7 +22,7 @@ bun test path/to/file.test.ts # Run a single test file
|
|
|
22
22
|
|
|
23
23
|
# Build
|
|
24
24
|
bun run build # Full production build (copies assets, builds Next.js, optimizes images)
|
|
25
|
-
bun run build:dev # Development build (no image optimization, faster)
|
|
25
|
+
bun run build:dev # Development build (no image optimization, faster) — also regenerates Pagefind search index in public/pagefind/
|
|
26
26
|
bun run clean # Remove .next, out, public/posts directories
|
|
27
27
|
|
|
28
28
|
# Content creation
|
|
@@ -58,6 +58,7 @@ bun run new-flow --mdx # Use .mdx format instead
|
|
|
58
58
|
|
|
59
59
|
- `site.config.ts` - Site configuration (nav, social, pagination, themes, i18n, analytics, comments)
|
|
60
60
|
- `src/lib/markdown.ts` - Data access layer with all content query functions
|
|
61
|
+
- `src/lib/search-utils.ts` - Pure search utilities (URL type detection, date extraction, title cleaning, markdown stripping) shared by `Search` and the search index route
|
|
61
62
|
- `src/app/globals.css` - Theme CSS variables and color palettes
|
|
62
63
|
- `src/components/MarkdownRenderer.tsx` - MDX rendering with all plugins
|
|
63
64
|
- `src/i18n/translations.ts` - Language strings for i18n
|
|
@@ -187,7 +188,7 @@ chapters:
|
|
|
187
188
|
- `SeriesCatalog` - Timeline-style series post listing with numbered entries and progress indicator
|
|
188
189
|
- `SeriesSidebar` - Series navigation sidebar with progress bar and color-coded states
|
|
189
190
|
- `SeriesList` - Mobile-optimized series navigation matching sidebar design
|
|
190
|
-
- `Search` -
|
|
191
|
+
- `Search` - Full-text search (Cmd/Ctrl+K) powered by Pagefind (build-time index); features type filter tabs (All/Post/Flow/Book), recent searches, keyboard navigation (arrows + number keys), debounced input, body scroll lock, focus trap, ARIA accessibility, and search syntax tips (`"phrase"`, `-exclude`)
|
|
191
192
|
- `TableOfContents` - Sticky TOC with scroll tracking, reading progress, and back-to-top
|
|
192
193
|
- `MarkdownRenderer` - MDX rendering with GFM, math, syntax highlighting, diagrams
|
|
193
194
|
- `CoverImage` - Optimized image component with WebP support
|
package/GEMINI.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# Amytis Project Context
|
|
2
2
|
|
|
3
3
|
## Project Overview
|
|
4
|
-
**Amytis** is a high-performance, elegant digital garden and blog engine built with **Next.js 16 (App Router)**, **React 19**, and **Tailwind CSS v4**. It is designed for cultivating thoughts and sharing knowledge with a focus on typography, readability, and flexible content organization.
|
|
4
|
+
**Amytis** (@hutusi/amytis) is a high-performance, elegant digital garden and blog engine built with **Next.js 16 (App Router)**, **React 19**, and **Tailwind CSS v4**. It is designed for cultivating thoughts and sharing knowledge with a focus on typography, readability, and flexible content organization.
|
|
5
|
+
|
|
6
|
+
The package is officially published to both **npm** and **GitHub Packages** with automated **Trusted Publishing** (OIDC).
|
|
5
7
|
|
|
6
8
|
### Main Technologies
|
|
7
9
|
- **Framework**: Next.js 16 (App Router)
|
|
8
10
|
- **Runtime/Package Manager**: [Bun](https://bun.sh/)
|
|
9
11
|
- **Styling**: Tailwind CSS v4 with CSS-variable based themes and `@tailwindcss/typography`.
|
|
10
12
|
- **Content**: Local MDX/Markdown files with Zod-validated frontmatter.
|
|
11
|
-
- **Search**:
|
|
13
|
+
- **Search**: Static full-text search using `Pagefind`.
|
|
12
14
|
- **Diagrams**: Native support for `Mermaid` diagrams.
|
|
13
15
|
- **Math**: LaTeX support via `rehype-katex`.
|
|
14
16
|
|
|
@@ -23,7 +25,7 @@ bun dev
|
|
|
23
25
|
```bash
|
|
24
26
|
bun run build
|
|
25
27
|
```
|
|
26
|
-
Generates a fully optimized static site in the `out/` directory.
|
|
28
|
+
Generates a fully optimized static site in the `out/` directory with Pagefind search index.
|
|
27
29
|
|
|
28
30
|
### Linting & Testing
|
|
29
31
|
```bash
|
|
@@ -37,13 +39,17 @@ bun test
|
|
|
37
39
|
- `posts/`: Paginated post listing and individual post routes.
|
|
38
40
|
- `flows/`: Stream-style daily notes or micro-blogging (`[year]/[month]/[day]`).
|
|
39
41
|
- `series/`: Series overview and individual series catalog pages with pagination support.
|
|
42
|
+
- `books/`: Books overview and individual book/chapter pages (`[slug]/[chapter]`).
|
|
40
43
|
- `archive/`: Timeline-based chronological archive grouped by year and month.
|
|
41
44
|
- `tags/`: Popularity-sorted tag cloud and filtered listings.
|
|
42
45
|
- `authors/`: Posts filtered by individual authors.
|
|
43
|
-
- `
|
|
46
|
+
- `subscribe/`: Subscription options (RSS, Newsletter, Social).
|
|
47
|
+
- `search.json/`: Static search index generator (supplementary).
|
|
44
48
|
- `src/lib/`: Core logic and utilities.
|
|
45
|
-
- `markdown.ts`: Advanced parsing for posts/series/flows, sorting,
|
|
46
|
-
- `
|
|
49
|
+
- `markdown.ts`: Advanced parsing for posts/series/flows, sorting, and metadata.
|
|
50
|
+
- `search-utils.ts`: Content cleaning and search result processing.
|
|
51
|
+
- `shuffle.ts`: Deterministic and random array shuffling.
|
|
52
|
+
- `src/components/`: Modular UI blocks (Hero, HorizontalScroll, Search, CoverImage, ShareBar, etc.).
|
|
47
53
|
- `content/`: Source Markdown/MDX content.
|
|
48
54
|
- `scripts/`: CLI tools for content management and asset processing.
|
|
49
55
|
|
|
@@ -70,6 +76,7 @@ bun test
|
|
|
70
76
|
### Build Pipeline
|
|
71
77
|
- **Asset Mapping**: `scripts/copy-assets.ts` mirrors content assets to the public folder, handling relative path resolution for both flat and nested structures.
|
|
72
78
|
- **Image Optimization**: Fully integrated with `next-image-export-optimizer` for optimized WebP delivery in static exports.
|
|
79
|
+
- **Search Indexing**: `Pagefind` runs after build to generate a static search index from the output directory.
|
|
73
80
|
|
|
74
81
|
## Recent Updates
|
|
75
82
|
- Added **Flows** feature: a stream for daily notes and micro-blogging.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- **Digital Garden Philosophy:** Non-linear navigation through tags, series, authors, books, flows, and chronological archives.
|
|
12
|
-
- **
|
|
12
|
+
- **Full-text Search:** Fast, static client-side search across all content (Cmd/Ctrl+K) powered by Pagefind.
|
|
13
13
|
- **Structured Content:**
|
|
14
14
|
- **Series:** Multi-part content organization with manual or automatic ordering.
|
|
15
15
|
- **Books:** Long-form content with explicit chapters, parts, and a dedicated reading interface.
|
package/TODO.md
CHANGED
|
@@ -1,76 +1,21 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
## 🚀
|
|
4
|
-
|
|
5
|
-
- [ ] **
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- [ ] **Visuals**
|
|
24
|
-
- [ ] **Dynamic OG Images**: Generate custom social cards with post title using `@vercel/og` (Satori).
|
|
25
|
-
|
|
26
|
-
- [ ] **CLI Enhancements**
|
|
27
|
-
- [ ] **Interactive Mode**: Use prompts to select series, tags, and layouts when creating new posts.
|
|
28
|
-
|
|
29
|
-
## ✅ Completed
|
|
30
|
-
|
|
31
|
-
- [x] **Content Types**
|
|
32
|
-
- [x] **Flows**: Stream-style daily notes or micro-blogging.
|
|
33
|
-
- [x] **Books**: Structured long-form content with chapters.
|
|
34
|
-
|
|
35
|
-
- [x] **Engagement**
|
|
36
|
-
- [x] **Comments**: Integrate Giscus (GitHub Discussions) for comments.
|
|
37
|
-
|
|
38
|
-
- [x] **Engineering**
|
|
39
|
-
- [x] **Validation**: Add Zod schema validation for content frontmatter to prevent build errors.
|
|
40
|
-
- [x] **Testing**: Add E2E tests for the new search and navigation features.
|
|
41
|
-
|
|
42
|
-
- [x] **SEO & Discovery**
|
|
43
|
-
- [x] Generate `sitemap.xml` for search engines.
|
|
44
|
-
- [x] Generate `rss.xml` / `atom.xml` for feed readers.
|
|
45
|
-
- [x] Add Open Graph (OG) meta tags for social sharing.
|
|
46
|
-
|
|
47
|
-
- [x] **Navigation & UX**
|
|
48
|
-
- [x] Implement a client-side fuzzy Search bar (Command+K).
|
|
49
|
-
- [x] Add a sticky Table of Contents (TOC) with Unicode/Multilingual support.
|
|
50
|
-
- [x] Add "Reading Time" estimate to post headers.
|
|
51
|
-
- [x] Refine link styling (clean default, underline on hover).
|
|
52
|
-
- [x] **Hero Section**: Configurable, collapsible welcome mat.
|
|
53
|
-
- [x] **Themes**: Configurable color palettes (default, blue, rose, amber).
|
|
54
|
-
- [x] **i18n**: Client-side language switcher infrastructure.
|
|
55
|
-
|
|
56
|
-
- [x] **Content & Architecture**
|
|
57
|
-
- [x] **Series**: Robust support for grouping related posts (file-based & folder-based).
|
|
58
|
-
- [x] **Series**: Manual sorting, cross-referencing, and configurable order.
|
|
59
|
-
- [x] **Related Posts**: Auto-suggest relevant articles.
|
|
60
|
-
- [x] **Cover Images**: Support local paths, external URLs, and generated text covers.
|
|
61
|
-
- [x] **Analytics**: Privacy-friendly configuration (Umami/Plausible/Google).
|
|
62
|
-
|
|
63
|
-
- [x] **Performance**
|
|
64
|
-
- [x] Static Image Optimization (`next-image-export-optimizer`).
|
|
65
|
-
- [x] Automated image dimension injection.
|
|
66
|
-
|
|
67
|
-
- [x] **CLI Tools**
|
|
68
|
-
- [x] `bun run new` script for scaffolding posts.
|
|
69
|
-
- [x] `bun run new-series` script for scaffolding series.
|
|
70
|
-
|
|
71
|
-
- [x] **UI Polish & Refinements**
|
|
72
|
-
- [x] **Animations**: Define missing animation classes.
|
|
73
|
-
- [x] **Color Contrast**: Improve accessibility.
|
|
74
|
-
- [x] **Responsive Grids**: Improve mobile/tablet layouts.
|
|
75
|
-
- [x] **Archive Timeline**: Refine CSS.
|
|
76
|
-
- [x] **Loading States**: Add skeleton loaders.
|
|
1
|
+
# Amytis Roadmap
|
|
2
|
+
|
|
3
|
+
## 🚀 Priority UX & Engineering
|
|
4
|
+
- [ ] **Image Zoom**: Implement medium-zoom or a lightbox for MDX images.
|
|
5
|
+
- [ ] **Breadcrumbs**: Extend Flow breadcrumbs to standard Posts and Books.
|
|
6
|
+
- [ ] **Dynamic OG**: Generate automated social cards with Satori for every post.
|
|
7
|
+
- [ ] **PWA Support**: Add manifest and service worker for offline reading.
|
|
8
|
+
|
|
9
|
+
## 🌿 Digital Garden Evolution
|
|
10
|
+
- [ ] **Backlinks**: Automatically list "Pages that link here" at the bottom of articles.
|
|
11
|
+
- [ ] **Wiki-links**: Support `[[internal-link]]` syntax for easier cross-referencing.
|
|
12
|
+
- [ ] **Knowledge Graph**: Interactive visual map of all connected notes.
|
|
13
|
+
|
|
14
|
+
## ✅ Completed Highlights
|
|
15
|
+
- [x] **Pagefind Search**: High-performance static full-text indexing with rich UI.
|
|
16
|
+
- [x] **Smart Navigation**: Persistent "Previous" and "Next" article links on all posts.
|
|
17
|
+
- [x] **Multi-format Content**: Native support for **Posts**, **Series**, **Books**, and **Flows**.
|
|
18
|
+
- [x] **Professional Publishing**: Scoped `@hutusi/amytis` package with OIDC Trusted Publishing.
|
|
19
|
+
- [x] **Robust Engineering**: Zero hydration mismatches, Zod validation, and 64+ automated tests.
|
|
20
|
+
- [x] **Refined UI**: High-contrast typography, four color palettes, and horizontal scroll featured sections.
|
|
21
|
+
- [x] **Sub-features**: Newsletter/Subscribe page, Reading Progress, and Author Ecosystem.
|
package/bun.lock
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@giscus/react": "^3.1.0",
|
|
9
9
|
"@tailwindcss/typography": "^0.5.19",
|
|
10
|
-
"fuse.js": "^7.1.0",
|
|
11
10
|
"github-slugger": "^2.0.0",
|
|
12
11
|
"gray-matter": "^4.0.3",
|
|
13
12
|
"image-size": "^2.0.2",
|
|
@@ -17,6 +16,7 @@
|
|
|
17
16
|
"next-themes": "^0.4.6",
|
|
18
17
|
"react": "19.2.4",
|
|
19
18
|
"react-dom": "19.2.4",
|
|
19
|
+
"react-icons": "^5.5.0",
|
|
20
20
|
"react-markdown": "^10.1.0",
|
|
21
21
|
"react-syntax-highlighter": "^16.1.0",
|
|
22
22
|
"rehype-katex": "^7.0.1",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"babel-plugin-react-compiler": "1.0.0",
|
|
39
39
|
"eslint": "^9.0.0",
|
|
40
40
|
"eslint-config-next": "16.1.6",
|
|
41
|
+
"pagefind": "^1.4.0",
|
|
41
42
|
"pdf-to-img": "^5.0.0",
|
|
42
43
|
"tailwindcss": "^4.1.18",
|
|
43
44
|
"typescript": "^5.9.3",
|
|
@@ -257,6 +258,18 @@
|
|
|
257
258
|
|
|
258
259
|
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
|
|
259
260
|
|
|
261
|
+
"@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ=="],
|
|
262
|
+
|
|
263
|
+
"@pagefind/darwin-x64": ["@pagefind/darwin-x64@1.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A=="],
|
|
264
|
+
|
|
265
|
+
"@pagefind/freebsd-x64": ["@pagefind/freebsd-x64@1.4.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q=="],
|
|
266
|
+
|
|
267
|
+
"@pagefind/linux-arm64": ["@pagefind/linux-arm64@1.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw=="],
|
|
268
|
+
|
|
269
|
+
"@pagefind/linux-x64": ["@pagefind/linux-x64@1.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg=="],
|
|
270
|
+
|
|
271
|
+
"@pagefind/windows-x64": ["@pagefind/windows-x64@1.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g=="],
|
|
272
|
+
|
|
260
273
|
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
|
|
261
274
|
|
|
262
275
|
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
|
@@ -767,8 +780,6 @@
|
|
|
767
780
|
|
|
768
781
|
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
|
769
782
|
|
|
770
|
-
"fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="],
|
|
771
|
-
|
|
772
783
|
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
|
773
784
|
|
|
774
785
|
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
|
@@ -1173,6 +1184,8 @@
|
|
|
1173
1184
|
|
|
1174
1185
|
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
|
|
1175
1186
|
|
|
1187
|
+
"pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="],
|
|
1188
|
+
|
|
1176
1189
|
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
|
1177
1190
|
|
|
1178
1191
|
"parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
|
|
@@ -1225,6 +1238,8 @@
|
|
|
1225
1238
|
|
|
1226
1239
|
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
|
1227
1240
|
|
|
1241
|
+
"react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="],
|
|
1242
|
+
|
|
1228
1243
|
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
|
1229
1244
|
|
|
1230
1245
|
"react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="],
|
package/content/about.mdx
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "关于 Amytis"
|
|
3
|
+
date: "2026-01-07"
|
|
4
|
+
excerpt: "了解这座数字花园背后的理念与技术。"
|
|
5
|
+
layout: "simple"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Amytis 是一座为长期思想培育而设计的数字花园。与博客不同,数字花园是一个持续演化的想法网络。
|
|
9
|
+
|
|
10
|
+
## 技术栈
|
|
11
|
+
|
|
12
|
+
- **Next.js 15+** — 基础框架
|
|
13
|
+
- **Tailwind CSS v4** — 样式系统
|
|
14
|
+
- **MDX** — 支持 React 组件的富内容格式
|
|
15
|
+
- **Bun** — 极速开发体验
|
|
16
|
+
|
|
17
|
+
## 理念
|
|
18
|
+
|
|
19
|
+
我们相信**公开学习**的力量。这里是种下种子(初始笔记)、悉心耕耘(提炼与关联)、最终长成长青文章的地方。
|
|
20
|
+
|
|
21
|
+
> "花园是最好的老师。它教人耐心与细心观察,教人勤劳与节俭,尤其教人全然的信任。" — Gertrude Jekyll
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Multimedia embeds in markdown"
|
|
3
|
+
tags: ["video", "podcast", "markdown", "web"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Tested multimedia embedding in Amytis today. Since `rehype-raw` is enabled, standard HTML `<iframe>`, `<video>`, and `<audio>` tags work directly inside Markdown — no plugins needed.
|
|
7
|
+
|
|
8
|
+
A few patterns that work well:
|
|
9
|
+
|
|
10
|
+
- **YouTube / Vimeo** — wrap in a `padding-bottom: 56.25%` container for responsive 16:9
|
|
11
|
+
- **Spotify / Apple Podcasts** — fixed height iframes (152px for compact, 352px for full player)
|
|
12
|
+
- **HTML5 `<video>`** — fully native, supports `poster`, `loop`, `autoplay muted`
|
|
13
|
+
- **HTML5 `<audio>`** — great for podcast episodes or background music samples
|
|
14
|
+
- **Twitch** — needs the `parent` query param set to your domain
|
|
15
|
+
|
|
16
|
+
The `loading="lazy"` attribute is underused — always worth adding to below-the-fold embeds to keep the initial page fast.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Links"
|
|
3
|
+
date: "2026-01-01"
|
|
4
|
+
excerpt: "A curated collection of resources, tools, and friends on the web."
|
|
5
|
+
layout: "simple"
|
|
6
|
+
toc: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
A living collection of places worth visiting — useful resources I return to, and friends whose writing I admire.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Resources
|
|
14
|
+
|
|
15
|
+
### Writing & Thinking
|
|
16
|
+
|
|
17
|
+
- [Zettelkasten Method](https://zettelkasten.de/) — The definitive guide to networked note-taking and knowledge management.
|
|
18
|
+
- [Andy Matuschak's Notes](https://notes.andymatuschak.org/) — A public working notebook from a researcher in tools for thought.
|
|
19
|
+
- [LessWrong](https://www.lesswrong.com/) — A community blog focused on rationality and careful reasoning.
|
|
20
|
+
|
|
21
|
+
### Development
|
|
22
|
+
|
|
23
|
+
- [MDN Web Docs](https://developer.mozilla.org/) — The most reliable reference for web platform APIs.
|
|
24
|
+
- [The Changelog](https://changelog.com/) — Podcasts and news for developers following open-source software.
|
|
25
|
+
- [Hacker News](https://news.ycombinator.com/) — Technology news and community discussion curated by Y Combinator.
|
|
26
|
+
|
|
27
|
+
### Design
|
|
28
|
+
|
|
29
|
+
- [Refactoring UI](https://www.refactoringui.com/) — Practical design advice for developers, by the creators of Tailwind CSS.
|
|
30
|
+
- [Sidebar.io](https://sidebar.io/) — Five design links every day, covering UI, typography, and visual inspiration.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Friends
|
|
35
|
+
|
|
36
|
+
- [Example Blog](https://example.com/) — A thoughtful writer exploring the intersection of technology and everyday life.
|
|
37
|
+
- [Another Garden](https://example.com/) — Beautifully tended digital garden with notes on philosophy and code.
|
|
38
|
+
- [Friend's Site](https://example.com/) — Short-form essays on creativity, craft, and the making of things.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
*Know a link that belongs here? [Send it my way](/about).*
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "链接"
|
|
3
|
+
date: "2026-01-01"
|
|
4
|
+
excerpt: "精选资源、工具与友情链接。"
|
|
5
|
+
layout: "simple"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
持续更新的精选集合——常常造访的实用资源,以及值得关注的朋友们。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 资源
|
|
13
|
+
|
|
14
|
+
### 写作与思考
|
|
15
|
+
|
|
16
|
+
- [卡片盒笔记法](https://zettelkasten.de/) — 网络化笔记与知识管理的权威指南。
|
|
17
|
+
- [Andy Matuschak 的笔记](https://notes.andymatuschak.org/) — 思维工具研究者的公开工作笔记本。
|
|
18
|
+
- [LessWrong](https://www.lesswrong.com/) — 专注于理性思维的社区博客。
|
|
19
|
+
|
|
20
|
+
### 开发
|
|
21
|
+
|
|
22
|
+
- [MDN Web Docs](https://developer.mozilla.org/) — 最可靠的 Web 平台 API 参考文档。
|
|
23
|
+
- [The Changelog](https://changelog.com/) — 面向开发者的开源软件播客与资讯。
|
|
24
|
+
- [Hacker News](https://news.ycombinator.com/) — Y Combinator 精选的技术资讯与社区讨论。
|
|
25
|
+
|
|
26
|
+
### 设计
|
|
27
|
+
|
|
28
|
+
- [Refactoring UI](https://www.refactoringui.com/) — Tailwind CSS 作者出品的开发者实用设计指南。
|
|
29
|
+
- [Sidebar.io](https://sidebar.io/) — 每日五条设计链接,涵盖 UI、排版与视觉灵感。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 朋友们
|
|
34
|
+
|
|
35
|
+
- [示例博客](https://example.com/) — 探索技术与日常生活交汇点的深度写作者。
|
|
36
|
+
- [另一座花园](https://example.com/) — 关于哲学与代码的精心耕耘的数字花园。
|
|
37
|
+
- [朋友的站点](https://example.com/) — 关于创造、手艺与制作过程的短篇随笔。
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
*有好链接想推荐?[告诉我](/about)。*
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "i18n in a Static Next.js Blog: Client-Side Toggle vs URL-Based Routing"
|
|
3
|
+
date: "2026-02-20"
|
|
4
|
+
excerpt: "A deep dive into the trade-offs between client-side language switching and proper URL-based locale routing for a statically exported Next.js digital garden."
|
|
5
|
+
tags: ["nextjs", "i18n", "seo", "static-site"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
When building a multilingual static blog with Next.js, there are two fundamentally different approaches to internationalisation. Choosing between them involves trade-offs across SEO, developer experience, content strategy, and hosting complexity.
|
|
9
|
+
|
|
10
|
+
## The Client-Side Toggle Approach
|
|
11
|
+
|
|
12
|
+
The simplest approach stores language preference in client state and resolves translations after hydration:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
// Language stored in context, resolved on the client
|
|
16
|
+
const { t, language } = useLanguage();
|
|
17
|
+
const label = t('about'); // "About" or "关于"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For page content that has locale variants, all versions are server-rendered into the HTML and a thin client wrapper toggles visibility:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// LocaleSwitch: shows/hides [data-locale] divs via useEffect
|
|
24
|
+
<LocaleSwitch>
|
|
25
|
+
<div data-locale="en"><MarkdownRenderer content={enContent} /></div>
|
|
26
|
+
<div data-locale="zh" style={{ display: 'none' }}><MarkdownRenderer content={zhContent} /></div>
|
|
27
|
+
</LocaleSwitch>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This works and is zero-config — no routing changes needed. But it has real limitations.
|
|
31
|
+
|
|
32
|
+
### SEO Problems
|
|
33
|
+
|
|
34
|
+
- **Crawlers always see the default language.** Googlebot doesn't execute `localStorage` reads, so it always indexes the default locale's content.
|
|
35
|
+
- **`display:none` content is risky.** Google may partially index hidden locale variants, or ignore them entirely.
|
|
36
|
+
- **No `hreflang` signals.** Search engines can't discover alternate language versions of a page.
|
|
37
|
+
- **`<html lang>` not updated.** Accessibility and search engine tooling rely on this attribute.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## URL-Based Locale Routing
|
|
42
|
+
|
|
43
|
+
The proper solution gives each language variant its own URL:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
/about → English (default, no prefix)
|
|
47
|
+
/zh/about → Chinese
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
With Next.js App Router, this means moving all routes under an `app/[locale]/` segment and using `generateStaticParams` to pre-render each route for each locale at build time.
|
|
51
|
+
|
|
52
|
+
Since Amytis uses `output: "export"`, this is pure **Static Site Generation** — the HTML is fully pre-built per locale. Crawlers get complete HTML with no JavaScript required, and each locale is independently indexable.
|
|
53
|
+
|
|
54
|
+
### The Default Locale Prefix Problem
|
|
55
|
+
|
|
56
|
+
Most sites want the default locale to have a clean URL (`/about`, not `/en/about`). With a runtime server, middleware handles this transparently. With static export, you need your **hosting layer** to do the rewrite.
|
|
57
|
+
|
|
58
|
+
#### Netlify / Cloudflare Pages
|
|
59
|
+
|
|
60
|
+
Drop a `_redirects` file in `public/`:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
/en / 301
|
|
64
|
+
/en/* /:splat 301
|
|
65
|
+
/* /en/:splat 200
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `200` status is a **silent rewrite** — the user sees `/about` in the URL bar, but the server serves `out/en/about/index.html`.
|
|
69
|
+
|
|
70
|
+
#### Vercel
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"redirects": [
|
|
75
|
+
{ "source": "/en/:path*", "destination": "/:path*", "permanent": true }
|
|
76
|
+
],
|
|
77
|
+
"rewrites": [
|
|
78
|
+
{ "source": "/((?!zh/).*)", "destination": "/en/$1" }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Nginx
|
|
84
|
+
|
|
85
|
+
```nginx
|
|
86
|
+
location ~ ^/en(/.*)?$ { return 301 ${1:-/}; }
|
|
87
|
+
location /zh/ { try_files $uri $uri/index.html =404; }
|
|
88
|
+
location / { try_files /en$uri /en$uri/index.html =404; }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### GitHub Pages
|
|
92
|
+
|
|
93
|
+
GitHub Pages has no native rewrite support. The simplest workaround is a root `index.html` with a JS language detector:
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<script>
|
|
97
|
+
const lang = navigator.language.startsWith('zh') ? 'zh' : 'en';
|
|
98
|
+
window.location.replace('/' + lang + '/');
|
|
99
|
+
</script>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or just accept the `/en/` prefix for all locales.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Content Strategy
|
|
107
|
+
|
|
108
|
+
URL-based routing changes more than just URLs — it changes how you think about content.
|
|
109
|
+
|
|
110
|
+
For a personal digital garden, most content is written in one language and won't be fully translated. The realistic scope is:
|
|
111
|
+
|
|
112
|
+
| Content Type | Translated? | Approach |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| Static pages (about, privacy) | Yes | Optional `.zh.mdx` variant files |
|
|
115
|
+
| Posts | Rarely | Fallback to original + notice |
|
|
116
|
+
| Series descriptions | Maybe | Optional `index.zh.mdx` |
|
|
117
|
+
| Books | Unlikely | Fallback |
|
|
118
|
+
| Flows (daily notes) | No | UI-only i18n, no locale in URL |
|
|
119
|
+
|
|
120
|
+
The `.{locale}.mdx` convention — already used for static pages in Amytis — extends naturally to posts and series:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
content/posts/my-post.mdx # default locale
|
|
124
|
+
content/posts/my-post.zh.mdx # optional Chinese translation
|
|
125
|
+
content/series/my-series/index.zh.mdx
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
At build time, `generateStaticParams` generates `/en/my-post` and `/zh/my-post`. If the `.zh.mdx` file doesn't exist, the Chinese URL falls back to the English content with a small "not available in this language" banner.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Trade-offs at a Glance
|
|
133
|
+
|
|
134
|
+
| Aspect | Client-Side Toggle | URL-Based Routing |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| SEO | Crawlers see default lang only | Each locale fully indexed |
|
|
137
|
+
| Default locale prefix | No prefix | CDN rewrite needed |
|
|
138
|
+
| Build size | Same | ~2× |
|
|
139
|
+
| Refactor scope | None | Large |
|
|
140
|
+
| Content strategy | `.zh.mdx` for pages only | `.zh.mdx` for all content types |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Conclusion
|
|
145
|
+
|
|
146
|
+
For a personal blog where SEO in a second language isn't critical, the client-side toggle approach is pragmatic and gets the job done. UI strings translate, page content can optionally have locale variants, and there's no hosting complexity.
|
|
147
|
+
|
|
148
|
+
For a project where bilingual SEO matters — where you genuinely want Chinese content indexed under Chinese URLs — URL-based routing is the right architecture. The CDN configuration is a one-time setup, and the `.{locale}.mdx` convention for content is already half-implemented.
|
|
149
|
+
|
|
150
|
+
The good news: both approaches share the same content file convention. Migrating from client-side toggle to URL-based routing is a routing and build change, not a content restructure.
|