Commit 5b28b20a authored by fisherdaddy's avatar fisherdaddy

chore: 优化 SEO

parent b113252d
node_modules/
package-lock.json
dist/
src/.DS_Store
\ No newline at end of file
src/.DS_Store
!robots.txt
\ No newline at end of file
......@@ -6,7 +6,7 @@
<meta name="description" content="AI工具箱 - 您的智能助手集合" />
<title>AI工具箱 | 智能助手集合</title>
<link rel="stylesheet" href="/src/styles/main.css" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="icon" href="/public/favicon.ico" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-GSZMX418JL"></script>
......
User-agent: *
Disallow:
Sitemap: https://fishersama.com/sitemap.xml
\ No newline at end of file
import React from 'react';
import React, { Suspense, lazy } from 'react';
import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import JsonFormatter from './components/JsonFormatter';
import Header from './components/Header';
import Footer from './components/Footer';
import TextToImage from './components/TextToImage';
import UrlDecode from './components/UrlDecode';
import NotFound from './pages/NotFound';
function App(op) {
const JsonFormatter = lazy(() => import('./components/JsonFormatter'));
const TextToImage = lazy(() => import('./components/TextToImage'));
const UrlDecode = lazy(() => import('./components/UrlDecode'));
function App() {
return (
<div className="app-container">
<Header />
<div className="content-wrapper">
<main>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/text2image" element={<TextToImage />} />
<Route path="/json-formatter" element={<JsonFormatter />} />
<Route path="/url-decode" element={<UrlDecode />} />
</Routes>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/text2image" element={<TextToImage />} />
<Route path="/json-formatter" element={<JsonFormatter />} />
<Route path="/url-decode" element={<UrlDecode />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Suspense>
</main>
</div>
<Footer />
......
// src/components/SEO.jsx
import React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from '../js/i18n';
function SEO({ title, description, lang = 'en', meta = [] }) {
const { t } = useTranslation();
const defaultTitle = t('title');
const defaultDescription = t('description'); // 确保在i18n配置中添加'description'
const languages = ['en', 'zh', 'ja', 'ko'];
const hostname = 'https://fishersama.com'; // 替换为您的网站域名
const links = languages.map((language) => ({
rel: 'alternate',
hrefLang: language,
href: `${hostname}/${language === 'en' ? '' : language}`,
}));
const structuredData = {
"@context": "https://schema.org",
"@type": "WebSite",
"name": defaultTitle,
"url": "https://fishersama.com/", // 请替换为您的网站URL
"description": defaultDescription,
"potentialAction": {
"@type": "SearchAction",
"target": "https://fishersama.com/search?q={search_term}",
"query-input": "required name=search_term"
}
};
return (
<Helmet
htmlAttributes={{
lang,
}}
title={title || defaultTitle}
titleTemplate={`%s | ${defaultTitle}`}
meta={[
{
name: 'description',
content: description || defaultDescription,
},
{
property: 'og:title',
content: title || defaultTitle,
},
{
property: 'og:description',
content: description || defaultDescription,
},
{
property: 'og:type',
content: 'website',
},
{
name: 'twitter:card',
content: 'summary',
},
{
name: 'twitter:title',
content: title || defaultTitle,
},
{
name: 'twitter:description',
content: description || defaultDescription,
},
// 可以根据需要添加更多元数据
].concat(meta)}
link={links}
>
<script type="application/ld+json">
{JSON.stringify(structuredData)}
</script>
</Helmet>
);
}
export default SEO;
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { useState, useEffect } from 'react';
const i18n = {
en: {
title: 'AI Toolbox',
description: 'Your one-stop solution for various AI tools.',
slogan: 'Your collection of intelligent assistants, solving various AI needs in one place.',
tools: {
text2image: {
......@@ -28,9 +29,15 @@ const i18n = {
copiedMessage: 'Copied'
},
},
notFound: {
title: '404 - Page Not Found',
description: 'Sorry, the page you are looking for does not exist.',
back_home: 'Back to Home'
}
},
zh: {
title: 'AI 工具箱',
description: '一站式解决各种AI工具需求。',
slogan: '您的智能助手集合,一站式解决各种 AI 需求。',
tools: {
text2image: {
......@@ -56,9 +63,15 @@ const i18n = {
copiedMessage: '已复制'
},
},
notFound: {
title: '404 - 页面未找到',
description: '抱歉,您访问的页面不存在。',
back_home: '返回首页'
}
},
ja: {
title: 'AIツールボックス',
description: 'あなたのインテリジェントアシスタントコレクション、様々なAIニーズを一箇所で解決します。',
slogan: 'あなたのインテリジェントアシスタントコレクション、様々なAIニーズを一箇所で解決します。',
tools: {
text2image: {
......@@ -84,9 +97,15 @@ const i18n = {
copiedMessage: 'コピーしました'
},
},
notFound: {
title: '404 - ページが見つかりません',
description: '申し訳ありませんが、お探しのページは存在しません。',
back_home: 'ホームに戻る'
}
},
ko: {
title: 'AI 도구 상자',
description: '당신의 지능형 어시스턴트 컬렉션, 다양한 AI 요구 사항을 한 곳에서 해결합니다.',
slogan: '당신의 지능형 어시스턴트 컬렉션, 다양한 AI 요구 사항을 한 곳에서 해결합니다.',
tools: {
text2image: {
......@@ -112,6 +131,11 @@ const i18n = {
copiedMessage: '복사됨'
},
},
notFound: {
title: '404 - 페이지를 찾을 수 없습니다',
description: '죄송합니다. 찾고 있는 페이지가 존재하지 않습니다.',
back_home: '홈으로 돌아가기'
}
},
};
......
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO';
const tools = [
{ id: 'text2image', icon: 'fa-image', path: '/text2image' },
......@@ -9,28 +9,35 @@ const tools = [
{ id: 'urlDecode', icon: 'fa-decode', path: '/url-decode' },
];
function Home() {
const Home = () => {
const { t } = useTranslation();
return (
<div>
<section className="hero">
{/* 添加 logo */}
<h1>{t('title')}</h1>
<p className="slogan">{t('slogan')}</p>
</section>
<section className="tools-section">
<div className="tools-grid">
{tools.map(tool => (
<Link to={tool.path} key={tool.id} className="tool-card">
<i className={`fas ${tool.icon} tool-icon`}></i>
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</Link>
))}
</div>
</section>
</div>
<>
<SEO
title={t('title')}
description={t('slogan')}
/>
<main>
<section className="hero">
<h1>{t('title')}</h1>
<p className="slogan">{t('slogan')}</p>
</section>
<section className="tools-section">
<h2>工具</h2>
<div className="tools-grid">
{tools.map(tool => (
<Link to={tool.path} key={tool.id} className="tool-card">
<i className={`fas ${tool.icon} tool-icon`}></i>
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</Link>
))}
</div>
</section>
</main>
</>
);
}
};
export default Home;
// src/pages/NotFound.jsx
import React from 'react';
import { Link } from 'react-router-dom';
import SEO from '../components/SEO';
import { useTranslation } from '../js/i18n';
const NotFound = () => {
const { t } = useTranslation(); // 使用 useTranslation 钩子
return (
<>
<SEO
title={t('notFound.title')}
description={t('notFound.description')}
/>
<main>
<h1>{t('notFound.title')}</h1>
<p>{t('notFound.description')}</p>
<Link to="/">{t('notFound.back_home')}</Link>
</main>
</>
);
};
export default NotFound;
\ No newline at end of file
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite';
import sitemap from 'vite-plugin-sitemap';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [
sitemap({
// plugin options
}),
],
server: {
port: 3000
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment