mirror of
https://github.com/reonokiy/blog.nokiy.net.git
synced 2025-06-15 19:22:52 +02:00
update: unified multilingual page styles
This commit is contained in:
parent
05d3a8034b
commit
91d27dd2ba
14 changed files with 118 additions and 85 deletions
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
import { themeConfig } from '@/config'
|
||||
import { getWalineLang } from '@/i18n/ui'
|
||||
import { getWalineLang } from '@/i18n/lang'
|
||||
|
||||
const {
|
||||
serverURL = '',
|
||||
|
|
|
@ -54,7 +54,7 @@ export const themeConfig: ThemeConfig = {
|
|||
locale: 'zh', // zh, zh-tw, ja, en, es, ru
|
||||
// more languages
|
||||
// not fill in the locale above again
|
||||
moreLocale: ['zh-tw', 'ja', 'en', 'es', 'ru'], // ['zh', 'zh-tw', 'ja', 'en', 'es', 'ru']
|
||||
moreLocales: ['zh-tw', 'ja', 'en', 'es', 'ru'], // ['zh', 'zh-tw', 'ja', 'en', 'es', 'ru']
|
||||
// font styles for text
|
||||
fontStyle: 'sans', // sans, serif
|
||||
// date format for posts
|
||||
|
|
5
src/i18n/config.ts
Normal file
5
src/i18n/config.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { themeConfig } from '@/config'
|
||||
|
||||
export const defaultLocale = themeConfig.global.locale
|
||||
export const moreLocales = themeConfig.global.moreLocales
|
||||
export const allLocales = [defaultLocale, ...moreLocales]
|
|
@ -1,8 +1,5 @@
|
|||
import themeConfig from '@/config'
|
||||
|
||||
// 从配置中获取默认语言和更多语言配置
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
// const moreLocale = themeConfig.global.moreLocale
|
||||
import { allLocales, defaultLocale, moreLocales } from '@/i18n/config'
|
||||
import { walineLocaleMap } from '@/i18n/ui'
|
||||
|
||||
/**
|
||||
* 获取下一个语言代码
|
||||
|
@ -11,8 +8,7 @@ const defaultLocale = themeConfig.global.locale
|
|||
*/
|
||||
export function getNextLang(currentLang: string): string {
|
||||
// 获取默认语言和所有支持的语言
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
const allLocales = [defaultLocale, ...themeConfig.global.moreLocale]
|
||||
// 直接使用导入的变量
|
||||
|
||||
// 找到当前语言在列表中的索引
|
||||
const currentIndex = allLocales.indexOf(currentLang)
|
||||
|
@ -37,7 +33,7 @@ export function getNextLang(currentLang: string): string {
|
|||
* @returns 下一个语言的URL
|
||||
*/
|
||||
export function buildNextLangUrl(currentPath: string, currentLang: string, nextLang: string): string {
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
// 直接使用导入的变量
|
||||
let nextUrl = ''
|
||||
|
||||
if (nextLang === defaultLocale) {
|
||||
|
@ -74,11 +70,11 @@ export function buildNextLangUrl(currentPath: string, currentLang: string, nextL
|
|||
* @returns 当前语言代码
|
||||
*/
|
||||
export function getLangFromPath(currentPath: string): string {
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
// 直接使用导入的变量
|
||||
let currentLang = ''
|
||||
|
||||
// 检查路径是否以/xx/开始,其中xx是支持的语言代码
|
||||
for (const lang of themeConfig.global.moreLocale) {
|
||||
for (const lang of moreLocales) {
|
||||
if (currentPath.startsWith(`/${lang}/`) || currentPath === `/${lang}`) {
|
||||
currentLang = lang
|
||||
break
|
||||
|
@ -99,8 +95,7 @@ export function getLangFromPath(currentPath: string): string {
|
|||
* @returns 支持的语言数组
|
||||
*/
|
||||
export function getSupportedLangs(lang?: string): string[] {
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
const allLocales = [defaultLocale, ...themeConfig.global.moreLocale]
|
||||
// 直接使用导入的变量
|
||||
|
||||
// 如果指定了语言且不为空
|
||||
if (lang && typeof lang === 'string' && lang.trim() !== '') {
|
||||
|
@ -159,3 +154,19 @@ export function getPostNextLangUrl(currentPath: string, supportedLangs: string[]
|
|||
// 构建下一个语言的URL
|
||||
return buildNextLangUrl(currentPath, currentLang, nextLang)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language code of Waline
|
||||
* @param currentPath Current page path
|
||||
* @param defaultLocale Default language
|
||||
* @returns Corresponding Waline language code
|
||||
*/
|
||||
export function getWalineLang(currentPath: string, defaultLocale: string): string {
|
||||
// Extract language code from path
|
||||
const pathLang = Object.keys(walineLocaleMap).find(code =>
|
||||
currentPath.startsWith(`/${code}/`),
|
||||
)
|
||||
// Return found path language or default language
|
||||
const lang = pathLang || defaultLocale
|
||||
return walineLocaleMap[lang as keyof typeof walineLocaleMap]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import themeConfig from '@/config'
|
||||
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
const moreLocales = themeConfig.global.moreLocale
|
||||
import { defaultLocale, moreLocales } from '@/i18n/config'
|
||||
|
||||
export function cleanPath(path: string) {
|
||||
return path.replace(/^\/|\/$/g, '')
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
import type { CollectionEntry } from 'astro:content'
|
||||
import { themeConfig } from '@/config'
|
||||
|
||||
// 默认语言和更多语言
|
||||
const defaultLocale = themeConfig.global.locale
|
||||
const moreLocale = themeConfig.global.moreLocale
|
||||
// 所有支持的语言
|
||||
const allLocales = [defaultLocale, ...moreLocale]
|
||||
import { allLocales, defaultLocale, moreLocales } from '@/i18n/config'
|
||||
|
||||
// 生成默认语言标签页面的路径配置
|
||||
export function generateTagPaths(tags: string[]) {
|
||||
|
@ -53,14 +47,14 @@ export function generatePostPaths(posts: CollectionEntry<'posts'>[]) {
|
|||
|
||||
// 生成更多语言静态路径
|
||||
export function generateMultiLangPaths() {
|
||||
return moreLocale.map(lang => ({
|
||||
return moreLocales.map(lang => ({
|
||||
params: { lang },
|
||||
}))
|
||||
}
|
||||
|
||||
// 生成更多语言标签页面的路径配置
|
||||
export function generateMultiLangTagPaths(tags: string[]) {
|
||||
return moreLocale.flatMap(lang => (
|
||||
return moreLocales.flatMap(lang => (
|
||||
tags.map(tag => ({
|
||||
params: { lang, tag },
|
||||
props: { tag },
|
||||
|
@ -111,7 +105,7 @@ export function generateMultiLangPostPaths(posts: CollectionEntry<'posts'>[]) {
|
|||
// 确定文章的语言支持
|
||||
const postLang = post.data.lang && typeof post.data.lang === 'string' && post.data.lang.trim() !== ''
|
||||
? [post.data.lang]
|
||||
: moreLocale
|
||||
: moreLocales
|
||||
|
||||
// 获取这篇文章支持的所有语言
|
||||
const supportedLangs = slugToLangs[slug] || []
|
||||
|
|
|
@ -8,6 +8,17 @@ export const langMap: Record<string, string[]> = {
|
|||
'ru': ['ru-RU'],
|
||||
}
|
||||
|
||||
// Waline Language Map
|
||||
// docs: https://waline.js.org/guide/i18n.html
|
||||
export const walineLocaleMap: Record<string, string> = {
|
||||
'zh': 'zh-CN',
|
||||
'zh-tw': 'zh-TW',
|
||||
'ja': 'jp-JP', // Waline uses jp-JP not ja-JP
|
||||
'en': 'en-US',
|
||||
'es': 'es-ES',
|
||||
'ru': 'ru-RU',
|
||||
}
|
||||
|
||||
// Standard Language Code (Unused)
|
||||
export const langCode = Object.values(langMap).flat()
|
||||
|
||||
|
@ -47,30 +58,3 @@ export const ui = {
|
|||
about: 'О себе',
|
||||
},
|
||||
}
|
||||
|
||||
// Waline Language Map
|
||||
// See more at https://waline.js.org/guide/i18n.html
|
||||
export const walineLocaleMap: Record<string, string> = {
|
||||
'zh': 'zh-CN',
|
||||
'zh-tw': 'zh-TW',
|
||||
'ja': 'jp-JP', // Waline uses jp-JP not ja-JP
|
||||
'en': 'en-US',
|
||||
'es': 'es-ES',
|
||||
'ru': 'ru-RU',
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language code of Waline
|
||||
* @param currentPath Current page path
|
||||
* @param defaultLocale Default language
|
||||
* @returns Corresponding Waline language code
|
||||
*/
|
||||
export function getWalineLang(currentPath: string, defaultLocale: string): string {
|
||||
// Extract language code from path
|
||||
const pathLang = Object.keys(walineLocaleMap).find(code =>
|
||||
currentPath.startsWith(`/${code}/`),
|
||||
)
|
||||
// Return found path language or default language
|
||||
const lang = pathLang || defaultLocale
|
||||
return walineLocaleMap[lang as keyof typeof walineLocaleMap]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import themeConfig from '@/config'
|
||||
import { allLocales, defaultLocale } from '@/i18n/config'
|
||||
import { ClientRouter } from 'astro:transitions'
|
||||
|
||||
interface Props {
|
||||
|
@ -12,7 +13,7 @@ const { postTitle, postDescription, postSlug } = Astro.props
|
|||
|
||||
const { title, subtitle, description, author, url, favicon } = themeConfig.site
|
||||
const { mode, light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
|
||||
const { locale, moreLocale } = themeConfig.global
|
||||
// const { locale, moreLocales } = themeConfig.global
|
||||
const { verification = {}, twitterID = '', googleAnalyticsID = '', umamiAnalyticsID = '' } = themeConfig.seo ?? {}
|
||||
const { google = '', bing = '', yandex = '', baidu = '' } = verification
|
||||
const { commentURL = '', imageHostURL = '', customGoogleAnalyticsJS = '', customUmamiAnalyticsJS = '' } = themeConfig.preload
|
||||
|
@ -48,10 +49,10 @@ const pageImage = postSlug ? `${url}/og/${postSlug}.png` : 'https://placehold.co
|
|||
<link rel="canonical" href={Astro.url} />
|
||||
|
||||
<!-- i18n hreflang generate -->
|
||||
{[locale, ...moreLocale].map(lang => (
|
||||
{allLocales.map(lang => (
|
||||
<link
|
||||
rel="alternate"
|
||||
href={`${url}${lang === locale ? '' : `/${lang}/`}`}
|
||||
href={`${url}${lang === defaultLocale ? '' : `/${lang}/`}`}
|
||||
hreflang={lang === 'zh-tw' ? 'zh-TW' : lang}
|
||||
/>
|
||||
))}
|
||||
|
|
|
@ -3,10 +3,10 @@ import themeConfig from '@/config'
|
|||
import { generateMultiLangPaths } from '@/i18n/route'
|
||||
import { generateRSS } from '@/utils/rss'
|
||||
|
||||
const { moreLocale } = themeConfig.global
|
||||
const { moreLocales } = themeConfig.global
|
||||
|
||||
// Type for supported non-default languages
|
||||
type SupportedLanguage = typeof moreLocale[number]
|
||||
type SupportedLanguage = typeof moreLocales[number]
|
||||
|
||||
// Generate static paths for all supported languages
|
||||
export function getStaticPaths() {
|
||||
|
@ -17,7 +17,7 @@ export async function GET({ params }: APIContext) {
|
|||
const lang = params.lang as SupportedLanguage
|
||||
|
||||
// Return 404 if language is not supported
|
||||
if (!moreLocale.includes(lang)) {
|
||||
if (!moreLocales.includes(lang)) {
|
||||
return new Response(null, {
|
||||
status: 404,
|
||||
statusText: 'Not found',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import Waline from '@/components/Comments/Waline.astro'
|
||||
import Comments from '@/components/Comments/index.astro'
|
||||
import PostTime from '@/components/PostTime.astro'
|
||||
import { generateMultiLangPostPaths } from '@/i18n/route'
|
||||
import Layout from '@/layouts/Layout.astro'
|
||||
import { checkSlugDuplication } from '@/utils/content'
|
||||
|
@ -17,6 +18,7 @@ export async function getStaticPaths() {
|
|||
return generateMultiLangPostPaths(posts)
|
||||
}
|
||||
|
||||
const { lang } = Astro.params
|
||||
const { post, supportedLangs = [] } = Astro.props
|
||||
const description = generateDescription(post)
|
||||
const { Content, remarkPluginFrontmatter } = await post.render()
|
||||
|
@ -28,13 +30,45 @@ const { Content, remarkPluginFrontmatter } = await post.render()
|
|||
postSlug={post.slug}
|
||||
supportedLangs={supportedLangs}
|
||||
>
|
||||
<article>
|
||||
<h1>{post.data.title}</h1>
|
||||
<time>
|
||||
{post.data.published.toLocaleDateString('en-US', { month: '2-digit', day: '2-digit' }).replace('/', '-')}
|
||||
{remarkPluginFrontmatter.minutes && <span> {remarkPluginFrontmatter.minutes} min</span>}
|
||||
</time>
|
||||
<article class="heti mb-12.6">
|
||||
<h1 class="post-title">
|
||||
<span
|
||||
transition:name={`post-${post.data.abbrlink || post.slug}`}
|
||||
data-disable-transition-on-theme
|
||||
>
|
||||
{post.data.title}
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div
|
||||
class="mb-17 block c-primary font-time"
|
||||
transition:name={`time-${post.data.abbrlink || post.slug}`}
|
||||
data-disable-transition-on-theme
|
||||
>
|
||||
<!-- published and updated time -->
|
||||
<PostTime
|
||||
date={post.data.published}
|
||||
updatedDate={post.data.updated}
|
||||
minutes={remarkPluginFrontmatter.minutes}
|
||||
/>
|
||||
</div>
|
||||
<Content />
|
||||
</article>
|
||||
<Waline />
|
||||
|
||||
<!-- Tags -->
|
||||
{post.data.tags && post.data.tags.length > 0 && (
|
||||
<div class="uno-decorative-line"></div>
|
||||
<div class="uno-tags-wrapper">
|
||||
{post.data.tags.map(tag => (
|
||||
<a
|
||||
href={`/${lang}/tags/${tag}/`}
|
||||
class="uno-tags-style"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Comments />
|
||||
</Layout>
|
||||
|
|
|
@ -3,10 +3,10 @@ import themeConfig from '@/config'
|
|||
import { generateMultiLangPaths } from '@/i18n/route'
|
||||
import { generateRSS } from '@/utils/rss'
|
||||
|
||||
const { moreLocale } = themeConfig.global
|
||||
const { moreLocales } = themeConfig.global
|
||||
|
||||
// Type for supported non-default languages
|
||||
type SupportedLanguage = typeof moreLocale[number]
|
||||
type SupportedLanguage = typeof moreLocales[number]
|
||||
|
||||
// Generate static paths for all supported languages
|
||||
export function getStaticPaths() {
|
||||
|
@ -17,7 +17,7 @@ export async function GET({ params }: APIContext) {
|
|||
const lang = params.lang as SupportedLanguage
|
||||
|
||||
// Return 404 if language is not supported
|
||||
if (!moreLocale.includes(lang)) {
|
||||
if (!moreLocales.includes(lang)) {
|
||||
return new Response(null, {
|
||||
status: 404,
|
||||
statusText: 'Not found',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import PostList from '@/components/PostList.astro'
|
||||
import { generateMultiLangTagPaths } from '@/i18n/route'
|
||||
import Layout from '@/layouts/Layout.astro'
|
||||
import { getAllTags, getPostsByTag } from '@/utils/content'
|
||||
|
@ -15,21 +16,23 @@ const allTags = await getAllTags()
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<div>
|
||||
<div class="uno-decorative-line"></div>
|
||||
<div class="uno-tags-wrapper">
|
||||
{allTags.map(tag => (
|
||||
<a href={`/${lang}/tags/${tag}/`}>
|
||||
<a
|
||||
href={`/${lang}/tags/${tag}/`}
|
||||
class={`uno-tags-style ${
|
||||
Astro.props.tag === tag
|
||||
? 'uno-tag-active'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
{posts.map(post => (
|
||||
<li>
|
||||
<a href={`/${lang}/posts/${post.data.abbrlink || post.slug}/`}>{post.data.title}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div class="mt-10.625">
|
||||
<PostList posts={posts} lang={lang} />
|
||||
</div>
|
||||
</Layout>
|
||||
|
|
|
@ -12,9 +12,13 @@ const allTags = await getAllTags()
|
|||
---
|
||||
|
||||
<Layout>
|
||||
<div>
|
||||
<div class="uno-decorative-line"></div>
|
||||
<div class="uno-tags-wrapper">
|
||||
{allTags.map(tag => (
|
||||
<a href={`/${lang}/tags/${tag}/`}>
|
||||
<a
|
||||
href={`/${lang}/tags/${tag}/`}
|
||||
class="uno-tags-style"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
|
|
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
|
@ -31,7 +31,7 @@ export interface ThemeConfig {
|
|||
|
||||
global: {
|
||||
locale: typeof langPath[number]
|
||||
moreLocale: typeof langPath[number][]
|
||||
moreLocales: typeof langPath[number][]
|
||||
fontStyle: 'sans' | 'serif'
|
||||
dateFormat: 'YYYY-MM-DD' | 'MM-DD-YYYY' | 'DD-MM-YYYY' | 'MONTH DAY YYYY' | 'DAY MONTH YYYY'
|
||||
titleSpace: 1 | 2 | 3
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue