添加i18n

This commit is contained in:
2025-12-18 18:32:35 +08:00
parent ebe385a624
commit cd40c603a1
17 changed files with 597 additions and 112 deletions
+11 -8
View File
@@ -1,7 +1,10 @@
import React from 'react';
import Image from 'next/image';
import { useTranslations } from 'next-intl';
const About: React.FC = () => {
const t = useTranslations('About');
return (
<section id="about" className="py-24 bg-feie-cream relative">
<div className="container mx-auto px-6">
@@ -23,32 +26,32 @@ const About: React.FC = () => {
{/* Text Content */}
<div className="w-full md:w-1/2 space-y-8">
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-2">关于我们 • About Us</h2>
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-2">{t('title')} • {t('subtitle')}</h2>
<h3 className="text-4xl md:text-5xl font-serif text-feie-dark leading-tight">
专注技术沉淀 <br />
<span className="italic text-gray-500">成就卓越品质</span>
{t('heading')} <br />
<span className="italic text-gray-500">{t('subheading')}</span>
</h3>
<div className="space-y-6 text-gray-600 font-light leading-relaxed">
<p>
南京市肥鹅信息技术有限公司坐落于历史悠久的六朝古都——南京。我们是一家专注于前沿信息技术研发与应用的高新技术企业。
{t('p1')}
</p>
<p>
自成立以来,我们始终秉承"技术驱动、服务至上"的理念,深耕软件开发、系统集成及数字化转型咨询领域。我们拥有一支经验丰富、富有创造力的技术团队,致力于通过代码改变世界,用智慧连接未来。
{t('p2')}
</p>
<p>
在溧水区这片充满活力的创新热土上,我们正以稳健的步伐,为各行各业的客户提供量身定制的IT解决方案,助力企业在数字经济浪潮中乘风破浪。
{t('p3')}
</p>
</div>
<div className="pt-4 grid grid-cols-2 gap-8">
<div>
<h4 className="text-3xl font-serif text-feie-gold">100+</h4>
<p className="text-sm text-gray-500 uppercase tracking-wider mt-1">成功案例</p>
<p className="text-sm text-gray-500 uppercase tracking-wider mt-1">{t('stats.cases')}</p>
</div>
<div>
<h4 className="text-3xl font-serif text-feie-gold">24/7</h4>
<p className="text-sm text-gray-500 uppercase tracking-wider mt-1">技术支持</p>
<p className="text-sm text-gray-500 uppercase tracking-wider mt-1">{t('stats.support')}</p>
</div>
</div>
</div>
+23 -21
View File
@@ -2,6 +2,7 @@
import React, { useState } from 'react';
import { MapPin, Phone, Mail } from 'lucide-react';
import { useTranslations } from 'next-intl';
interface FormData {
name: string;
@@ -18,6 +19,7 @@ interface FormErrors {
}
const Contact: React.FC = () => {
const t = useTranslations('Contact');
const [formData, setFormData] = useState<FormData>({
name: '',
phone: '',
@@ -33,25 +35,25 @@ const Contact: React.FC = () => {
const newErrors: FormErrors = {};
if (!formData.name.trim()) {
newErrors.name = '请输入姓名';
newErrors.name = t('form.errors.nameRequired');
}
const phoneRegex = /^1[3-9]\d{9}$/;
if (!formData.phone.trim()) {
newErrors.phone = '请输入电话';
newErrors.phone = t('form.errors.phoneRequired');
} else if (!phoneRegex.test(formData.phone)) {
newErrors.phone = '请输入正确的手机号';
newErrors.phone = t('form.errors.phoneInvalid');
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!formData.email.trim()) {
newErrors.email = '请输入邮箱';
newErrors.email = t('form.errors.emailRequired');
} else if (!emailRegex.test(formData.email)) {
newErrors.email = '请输入正确的邮箱地址';
newErrors.email = t('form.errors.emailInvalid');
}
if (!formData.description.trim()) {
newErrors.description = '请描述您的需求';
newErrors.description = t('form.errors.descRequired');
}
setErrors(newErrors);
@@ -91,7 +93,7 @@ const Contact: React.FC = () => {
if (response.ok) {
setSubmitStatus({
type: 'success',
message: data.message || '留言提交成功,我们会尽快与您联系!'
message: data.message || t('form.success')
});
// 清空表单
setFormData({
@@ -129,9 +131,9 @@ const Contact: React.FC = () => {
<div className="space-y-8">
<div>
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-3">联系方式 • Contact</h2>
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-3">{t('title')} • {t('subtitle')}</h2>
<h3 className="text-4xl md:text-5xl font-serif leading-tight">
期待与您的合作 <br/>
{t('heading')} <br/>
<span className="text-feie-white/50">共创价值</span>
</h3>
</div>
@@ -146,8 +148,8 @@ const Contact: React.FC = () => {
<MapPin className="w-6 h-6 text-feie-gold" />
</div>
<div>
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">注册地址</p>
<p className="text-xl font-serif">江苏省南京市溧水区永阳街道中大街81号8幢1630室</p>
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">{t('info.address')}</p>
<p className="text-xl font-serif">{t('info.addressValue')}</p>
</div>
</div>
@@ -156,7 +158,7 @@ const Contact: React.FC = () => {
<Phone className="w-6 h-6 text-feie-gold" />
</div>
<div>
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">联系电话</p>
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">{t('info.phone')}</p>
<p className="text-xl font-serif">19844561014</p>
</div>
</div>
@@ -166,7 +168,7 @@ const Contact: React.FC = () => {
<Mail className="w-6 h-6 text-feie-gold" />
</div>
<div>
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">电子邮箱</p>
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">{t('info.email')}</p>
<a href="mailto:feie9454@gmail.com" className="text-xl font-serif hover:text-feie-gold transition-colors">
feie9454@gmail.com
</a>
@@ -192,7 +194,7 @@ const Contact: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
姓名 <span className="text-red-500">*</span>
{t('form.name')} <span className="text-red-500">*</span>
</label>
<input
type="text"
@@ -202,13 +204,13 @@ const Contact: React.FC = () => {
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors ${
errors.name ? 'border-red-500' : 'border-gray-200'
}`}
placeholder="您的姓名"
placeholder={t('form.name')}
/>
{errors.name && <p className="text-red-500 text-sm mt-1">{errors.name}</p>}
</div>
<div>
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
电话 <span className="text-red-500">*</span>
{t('form.phone')} <span className="text-red-500">*</span>
</label>
<input
type="tel"
@@ -218,14 +220,14 @@ const Contact: React.FC = () => {
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors ${
errors.phone ? 'border-red-500' : 'border-gray-200'
}`}
placeholder="联系电话"
placeholder={t('form.phone')}
/>
{errors.phone && <p className="text-red-500 text-sm mt-1">{errors.phone}</p>}
</div>
</div>
<div>
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
邮箱 <span className="text-red-500">*</span>
{t('form.email')} <span className="text-red-500">*</span>
</label>
<input
type="email"
@@ -241,7 +243,7 @@ const Contact: React.FC = () => {
</div>
<div>
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
需求描述 <span className="text-red-500">*</span>
{t('form.description')} <span className="text-red-500">*</span>
</label>
<textarea
rows={4}
@@ -251,7 +253,7 @@ const Contact: React.FC = () => {
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors resize-none ${
errors.description ? 'border-red-500' : 'border-gray-200'
}`}
placeholder="请简要描述您的需求..."
placeholder={t('form.description')}
></textarea>
{errors.description && <p className="text-red-500 text-sm mt-1">{errors.description}</p>}
</div>
@@ -264,7 +266,7 @@ const Contact: React.FC = () => {
: 'bg-feie-dark text-feie-white hover:bg-feie-gold'
}`}
>
{isSubmitting ? '发送中...' : '发送信息'}
{isSubmitting ? t('form.submitting') : t('form.submit')}
</button>
</form>
</div>
+7 -4
View File
@@ -1,17 +1,20 @@
import React from 'react';
import { useTranslations } from 'next-intl';
const Footer: React.FC = () => {
const t = useTranslations('Footer');
return (
<footer className="bg-feie-dark text-feie-white border-t border-white/10 py-12">
<div className="container mx-auto px-6 flex flex-col md:flex-row justify-between items-center gap-6">
<div className="text-center md:text-left">
<h5 className="font-serif text-xl font-bold mb-2">南京市肥鹅信息技术有限公司</h5>
<p className="text-gray-500 text-sm">© {new Date().getFullYear()} Nanjing Feie Information Technology Co., Ltd. All rights reserved.</p>
<h5 className="font-serif text-xl font-bold mb-2">{t('company')}</h5>
<p className="text-gray-500 text-sm">{t('rights', {year: new Date().getFullYear()})}</p>
</div>
<div className="flex gap-6 text-sm text-gray-400">
<a href="#" className="hover:text-feie-gold transition-colors">隐私政策</a>
<a href="#" className="hover:text-feie-gold transition-colors">服务条款</a>
<a href="#" className="hover:text-feie-gold transition-colors">{t('privacy')}</a>
<a href="#" className="hover:text-feie-gold transition-colors">{t('terms')}</a>
</div>
</div>
</footer>
+9 -5
View File
@@ -4,6 +4,7 @@ import React, { useRef, useMemo } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { Float, Stars } from '@react-three/drei';
import * as THREE from 'three';
import { useTranslations } from 'next-intl';
// ------------------- Three.js Components -------------------
@@ -103,6 +104,9 @@ const Scene = () => {
// ------------------- Main Component -------------------
const Hero: React.FC = () => {
const t = useTranslations('Hero');
const tNav = useTranslations('Navbar');
return (
<section id="hero" className="relative w-full h-screen bg-feie-dark overflow-hidden">
{/* 3D Canvas Layer */}
@@ -122,24 +126,24 @@ const Hero: React.FC = () => {
</span>
</div>
<h1 className="text-5xl md:text-7xl font-serif text-feie-white leading-tight">
数字化未来的 <br />
<span className="text-feie-gold italic">构建者</span>
{t('title')} <br />
<span className="text-feie-gold italic">{t('subtitle')}</span>
</h1>
<p className="text-feie-white/70 text-lg md:text-xl font-light max-w-md leading-relaxed">
南京市肥鹅信息技术有限公司致力于为企业提供卓越的数字化解决方案,以创新技术驱动商业价值。
{t('description')}
</p>
<div className="pt-8 flex gap-4">
<a
href="#services"
className="px-8 py-3 bg-feie-gold text-feie-white font-serif hover:bg-yellow-600 transition-colors duration-300 rounded-sm"
>
探索服务
{t('cta')}
</a>
<a
href="#contact"
className="px-8 py-3 border border-feie-white text-feie-white font-serif hover:bg-feie-white hover:text-feie-dark transition-colors duration-300 rounded-sm"
>
联系我们
{tNav('contact')}
</a>
</div>
</div>
+77 -29
View File
@@ -1,18 +1,34 @@
'use client';
import React, { useState, useEffect } from 'react';
import { Menu, X } from 'lucide-react';
const navItems = [
{ label: '首页', href: '#hero' },
{ label: '关于我们', href: '#about' },
{ label: '核心业务', href: '#services' },
{ label: '联系方式', href: '#contact' },
];
import React, { useState, useEffect, useTransition } from 'react';
import { Menu, X, Globe } from 'lucide-react';
import { useTranslations, useLocale } from 'next-intl';
import { usePathname, useRouter } from '@/i18n/routing';
const Navbar: React.FC = () => {
const t = useTranslations('Navbar');
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const [isPending, startTransition] = useTransition();
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isLangMenuOpen, setIsLangMenuOpen] = useState(false);
const navItems = [
{ label: t('home'), href: '#hero' },
{ label: t('about'), href: '#about' },
{ label: t('services'), href: '#services' },
{ label: t('contact'), href: '#contact' },
];
const onSelectChange = (nextLocale: string) => {
startTransition(() => {
router.replace(pathname, {locale: nextLocale});
});
setIsLangMenuOpen(false);
};
useEffect(() => {
const handleScroll = () => {
@@ -63,29 +79,61 @@ const Navbar: React.FC = () => {
{item.label}
</a>
))}
<a
href="#contact"
className={`px-5 py-2 border font-serif text-sm transition-all duration-300 ${
isScrolled
? 'border-feie-dark text-feie-dark hover:bg-feie-dark hover:text-feie-white'
: 'border-feie-white text-feie-white hover:bg-feie-white hover:text-feie-dark'
}`}
>
合作咨询
</a>
{/* Language Switcher */}
<div className="relative">
<button
onClick={() => setIsLangMenuOpen(!isLangMenuOpen)}
className={`flex items-center gap-1 font-serif text-sm tracking-widest uppercase hover:text-feie-gold transition-colors duration-300 ${
isScrolled ? 'text-feie-dark' : 'text-feie-white/90'
}`}
>
<Globe size={16} />
<span>{locale.toUpperCase()}</span>
</button>
{isLangMenuOpen && (
<div className="absolute top-full right-0 mt-2 w-24 bg-white rounded-md shadow-lg py-1 z-50">
{['zh', 'en', 'ja'].map((l) => (
<button
key={l}
onClick={() => onSelectChange(l)}
className={`block w-full text-left px-4 py-2 text-sm hover:bg-gray-100 ${locale === l ? 'text-feie-gold font-bold' : 'text-gray-700'}`}
>
{l === 'zh' ? '中文' : l === 'en' ? 'English' : '日本語'}
</button>
))}
</div>
)}
</div>
</div>
{/* Mobile Toggle */}
<button
className="md:hidden focus:outline-none"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? (
<X className={isScrolled || isMobileMenuOpen ? 'text-feie-dark' : 'text-feie-white'} />
) : (
<Menu className={isScrolled || isMobileMenuOpen ? 'text-feie-dark' : 'text-feie-white'} />
)}
</button>
<div className="md:hidden flex items-center gap-4">
{/* Mobile Language Switcher */}
<button
onClick={() => {
const nextLocale = locale === 'zh' ? 'en' : locale === 'en' ? 'ja' : 'zh';
onSelectChange(nextLocale);
}}
className={`flex items-center gap-1 font-serif text-sm tracking-widest uppercase ${
isScrolled || isMobileMenuOpen ? 'text-feie-dark' : 'text-feie-white'
}`}
>
{locale.toUpperCase()}
</button>
<button
className="focus:outline-none"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? (
<X className={isScrolled || isMobileMenuOpen ? 'text-feie-dark' : 'text-feie-white'} />
) : (
<Menu className={isScrolled || isMobileMenuOpen ? 'text-feie-dark' : 'text-feie-white'} />
)}
</button>
</div>
</div>
{/* Mobile Menu */}
+38 -35
View File
@@ -1,48 +1,51 @@
import React from 'react';
import { Monitor, Server, Code, Database, Globe, Shield } from 'lucide-react';
import { ServiceItem } from '../types';
const services: ServiceItem[] = [
{
title: '定制软件开发',
description: '根据企业独特需求,打造专属的Web应用、移动端APP及桌面软件,优化业务流程。',
icon: Code,
},
{
title: '企业数字化转型',
description: '提供全方位的数字化咨询与落地实施方案,助力传统企业实现智能化升级。',
icon: Monitor,
},
{
title: '系统集成服务',
description: '打破信息孤岛,实现异构系统间的数据互通与功能协同,提升整体运营效率。',
icon: Server,
},
{
title: '大数据分析',
description: '挖掘数据价值,提供可视化报表与智能决策支持,让数据成为企业的核心资产。',
icon: Database,
},
{
title: '云服务与部署',
description: '提供安全可靠的云端架构设计、迁移及运维服务,保障业务的高可用性。',
icon: Globe,
},
{
title: '信息安全保障',
description: '构建多层次的安全防护体系,保护企业核心数据资产不受威胁。',
icon: Shield,
},
];
import { useTranslations } from 'next-intl';
const Services: React.FC = () => {
const t = useTranslations('Services');
const services: ServiceItem[] = [
{
title: t('items.customDev.title'),
description: t('items.customDev.desc'),
icon: Code,
},
{
title: t('items.digitalTrans.title'),
description: t('items.digitalTrans.desc'),
icon: Monitor,
},
{
title: t('items.systemIntegration.title'),
description: t('items.systemIntegration.desc'),
icon: Server,
},
{
title: t('items.bigData.title'),
description: t('items.bigData.desc'),
icon: Database,
},
{
title: t('items.cloudService.title'),
description: t('items.cloudService.desc'),
icon: Globe,
},
{
title: t('items.security.title'),
description: t('items.security.desc'),
icon: Shield,
},
];
return (
<section id="services" className="py-24 bg-white">
<div className="container mx-auto px-6">
<div className="text-center max-w-3xl mx-auto mb-16">
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-3">核心业务 • Services</h2>
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-3">{t('title')} • {t('subtitle')}</h2>
<h3 className="text-3xl md:text-4xl font-serif text-feie-dark mb-6">
全方位技术解决方案
{t('heading')}
</h3>
<div className="w-16 h-1 bg-feie-dark mx-auto"></div>
</div>