Split up language utils file
This commit is contained in:
@@ -21,7 +21,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Virtuoso } from 'react-virtuoso';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
||||||
import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts';
|
import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts';
|
||||||
import { languageSortComparator, toUniqueLanguageCodes } from '@/base/utils/Languages.ts';
|
import { languageSortComparator } from '@/base/utils/Languages.ts';
|
||||||
|
import { toUniqueISOLanguageCodes } from '@/lib/ISOLanguageUtil.ts';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
selectedLanguages: string[];
|
selectedLanguages: string[];
|
||||||
@@ -33,12 +34,12 @@ export function LanguageSelect(props: IProps) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { selectedLanguages, setSelectedLanguages, languages } = props;
|
const { selectedLanguages, setSelectedLanguages, languages } = props;
|
||||||
const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueLanguageCodes(selectedLanguages));
|
const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueISOLanguageCodes(selectedLanguages));
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const languagesSortedBySelectState = useMemo(
|
const languagesSortedBySelectState = useMemo(
|
||||||
() =>
|
() =>
|
||||||
toUniqueLanguageCodes([
|
toUniqueISOLanguageCodes([
|
||||||
...tmpSelectedLanguages.toSorted(languageSortComparator),
|
...tmpSelectedLanguages.toSorted(languageSortComparator),
|
||||||
...languages.toSorted(languageSortComparator),
|
...languages.toSorted(languageSortComparator),
|
||||||
]),
|
]),
|
||||||
@@ -47,12 +48,12 @@ export function LanguageSelect(props: IProps) {
|
|||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages));
|
setTmpSelectedLanguages(toUniqueISOLanguageCodes(selectedLanguages));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setSelectedLanguages(toUniqueLanguageCodes(tmpSelectedLanguages));
|
setSelectedLanguages(toUniqueISOLanguageCodes(tmpSelectedLanguages));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (language: string, selected: boolean) => {
|
const handleChange = (language: string, selected: boolean) => {
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { ISOLanguage, IsoLanguages } from '@/base/IsoLanguages.ts';
|
|
||||||
|
|
||||||
import { TranslationKey } from '@/base/Base.types.ts';
|
import { TranslationKey } from '@/base/Base.types.ts';
|
||||||
|
|
||||||
|
import { getISOLanguage, getPreferredISOLanguageCodes, LanguageObject } from '@/lib/ISOLanguageUtil.ts';
|
||||||
|
|
||||||
export enum DefaultLanguage {
|
export enum DefaultLanguage {
|
||||||
ALL = 'all',
|
ALL = 'all',
|
||||||
OTHER = 'other',
|
OTHER = 'other',
|
||||||
@@ -27,31 +27,6 @@ const DEFAULT_LANGUAGE_TO_TRANSLATION: Record<DefaultLanguage, TranslationKey> =
|
|||||||
[DefaultLanguage.LAST_USED_SOURCE]: 'global.label.last_used',
|
[DefaultLanguage.LAST_USED_SOURCE]: 'global.label.last_used',
|
||||||
};
|
};
|
||||||
|
|
||||||
type LanguageObject = ISOLanguage & { orgCode: string; isoCode: string };
|
|
||||||
|
|
||||||
function getISOLanguageFor(code: string, orgCode: string = code, isoCode: string = code): LanguageObject | null {
|
|
||||||
if (IsoLanguages[code]) {
|
|
||||||
return {
|
|
||||||
...IsoLanguages[code],
|
|
||||||
orgCode,
|
|
||||||
isoCode,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getISOLanguage(code: string): LanguageObject | null {
|
|
||||||
return (
|
|
||||||
getISOLanguageFor(code) ??
|
|
||||||
getISOLanguageFor(code.toLowerCase(), code) ??
|
|
||||||
getISOLanguageFor(code.replace('-', '_'), code) ??
|
|
||||||
getISOLanguageFor(code.replace('_', '-'), code) ??
|
|
||||||
getISOLanguageFor(code.split('-')[0], code) ??
|
|
||||||
getISOLanguageFor(code.split('_')[0], code)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLanguage(code: string): LanguageObject {
|
export function getLanguage(code: string): LanguageObject {
|
||||||
const isoLanguage = getISOLanguage(code);
|
const isoLanguage = getISOLanguage(code);
|
||||||
|
|
||||||
@@ -76,31 +51,12 @@ export function languageCodeToName(code: string): string {
|
|||||||
return getLanguage(code).nativeName;
|
return getLanguage(code).nativeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const toComparableLanguage = (code: string): string => getLanguage(code).isoCode;
|
export const toComparableLanguage = (code: string): string => getLanguage(code).isoCode;
|
||||||
|
|
||||||
export const toComparableLanguages = (codes: string[]): string[] => codes.map(toComparableLanguage);
|
export const toComparableLanguages = (codes: string[]): string[] => codes.map(toComparableLanguage);
|
||||||
|
|
||||||
function defaultNativeLang(): readonly string[] {
|
|
||||||
const preferredLanguages = toUniqueLanguageCodes([...navigator.languages]);
|
|
||||||
|
|
||||||
if (!preferredLanguages.length) {
|
|
||||||
return ['en'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return preferredLanguages;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDefaultLanguages(): string[] {
|
export function getDefaultLanguages(): string[] {
|
||||||
return [...defaultNativeLang(), DefaultLanguage.ALL];
|
return [...getPreferredISOLanguageCodes(), DefaultLanguage.ALL];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
languageSpecialSortComparator,
|
languageSpecialSortComparator,
|
||||||
toComparableLanguage,
|
toComparableLanguage,
|
||||||
toComparableLanguages,
|
toComparableLanguages,
|
||||||
toUniqueLanguageCodes,
|
|
||||||
} from '@/base/utils/Languages.ts';
|
} from '@/base/utils/Languages.ts';
|
||||||
import {
|
import {
|
||||||
EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP,
|
EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP,
|
||||||
@@ -31,6 +30,7 @@ import { enhancedCleanup } from '@/base/utils/Strings.ts';
|
|||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { makeToast } from '@/base/utils/Toast.ts';
|
import { makeToast } from '@/base/utils/Toast.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
|
import { toUniqueISOLanguageCodes } from '@/lib/ISOLanguageUtil.ts';
|
||||||
|
|
||||||
export const getInstalledState = (
|
export const getInstalledState = (
|
||||||
isInstalled: boolean,
|
isInstalled: boolean,
|
||||||
@@ -131,7 +131,7 @@ export const filterExtensions = (
|
|||||||
query?: string | null | undefined;
|
query?: string | null | undefined;
|
||||||
} = {},
|
} = {},
|
||||||
): TExtension[] => {
|
): TExtension[] => {
|
||||||
const normalizedSelectedLanguages = toComparableLanguages(toUniqueLanguageCodes(selectedLanguages ?? []));
|
const normalizedSelectedLanguages = toComparableLanguages(toUniqueISOLanguageCodes(selectedLanguages ?? []));
|
||||||
|
|
||||||
return extensions
|
return extensions
|
||||||
.filter(
|
.filter(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import Box from '@mui/material/Box';
|
|||||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
||||||
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
||||||
import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts';
|
import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts';
|
||||||
import { languageSortComparator, toUniqueLanguageCodes } from '@/base/utils/Languages.ts';
|
import { languageSortComparator } from '@/base/utils/Languages.ts';
|
||||||
import {
|
import {
|
||||||
SourceDisplayNameInfo,
|
SourceDisplayNameInfo,
|
||||||
SourceIconInfo,
|
SourceIconInfo,
|
||||||
@@ -38,6 +38,7 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
|
|||||||
import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx';
|
import { ListCardAvatar } from '@/base/components/lists/cards/ListCardAvatar.tsx';
|
||||||
import { makeToast } from '@/base/utils/Toast.ts';
|
import { makeToast } from '@/base/utils/Toast.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
|
import { toUniqueISOLanguageCodes } from '@/lib/ISOLanguageUtil.ts';
|
||||||
|
|
||||||
export const SourceLanguageSelect = ({
|
export const SourceLanguageSelect = ({
|
||||||
selectedLanguages,
|
selectedLanguages,
|
||||||
@@ -59,14 +60,14 @@ export const SourceLanguageSelect = ({
|
|||||||
|
|
||||||
const [tmpSourceIdToEnabledState, setTmpSourceIdToEnabledState] = useState<Record<SourceIdInfo['id'], boolean>>({});
|
const [tmpSourceIdToEnabledState, setTmpSourceIdToEnabledState] = useState<Record<SourceIdInfo['id'], boolean>>({});
|
||||||
|
|
||||||
const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueLanguageCodes(selectedLanguages));
|
const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueISOLanguageCodes(selectedLanguages));
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const sourcesByLanguage = useMemo(() => Sources.groupByLanguage(sources), [sources]);
|
const sourcesByLanguage = useMemo(() => Sources.groupByLanguage(sources), [sources]);
|
||||||
|
|
||||||
const languagesSortedBySelectState = useMemo(
|
const languagesSortedBySelectState = useMemo(
|
||||||
() =>
|
() =>
|
||||||
toUniqueLanguageCodes([
|
toUniqueISOLanguageCodes([
|
||||||
...tmpSelectedLanguages.toSorted(languageSortComparator),
|
...tmpSelectedLanguages.toSorted(languageSortComparator),
|
||||||
...languages.toSorted(languageSortComparator),
|
...languages.toSorted(languageSortComparator),
|
||||||
]),
|
]),
|
||||||
@@ -77,7 +78,7 @@ export const SourceLanguageSelect = ({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
||||||
setTmpSourceIdToEnabledState({});
|
setTmpSourceIdToEnabledState({});
|
||||||
setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages));
|
setTmpSelectedLanguages(toUniqueISOLanguageCodes(selectedLanguages));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
@@ -89,7 +90,7 @@ export const SourceLanguageSelect = ({
|
|||||||
),
|
),
|
||||||
).catch((e) => makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)));
|
).catch((e) => makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)));
|
||||||
setTmpSourceIdToEnabledState({});
|
setTmpSourceIdToEnabledState({});
|
||||||
setSelectedLanguages(toUniqueLanguageCodes(tmpSelectedLanguages));
|
setSelectedLanguages(toUniqueISOLanguageCodes(tmpSelectedLanguages));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (language: string, selected: boolean) => {
|
const handleChange = (language: string, selected: boolean) => {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
languageSpecialSortComparator,
|
languageSpecialSortComparator,
|
||||||
toComparableLanguage,
|
toComparableLanguage,
|
||||||
toComparableLanguages,
|
toComparableLanguages,
|
||||||
toUniqueLanguageCodes,
|
|
||||||
} from '@/base/utils/Languages.ts';
|
} from '@/base/utils/Languages.ts';
|
||||||
import { getSourceMetadata } from '@/features/source/services/SourceMetadata.ts';
|
import { getSourceMetadata } from '@/features/source/services/SourceMetadata.ts';
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +29,7 @@ import {
|
|||||||
} from '@/features/settings/services/ServerSettingsMetadata.ts';
|
} from '@/features/settings/services/ServerSettingsMetadata.ts';
|
||||||
import { makeToast } from '@/base/utils/Toast.ts';
|
import { makeToast } from '@/base/utils/Toast.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
|
import { toUniqueISOLanguageCodes } from '@/lib/ISOLanguageUtil.ts';
|
||||||
|
|
||||||
export class Sources {
|
export class Sources {
|
||||||
static readonly LOCAL_SOURCE_ID = '0';
|
static readonly LOCAL_SOURCE_ID = '0';
|
||||||
@@ -98,7 +98,7 @@ export class Sources {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
} = {},
|
} = {},
|
||||||
): Source[] {
|
): Source[] {
|
||||||
const normalizedLanguages = toComparableLanguages(toUniqueLanguageCodes(languages ?? []));
|
const normalizedLanguages = toComparableLanguages(toUniqueISOLanguageCodes(languages ?? []));
|
||||||
|
|
||||||
return sources
|
return sources
|
||||||
.filter(
|
.filter(
|
||||||
|
|||||||
55
src/lib/ISOLanguageUtil.ts
Normal file
55
src/lib/ISOLanguageUtil.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ISOLanguage, IsoLanguages } from '@/base/IsoLanguages.ts';
|
||||||
|
|
||||||
|
export type LanguageObject = ISOLanguage & { orgCode: string; isoCode: string };
|
||||||
|
|
||||||
|
export const DEFAULT_LANGUAGE = 'en';
|
||||||
|
|
||||||
|
export function getISOLanguageFor(code: string, orgCode: string = code, isoCode: string = code): LanguageObject | null {
|
||||||
|
if (IsoLanguages[code]) {
|
||||||
|
return {
|
||||||
|
...IsoLanguages[code],
|
||||||
|
orgCode,
|
||||||
|
isoCode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getISOLanguage(code: string): LanguageObject | null {
|
||||||
|
return (
|
||||||
|
getISOLanguageFor(code) ??
|
||||||
|
getISOLanguageFor(code.toLowerCase(), code) ??
|
||||||
|
getISOLanguageFor(code.replace('-', '_'), code) ??
|
||||||
|
getISOLanguageFor(code.replace('_', '-'), code) ??
|
||||||
|
getISOLanguageFor(code.split('-')[0], code) ??
|
||||||
|
getISOLanguageFor(code.split('_')[0], code)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toUniqueISOLanguageCodes = (codes: string[]): string[] => {
|
||||||
|
const languages = codes.map((code) => getISOLanguage(code)).filter((code) => code !== null);
|
||||||
|
const languagesByIsoCode = Object.groupBy(languages, (language) => language.isoCode);
|
||||||
|
|
||||||
|
return Object.entries(languagesByIsoCode)
|
||||||
|
.filter(([, languagesOfIsoCode]) => !!languagesOfIsoCode?.length)
|
||||||
|
.map(([, languagesOfIsoCode]) => languagesOfIsoCode![0].orgCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getPreferredISOLanguageCodes(): readonly string[] {
|
||||||
|
const preferredLanguages = toUniqueISOLanguageCodes([...navigator.languages]);
|
||||||
|
|
||||||
|
if (!preferredLanguages.length) {
|
||||||
|
return [DEFAULT_LANGUAGE];
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferredLanguages;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user