Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions frontend/src/i18n/LocaleSelect/LocaleSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import FormControl, { FormControlProps } from '@mui/material/FormControl';
import FormLabel from '@mui/material/FormLabel';
import MenuItem from '@mui/material/MenuItem';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import { useTheme } from '@mui/material/styles';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { supportedLanguages } from '../config';
Expand All @@ -38,7 +37,6 @@ export interface LocaleSelectProps {
export default function LocaleSelect(props: LocaleSelectProps) {
const { formControlProps, showFullNames } = props;
const { t, i18n } = useTranslation();
const theme = useTheme();
/**
* Returns a memoized mapping of language codes to their full names if showFullNames is true.
*
Expand All @@ -62,7 +60,6 @@ export default function LocaleSelect(props: LocaleSelectProps) {
const lng = event.target.value as string;

i18n.changeLanguage(lng);
theme.direction = i18n.dir();
};

/**
Expand All @@ -81,7 +78,7 @@ export default function LocaleSelect(props: LocaleSelectProps) {
return;
}

fullNames[lng] = supportedLanguages[lng] || lng;
fullNames[lng] = supportedLanguages[lng]?.label || lng;
});

return fullNames;
Expand Down
42 changes: 36 additions & 6 deletions frontend/src/i18n/ThemeProviderNexti18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,26 @@
*/

import {
arSA,
Comment thread
mahmoodalisha marked this conversation as resolved.
deDE,
enUS,
esES,
frFR,
heIL,
hiIN,
itIT,
jaJP,
koKR,
ptPT,
ruRU,
urPK,
zhCN,
zhTW,
} from '@mui/material/locale';
import { createTheme, StyledEngineProvider, Theme, ThemeProvider } from '@mui/material/styles';
import React, { ReactNode, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { supportedLanguages } from './config';

/**
* Returns the Material-UI locale object for a given language code.
Expand All @@ -39,6 +43,8 @@ import { useTranslation } from 'react-i18next';
* @returns A Material-UI locale object, defaults to enUS if locale is unsupported.
*/
function getLocale(locale: string): typeof enUS {
const normalizedLocale = locale.toLowerCase();

const LOCALES = {
en: enUS,
pt: ptPT,
Expand All @@ -53,7 +59,11 @@ function getLocale(locale: string): typeof enUS {
'zh-tw': zhTW,
ja: jaJP,
zh: zhCN,
ar: arSA,
ur: urPK,
he: heIL,
};

type LocalesType =
| 'en'
| 'pt'
Expand All @@ -67,20 +77,25 @@ function getLocale(locale: string): typeof enUS {
| 'zh-tw'
| 'ja'
| 'ko'
| 'zh';
return locale in LOCALES ? LOCALES[locale as LocalesType] : LOCALES['en'];
| 'zh'
| 'ar'
| 'ur'
| 'he';

return normalizedLocale in LOCALES ? LOCALES[normalizedLocale as LocalesType] : LOCALES['en'];
}

interface ThemeProviderNexti18nProps {
/** The Material-UI theme to apply. */
theme: Theme;

/** The child components to render within the theme provider. */
children: ReactNode;
}

/**
* A ThemeProvider that integrates with react-i18next for language selection,
* applying Material-UI localization based on the selected language.
* applying Material-UI localization and RTL/LTR direction support.
*
* @param props - The properties to configure the ThemeProviderNexti18n component.
* @returns A themed container with localized child components.
Expand All @@ -89,6 +104,7 @@ const ThemeProviderNexti18n: React.FunctionComponent<ThemeProviderNexti18nProps>
const { i18n, ready: isI18nReady } = useTranslation(['translation', 'glossary'], {
useSuspense: false,
});

const [lang, setLang] = useState(i18n.language);

/**
Expand All @@ -98,8 +114,12 @@ const ThemeProviderNexti18n: React.FunctionComponent<ThemeProviderNexti18nProps>
*/
function changeLang(lng: string) {
if (lng) {
const dir = supportedLanguages[lng]?.dir || 'ltr';

document.documentElement.lang = lng;
document.body.dir = i18n.dir();
document.documentElement.dir = dir;
document.body.dir = dir;

setLang(lng);
}
}
Expand All @@ -109,17 +129,27 @@ const ThemeProviderNexti18n: React.FunctionComponent<ThemeProviderNexti18nProps>
*/
useEffect(() => {
i18n.on('languageChanged', changeLang);

if (i18n.language) {
// Set the lang when the page loads too.
// Set the language when the page loads.
changeLang(i18n.language);
}

return () => {
i18n.off('languageChanged', changeLang);
};

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const theme = createTheme(props.theme, getLocale(lang));
const theme = createTheme(
{
...props.theme,
// Use the local config metadata for the theme direction too
direction: supportedLanguages[lang]?.dir || 'ltr',
},
getLocale(lang)
Comment thread
mahmoodalisha marked this conversation as resolved.
Comment thread
mahmoodalisha marked this conversation as resolved.
);

return (
<StyledEngineProvider injectFirst>
Expand Down
86 changes: 72 additions & 14 deletions frontend/src/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,80 @@ import sharedConfig from './i18nextSharedConfig.mjs';

const en = {}; // To keep TS happy.

export const supportedLanguages: { [langCode: string]: string } = {
en: 'English',
es: 'Español',
fr: 'Français',
pt: 'Português',
ru: 'Русский',
de: 'Deutsch',
it: 'Italiano',
'zh-TW': '繁體中文',
zh: '简体中文',
ko: '한국어',
ja: '日本語',
hi: 'हिन्दी',
ta: 'தமிழ்',
export const supportedLanguages: {
[langCode: string]: {
label: string;
dir: 'ltr' | 'rtl';
};
} = {
en: {
label: 'English',
dir: 'ltr',
},
es: {
label: 'Español',
dir: 'ltr',
},
fr: {
label: 'Français',
dir: 'ltr',
},
ru: {
label: 'Русский',
dir: 'ltr',
},
pt: {
label: 'Português',
dir: 'ltr',
},
de: {
label: 'Deutsch',
dir: 'ltr',
},
it: {
label: 'Italiano',
dir: 'ltr',
},
'zh-TW': {
label: '繁體中文',
dir: 'ltr',
},
zh: {
label: '简体中文',
dir: 'ltr',
},
ko: {
label: '한국어',
dir: 'ltr',
},
ja: {
label: '日本語',
dir: 'ltr',
},
hi: {
label: 'हिन्दी',
dir: 'ltr',
},
ta: {
label: 'தமிழ்',
dir: 'ltr',
},
ar: {
label: 'العربية',
dir: 'rtl',
},
ur: {
label: 'اردو',
dir: 'rtl',
},
he: {
label: 'עברית',
dir: 'rtl',
Comment thread
mahmoodalisha marked this conversation as resolved.
},
};

export const isRTL = (lang: string) => supportedLanguages[lang]?.dir === 'rtl';

i18next
// detect user language https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
Expand Down
57 changes: 57 additions & 0 deletions frontend/src/i18n/locales/ar/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"Failed to fetch plugin info": "فشل جلب معلومات الإضافة",
"An error occurred while fetching plugin info from {{ URL }}.": "حدث خطأ أثناء جلب معلومات الإضافة من {{ URL }}.",
"Yes": "نعم",
"No": "لا",
"Plugin Installation": "تثبيت الإضافة",
"Do you want to install the plugin \"{{ pluginName }}\"?": "هل تريد تثبيت الإضافة \"{{ pluginName }}\"؟",
"You are about to install a plugin from: {{ url }}\nDo you want to proceed?": "أنت على وشك تثبيت إضافة من: {{ url }}\nهل تريد المتابعة؟",
"About": "حول",
"Quit": "إنهاء",
"Select All": "تحديد الكل",
"Delete": "حذف",
"Services": "الخدمات",
"Hide": "إخفاء",
"Hide Others": "إخفاء الآخرين",
"Show All": "إظهار الكل",
"File": "ملف",
"Close": "إغلاق",
"Edit": "تعديل",
"Cut": "قص",
"Copy": "نسخ",
"Paste": "لصق",
"Paste and Match Style": "لصق مع مطابقة التنسيق",
"Speech": "الكلام",
"Start Speaking": "بدء التحدث",
"Stop Speaking": "إيقاف التحدث",
"View": "عرض",
"Toggle Developer Tools": "تبديل أدوات المطور",
"Reset Zoom": "إعادة ضبط التكبير",
"Zoom In": "تكبير",
"Zoom Out": "تصغير",
"Toggle Fullscreen": "تبديل ملء الشاشة",
"Navigate": "التنقل",
"Reload": "إعادة تحميل",
"Go to Home": "الانتقال إلى الصفحة الرئيسية",
"Go Back": "رجوع",
"Go Forward": "تقدم",
"Window": "نافذة",
"Minimize": "تصغير",
"Bring All to Front": "إحضار الكل إلى الأمام",
"Help": "مساعدة",
"Documentation": "التوثيق",
"Open an Issue": "فتح مشكلة",
"Another process is running": "هناك عملية أخرى قيد التشغيل",
"Looks like another process is already running. Continue by terminating that process automatically, or quit?": "يبدو أن هناك عملية أخرى قيد التشغيل بالفعل. هل تريد المتابعة بإنهاء العملية تلقائيًا أم الخروج؟",
"Continue": "متابعة",
"Failed to quit the other running process": "فشل إنهاء العملية الأخرى",
"Could not quit the other running process, PIDs: {{ process_list }}. Please stop that process and relaunch the app.": "تعذر إنهاء العملية الأخرى، المعرفات: {{ process_list }}. يرجى إيقاف العملية ثم إعادة تشغيل التطبيق.",
"No available ports": "لا توجد منافذ متاحة",
"Could not find an available port. There are processes running on ports {{startPort}}-{{endPort}}. Terminate these processes and retry?": "تعذر العثور على منفذ متاح. توجد عمليات تعمل على المنافذ {{startPort}}-{{endPort}}. هل تريد إنهاء هذه العمليات وإعادة المحاولة؟",
"Terminate and Retry": "إنهاء وإعادة المحاولة",
"Failed to start": "فشل التشغيل",
"Could not start the server even after terminating existing processes.": "تعذر تشغيل الخادم حتى بعد إنهاء العمليات الحالية.",
"Could not find an available port in the range {{startPort}}-{{endPort}}. Please free up a port and try again.": "تعذر العثور على منفذ متاح في النطاق {{startPort}}-{{endPort}}. يرجى تحرير منفذ والمحاولة مرة أخرى.",
"Invalid URL": "رابط غير صالح",
"Application opened with an invalid URL: {{ url }}": "تم فتح التطبيق برابط غير صالح: {{ url }}"
}
Loading
Loading