Skip to content

Commit a6cbbf0

Browse files
behitekclaude
andcommitted
feat: add i18n infrastructure for English and Vietnamese
i18n Configuration: - Configured Astro i18n routing with 'en' (default) and 'vi' locales - Set prefixDefaultLocale: false (English URLs without /en prefix) - Updated tsconfig.json to add @i18n/* path alias Translation Files: - Created comprehensive translation files for English (en.ts) and Vietnamese (vi.ts) - Organized translations by page/section: nav, home, projects, contact, blog, footer, common - Vietnamese translations provided for all UI elements Components: - Created LanguageSwitcher component with toggle button (EN ⇄ VI) - Shows current language with globe icon - Preserves current path when switching languages - Integrated into Navbar (desktop & mobile) BaseLayout Updates: - Added dynamic lang attribute based on URL locale - Supports proper HTML lang tags for SEO and accessibility Build Status: - ✅ Dev server running successfully on http://localhost:4321/ - ✅ No errors, pages load correctly - ✅ TypeScript paths configured correctly Next Steps: - Update individual pages to use translation strings - Implement localized routing for all pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 22793d1 commit a6cbbf0

8 files changed

Lines changed: 364 additions & 2 deletions

File tree

new-site/astro.config.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import tailwind from '@astrojs/tailwind';
66
// https://astro.build/config
77
export default defineConfig({
88
site: 'https://behitek.com',
9+
i18n: {
10+
defaultLocale: 'en',
11+
locales: ['en', 'vi'],
12+
routing: {
13+
prefixDefaultLocale: false,
14+
},
15+
},
916
integrations: [
1017
mdx(),
1118
sitemap(),
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
import { languages, defaultLang, getLangFromUrl, getLocalizedPath } from '@i18n/utils';
3+
4+
const currentLang = getLangFromUrl(Astro.url);
5+
const currentPath = Astro.url.pathname.replace(new RegExp(`^/${currentLang}`), '') || '/';
6+
7+
// Generate alternate language URL
8+
const alternateLang = currentLang === 'en' ? 'vi' : 'en';
9+
const alternateUrl = getLocalizedPath(currentPath, alternateLang);
10+
---
11+
12+
<div class="language-switcher">
13+
<a
14+
href={alternateUrl}
15+
class="flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors"
16+
aria-label={`Switch to ${languages[alternateLang]}`}
17+
title={`Switch to ${languages[alternateLang]}`}
18+
>
19+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
20+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"></path>
21+
</svg>
22+
<span class="text-sm font-medium">
23+
{currentLang === 'en' ? 'VI' : 'EN'}
24+
</span>
25+
</a>
26+
</div>
27+
28+
<style>
29+
.language-switcher {
30+
display: inline-block;
31+
}
32+
</style>

new-site/src/components/Navbar.astro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import { SITE, NAV_LINKS } from '@utils/constants';
33
import ThemeToggle from './ThemeToggle.astro';
4+
import LanguageSwitcher from './LanguageSwitcher.astro';
45
56
const currentPath = Astro.url.pathname;
67
---
@@ -25,11 +26,13 @@ const currentPath = Astro.url.pathname;
2526
{link.label}
2627
</a>
2728
))}
29+
<LanguageSwitcher />
2830
<ThemeToggle />
2931
</div>
3032

3133
<!-- Mobile Menu Button -->
3234
<div class="md:hidden flex items-center space-x-2">
35+
<LanguageSwitcher />
3336
<ThemeToggle />
3437
<button
3538
id="mobile-menu-button"

new-site/src/i18n/en.ts

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
export default {
2+
// Navigation
3+
nav: {
4+
blog: 'Blog',
5+
projects: 'Projects',
6+
contact: 'Contact',
7+
},
8+
9+
// Homepage
10+
home: {
11+
welcome: '👋 Welcome to my portfolio',
12+
greeting: "Hi, I'm",
13+
name: 'Hieu Nguyen',
14+
role: 'AI Engineer',
15+
tagline: 'Building production-ready AI systems with NLP, RAG, and LLMs. Passionate about bringing AI into real-world applications.',
16+
cta: {
17+
viewWork: 'View My Work',
18+
getInTouch: 'Get in Touch',
19+
},
20+
status: 'Available for hire',
21+
stats: {
22+
followers: 'Followers',
23+
repositories: 'Repositories',
24+
yearsExp: 'Years Exp',
25+
},
26+
about: {
27+
title: 'About Me',
28+
subtitle: 'AI Engineer with Master\'s degree from JAIST, specializing in production ML systems',
29+
greeting: 'Hello there!',
30+
bio1: 'I\'m Hieu Nguyen, an AI Engineer from Vietnam 🇻🇳 with a passion for transforming research into production-ready systems.',
31+
bio2: 'My expertise lies in Natural Language Processing, RAG systems, and Large Language Models. I specialize in building scalable AI solutions that solve real-world problems.',
32+
bio3: 'I hold a {degree} from {school} in Japan, where I deepened my knowledge in information retrieval and machine learning.',
33+
bio4: 'When I\'m not coding, you\'ll find me exploring self-hosted solutions, contributing to open source, or writing about AI/ML on my blog.',
34+
},
35+
education: {
36+
title: 'Education',
37+
},
38+
techStack: {
39+
title: 'Tech Stack & Skills',
40+
},
41+
featuredProjects: {
42+
title: '🚀 Featured Projects',
43+
subtitle: 'Check out some of my recent work and contributions',
44+
viewAll: 'View All Projects',
45+
},
46+
latestArticles: {
47+
title: '✍️ Latest Articles',
48+
subtitle: 'Thoughts on AI, ML, and software engineering',
49+
viewAll: 'View All Articles',
50+
},
51+
cta2: {
52+
title: 'Let\'s Build Something Amazing Together',
53+
subtitle: 'Looking for an AI engineer to bring your ideas to life? I\'m available for consulting, collaboration, and full-time opportunities.',
54+
getInTouch: '📧 Get in Touch',
55+
viewWork: '🚀 View My Work',
56+
},
57+
},
58+
59+
// Projects Page
60+
projects: {
61+
title: 'Projects',
62+
subtitle: 'Personal and open source projects I\'ve built',
63+
filter: {
64+
all: 'All',
65+
product: 'Product',
66+
research: 'Research',
67+
tutorial: 'Tutorial',
68+
tool: 'Tool',
69+
fun: 'Fun',
70+
},
71+
links: {
72+
website: 'Website',
73+
github: 'GitHub',
74+
blog: 'Read',
75+
},
76+
},
77+
78+
// Contact Page
79+
contact: {
80+
title: '💬 Let\'s Connect',
81+
subtitle: 'Always happy to discuss AI, production ML, RAG systems, or self-hosted solutions',
82+
getInTouch: 'Get in Touch',
83+
quickInfo: {
84+
title: 'Quick Info',
85+
location: 'Location',
86+
locationValue: 'Hanoi, Vietnam 🇻🇳',
87+
role: 'Role',
88+
roleValue: 'AI Engineer',
89+
education: 'Education',
90+
educationValue: 'Master\'s in Information Science (JAIST)',
91+
},
92+
interests: {
93+
title: 'Interests',
94+
aiml: 'AI/ML',
95+
nlp: 'NLP',
96+
rag: 'RAG',
97+
llms: 'LLMs',
98+
python: 'Python',
99+
ir: 'IR',
100+
teaching: 'Teaching',
101+
selfHosting: 'Self-hosting',
102+
},
103+
availability: {
104+
title: '📬 Response Time',
105+
text: 'I typically respond to emails and LinkedIn messages within 24-48 hours. For quick questions, Twitter/X DMs work best!',
106+
},
107+
cta: {
108+
title: 'Open to Collaboration',
109+
subtitle: 'Interested in collaborating on AI/ML projects, writing guest posts, or speaking opportunities?',
110+
button: '📧 Send me an email',
111+
},
112+
},
113+
114+
// Blog Page
115+
blog: {
116+
title: 'Blog',
117+
subtitle: 'Articles about AI, ML, and software engineering',
118+
filter: {
119+
all: 'All',
120+
aiml: 'AI/ML',
121+
python: 'Python',
122+
tutorial: 'Tutorial',
123+
data: 'Data',
124+
},
125+
readMore: 'Read More',
126+
minRead: 'min read',
127+
},
128+
129+
// Footer
130+
footer: {
131+
builtWith: 'Built with',
132+
and: 'and',
133+
sourceCode: 'Source Code',
134+
rights: 'All rights reserved.',
135+
},
136+
137+
// Common
138+
common: {
139+
readMore: 'Read More',
140+
viewAll: 'View All',
141+
loading: 'Loading...',
142+
},
143+
} as const;

new-site/src/i18n/utils.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import en from './en';
2+
import vi from './vi';
3+
4+
export const languages = {
5+
en: 'English',
6+
vi: 'Tiếng Việt',
7+
} as const;
8+
9+
export const defaultLang = 'en';
10+
11+
export const translations = {
12+
en,
13+
vi,
14+
} as const;
15+
16+
export function getLangFromUrl(url: URL) {
17+
const [, lang] = url.pathname.split('/');
18+
if (lang in translations) return lang as keyof typeof translations;
19+
return defaultLang;
20+
}
21+
22+
export function useTranslations(lang: keyof typeof translations) {
23+
return translations[lang];
24+
}
25+
26+
export function getLocalizedPath(path: string, lang: string) {
27+
if (lang === defaultLang) {
28+
return path;
29+
}
30+
return `/${lang}${path}`;
31+
}

new-site/src/i18n/vi.ts

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
export default {
2+
// Navigation
3+
nav: {
4+
blog: 'Blog',
5+
projects: 'Dự Án',
6+
contact: 'Liên Hệ',
7+
},
8+
9+
// Homepage
10+
home: {
11+
welcome: '👋 Chào mừng đến với portfolio của tôi',
12+
greeting: 'Xin chào, tôi là',
13+
name: 'Hiếu Nguyễn',
14+
role: 'Kỹ Sư AI',
15+
tagline: 'Xây dựng hệ thống AI production-ready với NLP, RAG và LLMs. Đam mê đưa AI vào các ứng dụng thực tế.',
16+
cta: {
17+
viewWork: 'Xem Dự Án',
18+
getInTouch: 'Liên Hệ',
19+
},
20+
status: 'Sẵn sàng nhận việc',
21+
stats: {
22+
followers: 'Người Theo Dõi',
23+
repositories: 'Repositories',
24+
yearsExp: 'Năm Kinh Nghiệm',
25+
},
26+
about: {
27+
title: 'Về Tôi',
28+
subtitle: 'Kỹ sư AI với bằng Thạc sĩ từ JAIST, chuyên về hệ thống ML production',
29+
greeting: 'Xin chào!',
30+
bio1: 'Tôi là Hiếu Nguyễn, một Kỹ sư AI từ Việt Nam 🇻🇳 với đam mê chuyển đổi nghiên cứu thành các hệ thống production.',
31+
bio2: 'Chuyên môn của tôi nằm ở Xử lý Ngôn ngữ Tự nhiên, hệ thống RAG và Mô hình Ngôn ngữ Lớn. Tôi chuyên xây dựng các giải pháp AI có khả năng mở rộng để giải quyết các vấn đề thực tế.',
32+
bio3: 'Tôi có bằng {degree} từ {school} ở Nhật Bản, nơi tôi đã nâng cao kiến thức về truy xuất thông tin và học máy.',
33+
bio4: 'Khi không code, bạn sẽ thấy tôi khám phá các giải pháp self-hosted, đóng góp cho open source, hoặc viết về AI/ML trên blog.',
34+
},
35+
education: {
36+
title: 'Học Vấn',
37+
},
38+
techStack: {
39+
title: 'Kỹ Năng & Công Nghệ',
40+
},
41+
featuredProjects: {
42+
title: '🚀 Dự Án Nổi Bật',
43+
subtitle: 'Một số dự án và đóng góp gần đây của tôi',
44+
viewAll: 'Xem Tất Cả Dự Án',
45+
},
46+
latestArticles: {
47+
title: '✍️ Bài Viết Mới Nhất',
48+
subtitle: 'Suy nghĩ về AI, ML và kỹ thuật phần mềm',
49+
viewAll: 'Xem Tất Cả Bài Viết',
50+
},
51+
cta2: {
52+
title: 'Hãy Cùng Xây Dựng Điều Gì Đó Tuyệt Vời',
53+
subtitle: 'Đang tìm kiếm một kỹ sư AI để hiện thực hóa ý tưởng của bạn? Tôi sẵn sàng cho tư vấn, hợp tác và cơ hội toàn thời gian.',
54+
getInTouch: '📧 Liên Hệ',
55+
viewWork: '🚀 Xem Dự Án',
56+
},
57+
},
58+
59+
// Projects Page
60+
projects: {
61+
title: 'Dự Án',
62+
subtitle: 'Các dự án cá nhân và open source mà tôi đã xây dựng',
63+
filter: {
64+
all: 'Tất Cả',
65+
product: 'Sản Phẩm',
66+
research: 'Nghiên Cứu',
67+
tutorial: 'Hướng Dẫn',
68+
tool: 'Công Cụ',
69+
fun: 'Giải Trí',
70+
},
71+
links: {
72+
website: 'Website',
73+
github: 'GitHub',
74+
blog: 'Đọc',
75+
},
76+
},
77+
78+
// Contact Page
79+
contact: {
80+
title: '💬 Kết Nối',
81+
subtitle: 'Luôn vui lòng thảo luận về AI, production ML, hệ thống RAG, hoặc giải pháp self-hosted',
82+
getInTouch: 'Liên Hệ',
83+
quickInfo: {
84+
title: 'Thông Tin Nhanh',
85+
location: 'Vị Trí',
86+
locationValue: 'Hà Nội, Việt Nam 🇻🇳',
87+
role: 'Vai Trò',
88+
roleValue: 'Kỹ Sư AI',
89+
education: 'Học Vấn',
90+
educationValue: 'Thạc sĩ Khoa học Thông tin (JAIST)',
91+
},
92+
interests: {
93+
title: 'Sở Thích',
94+
aiml: 'AI/ML',
95+
nlp: 'NLP',
96+
rag: 'RAG',
97+
llms: 'LLMs',
98+
python: 'Python',
99+
ir: 'IR',
100+
teaching: 'Giảng Dạy',
101+
selfHosting: 'Self-hosting',
102+
},
103+
availability: {
104+
title: '📬 Thời Gian Phản Hồi',
105+
text: 'Tôi thường phản hồi email và tin nhắn LinkedIn trong vòng 24-48 giờ. Với các câu hỏi nhanh, Twitter/X DM là tốt nhất!',
106+
},
107+
cta: {
108+
title: 'Sẵn Sàng Hợp Tác',
109+
subtitle: 'Quan tâm đến việc hợp tác trong các dự án AI/ML, viết bài khách mời, hoặc cơ hội diễn thuyết?',
110+
button: '📧 Gửi email cho tôi',
111+
},
112+
},
113+
114+
// Blog Page
115+
blog: {
116+
title: 'Blog',
117+
subtitle: 'Bài viết về AI, ML và kỹ thuật phần mềm',
118+
filter: {
119+
all: 'Tất Cả',
120+
aiml: 'AI/ML',
121+
python: 'Python',
122+
tutorial: 'Hướng Dẫn',
123+
data: 'Dữ Liệu',
124+
},
125+
readMore: 'Đọc Thêm',
126+
minRead: 'phút đọc',
127+
},
128+
129+
// Footer
130+
footer: {
131+
builtWith: 'Được xây dựng với',
132+
and: 'và',
133+
sourceCode: 'Mã Nguồn',
134+
rights: 'Bản quyền thuộc về.',
135+
},
136+
137+
// Common
138+
common: {
139+
readMore: 'Đọc Thêm',
140+
viewAll: 'Xem Tất Cả',
141+
loading: 'Đang tải...',
142+
},
143+
} as const;

0 commit comments

Comments
 (0)