feat: internationalize marketing configuration and components
- Add translation support for menu links, footer links, and avatar links - Create `createTranslator` utility function for dynamic translations - Update marketing configuration to use translation-based menu and link generation - Modify navbar, footer, and user button components to use translated content - Add new "Common" translation keys for login and signup buttons - Refactor marketing configuration to dynamically generate links with translations - Improve internationalization across marketing-related components
This commit is contained in:
parent
f4e51757e5
commit
8d8b66a7fe
117
messages/en.json
117
messages/en.json
@ -1,4 +1,8 @@
|
||||
{
|
||||
"Common": {
|
||||
"login": "Log in",
|
||||
"signUp": "Sign up"
|
||||
},
|
||||
"HomePage": {
|
||||
"title": "next-intl example"
|
||||
},
|
||||
@ -19,7 +23,7 @@
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"signIn": "Sign In",
|
||||
"signUp": "Don't have an account? Sign up",
|
||||
"signUpHint": "Don't have an account? Sign up",
|
||||
"forgotPassword": "Forgot Password?",
|
||||
"signInWithGoogle": "Sign In with Google",
|
||||
"signInWithGitHub": "Sign In with GitHub",
|
||||
@ -35,7 +39,7 @@
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"signUp": "Sign Up",
|
||||
"signIn": "Already have an account? Sign in",
|
||||
"signInHint": "Already have an account? Sign in",
|
||||
"checkEmail": "Please check your email inbox"
|
||||
},
|
||||
"forgotPassword": {
|
||||
@ -68,6 +72,115 @@
|
||||
"noPostsFound": "No posts found",
|
||||
"allPosts": "All Posts"
|
||||
},
|
||||
"Marketing": {
|
||||
"menu": {
|
||||
"features": {
|
||||
"title": "Features"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "Pricing"
|
||||
},
|
||||
"blog": {
|
||||
"title": "Blog"
|
||||
},
|
||||
"ai": {
|
||||
"title": "AI",
|
||||
"items": {
|
||||
"text": {
|
||||
"title": "AI Text",
|
||||
"description": "Show how to use AI to write stunning text"
|
||||
},
|
||||
"image": {
|
||||
"title": "AI Image",
|
||||
"description": "Show how to use AI to generate beautiful images"
|
||||
},
|
||||
"video": {
|
||||
"title": "AI Video",
|
||||
"description": "Show how to use AI to generate amazing videos"
|
||||
},
|
||||
"audio": {
|
||||
"title": "AI Audio",
|
||||
"description": "Show how to use AI to generate wonderful audio"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
"title": "Pages",
|
||||
"items": {
|
||||
"about": {
|
||||
"title": "About",
|
||||
"description": "Learn more about our company, mission, and values"
|
||||
},
|
||||
"contact": {
|
||||
"title": "Contact",
|
||||
"description": "Get in touch with our team for support or inquiries"
|
||||
},
|
||||
"waitlist": {
|
||||
"title": "Waitlist",
|
||||
"description": "Join our waitlist for latest news and updates"
|
||||
},
|
||||
"changelog": {
|
||||
"title": "Changelog",
|
||||
"description": "See the latest updates to our products"
|
||||
},
|
||||
"roadmap": {
|
||||
"title": "Roadmap",
|
||||
"description": "Explore our future plans and upcoming features"
|
||||
},
|
||||
"cookiePolicy": {
|
||||
"title": "Cookie Policy",
|
||||
"description": "Information about how we use cookies on our website"
|
||||
},
|
||||
"privacyPolicy": {
|
||||
"title": "Privacy Policy",
|
||||
"description": "Details about how we protect and handle your data"
|
||||
},
|
||||
"termsOfService": {
|
||||
"title": "Terms of Service",
|
||||
"description": "The legal agreement between you and our company"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"product": {
|
||||
"title": "Product",
|
||||
"items": {
|
||||
"features": "Features",
|
||||
"pricing": "Pricing",
|
||||
"faq": "FAQ"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"title": "Resources",
|
||||
"items": {
|
||||
"blog": "Blog",
|
||||
"changelog": "Changelog",
|
||||
"roadmap": "Roadmap"
|
||||
}
|
||||
},
|
||||
"company": {
|
||||
"title": "Company",
|
||||
"items": {
|
||||
"about": "About",
|
||||
"contact": "Contact",
|
||||
"waitlist": "Waitlist"
|
||||
}
|
||||
},
|
||||
"legal": {
|
||||
"title": "Legal",
|
||||
"items": {
|
||||
"cookiePolicy": "Cookie Policy",
|
||||
"privacyPolicy": "Privacy Policy",
|
||||
"termsOfService": "Terms of Service"
|
||||
}
|
||||
}
|
||||
},
|
||||
"avatar": {
|
||||
"dashboard": "Dashboard",
|
||||
"settings": "Settings"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"common": {
|
||||
"team": "{name} Team",
|
||||
|
120
messages/zh.json
120
messages/zh.json
@ -1,4 +1,8 @@
|
||||
{
|
||||
"Common": {
|
||||
"login": "登录",
|
||||
"signUp": "注册"
|
||||
},
|
||||
"HomePage": {
|
||||
"title": "next-intl 示例"
|
||||
},
|
||||
@ -19,7 +23,7 @@
|
||||
"email": "邮箱",
|
||||
"password": "密码",
|
||||
"signIn": "登录",
|
||||
"signUp": "没有账号?注册",
|
||||
"signUpHint": "没有账号?注册",
|
||||
"forgotPassword": "忘记密码?",
|
||||
"signInWithGoogle": "使用 Google 登录",
|
||||
"signInWithGitHub": "使用 GitHub 登录",
|
||||
@ -35,7 +39,7 @@
|
||||
"email": "邮箱",
|
||||
"password": "密码",
|
||||
"signUp": "注册",
|
||||
"signIn": "已经有账号?登录",
|
||||
"signInHint": "已经有账号?登录",
|
||||
"checkEmail": "请检查您的邮箱"
|
||||
},
|
||||
"forgotPassword": {
|
||||
@ -68,10 +72,120 @@
|
||||
"noPostsFound": "没有找到文章",
|
||||
"allPosts": "全部文章"
|
||||
},
|
||||
"Marketing": {
|
||||
"menu": {
|
||||
"features": {
|
||||
"title": "功能"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "价格"
|
||||
},
|
||||
"blog": {
|
||||
"title": "博客"
|
||||
},
|
||||
"ai": {
|
||||
"title": "人工智能",
|
||||
"items": {
|
||||
"text": {
|
||||
"title": "AI 文本",
|
||||
"description": "展示如何使用 AI 生成精彩文本"
|
||||
},
|
||||
"image": {
|
||||
"title": "AI 图像",
|
||||
"description": "展示如何使用 AI 生成精美图像"
|
||||
},
|
||||
"video": {
|
||||
"title": "AI 视频",
|
||||
"description": "展示如何使用 AI 生成惊人视频"
|
||||
},
|
||||
"audio": {
|
||||
"title": "AI 音频",
|
||||
"description": "展示如何使用 AI 生成动听音频"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
"title": "页面",
|
||||
"items": {
|
||||
"about": {
|
||||
"title": "关于我们",
|
||||
"description": "了解更多关于我们的公司、使命和价值观"
|
||||
},
|
||||
"contact": {
|
||||
"title": "联系我们",
|
||||
"description": "与我们的团队联系,以获取支持或咨询"
|
||||
},
|
||||
"waitlist": {
|
||||
"title": "邮件列表",
|
||||
"description": "加入我们的邮件列表,获取最新消息和更新"
|
||||
},
|
||||
"changelog": {
|
||||
"title": "更新日志",
|
||||
"description": "查看我们产品的更新历史,查看最新动态"
|
||||
},
|
||||
"roadmap": {
|
||||
"title": "路线图",
|
||||
"description": "探索我们的未来计划和即将推出的功能"
|
||||
},
|
||||
"cookiePolicy": {
|
||||
"title": "Cookie 政策",
|
||||
"description": "关于我们如何在网站上使用 Cookie 的信息"
|
||||
},
|
||||
"privacyPolicy": {
|
||||
"title": "隐私政策",
|
||||
"description": "关于我们将如何保护和处理您在网站上的数据"
|
||||
},
|
||||
"termsOfService": {
|
||||
"title": "服务条款",
|
||||
"description": "关于您与我们公司之间的法律协议和条款"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"product": {
|
||||
"title": "产品",
|
||||
"items": {
|
||||
"features": "功能",
|
||||
"pricing": "价格",
|
||||
"faq": "常见问题"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"title": "资源",
|
||||
"items": {
|
||||
"blog": "博客",
|
||||
"changelog": "更新日志",
|
||||
"roadmap": "路线图"
|
||||
}
|
||||
},
|
||||
"company": {
|
||||
"title": "公司",
|
||||
"items": {
|
||||
"about": "关于我们",
|
||||
"contact": "联系我们",
|
||||
"waitlist": "邮件列表"
|
||||
}
|
||||
},
|
||||
"legal": {
|
||||
"title": "法律",
|
||||
"items": {
|
||||
"cookiePolicy": "Cookie 政策",
|
||||
"privacyPolicy": "隐私政策",
|
||||
"termsOfService": "服务条款"
|
||||
}
|
||||
}
|
||||
},
|
||||
"avatar": {
|
||||
"dashboard": "工作台",
|
||||
"settings": "设置",
|
||||
"logout": "退出"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"common": {
|
||||
"team": "{name} 团队",
|
||||
"copyright": "版权所有 {year}"
|
||||
"copyright": "版权所有 {year} All Rights Reserved."
|
||||
},
|
||||
"verifyEmail": {
|
||||
"title": "你好, {name}.",
|
||||
|
@ -27,19 +27,19 @@ export default async function HomePage(props: HomePageProps) {
|
||||
|
||||
{/* <Features /> */}
|
||||
|
||||
<FeaturesSection />
|
||||
{/* <FeaturesSection /> */}
|
||||
|
||||
<ContentSection />
|
||||
{/* <ContentSection /> */}
|
||||
|
||||
<Pricing />
|
||||
|
||||
<FAQs />
|
||||
{/* <FAQs />
|
||||
|
||||
<WallOfLoveSection />
|
||||
<WallOfLoveSection /> */}
|
||||
|
||||
<StatsSection />
|
||||
{/* <StatsSection />
|
||||
|
||||
<CallToAction />
|
||||
<CallToAction /> */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -78,7 +78,7 @@ export const LoginForm = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<AuthCard
|
||||
headerLabel={t("welcomeBack")}
|
||||
bottomButtonLabel={t("signUp")}
|
||||
bottomButtonLabel={t("signUpHint")}
|
||||
bottomButtonHref={`${Routes.Register}`}
|
||||
showSocialLoginButton
|
||||
className={cn("border-none", className)}
|
||||
|
@ -79,7 +79,7 @@ export const RegisterForm = () => {
|
||||
return (
|
||||
<AuthCard
|
||||
headerLabel={t("createAccount")}
|
||||
bottomButtonLabel={t("signIn")}
|
||||
bottomButtonLabel={t("signInHint")}
|
||||
bottomButtonHref={`${Routes.Login}`}
|
||||
showSocialLoginButton
|
||||
className="border-none"
|
||||
|
@ -4,13 +4,18 @@ import Container from "@/components/container";
|
||||
import { ThemeSwitcherHorizontal } from "@/components/layout/theme-switcher-horizontal";
|
||||
import { Logo } from "@/components/logo";
|
||||
import BuiltWithButton from "@/components/shared/built-with-button";
|
||||
import { FOOTER_LINKS, SOCIAL_LINKS } from "@/config/marketing";
|
||||
import { createTranslator, getFooterLinks, SOCIAL_LINKS } from "@/config/marketing";
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { LocaleLink } from "@/i18n/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "next-intl";
|
||||
import React from "react";
|
||||
|
||||
export function Footer({ className }: React.HTMLAttributes<HTMLElement>) {
|
||||
const t = useTranslations();
|
||||
const translator = createTranslator(t);
|
||||
const footerLinks = getFooterLinks(translator);
|
||||
|
||||
return (
|
||||
<footer className={cn("border-t", className)}>
|
||||
<Container className="px-4">
|
||||
@ -53,7 +58,7 @@ export function Footer({ className }: React.HTMLAttributes<HTMLElement>) {
|
||||
</div>
|
||||
|
||||
{/* footer links */}
|
||||
{FOOTER_LINKS.map((section) => (
|
||||
{footerLinks.map((section) => (
|
||||
<div
|
||||
key={section.title}
|
||||
className="col-span-1 md:col-span-1 items-start"
|
||||
@ -85,7 +90,7 @@ export function Footer({ className }: React.HTMLAttributes<HTMLElement>) {
|
||||
<div className="border-t py-8">
|
||||
<Container className="px-4 flex items-center justify-between">
|
||||
<span className="text-muted-foreground text-sm">
|
||||
© {new Date().getFullYear()} {siteConfig.name}. All Rights Reserved.
|
||||
© {new Date().getFullYear()} {siteConfig.name} All Rights Reserved.
|
||||
</span>
|
||||
|
||||
<ThemeSwitcherHorizontal />
|
||||
|
@ -9,14 +9,14 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger
|
||||
} from '@/components/ui/collapsible';
|
||||
import { MENU_LINKS } from '@/config/marketing';
|
||||
import { createTranslator, getMenuLinks } from '@/config/marketing';
|
||||
import { siteConfig } from '@/config/site';
|
||||
import { LocaleLink } from '@/i18n/navigation';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Routes } from '@/routes';
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Portal } from '@radix-ui/react-portal';
|
||||
import { ArrowUpRightIcon, ChevronDownIcon, ChevronUpIcon, MenuIcon, XIcon } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import * as React from 'react';
|
||||
import { RemoveScroll } from 'react-remove-scroll';
|
||||
@ -107,6 +107,10 @@ interface MainMobileMenuProps {
|
||||
|
||||
function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
|
||||
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
|
||||
const t = useTranslations();
|
||||
const translator = createTranslator(t);
|
||||
const menuLinks = getMenuLinks(translator);
|
||||
const commonTranslations = useTranslations("Common");
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 mt-[72px] overflow-y-auto bg-background backdrop-blur-md animate-in fade-in-0">
|
||||
<div className="flex size-full flex-col items-start space-y-4 p-4">
|
||||
@ -123,7 +127,7 @@ function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
|
||||
'w-full'
|
||||
)}
|
||||
>
|
||||
Log in
|
||||
{commonTranslations("login")}
|
||||
</LocaleLink>
|
||||
<LocaleLink
|
||||
href={Routes.Register}
|
||||
@ -136,13 +140,13 @@ function MainMobileMenu({ onLinkClicked }: MainMobileMenuProps) {
|
||||
)}
|
||||
onClick={onLinkClicked}
|
||||
>
|
||||
Sign up
|
||||
{commonTranslations("signUp")}
|
||||
</LocaleLink>
|
||||
</div>
|
||||
|
||||
{/* main menu */}
|
||||
<ul className="w-full">
|
||||
{MENU_LINKS.map((item) => (
|
||||
{menuLinks.map((item) => (
|
||||
<li key={item.title} className="py-2">
|
||||
{item.items ? (
|
||||
<Collapsible
|
||||
|
@ -17,13 +17,14 @@ import {
|
||||
NavigationMenuTrigger,
|
||||
navigationMenuTriggerStyle
|
||||
} from '@/components/ui/navigation-menu';
|
||||
import { MENU_LINKS } from '@/config/marketing';
|
||||
import { createTranslator, getMenuLinks } from '@/config/marketing';
|
||||
import { siteConfig } from '@/config/site';
|
||||
import { useScroll } from "@/hooks/use-scroll";
|
||||
import { LocaleLink } from '@/i18n/navigation';
|
||||
import { authClient } from '@/lib/auth-client';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Routes } from '@/routes';
|
||||
import { useTranslations } from "next-intl";
|
||||
import { ArrowUpRightIcon } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
@ -41,6 +42,10 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
const scrolled = useScroll(50);
|
||||
const { data: session, error } = authClient.useSession();
|
||||
const user = session?.user;
|
||||
const t = useTranslations();
|
||||
const translator = createTranslator(t);
|
||||
const menuLinks = getMenuLinks(translator);
|
||||
const commonTranslations = useTranslations("Common");
|
||||
// console.log(`Navbar, user:`, user);
|
||||
|
||||
const pathname = usePathname();
|
||||
@ -69,13 +74,13 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
<div className="flex-1 flex items-center justify-center space-x-2">
|
||||
<NavigationMenu className="relative">
|
||||
<NavigationMenuList className="flex items-center">
|
||||
{MENU_LINKS.map((item, index) =>
|
||||
{menuLinks.map((item, index) =>
|
||||
item.items ? (
|
||||
<NavigationMenuItem key={index} className="relative">
|
||||
<NavigationMenuTrigger
|
||||
data-active={
|
||||
item.items.some((subItem) =>
|
||||
pathname.startsWith(subItem.href)
|
||||
subItem.href && pathname.startsWith(subItem.href)
|
||||
) ? "true" : undefined
|
||||
}
|
||||
className={cn(
|
||||
@ -90,7 +95,7 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
{item.items.map((subItem, subIndex) => (
|
||||
<li key={subIndex}>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
<LocaleLink
|
||||
href={subItem.href || '#'}
|
||||
target={subItem.external ? '_blank' : undefined}
|
||||
rel={
|
||||
@ -116,7 +121,7 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
{subItem.external && (
|
||||
<ArrowUpRightIcon className="size-4 shrink-0 text-muted-foreground transition-colors group-hover:text-foreground" />
|
||||
)}
|
||||
</Link>
|
||||
</LocaleLink>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
))}
|
||||
@ -127,7 +132,7 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
<NavigationMenuItem key={index}>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
active={pathname.startsWith(item.href)}
|
||||
active={item.href && pathname.startsWith(item.href) ? true : undefined}
|
||||
className={cn(
|
||||
customNavigationMenuTriggerStyle,
|
||||
"data-[active]:text-primary data-[active]:font-bold dark:data-[active]:text-white"
|
||||
@ -163,7 +168,7 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
<span>Log in</span>
|
||||
{commonTranslations("login")}
|
||||
</Button>
|
||||
</LoginWrapper>
|
||||
|
||||
@ -173,7 +178,7 @@ export function Navbar({ scroll }: NavBarProps) {
|
||||
asChild
|
||||
>
|
||||
<LocaleLink href={Routes.Register}>
|
||||
Sign up
|
||||
{commonTranslations("signUp")}
|
||||
</LocaleLink>
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -17,16 +17,20 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { AVATAR_LINKS } from "@/config/marketing";
|
||||
import { createTranslator, getAvatarLinks } from "@/config/marketing";
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import { LocaleLink, useLocaleRouter } from "@/i18n/navigation";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { LogOutIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export function UserButton() {
|
||||
const { data: session, error } = authClient.useSession();
|
||||
const user = session?.user;
|
||||
const t = useTranslations();
|
||||
const translator = createTranslator(t);
|
||||
const avatarLinks = getAvatarLinks(translator);
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await authClient.signOut({
|
||||
@ -85,7 +89,7 @@ export function UserButton() {
|
||||
</div>
|
||||
|
||||
<ul className="mb-14 mt-1 w-full text-muted-foreground">
|
||||
{AVATAR_LINKS.map((item) => (
|
||||
{avatarLinks.map((item) => (
|
||||
<li
|
||||
key={item.title}
|
||||
className="rounded-lg text-foreground hover:bg-muted"
|
||||
@ -147,7 +151,7 @@ export function UserButton() {
|
||||
</div>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{AVATAR_LINKS.map((item) => (
|
||||
{avatarLinks.map((item) => (
|
||||
<DropdownMenuItem
|
||||
key={item.title}
|
||||
asChild
|
||||
|
@ -25,158 +25,185 @@ import {
|
||||
SquarePenIcon
|
||||
} from 'lucide-react';
|
||||
|
||||
/**
|
||||
* list all the menu links here, you can customize the links as you want
|
||||
*/
|
||||
export const MENU_LINKS: NestedMenuItem[] = [
|
||||
{
|
||||
title: 'Features',
|
||||
href: Routes.Pricing,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Pricing',
|
||||
href: Routes.Pricing,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Blog',
|
||||
href: Routes.Blog,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'AI',
|
||||
items: [
|
||||
{
|
||||
title: 'AI Tex',
|
||||
description: 'show how to use AI to write stunning text',
|
||||
icon: <SquarePenIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIText,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'AI Image',
|
||||
description: 'show how to use AI to generate beautiful images',
|
||||
icon: <ImageIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIImage,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'AI Video',
|
||||
description: 'show how to use AI to generate amazing videos',
|
||||
icon: <FilmIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIVideo,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'AI Audio',
|
||||
description: 'show how to use AI to generate wonderful audio',
|
||||
icon: <AudioLinesIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIAudio,
|
||||
external: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Pages',
|
||||
items: [
|
||||
{
|
||||
title: 'About',
|
||||
description: 'Learn more about our company, mission, and values',
|
||||
icon: <InfoIcon className="size-5 shrink-0" />,
|
||||
href: Routes.About,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Contact',
|
||||
description: 'Get in touch with our team for support or inquiries',
|
||||
icon: <MailIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Contact,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Waitlist',
|
||||
description: 'Join our waitlist for latest news and updates',
|
||||
icon: <MailboxIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Waitlist,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Changelog',
|
||||
description: 'See the latest updates to our products',
|
||||
icon: <ListChecksIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Changelog,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Roadmap',
|
||||
description: 'Explore our future plans and upcoming features',
|
||||
icon: <SquareKanbanIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Roadmap,
|
||||
external: true
|
||||
},
|
||||
{
|
||||
title: 'Cookie Policy',
|
||||
description: 'Information about how we use cookies on our website',
|
||||
icon: <CookieIcon className="size-5 shrink-0" />,
|
||||
href: Routes.CookiePolicy,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Privacy Policy',
|
||||
description: 'Details about how we protect and handle your data',
|
||||
icon: <ShieldIcon className="size-5 shrink-0" />,
|
||||
href: Routes.PrivacyPolicy,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: 'Terms of Service',
|
||||
description: 'The legal agreement between you and our company',
|
||||
icon: <FileTextIcon className="size-5 shrink-0" />,
|
||||
href: Routes.TermsOfService,
|
||||
external: false
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
type TranslationFunction = (key: string, ...args: any[]) => string;
|
||||
|
||||
/**
|
||||
* list all the footer links here, you can customize the links as you want
|
||||
* Creates a translation function that works with our menu functions
|
||||
* @param t - The next-intl translation function
|
||||
* @returns A translation function that accepts string keys
|
||||
*/
|
||||
export const FOOTER_LINKS: NestedMenuItem[] = [
|
||||
{
|
||||
title: 'Product',
|
||||
items: [
|
||||
{ title: 'Features', href: Routes.Features, external: false },
|
||||
{ title: 'Pricing', href: Routes.Pricing, external: false },
|
||||
{ title: 'FAQ', href: Routes.FAQ, external: false },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Resources',
|
||||
items: [
|
||||
{ title: 'Blog', href: Routes.Blog, external: false },
|
||||
{ title: 'Changelog', href: Routes.Changelog, external: false },
|
||||
{ title: 'Roadmap', href: Routes.Roadmap, external: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Company',
|
||||
items: [
|
||||
{ title: 'About', href: Routes.About, external: false },
|
||||
{ title: 'Contact', href: Routes.Contact, external: false },
|
||||
{ title: 'Waitlist', href: Routes.Waitlist, external: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Legal',
|
||||
items: [
|
||||
{ title: 'Cookie Policy', href: Routes.CookiePolicy, external: false },
|
||||
{ title: 'Privacy Policy', href: Routes.PrivacyPolicy, external: false },
|
||||
{ title: 'Terms of Service', href: Routes.TermsOfService, external: false },
|
||||
]
|
||||
}
|
||||
];
|
||||
export function createTranslator(t: any): TranslationFunction {
|
||||
return (key: string) => {
|
||||
try {
|
||||
// @ts-ignore - We know this is a valid key because we've defined it in our messages
|
||||
return t(key);
|
||||
} catch (error) {
|
||||
console.error(`Translation key not found: ${key}`);
|
||||
return key.split('.').pop() || key;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu links with translations
|
||||
* @param t - The translation function
|
||||
* @returns The menu links with translated titles and descriptions
|
||||
*/
|
||||
export function getMenuLinks(t: TranslationFunction): NestedMenuItem[] {
|
||||
return [
|
||||
{
|
||||
title: t('Marketing.menu.features.title'),
|
||||
href: Routes.Features,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pricing.title'),
|
||||
href: Routes.Pricing,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.blog.title'),
|
||||
href: Routes.Blog,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.title'),
|
||||
items: [
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.text.title'),
|
||||
description: t('Marketing.menu.ai.items.text.description'),
|
||||
icon: <SquarePenIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIText,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.image.title'),
|
||||
description: t('Marketing.menu.ai.items.image.description'),
|
||||
icon: <ImageIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIImage,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.video.title'),
|
||||
description: t('Marketing.menu.ai.items.video.description'),
|
||||
icon: <FilmIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIVideo,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.audio.title'),
|
||||
description: t('Marketing.menu.ai.items.audio.description'),
|
||||
icon: <AudioLinesIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIAudio,
|
||||
external: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.title'),
|
||||
items: [
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.about.title'),
|
||||
description: t('Marketing.menu.pages.items.about.description'),
|
||||
icon: <InfoIcon className="size-5 shrink-0" />,
|
||||
href: Routes.About,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.contact.title'),
|
||||
description: t('Marketing.menu.pages.items.contact.description'),
|
||||
icon: <MailIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Contact,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.waitlist.title'),
|
||||
description: t('Marketing.menu.pages.items.waitlist.description'),
|
||||
icon: <MailboxIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Waitlist,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.changelog.title'),
|
||||
description: t('Marketing.menu.pages.items.changelog.description'),
|
||||
icon: <ListChecksIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Changelog,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.roadmap.title'),
|
||||
description: t('Marketing.menu.pages.items.roadmap.description'),
|
||||
icon: <SquareKanbanIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Roadmap,
|
||||
external: true
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.cookiePolicy.title'),
|
||||
description: t('Marketing.menu.pages.items.cookiePolicy.description'),
|
||||
icon: <CookieIcon className="size-5 shrink-0" />,
|
||||
href: Routes.CookiePolicy,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.privacyPolicy.title'),
|
||||
description: t('Marketing.menu.pages.items.privacyPolicy.description'),
|
||||
icon: <ShieldIcon className="size-5 shrink-0" />,
|
||||
href: Routes.PrivacyPolicy,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.termsOfService.title'),
|
||||
description: t('Marketing.menu.pages.items.termsOfService.description'),
|
||||
icon: <FileTextIcon className="size-5 shrink-0" />,
|
||||
href: Routes.TermsOfService,
|
||||
external: false
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get footer links with translations
|
||||
* @param t - The translation function
|
||||
* @returns The footer links with translated titles
|
||||
*/
|
||||
export function getFooterLinks(t: TranslationFunction): NestedMenuItem[] {
|
||||
return [
|
||||
{
|
||||
title: t('Marketing.footer.product.title'),
|
||||
items: [
|
||||
{ title: t('Marketing.footer.product.items.features'), href: Routes.Features, external: false },
|
||||
{ title: t('Marketing.footer.product.items.pricing'), href: Routes.Pricing, external: false },
|
||||
{ title: t('Marketing.footer.product.items.faq'), href: Routes.FAQ, external: false },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Marketing.footer.resources.title'),
|
||||
items: [
|
||||
{ title: t('Marketing.footer.resources.items.blog'), href: Routes.Blog, external: false },
|
||||
{ title: t('Marketing.footer.resources.items.changelog'), href: Routes.Changelog, external: false },
|
||||
{ title: t('Marketing.footer.resources.items.roadmap'), href: Routes.Roadmap, external: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Marketing.footer.company.title'),
|
||||
items: [
|
||||
{ title: t('Marketing.footer.company.items.about'), href: Routes.About, external: false },
|
||||
{ title: t('Marketing.footer.company.items.contact'), href: Routes.Contact, external: false },
|
||||
{ title: t('Marketing.footer.company.items.waitlist'), href: Routes.Waitlist, external: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Marketing.footer.legal.title'),
|
||||
items: [
|
||||
{ title: t('Marketing.footer.legal.items.cookiePolicy'), href: Routes.CookiePolicy, external: false },
|
||||
{ title: t('Marketing.footer.legal.items.privacyPolicy'), href: Routes.PrivacyPolicy, external: false },
|
||||
{ title: t('Marketing.footer.legal.items.termsOfService'), href: Routes.TermsOfService, external: false },
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* list all the social links here, you can delete the ones that are not needed
|
||||
@ -230,17 +257,21 @@ export const SOCIAL_LINKS: MenuItem[] = [
|
||||
];
|
||||
|
||||
/**
|
||||
* list all the avatar links here, you can customize the links as you want
|
||||
* Get avatar links with translations
|
||||
* @param t - The translation function
|
||||
* @returns The avatar links with translated titles
|
||||
*/
|
||||
export const AVATAR_LINKS: MenuItem[] = [
|
||||
{
|
||||
title: 'Dashboard',
|
||||
href: Routes.Dashboard,
|
||||
icon: <DashboardIcon className="size-4 shrink-0" />
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
href: Routes.Settings,
|
||||
icon: <SettingsIcon className="size-4 shrink-0" />
|
||||
}
|
||||
];
|
||||
export function getAvatarLinks(t: TranslationFunction): MenuItem[] {
|
||||
return [
|
||||
{
|
||||
title: t('Marketing.avatar.dashboard'),
|
||||
href: Routes.Dashboard,
|
||||
icon: <DashboardIcon className="size-4 shrink-0" />
|
||||
},
|
||||
{
|
||||
title: t('Marketing.avatar.settings'),
|
||||
href: Routes.Settings,
|
||||
icon: <SettingsIcon className="size-4 shrink-0" />
|
||||
}
|
||||
];
|
||||
}
|
Loading…
Reference in New Issue
Block a user