|
| 1 | +"use client"; |
| 2 | + |
| 3 | +import { siteConfig as baseSiteConfig } from "@/config/site-config"; |
| 4 | +import { createContext, ReactNode, useContext, useMemo, useState } from "react"; |
| 5 | + |
| 6 | +type Language = "en" | "ml" | "zh" | "ja" | "hi"; |
| 7 | +type SiteConfig = typeof baseSiteConfig; |
| 8 | + |
| 9 | +const translations: Record<Language, Record<string, string>> = { |
| 10 | + en: {}, |
| 11 | + ml: { |
| 12 | + "Hi!": "ഹായ്!", |
| 13 | + "I am Bala": "ഞാൻ ബാലയാണ്", |
| 14 | + "Senior SWE - ELT, Web, OSS Dev": "സീനിയർ SWE - ELT, വെബ്, OSS ഡെവ്", |
| 15 | + "Remote, India": "റിമോട്ട്, ഇന്ത്യ", |
| 16 | + "Contact Me": "ബന്ധപ്പെടുക", |
| 17 | + "Before you go... Please reach out if you have any Research Software Engineer Positions available. Very much interested :)": |
| 18 | + "പോകുന്നതിന് മുൻപ്... Research Software Engineer ഒഴിവുകൾ ഉണ്ടെങ്കിൽ പറയണം. വളരെ താൽപ്പര്യം :)", |
| 19 | + "Portfolio website powered by Next.js, Tailwind CSS": |
| 20 | + "Next.js, Tailwind CSS ഉപയോഗിച്ച് നിർമ്മിച്ച പോർട്ട്ഫോളിയോ വെബ്സൈറ്റ്", |
| 21 | + Follow: "ഫോളോ", |
| 22 | + Subscribe: "സബ്സ്ക്രൈബ്", |
| 23 | + Join: "ചേരുക", |
| 24 | + "View profile": "പ്രൊഫൈൽ കാണുക", |
| 25 | + "See badges 🏅": "ബാഡ്ജുകൾ കാണുക 🏅", |
| 26 | + "Support me ☕": "സപ്പോർട്ട് ചെയ്യുക ☕", |
| 27 | + "See my profile": "പ്രൊഫൈൽ കാണുക", |
| 28 | + "See my posts": "പോസ്റ്റുകൾ കാണുക", |
| 29 | + "Open source contributions": "ഓപ്പൺ സോഴ്സ് സംഭാവനകൾ", |
| 30 | + "Things I Love": "എനിക്ക് ഇഷ്ടമുള്ള കാര്യങ്ങൾ", |
| 31 | + "Technical certificates": "ടെക്നിക്കൽ സർട്ടിഫിക്കറ്റുകൾ", |
| 32 | + "Community Workshops": "കമ്മ്യൂണിറ്റി വർക്ക്ഷോപ്പുകൾ", |
| 33 | + "Best Outgoing Student": "മികച്ച ഔട്ട്ഗോയിങ് വിദ്യാർത്ഥി", |
| 34 | + "Department Topper": "ഡിപ്പാർട്ട്മെന്റ് ടോപ്പർ", |
| 35 | + "Mathematics Topper": "മാത്തമാറ്റിക്സ് ടോപ്പർ", |
| 36 | + "Best Final Year Project": "മികച്ച ഫൈനൽ ഇയർ പ്രോജക്റ്റ്", |
| 37 | + "Nice to meet you": "കണ്ടതിൽ സന്തോഷം", |
| 38 | + About: "കുറിച്ച്", |
| 39 | + Experience: "പരിചയം", |
| 40 | + Achievements: "നേട്ടങ്ങൾ", |
| 41 | + Projects: "പ്രോജക്റ്റുകൾ", |
| 42 | + }, |
| 43 | + zh: { |
| 44 | + "Hi!": "你好!", |
| 45 | + "I am Bala": "我是 Bala", |
| 46 | + "Senior SWE - ELT, Web, OSS Dev": "高级软件工程师 - ELT、Web、开源开发", |
| 47 | + "Remote, India": "远程,印度", |
| 48 | + "Contact Me": "联系我", |
| 49 | + "Before you go... Please reach out if you have any Research Software Engineer Positions available. Very much interested :)": |
| 50 | + "在你离开前... 如果有研究软件工程师职位,请联系我。我非常感兴趣 :)", |
| 51 | + "Portfolio website powered by Next.js, Tailwind CSS": |
| 52 | + "由 Next.js 和 Tailwind CSS 驱动的作品集网站", |
| 53 | + Follow: "关注", |
| 54 | + Subscribe: "订阅", |
| 55 | + Join: "加入", |
| 56 | + "View profile": "查看资料", |
| 57 | + "See badges 🏅": "查看徽章 🏅", |
| 58 | + "Support me ☕": "支持我 ☕", |
| 59 | + "See my profile": "查看我的资料", |
| 60 | + "See my posts": "查看我的文章", |
| 61 | + "Open source contributions": "开源贡献", |
| 62 | + "Things I Love": "我喜欢的事物", |
| 63 | + "Technical certificates": "技术证书", |
| 64 | + "Community Workshops": "社区工作坊", |
| 65 | + "Best Outgoing Student": "最佳毕业生", |
| 66 | + "Department Topper": "系级第一名", |
| 67 | + "Mathematics Topper": "数学第一名", |
| 68 | + "Best Final Year Project": "最佳毕业项目", |
| 69 | + "Nice to meet you": "很高兴认识你", |
| 70 | + About: "关于", |
| 71 | + Experience: "经历", |
| 72 | + Achievements: "成就", |
| 73 | + Projects: "项目", |
| 74 | + }, |
| 75 | + ja: { |
| 76 | + "Hi!": "こんにちは!", |
| 77 | + "I am Bala": "Bala です", |
| 78 | + "Senior SWE - ELT, Web, OSS Dev": "シニアソフトウェアエンジニア - ELT、Web、OSS 開発", |
| 79 | + "Remote, India": "リモート、インド", |
| 80 | + "Contact Me": "連絡する", |
| 81 | + "Before you go... Please reach out if you have any Research Software Engineer Positions available. Very much interested :)": |
| 82 | + "行く前に... 研究ソフトウェアエンジニアの募集があればぜひ連絡してください。とても興味があります :)", |
| 83 | + "Portfolio website powered by Next.js, Tailwind CSS": |
| 84 | + "Next.js と Tailwind CSS で作られたポートフォリオサイト", |
| 85 | + Follow: "フォロー", |
| 86 | + Subscribe: "登録", |
| 87 | + Join: "参加", |
| 88 | + "View profile": "プロフィールを見る", |
| 89 | + "See badges 🏅": "バッジを見る 🏅", |
| 90 | + "Support me ☕": "支援する ☕", |
| 91 | + "See my profile": "プロフィールを見る", |
| 92 | + "See my posts": "投稿を見る", |
| 93 | + "Open source contributions": "オープンソース貢献", |
| 94 | + "Things I Love": "好きなもの", |
| 95 | + "Technical certificates": "技術証明書", |
| 96 | + "Community Workshops": "コミュニティワークショップ", |
| 97 | + "Best Outgoing Student": "最優秀卒業生", |
| 98 | + "Department Topper": "学科首席", |
| 99 | + "Mathematics Topper": "数学首席", |
| 100 | + "Best Final Year Project": "最優秀卒業制作", |
| 101 | + "Nice to meet you": "よろしくお願いします", |
| 102 | + About: "概要", |
| 103 | + Experience: "経験", |
| 104 | + Achievements: "実績", |
| 105 | + Projects: "プロジェクト", |
| 106 | + }, |
| 107 | + hi: { |
| 108 | + "Hi!": "नमस्ते!", |
| 109 | + "I am Bala": "मैं बाला हूं", |
| 110 | + "Senior SWE - ELT, Web, OSS Dev": "सीनियर SWE - ELT, वेब, OSS डेवलपमेंट", |
| 111 | + "Remote, India": "रिमोट, भारत", |
| 112 | + "Contact Me": "संपर्क करें", |
| 113 | + "Before you go... Please reach out if you have any Research Software Engineer Positions available. Very much interested :)": |
| 114 | + "जाने से पहले... अगर कोई Research Software Engineer पद उपलब्ध हो तो कृपया संपर्क करें। बहुत रुचि है :)", |
| 115 | + "Portfolio website powered by Next.js, Tailwind CSS": |
| 116 | + "Next.js और Tailwind CSS से बना पोर्टफोलियो वेबसाइट", |
| 117 | + Follow: "फॉलो करें", |
| 118 | + Subscribe: "सब्सक्राइब", |
| 119 | + Join: "जुड़ें", |
| 120 | + "View profile": "प्रोफाइल देखें", |
| 121 | + "See badges 🏅": "बैज देखें 🏅", |
| 122 | + "Support me ☕": "सपोर्ट करें ☕", |
| 123 | + "See my profile": "मेरी प्रोफाइल देखें", |
| 124 | + "See my posts": "मेरी पोस्ट देखें", |
| 125 | + "Open source contributions": "ओपन सोर्स योगदान", |
| 126 | + "Things I Love": "मेरी पसंदीदा चीजें", |
| 127 | + "Technical certificates": "तकनीकी प्रमाणपत्र", |
| 128 | + "Community Workshops": "कम्युनिटी वर्कशॉप", |
| 129 | + "Best Outgoing Student": "सर्वश्रेष्ठ आउटगोइंग छात्र", |
| 130 | + "Department Topper": "विभाग टॉपर", |
| 131 | + "Mathematics Topper": "गणित टॉपर", |
| 132 | + "Best Final Year Project": "सर्वश्रेष्ठ अंतिम वर्ष प्रोजेक्ट", |
| 133 | + "Nice to meet you": "आपसे मिलकर खुशी हुई", |
| 134 | + About: "परिचय", |
| 135 | + Experience: "अनुभव", |
| 136 | + Achievements: "उपलब्धियां", |
| 137 | + Projects: "प्रोजेक्ट्स", |
| 138 | + }, |
| 139 | +}; |
| 140 | + |
| 141 | +const translateText = (value: string, language: Language) => |
| 142 | + translations[language][value] ?? value; |
| 143 | + |
| 144 | +const translateValue = (value: unknown, language: Language): unknown => { |
| 145 | + if (typeof value === "string") { |
| 146 | + return translations[language][value] ?? value; |
| 147 | + } |
| 148 | + |
| 149 | + if (Array.isArray(value)) { |
| 150 | + return value.map((item) => translateValue(item, language)); |
| 151 | + } |
| 152 | + |
| 153 | + if (value && typeof value === "object") { |
| 154 | + return Object.fromEntries( |
| 155 | + Object.entries(value).map(([key, entry]) => [key, translateValue(entry, language)]) |
| 156 | + ); |
| 157 | + } |
| 158 | + |
| 159 | + return value; |
| 160 | +}; |
| 161 | + |
| 162 | +const I18nContext = createContext<{ |
| 163 | + language: Language; |
| 164 | + setLanguage: (language: Language) => void; |
| 165 | + t: (value: string) => string; |
| 166 | + siteConfig: SiteConfig; |
| 167 | +} | null>(null); |
| 168 | + |
| 169 | +export const I18nProvider = ({ children }: { children: ReactNode }) => { |
| 170 | + const [language, setLanguage] = useState<Language>("en"); |
| 171 | + |
| 172 | + const siteConfig = useMemo( |
| 173 | + () => translateValue(baseSiteConfig, language) as SiteConfig, |
| 174 | + [language] |
| 175 | + ); |
| 176 | + const t = (value: string) => translateText(value, language); |
| 177 | + |
| 178 | + return ( |
| 179 | + <I18nContext.Provider value={{ language, setLanguage, t, siteConfig }}> |
| 180 | + {children} |
| 181 | + </I18nContext.Provider> |
| 182 | + ); |
| 183 | +}; |
| 184 | + |
| 185 | +export const useI18n = () => { |
| 186 | + const context = useContext(I18nContext); |
| 187 | + if (!context) { |
| 188 | + return { |
| 189 | + language: "en" as Language, |
| 190 | + setLanguage: () => {}, |
| 191 | + t: (value: string) => value, |
| 192 | + siteConfig: baseSiteConfig, |
| 193 | + }; |
| 194 | + } |
| 195 | + |
| 196 | + return context; |
| 197 | +}; |
| 198 | + |
| 199 | +export const LanguageToggle = () => { |
| 200 | + const { language, setLanguage } = useI18n(); |
| 201 | + const languages: Language[] = ["en", "zh", "ja", "hi", "ml"]; |
| 202 | + const nextLanguage = languages[(languages.indexOf(language) + 1) % languages.length]; |
| 203 | + |
| 204 | + return ( |
| 205 | + <button |
| 206 | + type="button" |
| 207 | + onClick={() => setLanguage(nextLanguage)} |
| 208 | + className="mt-1 flex cursor-pointer rounded-lg bg-white px-2 py-2 text-sm font-semibold shadow-md transition-colors hover:bg-neutral-100 dark:bg-neutral-800 dark:hover:bg-neutral-700" |
| 209 | + aria-label="Change language" |
| 210 | + title={`Change language to ${nextLanguage.toUpperCase()}`} |
| 211 | + > |
| 212 | + {language.toUpperCase()} |
| 213 | + </button> |
| 214 | + ); |
| 215 | +}; |
0 commit comments