From 944e33f24bb32fbc2822837394b4e0fbc4c426a5 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 7 Feb 2026 19:20:30 -0800 Subject: [PATCH] Convert getLanguageName to hook --- frontend/src/Episode/Summary/MediaInfo.tsx | 4 +- frontend/src/EpisodeFile/MediaInfo.tsx | 18 +++++-- frontend/src/Helpers/Hooks/useAppPage.ts | 2 + frontend/src/Language/useLanguageName.ts | 53 +++++++++++++++++++ .../src/Utilities/String/getLanguageName.ts | 41 -------------- 5 files changed, 72 insertions(+), 46 deletions(-) create mode 100644 frontend/src/Language/useLanguageName.ts delete mode 100644 frontend/src/Utilities/String/getLanguageName.ts diff --git a/frontend/src/Episode/Summary/MediaInfo.tsx b/frontend/src/Episode/Summary/MediaInfo.tsx index ef5eb499a..d9c80db69 100644 --- a/frontend/src/Episode/Summary/MediaInfo.tsx +++ b/frontend/src/Episode/Summary/MediaInfo.tsx @@ -1,13 +1,15 @@ import React from 'react'; import DescriptionList from 'Components/DescriptionList/DescriptionList'; import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem'; +import useLanguageName from 'Language/useLanguageName'; import MediaInfoProps from 'typings/MediaInfo'; import formatBitrate from 'Utilities/Number/formatBitrate'; import getEntries from 'Utilities/Object/getEntries'; -import getLanguageName from 'Utilities/String/getLanguageName'; import translate from 'Utilities/String/translate'; function MediaInfo(props: MediaInfoProps) { + const getLanguageName = useLanguageName(); + return ( {getEntries(props).map(([key, value]) => { diff --git a/frontend/src/EpisodeFile/MediaInfo.tsx b/frontend/src/EpisodeFile/MediaInfo.tsx index f737e6fb4..de137f0d2 100644 --- a/frontend/src/EpisodeFile/MediaInfo.tsx +++ b/frontend/src/EpisodeFile/MediaInfo.tsx @@ -1,9 +1,12 @@ import React from 'react'; -import getLanguageName from 'Utilities/String/getLanguageName'; +import useLanguageName from 'Language/useLanguageName'; import translate from 'Utilities/String/translate'; import { useEpisodeFile } from './EpisodeFileProvider'; -function formatLanguages(languages: string[] | undefined) { +function formatLanguages( + languages: string[] | undefined, + getLanguageName: (code: string) => string +) { if (!languages) { return null; } @@ -43,6 +46,7 @@ interface MediaInfoProps { } function MediaInfo({ episodeFileId, type }: MediaInfoProps) { + const getLanguageName = useLanguageName(); const episodeFile = useEpisodeFile(episodeFileId); if (!episodeFile?.mediaInfo) { @@ -76,11 +80,17 @@ function MediaInfo({ episodeFileId, type }: MediaInfoProps) { } if (type === 'audioLanguages') { - return formatLanguages(audioStreams.map(({ language }) => language)); + return formatLanguages( + audioStreams.map(({ language }) => language), + getLanguageName + ); } if (type === 'subtitles') { - return formatLanguages(subtitleStreams.map(({ language }) => language)); + return formatLanguages( + subtitleStreams.map(({ language }) => language), + getLanguageName + ); } if (type === 'video') { diff --git a/frontend/src/Helpers/Hooks/useAppPage.ts b/frontend/src/Helpers/Hooks/useAppPage.ts index cd554f188..dfd069bd1 100644 --- a/frontend/src/Helpers/Hooks/useAppPage.ts +++ b/frontend/src/Helpers/Hooks/useAppPage.ts @@ -5,6 +5,7 @@ import AppState from 'App/State/AppState'; import { useTranslations } from 'App/useTranslations'; import useCommands from 'Commands/useCommands'; import useCustomFilters from 'Filters/useCustomFilters'; +import { useInitializeLanguage } from 'Language/useLanguageName'; import useSeries from 'Series/useSeries'; import { useQualityProfiles } from 'Settings/Profiles/Quality/useQualityProfiles'; import { useUiSettings } from 'Settings/UI/useUiSettings'; @@ -76,6 +77,7 @@ const useAppPage = () => { const dispatch = useDispatch(); useCommands(); + useInitializeLanguage(); const { isFetched: isCustomFiltersFetched, error: customFiltersError } = useCustomFilters(); diff --git a/frontend/src/Language/useLanguageName.ts b/frontend/src/Language/useLanguageName.ts new file mode 100644 index 000000000..b451ccd68 --- /dev/null +++ b/frontend/src/Language/useLanguageName.ts @@ -0,0 +1,53 @@ +import { useCallback } from 'react'; +import useApiQuery from 'Helpers/Hooks/useApiQuery'; + +interface LanguageResponse { + identifier: string; +} + +function getDisplayName(code: string) { + return Intl.DisplayNames + ? new Intl.DisplayNames([code], { type: 'language' }) + : null; +} + +const useLanguage = () => { + return useApiQuery({ + path: '/localization/language', + queryOptions: { + staleTime: Infinity, + gcTime: Infinity, + }, + }); +}; + +export const useInitializeLanguage = () => { + useLanguage(); +}; + +const useLanguageName = () => { + const { data } = useLanguage(); + + const getLanguageName = useCallback( + (code: string): string => { + const languageNames = data?.identifier + ? getDisplayName(data.identifier) + : getDisplayName('en'); + + if (!languageNames) { + return code; + } + + try { + return languageNames.of(code) ?? code; + } catch { + return code; + } + }, + [data] + ); + + return getLanguageName; +}; + +export default useLanguageName; diff --git a/frontend/src/Utilities/String/getLanguageName.ts b/frontend/src/Utilities/String/getLanguageName.ts deleted file mode 100644 index 6bbaf3252..000000000 --- a/frontend/src/Utilities/String/getLanguageName.ts +++ /dev/null @@ -1,41 +0,0 @@ -import createAjaxRequest from 'Utilities/createAjaxRequest'; - -interface LanguageResponse { - identifier: string; -} - -function getLanguage() { - return createAjaxRequest({ - global: false, - dataType: 'json', - url: '/localization/language', - }).request; -} - -function getDisplayName(code: string) { - return Intl.DisplayNames - ? new Intl.DisplayNames([code], { type: 'language' }) - : null; -} - -let languageNames = getDisplayName('en'); - -getLanguage().then((data: LanguageResponse) => { - const names = getDisplayName(data.identifier); - - if (names) { - languageNames = names; - } -}); - -export default function getLanguageName(code: string) { - if (!languageNames) { - return code; - } - - try { - return languageNames.of(code) ?? code; - } catch { - return code; - } -}