retypeset-odyssey 0.1.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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +168 -0
  3. package/astro.config.ts +18 -0
  4. package/default-config.yaml +136 -0
  5. package/discover-collections.ts +160 -0
  6. package/integration.ts +394 -0
  7. package/package.json +105 -0
  8. package/patches/@qwik.dev__partytown@0.11.2.patch +98 -0
  9. package/public/_redirects +819 -0
  10. package/public/feeds/atom-style.xsl +105 -0
  11. package/public/feeds/rss-style.xsl +105 -0
  12. package/public/fonts/EarlySummer-VF-Split/00785494587e3487ac63a0e7e4fa30f0.woff2 +0 -0
  13. package/public/fonts/EarlySummer-VF-Split/08e5d941a4c76fad7b68e7a937ebb21f.woff2 +0 -0
  14. package/public/fonts/EarlySummer-VF-Split/1268e5072156188d601f1eeb4473655d.woff2 +0 -0
  15. package/public/fonts/EarlySummer-VF-Split/12a385475353c815d7a5add53ee51e37.woff2 +0 -0
  16. package/public/fonts/EarlySummer-VF-Split/12b11ca08223c65a21fc731d59dcfc11.woff2 +0 -0
  17. package/public/fonts/EarlySummer-VF-Split/16d6676d3cb645c520ee6df8a1f89afd.woff2 +0 -0
  18. package/public/fonts/EarlySummer-VF-Split/2912a75ffef95e7a5ae9e2b2311ad61d.woff2 +0 -0
  19. package/public/fonts/EarlySummer-VF-Split/298d96ea561e419a4104bc9fc18499ce.woff2 +0 -0
  20. package/public/fonts/EarlySummer-VF-Split/2a2c71acc17ec39f6780835899e53096.woff2 +0 -0
  21. package/public/fonts/EarlySummer-VF-Split/2a7e2d0e59d3f638074c50fab39fdef1.woff2 +0 -0
  22. package/public/fonts/EarlySummer-VF-Split/36931fc4370e1670ed76af5d3feccba2.woff2 +0 -0
  23. package/public/fonts/EarlySummer-VF-Split/3a68fdc792e4a9e0399a04e32d0cc2e3.woff2 +0 -0
  24. package/public/fonts/EarlySummer-VF-Split/4054d6a4d6b37719b51e0f71da6e7cd9.woff2 +0 -0
  25. package/public/fonts/EarlySummer-VF-Split/429cb25f825c3cbde6bfac5b36ae9675.woff2 +0 -0
  26. package/public/fonts/EarlySummer-VF-Split/42a9efc11298368ecdc1b85ab46f0b4f.woff2 +0 -0
  27. package/public/fonts/EarlySummer-VF-Split/432018d2bdc9df92a7662056eb2b1261.woff2 +0 -0
  28. package/public/fonts/EarlySummer-VF-Split/44a6fb782f2a01560faa0f95248b60ef.woff2 +0 -0
  29. package/public/fonts/EarlySummer-VF-Split/45367b060e8ba0aa2507e6b91b86620b.woff2 +0 -0
  30. package/public/fonts/EarlySummer-VF-Split/571db7564bda7c1a93542881b8976f4b.woff2 +0 -0
  31. package/public/fonts/EarlySummer-VF-Split/58d55eeef4cf455e86a1142b1f3110d3.woff2 +0 -0
  32. package/public/fonts/EarlySummer-VF-Split/59ea41e77309160a0f63cdc76a010202.woff2 +0 -0
  33. package/public/fonts/EarlySummer-VF-Split/5d19d9174e568db4755981aa2e4ab380.woff2 +0 -0
  34. package/public/fonts/EarlySummer-VF-Split/5e811eb3b4175ee93d7ec000bf4631c2.woff2 +0 -0
  35. package/public/fonts/EarlySummer-VF-Split/6268e0cd5d66d6fe05b331f259e7b9e4.woff2 +0 -0
  36. package/public/fonts/EarlySummer-VF-Split/6549844aa3d833ca06a68a8e839db465.woff2 +0 -0
  37. package/public/fonts/EarlySummer-VF-Split/714b459658a7321ceeb1e1386ce165c2.woff2 +0 -0
  38. package/public/fonts/EarlySummer-VF-Split/7511d97a469915013683eae06cb21cd9.woff2 +0 -0
  39. package/public/fonts/EarlySummer-VF-Split/7784b4ebe543d13f62f6f6e05beb0b2e.woff2 +0 -0
  40. package/public/fonts/EarlySummer-VF-Split/77c9bea70b3c6ab24e1497d5468c825b.woff2 +0 -0
  41. package/public/fonts/EarlySummer-VF-Split/789ebea9e81df623e930b86de98fbfab.woff2 +0 -0
  42. package/public/fonts/EarlySummer-VF-Split/885bb7ab0717e8a47fc17f953adcdbf1.woff2 +0 -0
  43. package/public/fonts/EarlySummer-VF-Split/896c58aff69a9a857764cee0663bc56d.woff2 +0 -0
  44. package/public/fonts/EarlySummer-VF-Split/8fb6fc01c59d1e3ad1910b58dec7f5e7.woff2 +0 -0
  45. package/public/fonts/EarlySummer-VF-Split/95be5462b91b9a0458797cdc89d94cb5.woff2 +0 -0
  46. package/public/fonts/EarlySummer-VF-Split/9a5b2724f983ca0fc0d5ff8d10c41396.woff2 +0 -0
  47. package/public/fonts/EarlySummer-VF-Split/9ffe17f9c0e4cc4356cb3f08ffdb9c6d.woff2 +0 -0
  48. package/public/fonts/EarlySummer-VF-Split/EarlySummer-VF-Subset.woff2 +0 -0
  49. package/public/fonts/EarlySummer-VF-Split/EarlySummerSerif License.txt +91 -0
  50. package/public/fonts/EarlySummer-VF-Split/a097ef49be62cd2565aca45600e1e3ac.woff2 +0 -0
  51. package/public/fonts/EarlySummer-VF-Split/a17a1ae6063088e5b3a48c06b816929a.woff2 +0 -0
  52. package/public/fonts/EarlySummer-VF-Split/a83fdcfc5ecf2f6996704b0c02758689.woff2 +0 -0
  53. package/public/fonts/EarlySummer-VF-Split/a8cf15ff9b71e59407d8406866ff6f99.woff2 +0 -0
  54. package/public/fonts/EarlySummer-VF-Split/af530ed51dd519e4456f8a5e259e908b.woff2 +0 -0
  55. package/public/fonts/EarlySummer-VF-Split/b195a8924915deec4aa9c3ec777cc93f.woff2 +0 -0
  56. package/public/fonts/EarlySummer-VF-Split/b4b6bb5df9239dd67b52ca858fd2a506.woff2 +0 -0
  57. package/public/fonts/EarlySummer-VF-Split/b7592e1e027923f19e0e55dfdac69668.woff2 +0 -0
  58. package/public/fonts/EarlySummer-VF-Split/b965859f69d8ccceaf0e2d6292afbcfb.woff2 +0 -0
  59. package/public/fonts/EarlySummer-VF-Split/bbe9333f1ff242bd96ecb23ff9e723b1.woff2 +0 -0
  60. package/public/fonts/EarlySummer-VF-Split/be758580e295339ea98f0240b9869f24.woff2 +0 -0
  61. package/public/fonts/EarlySummer-VF-Split/c07099e1d025617f6d40966986e1941b.woff2 +0 -0
  62. package/public/fonts/EarlySummer-VF-Split/c1b593dda62fdeb7dde3af02016da282.woff2 +0 -0
  63. package/public/fonts/EarlySummer-VF-Split/c89f0335910a68a0958f2846108370e8.woff2 +0 -0
  64. package/public/fonts/EarlySummer-VF-Split/ca49aa409fdedd3f2f894cd20a16640a.woff2 +0 -0
  65. package/public/fonts/EarlySummer-VF-Split/ccd4a28d2f63797e0183c87792e20b75.woff2 +0 -0
  66. package/public/fonts/EarlySummer-VF-Split/d2718da923fce8e7ea229d65e306e92c.woff2 +0 -0
  67. package/public/fonts/EarlySummer-VF-Split/d893e9b307d96041e9cfcbd03761b9f4.woff2 +0 -0
  68. package/public/fonts/EarlySummer-VF-Split/dafaedaee41b75e21479d4ff324b6a34.woff2 +0 -0
  69. package/public/fonts/EarlySummer-VF-Split/db392af65f1867e5fd580eed2195df99.woff2 +0 -0
  70. package/public/fonts/EarlySummer-VF-Split/dc7c73a9e5577143ccd11e05ab55cb39.woff2 +0 -0
  71. package/public/fonts/EarlySummer-VF-Split/de396881189f747eba67685298363242.woff2 +0 -0
  72. package/public/fonts/EarlySummer-VF-Split/df625b213228bba22a7733d4eff8f148.woff2 +0 -0
  73. package/public/fonts/EarlySummer-VF-Split/e6e60b384f220b893ef31a926ece829a.woff2 +0 -0
  74. package/public/fonts/EarlySummer-VF-Split/e6e8ce2c5972ab665630bb705383d0fb.woff2 +0 -0
  75. package/public/fonts/EarlySummer-VF-Split/e963c7ed7104c2d6d68fcb5f952fe2f5.woff2 +0 -0
  76. package/public/fonts/EarlySummer-VF-Split/e966b23b4cd7783f43e31032d41784f4.woff2 +0 -0
  77. package/public/fonts/EarlySummer-VF-Split/edaac57c3856ec13128f4c6c3e00975c.woff2 +0 -0
  78. package/public/fonts/EarlySummer-VF-Split/ee54e0d86edf068c6c9cbddb76a856fe.woff2 +0 -0
  79. package/public/fonts/EarlySummer-VF-Split/f612c78a5544ff2dd3e8296ac3e58344.woff2 +0 -0
  80. package/public/fonts/EarlySummer-VF-Split/f9e539bd9b7bf999c3da82f5403ec3b6.woff2 +0 -0
  81. package/public/fonts/EarlySummer-VF-Split/fa5863b923ac15993c52a619f699ee63.woff2 +0 -0
  82. package/public/fonts/EarlySummer-VF-Split/fc759e56ec6f6e6d3d4cb163d62fb557.woff2 +0 -0
  83. package/public/fonts/Font Subset List/CJK Common Characters.txt +7534 -0
  84. package/public/fonts/Font Subset List/EarlySummer Subset.txt +3 -0
  85. package/public/fonts/Font Subset List/Japanese Kana + Korean Letters.txt +6123 -0
  86. package/public/fonts/Font Subset List/Latin + Cyrillic + Greek + Arabic Glyphs.txt +121 -0
  87. package/public/fonts/Font Subset List/unicode_range.py +49 -0
  88. package/public/fonts/NotoSansSC-Bold.otf +0 -0
  89. package/public/fonts/NotoSansSC-Regular.otf +0 -0
  90. package/public/fonts/STIX-Italic-VF.woff2 +0 -0
  91. package/public/fonts/STIX-VF.woff2 +0 -0
  92. package/public/fonts/Snell-Black-SF.woff2 +0 -0
  93. package/public/fonts/Snell-Bold-SF.woff2 +0 -0
  94. package/public/giscus/theme-dark.css +208 -0
  95. package/public/giscus/theme-light.css +208 -0
  96. package/public/icons/favicon.svg +4 -0
  97. package/public/icons/og-logo.png +0 -0
  98. package/public/robots.txt +4 -0
  99. package/public/sounds/tap_01.wav +0 -0
  100. package/public/sounds/tap_02.wav +0 -0
  101. package/public/sounds/tap_03.wav +0 -0
  102. package/public/sounds/tap_04.wav +0 -0
  103. package/public/sounds/tap_05.wav +0 -0
  104. package/public/sounds/type_01.wav +0 -0
  105. package/public/sounds/type_02.wav +0 -0
  106. package/public/sounds/type_03.wav +0 -0
  107. package/public/sounds/type_04.wav +0 -0
  108. package/public/sounds/type_05.wav +0 -0
  109. package/scripts/apply-lqip.ts +276 -0
  110. package/scripts/format-posts.ts +105 -0
  111. package/scripts/migration/README.md +52 -0
  112. package/scripts/migration/migrate-hexo.ts +185 -0
  113. package/scripts/migration/validate-abbrlinks.ts +161 -0
  114. package/scripts/new-post.ts +52 -0
  115. package/scripts/seo/generate-legacy-redirects.ts +407 -0
  116. package/scripts/update-theme.ts +46 -0
  117. package/src/assets/icons/copy-check.svg +3 -0
  118. package/src/assets/icons/copy-icon.svg +4 -0
  119. package/src/assets/icons/go-back.svg +3 -0
  120. package/src/assets/icons/heading-anchor.svg +4 -0
  121. package/src/assets/icons/lang-en.svg +3 -0
  122. package/src/assets/icons/lang-ja.svg +5 -0
  123. package/src/assets/icons/lang-zh.svg +5 -0
  124. package/src/assets/icons/language-switcher.svg +3 -0
  125. package/src/assets/icons/pin-icon.svg +3 -0
  126. package/src/assets/icons/search-icon.svg +3 -0
  127. package/src/assets/icons/theme-toggle.svg +3 -0
  128. package/src/assets/icons/toc-icon.svg +3 -0
  129. package/src/assets/icons/top-icon.svg +3 -0
  130. package/src/assets/lqip-map.json +10 -0
  131. package/src/components/Button.astro +152 -0
  132. package/src/components/CategoryList.astro +66 -0
  133. package/src/components/Comment/Giscus.astro +119 -0
  134. package/src/components/Comment/Index.astro +30 -0
  135. package/src/components/Comment/Twikoo.astro +114 -0
  136. package/src/components/Comment/Waline.astro +149 -0
  137. package/src/components/FloatingButtons.astro +101 -0
  138. package/src/components/Footer.astro +74 -0
  139. package/src/components/Header.astro +62 -0
  140. package/src/components/JournalList.astro +56 -0
  141. package/src/components/Navbar.astro +69 -0
  142. package/src/components/NoteList.astro +56 -0
  143. package/src/components/Pagination.astro +267 -0
  144. package/src/components/PostDate.astro +80 -0
  145. package/src/components/PostList.astro +87 -0
  146. package/src/components/SearchModal.astro +340 -0
  147. package/src/components/TagList.astro +135 -0
  148. package/src/components/Widgets/BackButton.astro +43 -0
  149. package/src/components/Widgets/CodeCopyButton.astro +47 -0
  150. package/src/components/Widgets/GithubCard.astro +110 -0
  151. package/src/components/Widgets/ImageZoom.astro +135 -0
  152. package/src/components/Widgets/MediaEmbed.astro +127 -0
  153. package/src/components/Widgets/SoundEffect.astro +179 -0
  154. package/src/components/Widgets/TOC.astro +198 -0
  155. package/src/config-schema.ts +164 -0
  156. package/src/config.ts +127 -0
  157. package/src/config.ts.example +205 -0
  158. package/src/content/about/_example-about-en.md +6 -0
  159. package/src/content/about/about-en.md +21 -0
  160. package/src/content/about/about-ja.md +21 -0
  161. package/src/content/about/about-zh.md +24 -0
  162. package/src/content.config.ts +247 -0
  163. package/src/env.d.ts +25 -0
  164. package/src/i18n/config.ts +65 -0
  165. package/src/i18n/lang.ts +70 -0
  166. package/src/i18n/path.ts +160 -0
  167. package/src/i18n/ui.ts +214 -0
  168. package/src/layouts/Head.astro +203 -0
  169. package/src/layouts/Layout.astro +69 -0
  170. package/src/pages/404.astro +20 -0
  171. package/src/pages/[...lang]/[...page].astro +48 -0
  172. package/src/pages/[...lang]/about.astro +28 -0
  173. package/src/pages/[...lang]/atom.xml.ts +14 -0
  174. package/src/pages/[...lang]/categories/index.astro +35 -0
  175. package/src/pages/[...lang]/journals/[slug].astro +89 -0
  176. package/src/pages/[...lang]/journals/index.astro +55 -0
  177. package/src/pages/[...lang]/journals/page/[page].astro +66 -0
  178. package/src/pages/[...lang]/notes/[slug].astro +88 -0
  179. package/src/pages/[...lang]/notes/index.astro +55 -0
  180. package/src/pages/[...lang]/notes/page/[page].astro +66 -0
  181. package/src/pages/[...lang]/posts/[slug].astro +101 -0
  182. package/src/pages/[...lang]/rss.xml.ts +14 -0
  183. package/src/pages/[...lang]/search.astro +65 -0
  184. package/src/pages/[...lang]/tags/[tag].astro +53 -0
  185. package/src/pages/[...lang]/tags/index.astro +36 -0
  186. package/src/pages/_dynamic/list.astro +101 -0
  187. package/src/pages/_dynamic/slug.astro +100 -0
  188. package/src/pages/og/[...image].ts +114 -0
  189. package/src/pages/robots.txt.ts +20 -0
  190. package/src/plugins/rehype-code-copy-button.mjs +82 -0
  191. package/src/plugins/rehype-external-links.mjs +18 -0
  192. package/src/plugins/rehype-heading-anchor.mjs +55 -0
  193. package/src/plugins/rehype-image-processor.mjs +77 -0
  194. package/src/plugins/remark-container-directives.mjs +135 -0
  195. package/src/plugins/remark-leaf-directives.mjs +184 -0
  196. package/src/plugins/remark-reading-time.mjs +11 -0
  197. package/src/styles/comment.css +205 -0
  198. package/src/styles/extension.css +180 -0
  199. package/src/styles/font.css +111 -0
  200. package/src/styles/global.css +91 -0
  201. package/src/styles/lqip.css +71 -0
  202. package/src/styles/markdown.css +276 -0
  203. package/src/styles/transition.css +173 -0
  204. package/src/types/global.d.ts +22 -0
  205. package/src/types/index.d.ts +111 -0
  206. package/src/utils/cache.ts +32 -0
  207. package/src/utils/content.ts +819 -0
  208. package/src/utils/description.ts +147 -0
  209. package/src/utils/dynamic-collections.ts +155 -0
  210. package/src/utils/feed.ts +238 -0
  211. package/src/utils/page.ts +107 -0
  212. package/tsconfig.json +13 -0
  213. package/uno.config.ts +75 -0
@@ -0,0 +1,205 @@
1
+ import type { ThemeConfig } from '@/types'
2
+
3
+ export const themeConfig: ThemeConfig = {
4
+ // SITE INFORMATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
5
+ site: {
6
+ // site title
7
+ title: 'Your Blog Title',
8
+ // site subtitle
9
+ subtitle: 'Your Blog Subtitle',
10
+ // site description
11
+ description: 'A personal blog about...',
12
+ // use i18n title/subtitle/description from src/i18n/ui.ts instead of static ones above
13
+ i18nTitle: true, // true | false
14
+ // author name
15
+ author: 'Your Name',
16
+ // site url
17
+ url: 'https://yourdomain.com',
18
+ // base path
19
+ // root directory for all pages and assets
20
+ base: '/', // e.g., '/blog', '/docs'
21
+ // favicon url
22
+ // recommended formats: svg, png or ico
23
+ favicon: '/icons/favicon.svg', // or https://example.com/favicon.svg
24
+ },
25
+ // SITE INFORMATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
26
+
27
+ // COLOR SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
28
+ color: {
29
+ // default theme mode
30
+ mode: 'light', // light | dark | auto
31
+ light: {
32
+ // primary color
33
+ // used for title, hover, etc
34
+ // oklch color picker: https://oklch.com/
35
+ primary: 'oklch(25% 0.005 298)',
36
+ // secondary color
37
+ // used for post text
38
+ secondary: 'oklch(40% 0.005 298)',
39
+ // background color
40
+ background: 'oklch(96% 0.005 298)',
41
+ // highlight color
42
+ // used for navbar, selected text, etc
43
+ highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)', // rgba(255,235,0,0.5)
44
+ },
45
+ dark: {
46
+ // primary color
47
+ primary: 'oklch(92% 0.005 298)',
48
+ // secondary color
49
+ secondary: 'oklch(77% 0.005 298)',
50
+ // background color
51
+ background: 'oklch(22% 0.005 298)',
52
+ // highlight color
53
+ highlight: 'oklch(0.93 0.195089 103.2532 / 0.2)', // rgba(255,235,0,0.2)
54
+ },
55
+ },
56
+ // COLOR SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
57
+
58
+ // GLOBAL SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
59
+ global: {
60
+ // default language
61
+ locale: 'zh', // de | en | es | fr | ja | ko | pl | pt | ru | zh | zh-tw
62
+ // more languages
63
+ // not fill in the locale code above again, can be an empty array []
64
+ moreLocales: ['en', 'ja'], // Only zh (default), en, ja
65
+ // post font style
66
+ fontStyle: 'sans', // sans | serif
67
+ // post date format
68
+ dateFormat: 'YYYY-MM-DD', // YYYY-MM-DD | MM-DD-YYYY | DD-MM-YYYY | MMM D YYYY | D MMM YYYY
69
+ // enable table of contents
70
+ toc: true, // true | false
71
+ // enable katex math rendering
72
+ katex: true, // true | false
73
+ // reduce motion
74
+ reduceMotion: false, // true | false
75
+ },
76
+ // GLOBAL SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
77
+
78
+ // COMMENT SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
79
+ comment: {
80
+ // enable comment system
81
+ enabled: false, // Disabled for now
82
+ // giscus
83
+ // https://giscus.app/
84
+ giscus: {
85
+ repo: '',
86
+ repoId: '',
87
+ category: '',
88
+ categoryId: '',
89
+ mapping: 'pathname',
90
+ strict: '0',
91
+ reactionsEnabled: '1',
92
+ emitMetadata: '0',
93
+ inputPosition: 'bottom',
94
+ },
95
+ // twikoo
96
+ // https://twikoo.js.org/
97
+ twikoo: {
98
+ envId: '',
99
+ // version: frontend version can be changed in package.json
100
+ },
101
+ // waline
102
+ // https://waline.js.org/en/
103
+ waline: {
104
+ // server url
105
+ serverURL: 'https://retypeset-comment.radishzz.cc',
106
+ // emoji url
107
+ emoji: [
108
+ 'https://unpkg.com/@waline/emojis@1.2.0/tw-emoji',
109
+ // 'https://unpkg.com/@waline/emojis@1.2.0/bmoji',
110
+ // more emojis: https://waline.js.org/en/guide/features/emoji.html
111
+ ],
112
+ // gif search
113
+ search: false, // true | false
114
+ // image uploader
115
+ imageUploader: false, // true | false
116
+ },
117
+ },
118
+ // COMMENT SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
119
+
120
+ // SEO SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
121
+ seo: {
122
+ // @twitter ID
123
+ twitterID: '',
124
+ // site verification
125
+ verification: {
126
+ // google search console
127
+ // https://search.google.com/search-console
128
+ google: '',
129
+ // bing webmaster tools
130
+ // https://www.bing.com/webmasters
131
+ bing: '',
132
+ // yandex webmaster
133
+ // https://webmaster.yandex.com
134
+ yandex: '',
135
+ // baidu search
136
+ // https://ziyuan.baidu.com
137
+ baidu: '',
138
+ },
139
+ // google analytics — GA4 measurement ID (format: G-XXXXXXXXXX)
140
+ // NOT the old UA-XXXXXXXX-X format (deprecated July 2023)
141
+ // Get yours at: https://analytics.google.com → Admin → Data Streams → Measurement ID
142
+ googleAnalyticsID: '',
143
+ // umami analytics
144
+ // https://cloud.umami.is
145
+ umamiAnalyticsID: '',
146
+ // follow verification
147
+ // https://follow.is/
148
+ follow: {
149
+ // feed ID
150
+ feedID: '',
151
+ // user ID
152
+ userID: '',
153
+ },
154
+ // apiflash access key
155
+ // generate website screenshots for open graph images
156
+ // get your access key at: https://apiflash.com/
157
+ apiflashKey: '',
158
+ },
159
+ // SEO SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
160
+
161
+ // FOOTER SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
162
+ footer: {
163
+ // social links
164
+ links: [
165
+ {
166
+ name: 'RSS',
167
+ url: '/atom.xml', // or /rss.xml
168
+ },
169
+ {
170
+ name: 'GitHub',
171
+ url: 'https://github.com/yourusername',
172
+ },
173
+ ],
174
+ // year of website start
175
+ startYear: 2024,
176
+ },
177
+ // FOOTER SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
178
+
179
+ // PRELOAD SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START
180
+ preload: {
181
+ // image hosting url
182
+ // optimize remote images and generate low-quality placeholders
183
+ imageHostURL: '',
184
+ // custom google analytics js
185
+ // for users who proxy tracking scripts to a custom domain
186
+ // see https://gist.github.com/xiaopc/0602f06ca465d76bd9efd3dda9393738
187
+ customGoogleAnalyticsJS: '',
188
+ // custom umami analytics js
189
+ // for users who self-deploy umami or proxy tracking scripts to a custom domain
190
+ // see https://umami.is/docs/bypass-ad-blockers
191
+ customUmamiAnalyticsJS: '',
192
+ },
193
+ // PRELOAD SETTINGS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> END
194
+ }
195
+
196
+ export const base = themeConfig.site.base === '/' ? '' : themeConfig.site.base.replace(/\/$/, '')
197
+ export const defaultLocale = themeConfig.global.locale
198
+ export const moreLocales = themeConfig.global.moreLocales
199
+ export const allLocales = [defaultLocale, ...moreLocales]
200
+
201
+ // Pagination
202
+ // Used by posts/notes/journals list pages.
203
+ export const POSTS_PER_PAGE = 7
204
+ export const NOTES_PER_PAGE = 7
205
+ export const JOURNALS_PER_PAGE = 7
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: About
3
+ description: About the author
4
+ ---
5
+
6
+ Write about yourself here. This file should be renamed to `about-en.md` (or `about-zh.md`, `about-ja.md`).
@@ -0,0 +1,21 @@
1
+ ---
2
+ lang: en
3
+ ---
4
+
5
+ # About Me
6
+
7
+ Thank you for visiting my blog.
8
+
9
+ I'm working as a software engineer.
10
+
11
+ Please feel free to contact me: zhenjiazhou0127@outlook.com
12
+
13
+ # Privacy & Analytics
14
+
15
+ I use Google Analytics to better understand my visitors. The data collection is based on Google's standard practices. Thank you for your understanding.
16
+
17
+ I'm very happy to receive any type of message. Please feel free to reach out.
18
+
19
+ # The End
20
+
21
+ Sometimes, this ocean of starlight seems to have reached the edge of darkness; I thought beyond it lay only endless void. Yet, with just a glance upward, there are still stars everywhere.
@@ -0,0 +1,21 @@
1
+ ---
2
+ lang: ja
3
+ ---
4
+
5
+ # 私について
6
+
7
+ ブログをご覧いただきありがとうございます。
8
+
9
+ 私はSoftware Engineerで働いています。
10
+
11
+ お気軽にご連絡ください:zhenjiazhou0127@outlook.com
12
+
13
+ # プライバシーと分析
14
+
15
+ 訪問者をより理解するために、Google Analyticsを使用しています。データ収集はGoogleの標準的な方法に基づいています。ご理解いただきありがとうございます。
16
+
17
+ どんなメッセージでも大歓迎です。
18
+
19
+ # 終わりに
20
+
21
+ 時々、この星の海は闇の果てに達したように見える。その先には無限の暗黒しかないと思っていた。しかし、ふと見上げれば、そこにはまだ星々が輝いている。
@@ -0,0 +1,24 @@
1
+ ---
2
+ lang: zh
3
+ ---
4
+
5
+ # 关于我
6
+
7
+ 欢迎你来我的博客,希望我写的东西能够帮到你。
8
+
9
+ 我目前是一名软件开发人员。
10
+
11
+ 如果有任何问题,或者想和我交流的话,欢迎联系我的邮件:zhenjiazhou0127@outlook.com
12
+
13
+ # 隐私统计与使用
14
+
15
+ 我在博客里加入了谷歌的统计,会根据浏览器标识搜集一部分访客信息。
16
+
17
+ 我很开心收到任何信息,请随意联系我。
18
+
19
+ # 结语
20
+
21
+ 有时候,这星光的海洋似乎已经达到了黑暗的边缘;我满以为,在此之外,已是无边无际的大黑暗了。然而,只要一转瞬,再往上一看,依然是一片星光。
22
+
23
+ 我志在寥阔,畴昔梦登天。摩挲素月,人世俯仰已千年。有客骖鸾并凤,云遇青山赤壁,相约上高寒。酌酒援北斗,我亦虱其间。
24
+ 少歌曰:神甚放,形如眠。鸿鹄一再高举,天地睹方圆。欲重歌兮梦觉,推枕惘然独念,人事底亏全?有美人可语,秋水隔婵娟。
@@ -0,0 +1,247 @@
1
+ import { glob } from 'astro/loaders'
2
+ import { defineCollection } from 'astro:content'
3
+ import { z } from 'astro/zod'
4
+ import { discoverCollections } from '../discover-collections'
5
+ import { allLocales, themeConfig } from '@/config'
6
+
7
+ const posts = defineCollection({
8
+ loader: glob({
9
+ pattern: '**/*.{md,mdx}',
10
+ base: './content/posts',
11
+ // Keep multi-dot filenames (e.g. `foo.en.md`) distinct in Astro Content Layer.
12
+ // Otherwise, `foo.md` and `foo.en.md` may collide and overwrite each other.
13
+ generateId: ({ entry }) => entry.replace(/\.(md|mdx)$/, ''),
14
+ }),
15
+ schema: z.object({
16
+ // Title - optional with fallback to empty (posts without title become drafts)
17
+ title: z.string().optional().default(''),
18
+ // Support both Hexo 'date' and Retypeset 'published' fields
19
+ published: z.coerce.date().optional(),
20
+ date: z.coerce.date().optional(),
21
+ // optional
22
+ description: z.string().optional().default(''),
23
+ updated: z.preprocess(
24
+ val => val === '' ? undefined : val,
25
+ z.coerce.date().optional(),
26
+ ),
27
+ tags: z.preprocess(
28
+ val => val === null ? [] : val,
29
+ z.union([
30
+ z.array(z.string()),
31
+ z.string().transform(s => s ? [s] : []),
32
+ ]).optional().default([]),
33
+ ),
34
+ // Hexo categories (convert to tags or keep separate)
35
+ categories: z.preprocess(
36
+ val => val === null ? [] : val,
37
+ z.union([
38
+ z.array(z.string()),
39
+ z.string().transform(s => s ? [s] : []),
40
+ ]).optional().default([]),
41
+ ),
42
+ // Advanced (handle null values)
43
+ draft: z.preprocess(val => val === null ? false : val, z.boolean().optional().default(false)),
44
+ pin: z.preprocess(val => val === null ? 0 : val, z.number().int().min(0).max(99).optional().default(0)),
45
+ toc: z.preprocess(val => val === null ? undefined : val, z.boolean().optional().default(themeConfig.global.toc)),
46
+ lang: z.preprocess(val => val === null ? '' : val, z.enum(['', ...allLocales]).optional().default('')),
47
+ // Hexo abbrlink - supports hex format like '17683e80' (handle null from YAML)
48
+ abbrlink: z.preprocess(
49
+ val => val === null ? '' : val,
50
+ z.string().optional().default('').refine(
51
+ abbrlink => !abbrlink || /^[a-zA-Z0-9\-]*$/.test(abbrlink),
52
+ { message: 'Abbrlink can only contain letters, numbers and hyphens' },
53
+ ),
54
+ ),
55
+ // Semantic URL slug - takes priority over abbrlink
56
+ slug: z.preprocess(
57
+ val => val === null ? '' : val,
58
+ z.string().optional().default('').refine(
59
+ slug => !slug || /^[a-zA-Z0-9\-]+$/.test(slug),
60
+ { message: 'Slug can only contain letters, numbers and hyphens' },
61
+ ),
62
+ ),
63
+ // Hexo-specific optional fields (handle null values from YAML)
64
+ mathjax: z.preprocess(
65
+ val => val === null ? undefined : val,
66
+ z.boolean().optional().default(false),
67
+ ),
68
+ password: z.preprocess(
69
+ val => val === null ? undefined : val,
70
+ z.string().optional(),
71
+ ),
72
+ copyright: z.preprocess(
73
+ val => val === null ? undefined : val,
74
+ z.boolean().optional().default(true),
75
+ ),
76
+ // Allow passthrough of unknown Hexo fields
77
+ }).passthrough().transform((data) => {
78
+ // Map Hexo 'date' to 'published' if 'published' is not set
79
+ const published = data.published || data.date || new Date();
80
+ // Mark posts without title as draft
81
+ const draft = data.draft || !data.title;
82
+ return { ...data, published, draft };
83
+ }),
84
+ })
85
+
86
+ const notes = defineCollection({
87
+ loader: glob({
88
+ pattern: '**/*.{md,mdx}',
89
+ base: './content/notes',
90
+ // Keep multi-dot filenames (e.g. `foo.en.md`) distinct in Astro Content Layer.
91
+ // Otherwise, `foo.md` and `foo.en.md` may collide and overwrite each other.
92
+ generateId: ({ entry }) => entry.replace(/\.(md|mdx)$/, ''),
93
+ }),
94
+ schema: z.object({
95
+ // Title - optional with fallback to empty (notes without title become drafts)
96
+ title: z.string().optional().default(''),
97
+ // Support both Hexo 'date' and Retypeset 'published' fields
98
+ published: z.coerce.date().optional(),
99
+ date: z.coerce.date().optional(),
100
+ // optional
101
+ description: z.string().optional().default(''),
102
+ updated: z.preprocess(
103
+ val => val === '' ? undefined : val,
104
+ z.coerce.date().optional(),
105
+ ),
106
+ tags: z.preprocess(
107
+ val => val === null ? [] : val,
108
+ z.union([
109
+ z.array(z.string()),
110
+ z.string().transform(s => s ? [s] : []),
111
+ ]).optional().default([]),
112
+ ),
113
+ // Hexo categories (convert to tags or keep separate)
114
+ categories: z.preprocess(
115
+ val => val === null ? [] : val,
116
+ z.union([
117
+ z.array(z.string()),
118
+ z.string().transform(s => s ? [s] : []),
119
+ ]).optional().default([]),
120
+ ),
121
+ // Advanced (handle null values)
122
+ draft: z.preprocess(val => val === null ? false : val, z.boolean().optional().default(false)),
123
+ pin: z.preprocess(val => val === null ? 0 : val, z.number().int().min(0).max(99).optional().default(0)),
124
+ toc: z.preprocess(val => val === null ? undefined : val, z.boolean().optional().default(themeConfig.global.toc)),
125
+ lang: z.preprocess(val => val === null ? '' : val, z.enum(['', ...allLocales]).optional().default('')),
126
+ // Semantic URL slug
127
+ slug: z.preprocess(
128
+ val => val === null ? '' : val,
129
+ z.string().optional().default('').refine(
130
+ slug => !slug || /^[a-zA-Z0-9\\-]+$/.test(slug),
131
+ { message: 'Slug can only contain letters, numbers and hyphens' },
132
+ ),
133
+ ),
134
+ // Allow passthrough of unknown fields
135
+ }).passthrough().transform((data) => {
136
+ const published = data.published || data.date || new Date()
137
+ const draft = data.draft || !data.title
138
+ return { ...data, published, draft }
139
+ }),
140
+ })
141
+
142
+ const journals = defineCollection({
143
+ loader: glob({
144
+ pattern: '**/*.{md,mdx}',
145
+ base: './content/journals',
146
+ // Keep multi-dot filenames (e.g. `foo.en.md`) distinct in Astro Content Layer.
147
+ // Otherwise, `foo.md` and `foo.en.md` may collide and overwrite each other.
148
+ generateId: ({ entry }) => entry.replace(/\.(md|mdx)$/, ''),
149
+ }),
150
+ schema: z.object({
151
+ // Title - optional with fallback to empty (journals without title become drafts)
152
+ title: z.string().optional().default(''),
153
+ // Support both Hexo 'date' and Retypeset 'published' fields
154
+ published: z.coerce.date().optional(),
155
+ date: z.coerce.date().optional(),
156
+ // optional
157
+ description: z.string().optional().default(''),
158
+ updated: z.preprocess(
159
+ val => val === '' ? undefined : val,
160
+ z.coerce.date().optional(),
161
+ ),
162
+ tags: z.preprocess(
163
+ val => val === null ? [] : val,
164
+ z.union([
165
+ z.array(z.string()),
166
+ z.string().transform(s => s ? [s] : []),
167
+ ]).optional().default([]),
168
+ ),
169
+ // Hexo categories (convert to tags or keep separate)
170
+ categories: z.preprocess(
171
+ val => val === null ? [] : val,
172
+ z.union([
173
+ z.array(z.string()),
174
+ z.string().transform(s => s ? [s] : []),
175
+ ]).optional().default([]),
176
+ ),
177
+ // Advanced (handle null values)
178
+ draft: z.preprocess(val => val === null ? false : val, z.boolean().optional().default(false)),
179
+ pin: z.preprocess(val => val === null ? 0 : val, z.number().int().min(0).max(99).optional().default(0)),
180
+ toc: z.preprocess(val => val === null ? undefined : val, z.boolean().optional().default(themeConfig.global.toc)),
181
+ lang: z.preprocess(val => val === null ? '' : val, z.enum(['', ...allLocales]).optional().default('')),
182
+ // Semantic URL slug
183
+ slug: z.preprocess(
184
+ val => val === null ? '' : val,
185
+ z.string().optional().default('').refine(
186
+ slug => !slug || /^[a-zA-Z0-9\\-]+$/.test(slug),
187
+ { message: 'Slug can only contain letters, numbers and hyphens' },
188
+ ),
189
+ ),
190
+ // Hexo-specific optional fields (handle null values from YAML)
191
+ mathjax: z.preprocess(
192
+ val => val === null ? undefined : val,
193
+ z.boolean().optional().default(false),
194
+ ),
195
+ password: z.preprocess(
196
+ val => val === null ? undefined : val,
197
+ z.string().optional(),
198
+ ),
199
+ copyright: z.preprocess(
200
+ val => val === null ? undefined : val,
201
+ z.boolean().optional().default(true),
202
+ ),
203
+ // Allow passthrough of unknown fields
204
+ }).passthrough().transform((data) => {
205
+ const published = data.published || data.date || new Date()
206
+ const draft = data.draft || !data.title
207
+ return { ...data, published, draft }
208
+ }),
209
+ })
210
+
211
+ const about = defineCollection({
212
+ loader: glob({ pattern: '**/*.{md,mdx}', base: './content/about' }),
213
+ schema: z.object({
214
+ lang: z.enum(['', ...allLocales]).optional().default(''),
215
+ }),
216
+ })
217
+
218
+ // --- Dynamic collections -----------------------------------------------------
219
+ //
220
+ // Auto-register a collection for every top-level folder under `content/` that
221
+ // isn't a built-in (posts/notes/journals/_drafts/etc). Each one reuses the
222
+ // `posts` collection schema, so any frontmatter that works in `posts/` also
223
+ // works in a dynamic folder. See `discover-collections.ts` for the scan rules.
224
+
225
+ const postSchema = (posts as any).schema
226
+
227
+ const { dynamicFolders } = discoverCollections(process.cwd())
228
+
229
+ const dynamicCollections: Record<string, ReturnType<typeof defineCollection>> = {}
230
+ for (const folder of dynamicFolders) {
231
+ dynamicCollections[folder] = defineCollection({
232
+ loader: glob({
233
+ pattern: '**/*.{md,mdx}',
234
+ base: `./content/${folder}`,
235
+ generateId: ({ entry }) => entry.replace(/\.(md|mdx)$/, ''),
236
+ }),
237
+ schema: postSchema,
238
+ })
239
+ }
240
+
241
+ export const collections = {
242
+ posts,
243
+ notes,
244
+ journals,
245
+ about,
246
+ ...dynamicCollections,
247
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /// <reference types="astro/client" />
2
+ /// <reference path="../.astro/types.d.ts" />
3
+
4
+ // Pagefind UI type declaration
5
+ interface PagefindUIOptions {
6
+ element: string
7
+ showImages?: boolean
8
+ showSubResults?: boolean
9
+ showEmptyFilters?: boolean
10
+ resetStyles?: boolean
11
+ bundlePath?: string
12
+ debounceTimeoutMs?: number
13
+ mergeIndex?: Array<{ bundlePath: string }>
14
+ }
15
+
16
+ interface PagefindUIConstructor {
17
+ new (options: PagefindUIOptions): PagefindUIInstance
18
+ }
19
+
20
+ interface PagefindUIInstance {
21
+ triggerSearch(term: string): void
22
+ destroy(): void
23
+ }
24
+
25
+ declare const PagefindUI: PagefindUIConstructor
@@ -0,0 +1,65 @@
1
+ // Global Language Map
2
+ export const langMap = {
3
+ 'de': ['de-DE'],
4
+ 'en': ['en-US'],
5
+ 'es': ['es-ES'],
6
+ 'fr': ['fr-FR'],
7
+ 'ja': ['ja-JP'],
8
+ 'ko': ['ko-KR'],
9
+ 'pl': ['pl-PL'],
10
+ 'pt': ['pt-BR'],
11
+ 'ru': ['ru-RU'],
12
+ 'zh': ['zh-CN'],
13
+ 'zh-tw': ['zh-TW'],
14
+ } as const
15
+
16
+ // Supported Languages
17
+ export type Language = keyof typeof langMap
18
+
19
+ // Giscus Language Map
20
+ // https://giscus.app/
21
+ export const giscusLocaleMap: Record<Language, string> = {
22
+ 'de': 'de',
23
+ 'en': 'en',
24
+ 'es': 'es',
25
+ 'fr': 'fr',
26
+ 'ja': 'ja',
27
+ 'ko': 'ko',
28
+ 'pl': 'pl',
29
+ 'pt': 'pt',
30
+ 'ru': 'ru',
31
+ 'zh': 'zh-CN',
32
+ 'zh-tw': 'zh-TW',
33
+ }
34
+
35
+ // Twikoo Language Map
36
+ // https://github.com/twikoojs/twikoo/blob/main/src/client/utils/i18n/index.js
37
+ export const twikooLocaleMap: Record<Language, string> = {
38
+ 'de': 'en', // fallback to English
39
+ 'en': 'en',
40
+ 'es': 'en', // fallback to English
41
+ 'fr': 'en', // fallback to English
42
+ 'ja': 'ja',
43
+ 'ko': 'ko',
44
+ 'pl': 'en', // fallback to English
45
+ 'pt': 'en', // fallback to English
46
+ 'ru': 'en', // fallback to English
47
+ 'zh': 'zh-cn',
48
+ 'zh-tw': 'zh-tw',
49
+ }
50
+
51
+ // Waline Language Map
52
+ // https://waline.js.org/en/guide/features/i18n.html
53
+ export const walineLocaleMap: Record<Language, string> = {
54
+ 'de': 'en-US', // fallback to English
55
+ 'en': 'en-US',
56
+ 'es': 'es',
57
+ 'fr': 'fr-FR',
58
+ 'ja': 'jp-JP',
59
+ 'ko': 'en-US', // fallback to English
60
+ 'pl': 'en-US', // fallback to English
61
+ 'pt': 'pt-BR',
62
+ 'ru': 'ru-RU',
63
+ 'zh': 'zh-CN',
64
+ 'zh-tw': 'zh-TW',
65
+ }