diff --git a/src/modules/core/components/inputs/LanguageSelect.tsx b/src/modules/core/components/inputs/LanguageSelect.tsx index e10401c0..c3f8bd32 100644 --- a/src/modules/core/components/inputs/LanguageSelect.tsx +++ b/src/modules/core/components/inputs/LanguageSelect.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import Button from '@mui/material/Button'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; @@ -21,6 +21,7 @@ import ListItem from '@mui/material/ListItem'; import { useTranslation } from 'react-i18next'; import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx'; import { translateExtensionLanguage } from '@/modules/extension/Extensions.utils.ts'; +import { langSortCmp, toUniqueLanguageCodes } from '@/modules/core/utils/Languages.ts'; interface IProps { selectedLanguages: string[]; @@ -32,16 +33,23 @@ export function LanguageSelect(props: IProps) { const { t } = useTranslation(); const { selectedLanguages, setSelectedLanguages, languages } = props; - const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(selectedLanguages); + const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueLanguageCodes(selectedLanguages)); const [open, setOpen] = useState(false); + const languagesSortedBySelectState = useMemo( + () => + toUniqueLanguageCodes([...tmpSelectedLanguages.toSorted(langSortCmp), ...languages.toSorted(langSortCmp)]), + [languages, tmpSelectedLanguages], + ); + const handleCancel = () => { setOpen(false); + setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages)); }; const handleOk = () => { setOpen(false); - setSelectedLanguages(tmpSelectedLanguages); + setSelectedLanguages(toUniqueLanguageCodes(selectedLanguages)); }; const handleChange = (language: string, selected: boolean) => { @@ -73,7 +81,7 @@ export function LanguageSelect(props: IProps) { {t('global.language.title.enabled_languages')} - {languages.map((language) => ( + {languagesSortedBySelectState.map((language) => ( diff --git a/src/modules/core/utils/Languages.ts b/src/modules/core/utils/Languages.ts index 368187f6..1eae80ff 100644 --- a/src/modules/core/utils/Languages.ts +++ b/src/modules/core/utils/Languages.ts @@ -15,25 +15,39 @@ export enum DefaultLanguage { LOCAL_SOURCE = 'localsourcelang', } -function getISOLanguage(code: string): ISOLanguage | null { +type LanguageObject = ISOLanguage & { orgCode: string; isoCode: string }; + +function getISOLanguage(code: string): LanguageObject | null { if (IsoLanguages[code]) { - return IsoLanguages[code]; + return { + ...IsoLanguages[code], + orgCode: code, + isoCode: code, + }; } if (IsoLanguages[code.toLocaleLowerCase()]) { - return IsoLanguages[code.toLocaleLowerCase()]; + return { + ...IsoLanguages[code.toLocaleLowerCase()], + orgCode: code, + isoCode: code.toLocaleLowerCase(), + }; } const whereToCut = code.indexOf('-') !== -1 ? code.indexOf('-') : code.length; const processedCode = code.toLocaleLowerCase().substring(0, whereToCut); if (IsoLanguages[processedCode]) { - return IsoLanguages[processedCode]; + return { + ...IsoLanguages[processedCode], + orgCode: code, + isoCode: processedCode, + }; } return null; } -export function getLanguage(code: string): { name: string; nativeName: string } { +export function getLanguage(code: string): LanguageObject { const isoLanguage = getISOLanguage(code); if (isoLanguage) { @@ -41,6 +55,8 @@ export function getLanguage(code: string): { name: string; nativeName: string } } return { + orgCode: code, + isoCode: code, name: t('global.language.label.language_with_code', { code }), nativeName: t('global.language.label.language_with_code', { code }), }; @@ -72,3 +88,12 @@ export const langSortCmp = (a: string, b: string) => { return aLang.localeCompare(bLang); }; + +export const toUniqueLanguageCodes = (codes: string[]): string[] => { + const languages = codes.map((code) => getLanguage(code)); + const languagesByIsoCode = Object.groupBy(languages, (language) => language.isoCode); + + return Object.entries(languagesByIsoCode) + .filter(([, languagesOfIsoCode]) => !!languagesOfIsoCode?.length) + .map(([, languagesOfIsoCode]) => languagesOfIsoCode![0].orgCode); +};