Skip to content

Commit

Permalink
Update for SEO
Browse files Browse the repository at this point in the history
  • Loading branch information
noobnooc committed Oct 28, 2024
1 parent b75b186 commit 8eec98b
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 54 deletions.
16 changes: 7 additions & 9 deletions app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Image from "next/image";
import avatar from "../../public/static/avatar.webp";
import Link from "next/link";
import { dictionaryKeys, getDictionary } from "../../dictionaries";
import { getAlternateLanguages } from "@/lib/metadata";

export const runtime = "edge";

Expand All @@ -15,20 +16,14 @@ export async function generateMetadata({
}): Promise<Metadata> {
const dictionary = await getDictionary(params.lang);

const langEntries = await Promise.all(
dictionaryKeys.map(async (lang) => {
const dictionary = await getDictionary(lang);

return [lang, dictionary.urls.home];
}),
);

return {
metadataBase: new URL(dictionary.meta.baseUrl),
title: dictionary.meta.websiteName,
description: dictionary.meta.motto,
keywords: dictionary.meta.fillKeywords([]),
openGraph: {
type: "website",
url: new URL(dictionary.urls.home, dictionary.meta.baseUrl).href,
title: dictionary.meta.websiteName,
description: dictionary.meta.motto,
siteName: dictionary.meta.websiteName,
Expand All @@ -42,7 +37,10 @@ export async function generateMetadata({
images: "/static/banner.png",
},
alternates: {
languages: Object.fromEntries(langEntries),
languages: await getAlternateLanguages(
(dictionary) =>
new URL(dictionary.urls.home, dictionary.meta.baseUrl).href,
),
},
};
}
Expand Down
22 changes: 17 additions & 5 deletions app/[lang]/posts/[postSlug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { CalendarDaysIcon, LanguageIcon } from "@heroicons/react/24/solid";
import { displayDate } from "@/lib/date";
import { notFound } from "next/navigation";
import Link from "next/link";
import { getDictionary, Language, languageLabels } from "@/dictionaries";
import {
dictionaryKeys,
getDictionary,
Language,
languageLabels,
} from "@/dictionaries";
import { SiX } from "@icons-pack/react-simple-icons";
import { Metadata } from "next";
import classNames from "classnames";
import { PostAdvertising } from "./Advertising";
import { getAlternateLanguages } from "@/lib/metadata";

export const runtime = "edge";

Expand All @@ -23,8 +29,6 @@ export async function generateMetadata({
(post) => post.lang === lang && post.slug === postSlug,
);

console.log("###", post);

if (!post) {
notFound();
}
Expand All @@ -36,6 +40,8 @@ export async function generateMetadata({
keywords: dictionary.meta.fillKeywords(post.keywords),
openGraph: {
type: "article",
url: new URL(post.permalink, dictionary.meta.baseUrl).href,
siteName: dictionary.meta.websiteName,
title: post.title,
description: post.description,
images: post.cover?.src ?? "/static/banner.png",
Expand All @@ -47,6 +53,11 @@ export async function generateMetadata({
card: "summary_large_image",
images: post.cover?.src ?? "/static/banner.png",
},
alternates: {
languages: await getAlternateLanguages(
(dictionary) => new URL(post.permalink, dictionary.meta.baseUrl).href,
),
},
};
}

Expand Down Expand Up @@ -109,8 +120,9 @@ export default async function PostPage({
className={classNames(
"prose dark:prose-invert",
"prose-headings:font-serif prose-headings:mt-8",
"prose-h1:text-xl",
"prose-h2:text-lg",
"prose-h1:text-3xl",
"prose-h2:text-xl",
"prose-h3:text-lg",
"prose-blockquote:font-normal",
"prose-pre:border prose-pre:rounded-xl",
"before:prose-p:content-none after:prose-p:content-none",
Expand Down
21 changes: 17 additions & 4 deletions app/[lang]/posts/categories/[categorySlug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { posts, categories } from "@/.velite";
import { CalendarDaysIcon } from "@heroicons/react/24/solid";
import Link from "next/link";
import { Language, getDictionary } from "@/dictionaries";
import { Language, dictionaryKeys, getDictionary } from "@/dictionaries";
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { displayDate } from "@/lib/date";
import { getAlternateLanguages } from "@/lib/metadata";

export const runtime = "edge";

Expand All @@ -28,15 +29,27 @@ export async function generateMetadata({
description: category.description?.[params.lang],
keywords: dictionary.meta.fillKeywords([]),
openGraph: {
type: "website",
url: new URL(category.permalink[params.lang], dictionary.meta.baseUrl)
.href,
siteName: dictionary.meta.websiteName,
title: category.name[params.lang],
description: category.description?.[params.lang],
images: "/static/banner.png",
},
twitter: {
title: category.name[params.lang],
description: category.description?.[params.lang],
site: "@noobnooc",
card: "summary_large_image",
},
alternates: {
languages: await getAlternateLanguages(
(dictionary) =>
new URL(category.permalink[params.lang], dictionary.meta.baseUrl)
.href,
),
},
};
}

Expand Down Expand Up @@ -69,14 +82,14 @@ export default async function CategoryPostsPage({

return (
<main className="mx-auto flex flex-col sm:flex-row w-full max-w-screen-lg gap-4 px-4 py-8">
<ul className="basis-3/4">
<ul className="basis-3/4 flex flex-col gap-4">
{posts.map((post) => (
<li
key={post.slug}
className="rounded-3xl p-4 border bg-white/50 dark:bg-indigo-100/5 flex flex-col gap-2"
className="rounded-3xl p-4 sm:px-8 border bg-white/50 dark:bg-indigo-100/5 flex flex-col gap-2"
>
<Link className="underline" href={post.permalink}>
<h1 className="text-xl font-serif">{post.title}</h1>
<h2 className="text-xl font-serif">{post.title}</h2>
</Link>
<p className="opacity-70">{post.description}</p>
<div className="opacity-50 flex items-center gap-4">
Expand Down
15 changes: 13 additions & 2 deletions app/[lang]/posts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { posts, categories } from "@/.velite";
import { CalendarDaysIcon } from "@heroicons/react/24/solid";
import Link from "next/link";
import { displayDate } from "../../../lib/date";
import { Language, getDictionary } from "@/dictionaries";
import { Language, dictionaryKeys, getDictionary } from "@/dictionaries";
import { Metadata } from "next";
import { getAlternateLanguages } from "@/lib/metadata";

export const runtime = "edge";

Expand All @@ -20,15 +21,25 @@ export async function generateMetadata({
description: dictionary.labels.posts,
keywords: dictionary.meta.fillKeywords([]),
openGraph: {
type: "website",
url: new URL(dictionary.urls.posts, dictionary.meta.baseUrl).href,
siteName: dictionary.meta.websiteName,
title: dictionary.labels.posts,
description: dictionary.labels.posts,
images: "/static/banner.png",
},
twitter: {
title: dictionary.labels.posts,
description: dictionary.labels.posts,
site: "@noobnooc",
card: "summary_large_image",
},
alternates: {
languages: await getAlternateLanguages(
(dictionary) =>
new URL(dictionary.urls.posts, dictionary.meta.baseUrl).href,
),
},
};
}

Expand Down Expand Up @@ -57,7 +68,7 @@ export default async function PostsPage({
className="rounded-3xl p-4 sm:px-8 border bg-white/50 dark:bg-indigo-100/5 flex flex-col gap-2"
>
<Link className="underline" href={post.permalink}>
<h1 className="text-xl font-serif">{post.title}</h1>
<h2 className="text-xl font-serif">{post.title}</h2>
</Link>
<p className="opacity-70">{post.description}</p>
<div className="opacity-50 flex items-center gap-4">
Expand Down
13 changes: 12 additions & 1 deletion app/[lang]/works/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Card from "../../../components/card";
import Image from "next/image";
import { twMerge } from "tailwind-merge";
import { getDictionary } from "../../../dictionaries";
import { dictionaryKeys, getDictionary } from "../../../dictionaries";
import { Metadata } from "next";
import { getAlternateLanguages } from "@/lib/metadata";

export const runtime = "edge";

Expand All @@ -19,15 +20,25 @@ export async function generateMetadata({
description: dictionary.labels.noocWorks,
keywords: dictionary.meta.fillKeywords([]),
openGraph: {
type: "website",
url: new URL(dictionary.urls.works, dictionary.meta.baseUrl).href,
siteName: dictionary.meta.websiteName,
title: dictionary.labels.works,
description: dictionary.labels.noocWorks,
images: "/static/banner.png",
},
twitter: {
title: dictionary.labels.works,
description: dictionary.labels.noocWorks,
site: "@noobnooc",
card: "summary_large_image",
},
alternates: {
languages: await getAlternateLanguages(
(dictionary) =>
new URL(dictionary.urls.works, dictionary.meta.baseUrl).href,
),
},
};
}

Expand Down
26 changes: 13 additions & 13 deletions content/posts/2024-03-15 set-up-blog-with-velite/en.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ keywords:
- Static blog
---

# Why I Built Another Blog
## Why I Built Another Blog

Yes, I've integrated another blog system into my personal homepage. Before this, I already had a personal blog called "[Subjective World](https://subnooc.com)", where I occasionally share my thoughts. However, I've always positioned that blog to share only my personal insights or book notes, more life-oriented content. From the beginning, I never intended to publish anything related to technical tinkering there, and I even decided not to include any images, keeping it purely text-based.

But sometimes I still have the urge to share some tinkering records. I tried using GitHub [Discussions](https://github.com/noobnooc/noobnooc/discussions) to publish, which is honestly sufficient and has all the necessary features. However, the tinkering gene always gets restless, and I can't help but find flaws in this approach, such as the lack of customization, the deep entry point making it hard to discover, and the inability to drive traffic to my own website, among others.

Naturally, this blog should be integrated into my personal homepage and should have a high degree of customization capability. And since I'm moving away from GitHub, I must add some features that weren't there before, so I implemented multilingual translation. Now, if you go back to the title of this article, you'll see language switch links for English and Chinese below.

# Why Use a Static Blog
## Why Use a Static Blog

Nowadays, there are generally two choices for setting up a blog: dynamic blogs represented by Wordpress, Ghost, Typeecho, etc., and static blogs represented by Hugo, Hexo, Jekyll, etc. A clear distinction between static and dynamic blogs is whether they use a database. If there's a database, it's a dynamic blog; if not, it's a static blog.

Expand All @@ -36,7 +36,7 @@ The advantages of static blogs are purity (because they're all in file form) and

My personal homepage is already hosted on Cloudflare Pages, and I don't want to introduce a behemoth like a database for now. Plus, I'm not too concerned about interactivity. So I chose a static blog without hesitation.

# Why Choose Velite
## Why Choose Velite

[Velite](https://github.com/zce/velite) is an open-source JavaScript content conversion tool that can convert Markdown/MDX, YAML, and other files into type-safe JavaScript data. For example, to implement a static blog, you can use it to convert Markdown format files into data, and then display it in your code. It might seem a bit roundabout, why not use ready-made solutions like [Hugo](https://gohugo.io) or [Hexo](https://hexo.io/index.html), which directly generate static websites from Markdown?

Expand All @@ -60,7 +60,7 @@ Velite uses Zod to provide data type checking, which can greatly ensure type saf

So based on these reasons, I ultimately chose to use Velite to integrate blog functionality into my personal homepage.

# Getting Started with Velite
## Getting Started with Velite

Velite has detailed usage documentation on its [official website](https://velite.js.org/guide/quick-start). If you want to carefully study what it can be used for and some specific details, you can go directly to the official website to check. Here, I'll just briefly introduce the process of integrating a static blog in a Next.js project using Velite.

Expand All @@ -73,7 +73,7 @@ The workflow of Velite is:
- Output the processed results to the directory set by `output` in the configuration file
- In the Next.js project, directly import the results processed by Velite, and then you can start various operations

## Installing Velite
### Installing Velite

First, we need to install Velite before we can properly import Velite-related configurations. Open the terminal and execute the following command:

Expand All @@ -82,7 +82,7 @@ First, we need to install Velite before we can properly import Velite-related co
npm install velite
```

## Adding Necessary Configuration
### Adding Necessary Configuration

As mentioned above, we will use the `velite.config.ts` configuration file to tell Velite how to work. So let's create a file named `velite.config.ts` in the project root directory and fill it in with the following content:

Expand Down Expand Up @@ -133,12 +133,12 @@ To achieve this operation, we can add Velite's processing logic in `next.config.
```js
// next.config.mjs

const isDev = process.argv.indexOf('dev') !== -1
const isBuild = process.argv.indexOf('build') !== -1
const isDev = process.argv.indexOf("dev") !== -1;
const isBuild = process.argv.indexOf("build") !== -1;
if (!process.env.VELITE_STARTED && (isDev || isBuild)) {
process.env.VELITE_STARTED = '1'
const { build } = await import('velite')
await build({ watch: isDev, clean: !isDev })
process.env.VELITE_STARTED = "1";
const { build } = await import("velite");
await build({ watch: isDev, clean: !isDev });
}

/** @type {import('next').NextConfig} */
Expand Down Expand Up @@ -172,7 +172,7 @@ If our project structure is deep, to avoid the situation of `import {posts} from

After adding the above configuration, I can easily use `import {posts} from '@/velite'` to import the processed files.

## Define file processing methods
### Define file processing methods

So far, we have completed all the necessary configurations for Velite, but we still need to tell Velite which files to process and how to process them.

Expand Down Expand Up @@ -249,7 +249,7 @@ Hello! This is my first article on my blog.

Now we can directly import our category information and article content from the `.velite` directory.

# Using Velite's generated data in Next.js
## Using Velite's generated data in Next.js

After the above settings, we can directly import the data built by Velite in the `.velite` directory, and then use it in the page as needed.

Expand Down
Loading

0 comments on commit 8eec98b

Please sign in to comment.