Skip to content

Commit 1aa5447

Browse files
Merge pull request #2 from DashAISoftware/translation
Integrate i18next for internationalization across components
2 parents 50de339 + 27aadd3 commit 1aa5447

27 files changed

Lines changed: 691 additions & 229 deletions

app/i18n.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import i18n from "i18next";
2+
import LanguageDetector from "i18next-browser-languagedetector";
3+
import { initReactI18next } from "react-i18next";
4+
import supportedByEN from "../public/locales/en/supportedBy.json";
5+
import supportedByES from "../public/locales/es/supportedBy.json";
6+
import navbarEN from "../public/locales/en/navbar.json";
7+
import navbarES from "../public/locales/es/navbar.json";
8+
import heroEN from "../public/locales/en/hero.json";
9+
import heroES from "../public/locales/es/hero.json";
10+
import featuresEN from "../public/locales/en/features.json";
11+
import featuresES from "../public/locales/es/features.json";
12+
import downloadEN from "../public/locales/en/download.json";
13+
import downloadES from "../public/locales/es/download.json";
14+
import communityEN from "../public/locales/en/community.json";
15+
import communityES from "../public/locales/es/community.json";
16+
import contactEN from "../public/locales/en/contact.json";
17+
import contactES from "../public/locales/es/contact.json";
18+
import footerEN from "../public/locales/en/footer.json";
19+
import footerES from "../public/locales/es/footer.json";
20+
21+
22+
i18n
23+
.use(initReactI18next)
24+
.use(LanguageDetector)
25+
.init({
26+
resources: {
27+
en: {
28+
navbar: navbarEN,
29+
hero: heroEN,
30+
supportedBy: supportedByEN,
31+
features: featuresEN,
32+
download: downloadEN,
33+
community: communityEN,
34+
contact: contactEN,
35+
footer: footerEN,
36+
},
37+
es: {
38+
navbar: navbarES,
39+
hero: heroES,
40+
supportedBy: supportedByES,
41+
features: featuresES,
42+
download: downloadES,
43+
community: communityES,
44+
contact: contactES,
45+
footer: footerES,
46+
}
47+
},
48+
supportedLngs: ["en", "es"],
49+
fallbackLng: "en",
50+
51+
interpolation: {
52+
escapeValue: false
53+
}
54+
});
55+
56+
export default i18n;

app/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client"
2+
13
import { Navbar } from "@/components/navbar"
24
import { HeroSection } from "@/components/hero-section"
35
import { FeaturesSection } from "@/components/features-section"
@@ -7,6 +9,7 @@ import { SupportedBySection } from "@/components/supported-by-section"
79
import { CommunitySection } from "@/components/community-section"
810
import { ContactSection } from "@/components/contact-section"
911
import { Footer } from "@/components/footer"
12+
import "./i18n"
1013

1114
export default function Home() {
1215
return (

components/community-section.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
"use client"
22

3-
import { Button } from "@/components/ui/button"
43
import { Card, CardContent } from "@/components/ui/card"
54
import { Github, BookOpen, Code2, Package } from "lucide-react"
65
import { siteConfig } from "@/lib/config"
6+
import { useTranslation } from "react-i18next"
77

88
const communityLinks = [
99
{
1010
icon: Github,
11-
title: "GitHub",
12-
description: "Explore the source code",
11+
key: "github",
12+
defaultTitle: "GitHub",
13+
defaultDescription: "Explore the source code",
1314
link: siteConfig.github.url,
1415
// stats: "2.5k stars",
1516
},
@@ -22,50 +23,60 @@ const communityLinks = [
2223
// },
2324
{
2425
icon: BookOpen,
25-
title: "Docs",
26-
description: "Visit the technical documentation",
26+
key: "docs",
27+
defaultTitle: "Docs",
28+
defaultDescription: "Visit the technical documentation",
2729
link: siteConfig.docs.url,
2830
// stats: "Complete guides",
2931
},
3032
{
3133
icon: Code2,
32-
title: "Contribute",
33-
description: "Improve the project",
34+
key: "contribute",
35+
defaultTitle: "Contribute",
36+
defaultDescription: "Improve the project",
3437
link: siteConfig.github.contribute,
3538
// stats: "Open to all",
3639
},
3740
]
3841

3942
export function CommunitySection() {
43+
const { t } = useTranslation()
4044
return (
4145
<section id="community" className="py-24 px-4">
4246
<div className="container mx-auto">
4347
<div className="text-center mb-16">
4448
<h2 className="text-3xl md:text-5xl font-bold mb-4 text-balance">
45-
{"Built and extended by the community"}
49+
{t("community:title", { defaultValue: "Built and extended by the community" })}
4650
</h2>
4751
<p className="text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed">
48-
{
49-
"DashAI's plugin architecture enables anyone to contribute new capabilities. Build plugins, improve the core, and shape the future of open source AI tools."
50-
}
52+
{t("community:description", {
53+
defaultValue:
54+
"DashAI's plugin architecture enables anyone to contribute new capabilities. Build plugins, improve the core, and shape the future of open source AI tools.",
55+
})}
5156
</p>
5257
</div>
5358

5459
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-6 mb-12">
5560
{communityLinks.map((item, index) => {
5661
const Icon = item.icon
62+
const title = t(`community:cards.${item.key}.title`, {
63+
defaultValue: item.defaultTitle,
64+
})
65+
const description = t(`community:cards.${item.key}.description`, {
66+
defaultValue: item.defaultDescription,
67+
})
5768
return (
5869
<Card
59-
key={index}
70+
key={item.key}
6071
className="bg-card border-border hover:border-primary/50 transition-all duration-300 cursor-pointer group"
6172
onClick={() => window.open(item.link, "_blank", "noopener,noreferrer")}
6273
>
6374
<CardContent className="p-6 text-center">
6475
<div className="w-14 h-14 rounded-xl bg-primary/10 flex items-center justify-center mx-auto mb-4 group-hover:bg-primary/20 transition-colors">
6576
<Icon className="h-7 w-7 text-primary" />
6677
</div>
67-
<h3 className="text-xl font-semibold mb-2">{item.title}</h3>
68-
<p className="text-muted-foreground mb-3 text-sm leading-relaxed">{item.description}</p>
78+
<h3 className="text-xl font-semibold mb-2">{title}</h3>
79+
<p className="text-muted-foreground mb-3 text-sm leading-relaxed">{description}</p>
6980
<span className="text-xs text-primary font-mono">{item.stats}</span>
7081
</CardContent>
7182
</Card>

components/contact-section.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import { useState } from 'react'
44
import { Button } from '@/components/ui/button'
5+
import { useTranslation } from 'react-i18next'
56

67
export function ContactSection() {
8+
const { t } = useTranslation()
79
const [formData, setFormData] = useState({
810
name: '',
911
email: '',
@@ -67,10 +69,12 @@ export function ContactSection() {
6769
<div className="max-w-2xl mx-auto">
6870
<div className="text-center mb-12">
6971
<h2 className="text-4xl md:text-5xl font-bold mb-4">
70-
Contact Us
72+
{t('contact:title', { defaultValue: 'Contact Us' })}
7173
</h2>
7274
<p className="text-muted-foreground text-lg">
73-
Have questions or feedback about DashAI? Feel free to get in touch — we’d love to hear from you.
75+
{t('contact:description', {
76+
defaultValue: 'Have questions or feedback about DashAI? Feel free to get in touch — we’d love to hear from you.',
77+
})}
7478
</p>
7579
</div>
7680

@@ -79,7 +83,7 @@ export function ContactSection() {
7983
<input
8084
type="text"
8185
name="name"
82-
placeholder="Name"
86+
placeholder={t('contact:fields.name', { defaultValue: 'Name' })}
8387
value={formData.name}
8488
onChange={handleChange}
8589
required
@@ -91,7 +95,7 @@ export function ContactSection() {
9195
<input
9296
type="email"
9397
name="email"
94-
placeholder="Email"
98+
placeholder={t('contact:fields.email', { defaultValue: 'Email' })}
9599
value={formData.email}
96100
onChange={handleChange}
97101
required
@@ -102,7 +106,7 @@ export function ContactSection() {
102106
<div>
103107
<textarea
104108
name="message"
105-
placeholder="Message"
109+
placeholder={t('contact:fields.message', { defaultValue: 'Message' })}
106110
value={formData.message}
107111
onChange={handleChange}
108112
required
@@ -116,18 +120,24 @@ export function ContactSection() {
116120
disabled={status === 'sending'}
117121
className="w-full py-6 text-lg font-medium cursor-pointer"
118122
>
119-
{status === 'sending' ? 'Sending...' : status === 'success' ? 'Sent!' : 'Send message'}
123+
{status === 'sending'
124+
? t('contact:status.sending', { defaultValue: 'Sending...' })
125+
: status === 'success'
126+
? t('contact:status.sent', { defaultValue: 'Sent!' })
127+
: t('contact:status.idle', { defaultValue: 'Send message' })}
120128
</Button>
121129

122130
{status === 'success' && (
123131
<p className="text-center text-primary animate-in fade-in duration-300">
124-
Thank you for contacting us! We will get back to you soon.
132+
{t('contact:feedback.success', {
133+
defaultValue: 'Thank you for contacting us! We will get back to you soon.',
134+
})}
125135
</p>
126136
)}
127137

128138
{status === 'error' && (
129139
<p className="text-center text-red-500 animate-in fade-in duration-300">
130-
There was an error. Please try again.
140+
{t('contact:feedback.error', { defaultValue: 'There was an error. Please try again.' })}
131141
</p>
132142
)}
133143
</form>

components/download-section.tsx

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,85 @@
1+
"use client"
2+
13
import { Button } from "@/components/ui/button"
24
import { Card, CardContent } from "@/components/ui/card"
3-
import { Download, Terminal, Package } from "lucide-react"
5+
import { Download, Package } from "lucide-react"
6+
import { useTranslation } from "react-i18next"
47

58
const downloads = [
69
{
710
icon: Package,
8-
platform: "macOS Intel processors",
11+
key: "macIntel",
12+
defaultPlatform: "macOS Intel processors",
913
version: "v0.1.15",
1014
size: "487 MB",
11-
format: "binary",
15+
defaultFormat: "binary",
1216
link: "https://dashai.nyc3.cdn.digitaloceanspaces.com/executables/DashAI-launcher-cpu-x86_64",
1317
},
1418
{
1519
icon: Package,
16-
platform: "macOS ARM processors",
20+
key: "macArm",
21+
defaultPlatform: "macOS ARM processors",
1722
version: "v0.1.15",
1823
size: "381 MB",
19-
format: "binary",
24+
defaultFormat: "binary",
2025
link: "https://dashai.nyc3.cdn.digitaloceanspaces.com/executables/DashAI-launcher-cpu-arm64",
2126
},
2227
{
2328
icon: Package,
24-
platform: "Windows",
29+
key: "windows",
30+
defaultPlatform: "Windows",
2531
version: "v0.1.15",
2632
size: "434 MB",
27-
format: ".exe",
33+
defaultFormat: ".exe",
2834
link: "https://dashai.nyc3.cdn.digitaloceanspaces.com/executables/DashAI-launcher-cpu.exe",
2935
},
30-
// {
31-
// icon: Package,
32-
// platform: "Docker",
33-
// version: "latest",
34-
// size: "~300 MB",
35-
// format: "container",
36-
// link: "#",
37-
// },
3836
]
3937

4038
export function DownloadSection() {
39+
const { t } = useTranslation()
4140
return (
4241
<section id="download" className="py-24 px-4 bg-secondary/30">
4342
<div className="container mx-auto">
4443
<div className="text-center mb-16">
45-
<h2 className="text-3xl md:text-5xl font-bold mb-2 text-balance">Download DashAI</h2>
46-
<span className="block text-base text-primary font-semibold mb-4">Beta Version</span>
44+
<h2 className="text-3xl md:text-5xl font-bold mb-2 text-balance">
45+
{t("download:title", { defaultValue: "Download DashAI" })}
46+
</h2>
47+
<span className="block text-base text-primary font-semibold mb-4">
48+
{t("download:badge", { defaultValue: "Beta Version" })}
49+
</span>
4750
<p className="text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed">
48-
This early version lets you explore DashAI’s main features. We appreciate your feedback to help us improve before the official release.
51+
{t("download:description", {
52+
defaultValue:
53+
"This early version lets you explore DashAI’s main features. We appreciate your feedback to help us improve before the official release.",
54+
})}
4955
</p>
5056
</div>
5157

5258
<div className="max-w-5xl mx-auto">
5359
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
5460
{downloads.map((download, index) => {
5561
const Icon = download.icon
62+
const platform = t(`download:cards.${download.key}.platform`, {
63+
defaultValue: download.defaultPlatform,
64+
})
65+
const format = t(`download:cards.${download.key}.format`, {
66+
defaultValue: download.defaultFormat,
67+
})
5668
return (
5769
<Card
58-
key={index}
70+
key={download.key}
5971
className="bg-card border-border hover:border-primary/50 transition-all duration-300 group cursor-pointer"
6072
>
6173
<CardContent className="p-6">
6274
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-4 group-hover:bg-primary/20 transition-colors">
6375
<Icon className="h-6 w-6 text-primary" />
6476
</div>
65-
<h3 className="text-xl font-semibold mb-2">{download.platform}</h3>
77+
<h3 className="text-xl font-semibold mb-2">{platform}</h3>
6678
<div className="space-y-1 mb-4">
6779
<p className="text-sm text-muted-foreground">
6880
{download.version}{download.size}
6981
</p>
70-
<p className="text-xs text-muted-foreground font-mono">{download.format}</p>
82+
<p className="text-xs text-muted-foreground font-mono">{format}</p>
7183
</div>
7284
<Button
7385
className="w-full bg-primary hover:bg-primary/90 text-primary-foreground cursor-pointer"
@@ -76,7 +88,7 @@ export function DownloadSection() {
7688
>
7789
<a href={download.link} download>
7890
<Download className="mr-2 h-4 w-4" />
79-
Download
91+
{t("download:button", { defaultValue: "Download" })}
8092
</a>
8193
</Button>
8294
</CardContent>
@@ -89,12 +101,20 @@ export function DownloadSection() {
89101
<CardContent className="p-8">
90102
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
91103
<div>
92-
<h3 className="text-xl font-semibold mb-2">Install via pip</h3>
93-
<p className="text-muted-foreground text-sm">For developers who prefer command-line installation</p>
104+
<h3 className="text-xl font-semibold mb-2">
105+
{t("download:pip.title", { defaultValue: "Install via pip" })}
106+
</h3>
107+
<p className="text-muted-foreground text-sm">
108+
{t("download:pip.subtitle", {
109+
defaultValue: "For developers who prefer command-line installation",
110+
})}
111+
</p>
94112
</div>
95113
<div className="w-full md:w-auto">
96114
<div className="bg-secondary/50 rounded-lg p-4 font-mono text-sm border border-border">
97-
<code className="text-primary">pip install dashai</code>
115+
<code className="text-primary">
116+
{t("download:pip.command", { defaultValue: "pip install dashai" })}
117+
</code>
98118
</div>
99119
</div>
100120
</div>

0 commit comments

Comments
 (0)