fix: fix build error for intl messages

This commit is contained in:
javayhu 2025-04-05 14:47:19 +08:00
parent 43d6b2cbf7
commit f380ba4484
8 changed files with 53 additions and 96 deletions

View File

@ -1,46 +0,0 @@
You are an expert in TypeScript, Node.js, Next.js with the app router, React, Shadcn/ui, Magic UI, Tailwind, Better Auth, Drizzle ORM, Content Collections, and Vercel AI SDK.
General Principles
- Write clean, concise, and well-commented TypeScript code
- Favor functional and declarative programming patterns over object-oriented approaches
- Prioritize code reuse and modularization over duplication
Naming and Conventions
- Use PascalCase for class names and type definitions
- Utilize camelCase for variables, functions, and methods
- Employ kebab-case for file and directory names
- Reserve UPPERCASE for environment variables and constants
- Avoid magic numbers by defining constants with meaningful names
- Start each function name with a verb to indicate its purpose
TypeScript Usage
- Leverage TypeScript for all code
- Prefer types over interfaces
- Favor functional components over class components
Code Organization
- Structure files logically, grouping related components, helpers, types, and static content
- Prefer named exports for components over default exports
- Favor small, single-purpose components over large, monolithic ones
- Separate concerns between presentational and container components
UI and Styling
- Utilize Shadcn/ui, Radix, and Tailwind for building consistent and accessible UI components
- Implement responsive design using Tailwind CSS with a mobile-first approach
- Use Magic UI for advanced components and animations
- Use Lucide for icons
Data Management
- Interact with the database using Drizzle ORM
- Leverage Drizzle's generated types
Next.js and React
- Minimize the use of `use client`, `useEffect`, and `setState`
- Favor React Server Components (RSC) whenever possible
- Wrap client-side components in `Suspense` with a fallback
- Implement dynamic loading for non-critical components
- Use server actions for mutations instead of route handlers
Error Handling and Logging
- Implement robust error handling and logging mechanisms
- Provide clear and user-friendly error messages to the end-users

View File

@ -1,5 +1,5 @@
import { DEFAULT_LOCALE, LOCALES } from "@/i18n/routing";
import { defineCollection, defineConfig, z } from "@content-collections/core";
import { defineCollection, defineConfig } from "@content-collections/core";
import {
createDocSchema,
createMetaSchema,
@ -50,7 +50,7 @@ const metas = defineCollection({
function extractLocaleAndBase(fileName: string): { locale: string; base: string } {
// Split filename into parts
const parts = fileName.split('.');
if (parts.length === 1) {
// Simple filename without locale: xxx
return { locale: DEFAULT_LOCALE, base: parts[0] };
@ -78,17 +78,18 @@ export const authors = defineCollection({
schema: (z) => ({
slug: z.string(),
name: z.string(),
avatar: z.string()
avatar: z.string(),
locale: z.string().optional().default(DEFAULT_LOCALE)
}),
transform: async (data, context) => {
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`author processed: ${fileName}, locale=${locale}`);
return {
...data,
locale,
@ -110,17 +111,18 @@ export const categories = defineCollection({
schema: (z) => ({
slug: z.string(),
name: z.string(),
description: z.string()
description: z.string(),
locale: z.string().optional().default(DEFAULT_LOCALE)
}),
transform: async (data, context) => {
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`category processed: ${fileName}, locale=${locale}`);
return {
...data,
locale
@ -160,49 +162,42 @@ export const posts = defineCollection({
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`post processed: ${fileName}, base=${base}, locale=${locale}`);
// Find the author by matching slug
// Find the author by matching slug and locale
const blogAuthor = context
.documents(authors)
.find((a) => a.slug === data.author && a.locale === locale) ||
context
.documents(authors)
.find((a) => a.slug === data.author);
// Find categories by matching slug
.find((a) => a.slug === data.author && a.locale === locale);
// Find categories by matching slug and locale
const blogCategories = data.categories.map(categorySlug => {
// Try to find a category with matching slug and locale
const category = context
.documents(categories)
.find(c => c.slug === categorySlug && c.locale === locale) ||
context
.documents(categories)
.find(c => c.slug === categorySlug);
.find(c => c.slug === categorySlug && c.locale === locale);
return category;
}).filter(Boolean); // Remove null values
// Get the collection name (e.g., "blog")
const pathParts = data._meta.path.split(path.sep);
const collectionName = pathParts[pathParts.length - 2];
// Create the slug and slugAsParams
const slug = `/${collectionName}/${base}`;
const slugAsParams = base;
// Calculate estimated reading time
const wordCount = data.content.split(/\s+/).length;
const wordsPerMinute = 200; // average reading speed: 200 words per minute
const estimatedTime = Math.max(Math.ceil(wordCount / wordsPerMinute), 1);
return {
...data,
locale,
@ -245,23 +240,23 @@ export const pages = defineCollection({
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`page processed: ${fileName}, base=${base}, locale=${locale}`);
// Get the collection name (e.g., "pages")
const pathParts = data._meta.path.split(path.sep);
const collectionName = pathParts[pathParts.length - 2];
// Create the slug and slugAsParams
const slug = `/${collectionName}/${base}`;
const slugAsParams = base;
return {
...data,
locale,
@ -302,23 +297,23 @@ export const releases = defineCollection({
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`release processed: ${fileName}, base=${base}, locale=${locale}`);
// Get the collection name (e.g., "release")
const pathParts = data._meta.path.split(path.sep);
const collectionName = pathParts[pathParts.length - 2];
// Create the slug and slugAsParams
const slug = `/${collectionName}/${base}`;
const slugAsParams = base;
return {
...data,
locale,

View File

@ -428,6 +428,10 @@
"title": "Current Plan",
"description": "Your current plan details"
},
"CustomerPortalButton": {
"loading": "Loading...",
"createCustomerPortalFailed": "Failed to open Stripe customer portal"
},
"nextBillingDate": "Next billing date:",
"trialEnds": "Trial ends:",
"manageSubscription": "Manage Subscription",

View File

@ -428,6 +428,10 @@
"title": "当前方案",
"description": "您当前的方案详情"
},
"CustomerPortalButton": {
"loading": "加载中...",
"createCustomerPortalFailed": "打开Stripe客户界面失败"
},
"nextBillingDate": "下次账单日期:",
"trialEnds": "试用结束日期:",
"manageSubscription": "管理订阅",

View File

@ -16,7 +16,7 @@ export default async function BillingLayout({
isCurrentPage: false,
},
{
label: t('items.billing.title'),
label: t('billing.title'),
isCurrentPage: true,
},
];
@ -29,10 +29,10 @@ export default async function BillingLayout({
<div className="max-w-6xl mx-auto space-y-10">
<div>
<h1 className="text-3xl font-bold tracking-tight">
{t('items.billing.title')}
{t('billing.title')}
</h1>
<p className="text-muted-foreground mt-2">
{t('items.billing.description')}
{t('billing.description')}
</p>
</div>

View File

@ -16,7 +16,7 @@ export default async function NotificationsLayout({
isCurrentPage: false,
},
{
label: t('items.notification.title'),
label: t('notification.title'),
isCurrentPage: true,
},
];
@ -29,10 +29,10 @@ export default async function NotificationsLayout({
<div className="max-w-6xl mx-auto space-y-10">
<div>
<h1 className="text-3xl font-bold tracking-tight">
{t('items.notification.title')}
{t('notification.title')}
</h1>
<p className="text-muted-foreground mt-2">
{t('items.notification.description')}
{t('notification.description')}
</p>
</div>

View File

@ -16,7 +16,7 @@ export default async function ProfileLayout({
isCurrentPage: false,
},
{
label: t('items.profile.title'),
label: t('profile.title'),
isCurrentPage: true,
},
];
@ -29,10 +29,10 @@ export default async function ProfileLayout({
<div className="max-w-6xl mx-auto space-y-10">
<div>
<h1 className="text-3xl font-bold tracking-tight">
{t('items.profile.title')}
{t('profile.title')}
</h1>
<p className="text-muted-foreground mt-2">
{t('items.profile.description')}
{t('profile.description')}
</p>
</div>

View File

@ -16,7 +16,7 @@ export default async function SecurityLayout({
isCurrentPage: false,
},
{
label: t('items.security.title'),
label: t('security.title'),
isCurrentPage: true,
},
];
@ -29,10 +29,10 @@ export default async function SecurityLayout({
<div className="max-w-6xl mx-auto space-y-10">
<div>
<h1 className="text-3xl font-bold tracking-tight">
{t('items.security.title')}
{t('security.title')}
</h1>
<p className="text-muted-foreground mt-2">
{t('items.security.description')}
{t('security.description')}
</p>
</div>