From 8e7f25b3a61693cbfd57516830a044c3574a9d01 Mon Sep 17 00:00:00 2001 From: Chris Veleris Date: Sun, 13 Jul 2025 14:40:36 +0300 Subject: [PATCH] Add multilanguage support --- README.md | 2 +- .../components/Shared/LanguageDropdown.tsx | 18 + frontend/i18n.ts | 2 +- index.html | 197 +- package.json | 7 +- public/locales/ar/quotes.json | 24 + public/locales/ar/translation.json | 733 +++ public/locales/bg/quotes.json | 24 + public/locales/bg/translation.json | 733 +++ public/locales/da/quotes.json | 24 + public/locales/da/translation.json | 740 +++ public/locales/de/translation.json | 448 +- public/locales/el/translation.json | 163 +- public/locales/en/translation.json | 18 + public/locales/es/translation.json | 214 +- public/locales/fi/quotes.json | 24 + public/locales/fi/translation.json | 740 +++ public/locales/fr/quotes.json | 24 + public/locales/fr/translation.json | 740 +++ public/locales/id/quotes.json | 24 + public/locales/id/translation.json | 740 +++ public/locales/it/quotes.json | 9 +- public/locales/it/translation.json | 26 +- public/locales/jp/translation.json | 453 +- public/locales/ko/quotes.json | 24 + public/locales/ko/translation.json | 740 +++ public/locales/nl/quotes.json | 24 + public/locales/nl/translation.json | 740 +++ public/locales/no/quotes.json | 24 + public/locales/no/translation.json | 740 +++ public/locales/pl/quotes.json | 24 + public/locales/pl/translation.json | 1 + public/locales/pt/quotes.json | 24 + public/locales/pt/translation.json | 733 +++ public/locales/ro/quotes.json | 24 + public/locales/ro/translation.json | 733 +++ public/locales/ru/quotes.json | 24 + public/locales/ru/translation.json | 733 +++ public/locales/sl/quotes.json | 24 + public/locales/sl/translation.json | 1 + public/locales/sv/quotes.json | 24 + public/locales/sv/translation.json | 1 + public/locales/tr/quotes.json | 24 + public/locales/tr/translation.json | 733 +++ public/locales/ua/translation.json | 443 +- public/locales/vi/quotes.json | 24 + public/locales/vi/translation.json | 733 +++ public/locales/zh/quotes.json | 24 + public/locales/zh/translation.json | 733 +++ scripts/README.md | 231 + scripts/example-usage.sh | 55 + scripts/missing-translations.json | 4391 +++++++++++++++++ scripts/package-lock.json | 28 + scripts/package.json | 17 + scripts/sync-translations.js | 684 +++ 55 files changed, 18613 insertions(+), 272 deletions(-) create mode 100644 public/locales/ar/quotes.json create mode 100644 public/locales/ar/translation.json create mode 100644 public/locales/bg/quotes.json create mode 100644 public/locales/bg/translation.json create mode 100644 public/locales/da/quotes.json create mode 100644 public/locales/da/translation.json create mode 100644 public/locales/fi/quotes.json create mode 100644 public/locales/fi/translation.json create mode 100644 public/locales/fr/quotes.json create mode 100644 public/locales/fr/translation.json create mode 100644 public/locales/id/quotes.json create mode 100644 public/locales/id/translation.json create mode 100644 public/locales/ko/quotes.json create mode 100644 public/locales/ko/translation.json create mode 100644 public/locales/nl/quotes.json create mode 100644 public/locales/nl/translation.json create mode 100644 public/locales/no/quotes.json create mode 100644 public/locales/no/translation.json create mode 100644 public/locales/pl/quotes.json create mode 100644 public/locales/pl/translation.json create mode 100644 public/locales/pt/quotes.json create mode 100644 public/locales/pt/translation.json create mode 100644 public/locales/ro/quotes.json create mode 100644 public/locales/ro/translation.json create mode 100644 public/locales/ru/quotes.json create mode 100644 public/locales/ru/translation.json create mode 100644 public/locales/sl/quotes.json create mode 100644 public/locales/sl/translation.json create mode 100644 public/locales/sv/quotes.json create mode 100644 public/locales/sv/translation.json create mode 100644 public/locales/tr/quotes.json create mode 100644 public/locales/tr/translation.json create mode 100644 public/locales/vi/quotes.json create mode 100644 public/locales/vi/translation.json create mode 100644 public/locales/zh/quotes.json create mode 100644 public/locales/zh/translation.json create mode 100644 scripts/README.md create mode 100755 scripts/example-usage.sh create mode 100644 scripts/missing-translations.json create mode 100644 scripts/package-lock.json create mode 100644 scripts/package.json create mode 100755 scripts/sync-translations.js diff --git a/README.md b/README.md index 10c37c1..11fa121 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ For the thinking behind tududi, read [Designing a Life Management System That Do - **Area Categorization**: Group projects into areas for better organization and focus. - **Due Date Tracking**: Set due dates for tasks and view them based on due date categories. - **Responsive Design**: Accessible from various devices, ensuring a consistent experience across desktops, tablets, and mobile phones. -- **Multi-Language Support**: Available in multiple languages including English, German (de), Greek (el), Spanish (es), Japanese (jp), and Ukrainian (ua) among others. +- **Multi-Language Support**: Available in 24 languages with full localization support for a truly global productivity experience. - **Telegram Integration**: - Create tasks directly through Telegram messages - Receive daily digests of your tasks diff --git a/frontend/components/Shared/LanguageDropdown.tsx b/frontend/components/Shared/LanguageDropdown.tsx index f2bc9bd..32458d1 100644 --- a/frontend/components/Shared/LanguageDropdown.tsx +++ b/frontend/components/Shared/LanguageDropdown.tsx @@ -24,13 +24,31 @@ const LanguageDropdown: React.FC = ({ const dropdownRef = useRef(null); const languages: LanguageOption[] = [ + { code: 'ar', name: t('profile.arabic'), flag: '🇸🇦' }, + { code: 'bg', name: t('profile.bulgarian'), flag: '🇧🇬' }, + { code: 'zh', name: t('profile.chinese'), flag: '🇨🇳' }, + { code: 'da', name: t('profile.danish'), flag: '🇩🇰' }, { code: 'de', name: t('profile.deutsch'), flag: '🇩🇪' }, + { code: 'nl', name: t('profile.dutch'), flag: '🇳🇱' }, { code: 'en', name: t('profile.english'), flag: '🇺🇸' }, + { code: 'fi', name: t('profile.finnish'), flag: '🇫🇮' }, + { code: 'fr', name: t('profile.french'), flag: '🇫🇷' }, { code: 'el', name: t('profile.greek'), flag: '🇬🇷' }, + { code: 'id', name: t('profile.indonesian'), flag: '🇮🇩' }, { code: 'it', name: t('profile.italian'), flag: '🇮🇹' }, { code: 'jp', name: t('profile.japanese'), flag: '🇯🇵' }, + { code: 'ko', name: t('profile.korean'), flag: '🇰🇷' }, + { code: 'no', name: t('profile.norwegian'), flag: '🇳🇴' }, + { code: 'pl', name: t('profile.polish'), flag: '🇵🇱' }, + { code: 'pt', name: t('profile.portuguese'), flag: '🇵🇹' }, + { code: 'ro', name: t('profile.romanian'), flag: '🇷🇴' }, + { code: 'ru', name: t('profile.russian'), flag: '🇷🇺' }, + { code: 'sl', name: t('profile.slovenian'), flag: '🇸🇮' }, { code: 'es', name: t('profile.spanish'), flag: '🇪🇸' }, + { code: 'sv', name: t('profile.swedish'), flag: '🇸🇪' }, + { code: 'tr', name: t('profile.turkish'), flag: '🇹🇷' }, { code: 'ua', name: t('profile.ukrainian'), flag: '🇺🇦' }, + { code: 'vi', name: t('profile.vietnamese'), flag: '🇻🇳' }, ].sort((a, b) => a.name.localeCompare(b.name)); const selectedLanguage = languages.find(lang => lang.code === value) || languages[0]; diff --git a/frontend/i18n.ts b/frontend/i18n.ts index ed48671..7bb9f6a 100644 --- a/frontend/i18n.ts +++ b/frontend/i18n.ts @@ -42,7 +42,7 @@ i18nInstance fallbackLng: 'en', debug: false, load: 'languageOnly', - supportedLngs: ['en', 'es', 'el', 'jp', 'ua', 'de', 'it'], + supportedLngs: ['en', 'es', 'el', 'jp', 'ua', 'de', 'it', 'fr', 'ru', 'tr', 'ko', 'vi', 'ar', 'nl', 'ro', 'zh', 'pt', 'id', 'no', 'fi', 'da', 'sv', 'pl', 'bg', 'sl'], nonExplicitSupportedLngs: true, resources: devResources, detection: { diff --git a/index.html b/index.html index 7338cd0..878b9c1 100644 --- a/index.html +++ b/index.html @@ -323,6 +323,88 @@ color: var(--secondary-color); } + /* Languages Section */ + .languages { + padding: 100px 0; + background: linear-gradient(135deg, var(--primary-color) 0%, #1565c0 100%); + color: white; + position: relative; + overflow: hidden; + } + + .languages::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,'); + opacity: 0.3; + } + + .languages .container { + position: relative; + z-index: 1; + } + + .languages .section-header { + margin-bottom: 60px; + } + + .languages .section-header h2 { + color: white; + font-size: 2.8rem; + margin-bottom: 20px; + } + + .languages .section-header p { + color: rgba(255, 255, 255, 0.9); + font-size: 1.2rem; + } + + .languages-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 20px; + max-width: 1000px; + margin: 0 auto; + } + + .language-item { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 15px; + padding: 20px 15px; + text-align: center; + transition: all 0.3s ease; + cursor: pointer; + } + + .language-item:hover { + transform: translateY(-5px) scale(1.02); + background: rgba(255, 255, 255, 0.15); + border-color: rgba(255, 255, 255, 0.4); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + } + + .language-flag { + font-size: 2.5rem; + margin-bottom: 10px; + display: block; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)); + } + + .language-name { + font-weight: 600; + font-size: 0.9rem; + color: white; + margin: 0; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + } + + /* Screenshots */ .screenshots { padding: 100px 0; @@ -580,6 +662,7 @@