mirror of
https://github.com/afoim/fuwari.git
synced 2026-01-31 00:53:19 +08:00
refactor: 移除国际化支持并清理相关代码
移除多语言i18n实现及相关翻译文件,简化代码结构 删除分类功能及相关组件和页面 将硬编码的中文文本直接替换原有国际化调用 清理不再使用的常量和工具函数
This commit is contained in:
@@ -46,11 +46,6 @@
|
||||
"name": "tags",
|
||||
"type": "list"
|
||||
},
|
||||
{
|
||||
"title": "category",
|
||||
"name": "category",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"title": "draft",
|
||||
"name": "draft",
|
||||
|
||||
@@ -51,7 +51,7 @@ published: ${getDate()}
|
||||
description: ''
|
||||
image: ''
|
||||
tags: []
|
||||
category: ''
|
||||
|
||||
draft: false
|
||||
lang: ''
|
||||
---
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
---
|
||||
import { UNCATEGORIZED } from "@constants/constants";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import { i18n } from "../i18n/translation";
|
||||
|
||||
import { getSortedPosts } from "../utils/content-utils";
|
||||
import { getPostUrlBySlug } from "../utils/url-utils";
|
||||
|
||||
interface Props {
|
||||
keyword?: string;
|
||||
tags?: string[];
|
||||
categories?: string[];
|
||||
}
|
||||
const { tags, categories } = Astro.props;
|
||||
const { tags } = Astro.props;
|
||||
|
||||
let posts = await getSortedPosts();
|
||||
|
||||
@@ -22,13 +19,7 @@ if (Array.isArray(tags) && tags.length > 0) {
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(categories) && categories.length > 0) {
|
||||
posts = posts.filter(
|
||||
(post) =>
|
||||
(post.data.category && categories.includes(post.data.category)) ||
|
||||
(!post.data.category && categories.includes(UNCATEGORIZED)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const groups: { year: number; posts: typeof posts }[] = (() => {
|
||||
const groupedPosts = posts.reduce(
|
||||
@@ -74,7 +65,7 @@ function formatTag(tag: string[]) {
|
||||
<div class="w-[15%] md:w-[10%]">
|
||||
<div class="h-3 w-3 bg-none rounded-full outline outline-[var(--primary)] mx-auto -outline-offset-[2px] z-50 outline-3"></div>
|
||||
</div>
|
||||
<div class="w-[70%] md:w-[80%] transition text-left text-50">{group.posts.length} {i18n(I18nKey.postsCount)}</div>
|
||||
<div class="w-[70%] md:w-[80%] transition text-left text-50">{group.posts.length} 篇文章</div>
|
||||
</div>
|
||||
{group.posts.map(post => (
|
||||
<a href={getPostUrlBySlug(post.slug)}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { LIGHT_DARK_MODE } from "@/types/config.ts";
|
||||
import { AUTO_MODE, DARK_MODE, LIGHT_MODE } from "@constants/constants.ts";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
import Icon from "@iconify/svelte";
|
||||
import {
|
||||
applyThemeToDocument,
|
||||
@@ -78,21 +77,21 @@ function hidePanel() {
|
||||
onclick={() => switchScheme(LIGHT_MODE)}
|
||||
>
|
||||
<Icon icon="material-symbols:wb-sunny-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
||||
{i18n(I18nKey.lightMode)}
|
||||
浅色模式
|
||||
</button>
|
||||
<button class="flex transition whitespace-nowrap items-center !justify-start w-full btn-plain scale-animation rounded-lg h-9 px-3 font-medium active:scale-95 mb-0.5"
|
||||
class:current-theme-btn={mode === DARK_MODE}
|
||||
onclick={() => switchScheme(DARK_MODE)}
|
||||
>
|
||||
<Icon icon="material-symbols:dark-mode-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
||||
{i18n(I18nKey.darkMode)}
|
||||
深色模式
|
||||
</button>
|
||||
<button class="flex transition whitespace-nowrap items-center !justify-start w-full btn-plain scale-animation rounded-lg h-9 px-3 font-medium active:scale-95"
|
||||
class:current-theme-btn={mode === AUTO_MODE}
|
||||
onclick={() => switchScheme(AUTO_MODE)}
|
||||
>
|
||||
<Icon icon="material-symbols:radio-button-partial-outline" class="text-[1.25rem] mr-3"></Icon>
|
||||
{i18n(I18nKey.systemMode)}
|
||||
跟随系统
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import path from "node:path";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { Icon } from "astro-icon/components";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import { i18n } from "../i18n/translation";
|
||||
|
||||
import { getDir } from "../utils/url-utils";
|
||||
import PostMetadata from "./PostMeta.astro";
|
||||
import ImageWrapper from "./misc/ImageWrapper.astro";
|
||||
@@ -17,7 +16,6 @@ interface Props {
|
||||
published: Date;
|
||||
updated?: Date;
|
||||
tags: string[];
|
||||
category: string;
|
||||
image: string;
|
||||
description: string;
|
||||
draft: boolean;
|
||||
@@ -30,7 +28,6 @@ const {
|
||||
published,
|
||||
updated,
|
||||
tags,
|
||||
category,
|
||||
image,
|
||||
description,
|
||||
style,
|
||||
@@ -65,7 +62,7 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||
</a>
|
||||
|
||||
<!-- metadata -->
|
||||
<PostMetadata published={published} updated={updated} tags={tags} category={category} hideTagsForMobile={true} hideUpdateDate={true} class="mb-4"></PostMetadata>
|
||||
<PostMetadata published={published} updated={updated} tags={tags} hideTagsForMobile={true} hideUpdateDate={true} class="mb-4"></PostMetadata>
|
||||
|
||||
<!-- description -->
|
||||
<div class:list={["transition text-75 mb-3.5 pr-4", {"line-clamp-2 md:line-clamp-1": !description}]}>
|
||||
@@ -74,9 +71,9 @@ const { remarkPluginFrontmatter } = await entry.render();
|
||||
|
||||
<!-- word count, read time and page views -->
|
||||
<div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition">
|
||||
<div>{remarkPluginFrontmatter.words} {" " + i18n(I18nKey.wordsCount)}</div>
|
||||
<div>{remarkPluginFrontmatter.words} 字</div>
|
||||
<div>|</div>
|
||||
<div>{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div>
|
||||
<div>{remarkPluginFrontmatter.minutes} 分钟</div>
|
||||
<div>|</div>
|
||||
<div>
|
||||
<span class="text-50 text-sm font-medium" id={`page-views-${entry.slug}`}>加载中...</span>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import { i18n } from "../i18n/translation";
|
||||
|
||||
import { getDir, url } from "../utils/url-utils";
|
||||
import { formatDateToYYYYMMDD } from "../utils/date-utils";
|
||||
import { umamiConfig } from "../config";
|
||||
@@ -11,7 +10,6 @@ interface Props {
|
||||
published: Date;
|
||||
updated?: Date;
|
||||
tags: string[];
|
||||
category: string;
|
||||
hideTagsForMobile?: boolean;
|
||||
hideUpdateDate?: boolean;
|
||||
slug?: string;
|
||||
@@ -20,7 +18,6 @@ const {
|
||||
published,
|
||||
updated,
|
||||
tags,
|
||||
category,
|
||||
hideTagsForMobile = false,
|
||||
hideUpdateDate = false,
|
||||
slug,
|
||||
@@ -49,21 +46,6 @@ const className = Astro.props.class;
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- categories -->
|
||||
<div class="flex items-center">
|
||||
<div class="meta-icon"
|
||||
>
|
||||
<Icon name="material-symbols:book-2-outline-rounded" class="text-xl"></Icon>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap items-center">
|
||||
<a href={url(`/archive/category/${category || 'uncategorized'}/`)} aria-label=`View all posts in the ${category} category`
|
||||
class="link-lg transition text-50 text-sm font-medium
|
||||
hover:text-[var(--primary)] dark:hover:text-[var(--primary)] whitespace-nowrap">
|
||||
{category || i18n(I18nKey.uncategorized)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tags -->
|
||||
<div class:list={["items-center", {"flex": !hideTagsForMobile, "hidden md:flex": hideTagsForMobile}]}>
|
||||
<div class="meta-icon"
|
||||
@@ -79,7 +61,7 @@ const className = Astro.props.class;
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
{!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">{i18n(I18nKey.noTags)}</div>}
|
||||
{!(tags && tags.length > 0) && <div class="transition text-50 text-sm font-medium">无标签</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ const interval = 50;
|
||||
entry={entry}
|
||||
title={entry.data.title}
|
||||
tags={entry.data.tags}
|
||||
category={entry.data.category}
|
||||
published={entry.data.published}
|
||||
updated={entry.data.updated}
|
||||
url={getPostUrlBySlug(entry.slug)}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
import Icon from "@iconify/svelte";
|
||||
import { url } from "@utils/url-utils.ts";
|
||||
import { onMount } from "svelte";
|
||||
@@ -143,7 +142,7 @@ $: search(keywordMobile, false);
|
||||
dark:bg-white/5 dark:hover:bg-white/10 dark:focus-within:bg-white/10
|
||||
">
|
||||
<Icon icon="material-symbols:search" class="absolute text-[1.25rem] pointer-events-none ml-3 transition my-auto text-black/30 dark:text-white/30"></Icon>
|
||||
<input placeholder="{i18n(I18nKey.search)}" bind:value={keywordDesktop} on:focus={() => search(keywordDesktop, true)}
|
||||
<input placeholder="搜索" bind:value={keywordDesktop} on:focus={() => search(keywordDesktop, true)}
|
||||
class="transition-all pl-10 text-sm bg-transparent outline-0
|
||||
h-full w-40 active:w-60 focus:w-60 text-black/50 dark:text-white/50"
|
||||
>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { licenseConfig, profileConfig } from "../../config";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import { i18n } from "../../i18n/translation";
|
||||
|
||||
import { formatDateToYYYYMMDD } from "../../utils/date-utils";
|
||||
|
||||
interface Props {
|
||||
@@ -27,15 +26,15 @@ const postUrl = decodeURIComponent(Astro.url.toString());
|
||||
</a>
|
||||
<div class="flex gap-6 mt-2">
|
||||
<div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.author)}</div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">作者</div>
|
||||
<div class="transition text-black/75 dark:text-white/75 line-clamp-2">{profileConf.name}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.publishedAt)}</div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">发布于</div>
|
||||
<div class="transition text-black/75 dark:text-white/75 line-clamp-2">{formatDateToYYYYMMDD(pubDate)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">{i18n(I18nKey.license)}</div>
|
||||
<div class="transition text-black/30 dark:text-white/30 text-sm">许可协议</div>
|
||||
<a href={licenseConf.url} target="_blank" class="link text-[var(--primary)] line-clamp-2">{licenseConf.name}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
import WidgetLayout from "./WidgetLayout.astro";
|
||||
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import { i18n } from "../../i18n/translation";
|
||||
import { getCategoryList } from "../../utils/content-utils";
|
||||
import { getCategoryUrl } from "../../utils/url-utils";
|
||||
import ButtonLink from "../control/ButtonLink.astro";
|
||||
|
||||
const categories = await getCategoryList();
|
||||
|
||||
const COLLAPSED_HEIGHT = "7.5rem";
|
||||
const COLLAPSE_THRESHOLD = 5;
|
||||
|
||||
const isCollapsed = categories.length >= COLLAPSE_THRESHOLD;
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
style?: string;
|
||||
}
|
||||
const className = Astro.props.class;
|
||||
const style = Astro.props.style;
|
||||
---
|
||||
|
||||
<WidgetLayout name={i18n(I18nKey.categories)} id="categories" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT}
|
||||
class={className} style={style}
|
||||
>
|
||||
{categories.map((c) =>
|
||||
<ButtonLink
|
||||
url={getCategoryUrl(c.name)}
|
||||
badge={String(c.count)}
|
||||
label=`View all posts in the ${c.name} category`
|
||||
>
|
||||
{c.name}
|
||||
</ButtonLink>
|
||||
)}
|
||||
</WidgetLayout>
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
import Icon from "@iconify/svelte";
|
||||
import { getDefaultHue, getHue, setHue } from "@utils/setting-utils";
|
||||
|
||||
@@ -22,7 +21,7 @@ $: if (hue || hue === 0) {
|
||||
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
|
||||
before:absolute before:-left-3 before:top-[0.33rem]"
|
||||
>
|
||||
{i18n(I18nKey.themeColor)}
|
||||
主题色彩
|
||||
<button aria-label="Reset to Default" class="btn-regular w-7 h-7 rounded-md active:scale-90"
|
||||
class:opacity-0={hue === defaultHue} class:pointer-events-none={hue === defaultHue} on:click={resetHue}>
|
||||
<div class="text-[var(--btn-content)]">
|
||||
@@ -38,7 +37,7 @@ $: if (hue || hue === 0) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full h-6 px-1 bg-[oklch(0.80_0.10_0)] dark:bg-[oklch(0.70_0.10_0)] rounded select-none">
|
||||
<input aria-label={i18n(I18nKey.themeColor)} type="range" min="0" max="360" bind:value={hue}
|
||||
<input aria-label="主题色彩" type="range" min="0" max="360" bind:value={hue}
|
||||
class="slider" id="colorSlider" step="5" style="width: 100%">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import Categories from "./Categories.astro";
|
||||
|
||||
import Profile from "./Profile.astro";
|
||||
import Tag from "./Tags.astro";
|
||||
|
||||
@@ -16,7 +16,7 @@ const className = Astro.props.class;
|
||||
<Profile></Profile>
|
||||
</div>
|
||||
<div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4">
|
||||
<Categories class="onload-animation" style="animation-delay: 150ms"></Categories>
|
||||
|
||||
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
|
||||
<!-- 赞助标 -->
|
||||
<div class="overflow-hidden flex justify-center">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import { i18n } from "../../i18n/translation";
|
||||
|
||||
import { getTagList } from "../../utils/content-utils";
|
||||
import { url } from "../../utils/url-utils";
|
||||
import ButtonTag from "../control/ButtonTag.astro";
|
||||
@@ -20,7 +19,7 @@ interface Props {
|
||||
const className = Astro.props.class;
|
||||
const style = Astro.props.style;
|
||||
---
|
||||
<WidgetLayout name={i18n(I18nKey.tags)} id="tags" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT} class={className} style={style}>
|
||||
<WidgetLayout name="标签" id="tags" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT} class={className} style={style}>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
{tags.map(t => (
|
||||
<ButtonTag href={url(`/archive/tag/${t.name}/`)} label={`View all posts with the ${t.name} tag`}>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import I18nKey from "../../i18n/i18nKey";
|
||||
import { i18n } from "../../i18n/translation";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
name?: string;
|
||||
@@ -23,7 +22,7 @@ const className = Astro.props.class;
|
||||
{isCollapsed && <div class="expand-btn px-4 -mb-2">
|
||||
<button class="btn-plain rounded-lg w-full h-9">
|
||||
<div class="text-[var(--primary)] flex items-center justify-center gap-2 -translate-x-2">
|
||||
<Icon name="material-symbols:more-horiz" class="text-[1.75rem]"></Icon> {i18n(I18nKey.more)}
|
||||
<Icon name="material-symbols:more-horiz" class="text-[1.75rem]"></Icon> 更多
|
||||
</div>
|
||||
</button>
|
||||
</div>}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { LinkPreset, type NavBarLink } from "@/types/config";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
|
||||
export const LinkPresets: { [key in LinkPreset]: NavBarLink } = {
|
||||
[LinkPreset.Home]: {
|
||||
name: i18n(I18nKey.home),
|
||||
name: "首页",
|
||||
url: "/",
|
||||
},
|
||||
[LinkPreset.About]: {
|
||||
name: i18n(I18nKey.about),
|
||||
name: "关于",
|
||||
url: "/about/",
|
||||
},
|
||||
[LinkPreset.Archive]: {
|
||||
name: i18n(I18nKey.archive),
|
||||
name: "归档",
|
||||
url: "/archive/",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ const postsCollection = defineCollection({
|
||||
description: z.string().optional().default(""),
|
||||
image: z.string().optional().default(""),
|
||||
tags: z.array(z.string()).optional().default([]),
|
||||
category: z.string().optional().default(""),
|
||||
lang: z.string().optional().default(""),
|
||||
pinned: z.boolean().optional().default(false),
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
enum I18nKey {
|
||||
home = "home",
|
||||
about = "about",
|
||||
archive = "archive",
|
||||
search = "search",
|
||||
|
||||
tags = "tags",
|
||||
categories = "categories",
|
||||
recentPosts = "recentPosts",
|
||||
|
||||
comments = "comments",
|
||||
|
||||
untitled = "untitled",
|
||||
uncategorized = "uncategorized",
|
||||
noTags = "noTags",
|
||||
|
||||
wordCount = "wordCount",
|
||||
wordsCount = "wordsCount",
|
||||
minuteCount = "minuteCount",
|
||||
minutesCount = "minutesCount",
|
||||
postCount = "postCount",
|
||||
postsCount = "postsCount",
|
||||
|
||||
themeColor = "themeColor",
|
||||
|
||||
lightMode = "lightMode",
|
||||
darkMode = "darkMode",
|
||||
systemMode = "systemMode",
|
||||
|
||||
more = "more",
|
||||
|
||||
author = "author",
|
||||
publishedAt = "publishedAt",
|
||||
license = "license",
|
||||
}
|
||||
|
||||
export default I18nKey;
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const en: Translation = {
|
||||
[Key.home]: "Home",
|
||||
[Key.about]: "About",
|
||||
[Key.archive]: "Archive",
|
||||
[Key.search]: "Search",
|
||||
|
||||
[Key.tags]: "Tags",
|
||||
[Key.categories]: "Categories",
|
||||
[Key.recentPosts]: "Recent Posts",
|
||||
|
||||
[Key.comments]: "Comments",
|
||||
|
||||
[Key.untitled]: "Untitled",
|
||||
[Key.uncategorized]: "Uncategorized",
|
||||
[Key.noTags]: "No Tags",
|
||||
|
||||
[Key.wordCount]: "word",
|
||||
[Key.wordsCount]: "words",
|
||||
[Key.minuteCount]: "minute",
|
||||
[Key.minutesCount]: "minutes",
|
||||
[Key.postCount]: "post",
|
||||
[Key.postsCount]: "posts",
|
||||
|
||||
[Key.themeColor]: "Theme Color",
|
||||
|
||||
[Key.lightMode]: "Light",
|
||||
[Key.darkMode]: "Dark",
|
||||
[Key.systemMode]: "System",
|
||||
|
||||
[Key.more]: "More",
|
||||
|
||||
[Key.author]: "Author",
|
||||
[Key.publishedAt]: "Published at",
|
||||
[Key.license]: "License",
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const es: Translation = {
|
||||
[Key.home]: "Inicio",
|
||||
[Key.about]: "Sobre mí",
|
||||
[Key.archive]: "Archivo",
|
||||
[Key.search]: "Buscar",
|
||||
|
||||
[Key.tags]: "Etiquetas",
|
||||
[Key.categories]: "Categorías",
|
||||
[Key.recentPosts]: "Publicaciones recientes",
|
||||
|
||||
[Key.comments]: "Comentarios",
|
||||
|
||||
[Key.untitled]: "Sin título",
|
||||
[Key.uncategorized]: "Sin categoría",
|
||||
[Key.noTags]: "Sin etiquetas",
|
||||
|
||||
[Key.wordCount]: "palabra",
|
||||
[Key.wordsCount]: "palabras",
|
||||
[Key.minuteCount]: "minuto",
|
||||
[Key.minutesCount]: "minutos",
|
||||
[Key.postCount]: "publicación",
|
||||
[Key.postsCount]: "publicaciones",
|
||||
|
||||
[Key.themeColor]: "Color del tema",
|
||||
|
||||
[Key.lightMode]: "Claro",
|
||||
[Key.darkMode]: "Oscuro",
|
||||
[Key.systemMode]: "Sistema",
|
||||
|
||||
[Key.more]: "Más",
|
||||
|
||||
[Key.author]: "Autor",
|
||||
[Key.publishedAt]: "Publicado el",
|
||||
[Key.license]: "Licencia",
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const ja: Translation = {
|
||||
[Key.home]: "Home",
|
||||
[Key.about]: "About",
|
||||
[Key.archive]: "Archive",
|
||||
[Key.search]: "検索",
|
||||
|
||||
[Key.tags]: "タグ",
|
||||
[Key.categories]: "カテゴリ",
|
||||
[Key.recentPosts]: "最近の投稿",
|
||||
|
||||
[Key.comments]: "コメント",
|
||||
|
||||
[Key.untitled]: "タイトルなし",
|
||||
[Key.uncategorized]: "カテゴリなし",
|
||||
[Key.noTags]: "タグなし",
|
||||
|
||||
[Key.wordCount]: "文字",
|
||||
[Key.wordsCount]: "文字",
|
||||
[Key.minuteCount]: "分",
|
||||
[Key.minutesCount]: "分",
|
||||
[Key.postCount]: "件の投稿",
|
||||
[Key.postsCount]: "件の投稿",
|
||||
|
||||
[Key.themeColor]: "テーマカラー",
|
||||
|
||||
[Key.lightMode]: "ライト",
|
||||
[Key.darkMode]: "ダーク",
|
||||
[Key.systemMode]: "システム",
|
||||
|
||||
[Key.more]: "もっと",
|
||||
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "公開日",
|
||||
[Key.license]: "ライセンス",
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const ko: Translation = {
|
||||
[Key.home]: "홈",
|
||||
[Key.about]: "소개",
|
||||
[Key.archive]: "아카이브",
|
||||
[Key.search]: "검색",
|
||||
|
||||
[Key.tags]: "태그",
|
||||
[Key.categories]: "카테고리",
|
||||
[Key.recentPosts]: "최근 게시물",
|
||||
|
||||
[Key.comments]: "댓글",
|
||||
|
||||
[Key.untitled]: "제목 없음",
|
||||
[Key.uncategorized]: "분류되지 않음",
|
||||
[Key.noTags]: "태그 없음",
|
||||
|
||||
[Key.wordCount]: "단어",
|
||||
[Key.wordsCount]: "단어",
|
||||
[Key.minuteCount]: "분",
|
||||
[Key.minutesCount]: "분",
|
||||
[Key.postCount]: "게시물",
|
||||
[Key.postsCount]: "게시물",
|
||||
|
||||
[Key.themeColor]: "테마 색상",
|
||||
|
||||
[Key.lightMode]: "밝은 모드",
|
||||
[Key.darkMode]: "어두운 모드",
|
||||
[Key.systemMode]: "시스템 모드",
|
||||
|
||||
[Key.more]: "더 보기",
|
||||
|
||||
[Key.author]: "저자",
|
||||
[Key.publishedAt]: "게시일",
|
||||
[Key.license]: "라이선스",
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const th: Translation = {
|
||||
[Key.home]: "หน้าแรก",
|
||||
[Key.about]: "เกี่ยวกับ",
|
||||
[Key.archive]: "คลัง",
|
||||
[Key.search]: "ค้นหา",
|
||||
|
||||
[Key.tags]: "ป้ายกำกับ",
|
||||
[Key.categories]: "หมวดหมู่",
|
||||
[Key.recentPosts]: "โพสต์ล่าสุด",
|
||||
|
||||
[Key.comments]: "ความคิดเห็น",
|
||||
|
||||
[Key.untitled]: "ไม่ได้ตั้งชื่อ",
|
||||
[Key.uncategorized]: "ไม่ได้จัดหมวดหมู่",
|
||||
[Key.noTags]: "ไม่มีป้ายกำกับ",
|
||||
|
||||
[Key.wordCount]: "คำ",
|
||||
[Key.wordsCount]: "คำ",
|
||||
[Key.minuteCount]: "นาที",
|
||||
[Key.minutesCount]: "นาที",
|
||||
[Key.postCount]: "โพสต์",
|
||||
[Key.postsCount]: "โพสต์",
|
||||
|
||||
[Key.themeColor]: "สีของธีม",
|
||||
|
||||
[Key.lightMode]: "สว่าง",
|
||||
[Key.darkMode]: "มืด",
|
||||
[Key.systemMode]: "ตามระบบ",
|
||||
|
||||
[Key.more]: "ดูเพิ่ม",
|
||||
|
||||
[Key.author]: "ผู้เขียน",
|
||||
[Key.publishedAt]: "เผยแพร่เมื่อ",
|
||||
[Key.license]: "สัญญาอนุญาต",
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const zh_CN: Translation = {
|
||||
[Key.home]: "主页",
|
||||
[Key.about]: "关于&隐私政策",
|
||||
[Key.archive]: "归档",
|
||||
[Key.search]: "搜索",
|
||||
|
||||
[Key.tags]: "标签",
|
||||
[Key.categories]: "分类",
|
||||
[Key.recentPosts]: "最新文章",
|
||||
|
||||
[Key.comments]: "评论",
|
||||
|
||||
[Key.untitled]: "无标题",
|
||||
[Key.uncategorized]: "未分类",
|
||||
[Key.noTags]: "无标签",
|
||||
|
||||
[Key.wordCount]: "字",
|
||||
[Key.wordsCount]: "字",
|
||||
[Key.minuteCount]: "分钟",
|
||||
[Key.minutesCount]: "分钟",
|
||||
[Key.postCount]: "篇文章",
|
||||
[Key.postsCount]: "篇文章",
|
||||
|
||||
[Key.themeColor]: "主题色",
|
||||
|
||||
[Key.lightMode]: "亮色",
|
||||
[Key.darkMode]: "暗色",
|
||||
[Key.systemMode]: "跟随系统",
|
||||
|
||||
[Key.more]: "更多",
|
||||
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "发布于",
|
||||
[Key.license]: "许可协议",
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import Key from "../i18nKey";
|
||||
import type { Translation } from "../translation";
|
||||
|
||||
export const zh_TW: Translation = {
|
||||
[Key.home]: "首頁",
|
||||
[Key.about]: "關於",
|
||||
[Key.archive]: "彙整",
|
||||
[Key.search]: "搜尋",
|
||||
|
||||
[Key.tags]: "標籤",
|
||||
[Key.categories]: "分類",
|
||||
[Key.recentPosts]: "最新文章",
|
||||
|
||||
[Key.comments]: "評論",
|
||||
|
||||
[Key.untitled]: "無標題",
|
||||
[Key.uncategorized]: "未分類",
|
||||
[Key.noTags]: "無標籤",
|
||||
|
||||
[Key.wordCount]: "字",
|
||||
[Key.wordsCount]: "字",
|
||||
[Key.minuteCount]: "分鐘",
|
||||
[Key.minutesCount]: "分鐘",
|
||||
[Key.postCount]: "篇文章",
|
||||
[Key.postsCount]: "篇文章",
|
||||
|
||||
[Key.themeColor]: "主題色",
|
||||
|
||||
[Key.lightMode]: "亮色",
|
||||
[Key.darkMode]: "暗色",
|
||||
[Key.systemMode]: "跟隨系統",
|
||||
|
||||
[Key.more]: "更多",
|
||||
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "發佈於",
|
||||
[Key.license]: "許可協議",
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import { siteConfig } from "../config";
|
||||
import type I18nKey from "./i18nKey";
|
||||
import { en } from "./languages/en";
|
||||
import { es } from "./languages/es";
|
||||
import { ja } from "./languages/ja";
|
||||
import { ko } from "./languages/ko";
|
||||
import { th } from "./languages/th";
|
||||
import { zh_CN } from "./languages/zh_CN";
|
||||
import { zh_TW } from "./languages/zh_TW";
|
||||
|
||||
export type Translation = {
|
||||
[K in I18nKey]: string;
|
||||
};
|
||||
|
||||
const defaultTranslation = en;
|
||||
|
||||
const map: { [key: string]: Translation } = {
|
||||
es: es,
|
||||
en: en,
|
||||
en_us: en,
|
||||
en_gb: en,
|
||||
en_au: en,
|
||||
zh_cn: zh_CN,
|
||||
zh_tw: zh_TW,
|
||||
ja: ja,
|
||||
ja_jp: ja,
|
||||
ko: ko,
|
||||
ko_kr: ko,
|
||||
th: th,
|
||||
th_th: th,
|
||||
};
|
||||
|
||||
export function getTranslation(lang: string): Translation {
|
||||
return map[lang.toLowerCase()] || defaultTranslation;
|
||||
}
|
||||
|
||||
export function i18n(key: I18nKey): string {
|
||||
const lang = siteConfig.lang || "en";
|
||||
return getTranslation(lang)[key];
|
||||
}
|
||||
@@ -5,8 +5,7 @@ import MainGridLayout from "../layouts/MainGridLayout.astro";
|
||||
import { getEntry } from "astro:content";
|
||||
import { render } from "astro:content";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import { i18n } from "../i18n/translation";
|
||||
|
||||
|
||||
const aboutPost = await getEntry("spec", "about");
|
||||
|
||||
@@ -16,7 +15,7 @@ if (!aboutPost) {
|
||||
|
||||
const { Content } = await render(aboutPost);
|
||||
---
|
||||
<MainGridLayout title={i18n(I18nKey.about)} description={i18n(I18nKey.about)}>
|
||||
<MainGridLayout title="关于" description="关于">
|
||||
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32">
|
||||
<div class="card-base z-10 px-9 py-6 relative w-full ">
|
||||
<Markdown class="mt-2">
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import { getCategoryList } from "@utils/content-utils";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const categories = await getCategoryList();
|
||||
return categories.map((category) => {
|
||||
return {
|
||||
params: {
|
||||
category: category.name,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const category = Astro.params.category as string;
|
||||
---
|
||||
|
||||
<MainGridLayout title={`${category} - ${i18n(I18nKey.categories)}`} description={`${i18n(I18nKey.archive)} - ${category}`}>
|
||||
<ArchivePanel categories={[category]}></ArchivePanel>
|
||||
</MainGridLayout>
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import { UNCATEGORIZED } from "@constants/constants";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
---
|
||||
|
||||
<MainGridLayout title={`${i18n(I18nKey.uncategorized)} - ${i18n(I18nKey.categories)}`} description={`${i18n(I18nKey.archive)} - ${i18n(I18nKey.uncategorized)}`}>
|
||||
<ArchivePanel categories={[UNCATEGORIZED]}></ArchivePanel>
|
||||
</MainGridLayout>
|
||||
@@ -1,11 +1,10 @@
|
||||
---
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
---
|
||||
|
||||
<MainGridLayout title={i18n(I18nKey.archive)}>
|
||||
<MainGridLayout title="归档">
|
||||
<ArchivePanel></ArchivePanel>
|
||||
</MainGridLayout>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import { getSortedPosts } from "@utils/content-utils";
|
||||
|
||||
@@ -27,6 +26,6 @@ export async function getStaticPaths() {
|
||||
const tag = Astro.params.tag as string;
|
||||
---
|
||||
|
||||
<MainGridLayout title={`${tag} - ${i18n(I18nKey.tags)}`} description={`${i18n(I18nKey.archive)} - ${tag}`}>
|
||||
<MainGridLayout title={`${tag} - 标签`} description={`归档 - ${tag}`}>
|
||||
<ArchivePanel tags={[tag]}></ArchivePanel>
|
||||
</MainGridLayout>
|
||||
@@ -2,8 +2,7 @@
|
||||
import path from "node:path";
|
||||
import License from "@components/misc/License.astro";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import { getSortedPosts } from "@utils/content-utils";
|
||||
import { getDir, getPostUrlBySlug } from "@utils/url-utils";
|
||||
@@ -57,13 +56,13 @@ const jsonLd = {
|
||||
<div class="transition h-6 w-6 rounded-md bg-black/5 dark:bg-white/10 text-black/50 dark:text-white/50 flex items-center justify-center mr-2">
|
||||
<Icon name="material-symbols:notes-rounded"></Icon>
|
||||
</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.words} {" " + i18n(I18nKey.wordsCount)}</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.words} 字</div>
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="transition h-6 w-6 rounded-md bg-black/5 dark:bg-white/10 text-black/50 dark:text-white/50 flex items-center justify-center mr-2">
|
||||
<Icon name="material-symbols:schedule-outline-rounded"></Icon>
|
||||
</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.minutes} 分钟</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +100,6 @@ const jsonLd = {
|
||||
published={entry.data.published}
|
||||
updated={entry.data.updated}
|
||||
tags={entry.data.tags}
|
||||
category={entry.data.category}
|
||||
slug={entry.slug}
|
||||
></PostMetadata>
|
||||
{!entry.data.image && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>}
|
||||
|
||||
@@ -105,7 +105,6 @@ export type BlogPostData = {
|
||||
tags: string[];
|
||||
draft?: boolean;
|
||||
image?: string;
|
||||
category?: string;
|
||||
prevTitle?: string;
|
||||
prevSlug?: string;
|
||||
nextTitle?: string;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { getCollection } from "astro:content";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
|
||||
export async function getSortedPosts() {
|
||||
const allBlogPosts = await getCollection("posts", ({ data }) => {
|
||||
@@ -54,35 +53,3 @@ export async function getTagList(): Promise<Tag[]> {
|
||||
|
||||
return keys.map((key) => ({ name: key, count: countMap[key] }));
|
||||
}
|
||||
|
||||
export type Category = {
|
||||
name: string;
|
||||
count: number;
|
||||
};
|
||||
|
||||
export async function getCategoryList(): Promise<Category[]> {
|
||||
const allBlogPosts = await getCollection<"posts">("posts", ({ data }) => {
|
||||
return import.meta.env.PROD ? data.draft !== true : true;
|
||||
});
|
||||
const count: { [key: string]: number } = {};
|
||||
allBlogPosts.map((post: { data: { category: string | number } }) => {
|
||||
if (!post.data.category) {
|
||||
const ucKey = i18n(I18nKey.uncategorized);
|
||||
count[ucKey] = count[ucKey] ? count[ucKey] + 1 : 1;
|
||||
return;
|
||||
}
|
||||
count[post.data.category] = count[post.data.category]
|
||||
? count[post.data.category] + 1
|
||||
: 1;
|
||||
});
|
||||
|
||||
const lst = Object.keys(count).sort((a, b) => {
|
||||
return a.toLowerCase().localeCompare(b.toLowerCase());
|
||||
});
|
||||
|
||||
const ret: Category[] = [];
|
||||
for (const c of lst) {
|
||||
ret.push({ name: c, count: count[c] });
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import i18nKey from "@i18n/i18nKey";
|
||||
import { i18n } from "@i18n/translation";
|
||||
|
||||
export function pathsEqual(path1: string, path2: string) {
|
||||
const normalizedPath1 = path1.replace(/^\/|\/$/g, "").toLowerCase();
|
||||
const normalizedPath2 = path2.replace(/^\/|\/$/g, "").toLowerCase();
|
||||
@@ -16,11 +13,7 @@ export function getPostUrlBySlug(slug: string): string {
|
||||
return url(`/posts/${slug}/`);
|
||||
}
|
||||
|
||||
export function getCategoryUrl(category: string): string {
|
||||
if (category === i18n(i18nKey.uncategorized))
|
||||
return url("/archive/category/uncategorized/");
|
||||
return url(`/archive/category/${category}/`);
|
||||
}
|
||||
|
||||
|
||||
export function getDir(path: string): string {
|
||||
const lastSlashIndex = path.lastIndexOf("/");
|
||||
|
||||
Reference in New Issue
Block a user