diff --git a/app/(auth)/(tabs)/(home)/settings.tv.tsx b/app/(auth)/(tabs)/(home)/settings.tv.tsx index 10ad9029..5f2afc8f 100644 --- a/app/(auth)/(tabs)/(home)/settings.tv.tsx +++ b/app/(auth)/(tabs)/(home)/settings.tv.tsx @@ -142,22 +142,22 @@ export default function SettingsTV() { const typographyScaleOptions: TVOptionItem[] = useMemo( () => [ { - label: t("home.settings.appearance.text_size_small"), + label: t("home.settings.appearance.display_size_small"), value: TVTypographyScale.Small, selected: currentTypographyScale === TVTypographyScale.Small, }, { - label: t("home.settings.appearance.text_size_default"), + label: t("home.settings.appearance.display_size_default"), value: TVTypographyScale.Default, selected: currentTypographyScale === TVTypographyScale.Default, }, { - label: t("home.settings.appearance.text_size_large"), + label: t("home.settings.appearance.display_size_large"), value: TVTypographyScale.Large, selected: currentTypographyScale === TVTypographyScale.Large, }, { - label: t("home.settings.appearance.text_size_extra_large"), + label: t("home.settings.appearance.display_size_extra_large"), value: TVTypographyScale.ExtraLarge, selected: currentTypographyScale === TVTypographyScale.ExtraLarge, }, @@ -188,7 +188,7 @@ export default function SettingsTV() { const typographyScaleLabel = useMemo(() => { const option = typographyScaleOptions.find((o) => o.selected); - return option?.label || t("home.settings.appearance.text_size_default"); + return option?.label || t("home.settings.appearance.display_size_default"); }, [typographyScaleOptions, t]); return ( @@ -385,11 +385,11 @@ export default function SettingsTV() { {/* Appearance Section */} showOptions({ - title: t("home.settings.appearance.text_size"), + title: t("home.settings.appearance.display_size"), options: typographyScaleOptions, onSelect: (value) => updateSettings({ tvTypographyScale: value }), diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx index aca3b452..1a6ca0b7 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/collections/[collectionId].tsx @@ -27,15 +27,14 @@ import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton"; import { ItemCardText } from "@/components/ItemCardText"; import { Loader } from "@/components/Loader"; import { ItemPoster } from "@/components/posters/ItemPoster"; -import MoviePoster, { - TV_POSTER_WIDTH, -} from "@/components/posters/MoviePoster.tv"; +import MoviePoster from "@/components/posters/MoviePoster.tv"; import SeriesPoster from "@/components/posters/SeriesPoster.tv"; import { TVFilterButton, TVFocusablePoster, TVItemCardText, } from "@/components/tv"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import useRouter from "@/hooks/useAppRouter"; import { useTVOptionModal } from "@/hooks/useTVOptionModal"; import * as ScreenOrientation from "@/packages/expo-screen-orientation"; @@ -60,6 +59,7 @@ const page: React.FC = () => { const searchParams = useLocalSearchParams(); const { collectionId } = searchParams as { collectionId: string }; + const posterSizes = useScaledTVPosterSizes(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const navigation = useNavigation(); @@ -153,7 +153,7 @@ const page: React.FC = () => { // Calculate columns for TV grid const nrOfCols = useMemo(() => { if (Platform.isTV) { - const itemWidth = TV_POSTER_WIDTH + TV_ITEM_GAP; + const itemWidth = posterSizes.poster + TV_ITEM_GAP; return Math.max( 1, Math.floor((screenWidth - TV_SCALE_PADDING * 2) / itemWidth), @@ -291,7 +291,7 @@ const page: React.FC = () => { style={{ marginRight: TV_ITEM_GAP, marginBottom: TV_ITEM_GAP, - width: TV_POSTER_WIDTH, + width: posterSizes.poster, }} > diff --git a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx index 7a363268..02bc671e 100644 --- a/app/(auth)/(tabs)/(libraries)/[libraryId].tsx +++ b/app/(auth)/(tabs)/(libraries)/[libraryId].tsx @@ -33,15 +33,14 @@ import { ResetFiltersButton } from "@/components/filters/ResetFiltersButton"; import { ItemCardText } from "@/components/ItemCardText"; import { Loader } from "@/components/Loader"; import { ItemPoster } from "@/components/posters/ItemPoster"; -import MoviePoster, { - TV_POSTER_WIDTH, -} from "@/components/posters/MoviePoster.tv"; +import MoviePoster from "@/components/posters/MoviePoster.tv"; import SeriesPoster from "@/components/posters/SeriesPoster.tv"; import { TVFilterButton, TVFocusablePoster, TVItemCardText, } from "@/components/tv"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; import { useOrientation } from "@/hooks/useOrientation"; @@ -85,6 +84,7 @@ const Page = () => { const { libraryId } = searchParams; const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const { width: screenWidth } = useWindowDimensions(); @@ -409,7 +409,7 @@ const Page = () => { diff --git a/app/(auth)/(tabs)/(watchlists)/[watchlistId].tsx b/app/(auth)/(tabs)/(watchlists)/[watchlistId].tsx index 05620423..0adee973 100644 --- a/app/(auth)/(tabs)/(watchlists)/[watchlistId].tsx +++ b/app/(auth)/(tabs)/(watchlists)/[watchlistId].tsx @@ -24,11 +24,10 @@ import { } from "@/components/common/TouchableItemRouter"; import { ItemCardText } from "@/components/ItemCardText"; import { ItemPoster } from "@/components/posters/ItemPoster"; -import MoviePoster, { - TV_POSTER_WIDTH, -} from "@/components/posters/MoviePoster.tv"; +import MoviePoster from "@/components/posters/MoviePoster.tv"; import SeriesPoster from "@/components/posters/SeriesPoster.tv"; import { TVFocusablePoster } from "@/components/tv/TVFocusablePoster"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; import { useOrientation } from "@/hooks/useOrientation"; @@ -73,6 +72,7 @@ const TVItemCardText: React.FC<{ export default function WatchlistDetailScreen() { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const { t } = useTranslation(); const router = useRouter(); const navigation = useNavigation(); @@ -206,7 +206,7 @@ export default function WatchlistDetailScreen() { = ({ showPlayButton = false, }) => { const api = useAtomValue(apiAtom); + const posterSizes = useScaledTVPosterSizes(); const url = useMemo(() => { if (!api) { @@ -91,7 +91,7 @@ const ContinueWatchingPoster: React.FC = ({ return ( = ({ progress={progress} showWatchedIndicator={isWatched} isFocused={false} - width={TV_LANDSCAPE_WIDTH} - style={{ width: TV_LANDSCAPE_WIDTH }} + width={posterSizes.landscape} + style={{ width: posterSizes.landscape }} /> {showPlayButton && ( = ({ ; + // TV-specific "See All" card for end of lists const TVSeeAllCard: React.FC<{ onPress: () => void; @@ -109,10 +108,19 @@ const TVSeeAllCard: React.FC<{ onFocus?: () => void; onBlur?: () => void; typography: Typography; -}> = ({ onPress, orientation, disabled, onFocus, onBlur, typography }) => { + posterSizes: PosterSizes; +}> = ({ + onPress, + orientation, + disabled, + onFocus, + onBlur, + typography, + posterSizes, +}) => { const { t } = useTranslation(); const width = - orientation === "horizontal" ? TV_LANDSCAPE_WIDTH : TV_POSTER_WIDTH; + orientation === "horizontal" ? posterSizes.landscape : posterSizes.poster; const aspectRatio = orientation === "horizontal" ? 16 / 9 : 10 / 15; return ( @@ -172,6 +180,7 @@ export const InfiniteScrollingCollectionList: React.FC = ({ ...props }) => { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const effectivePageSize = Math.max(1, pageSize); const hasCalledOnLoaded = useRef(false); const router = useRouter(); @@ -250,7 +259,7 @@ export const InfiniteScrollingCollectionList: React.FC = ({ }, [data]); const itemWidth = - orientation === "horizontal" ? TV_LANDSCAPE_WIDTH : TV_POSTER_WIDTH; + orientation === "horizontal" ? posterSizes.landscape : posterSizes.poster; const handleItemPress = useCallback( (item: BaseItemDto) => { @@ -487,6 +496,7 @@ export const InfiniteScrollingCollectionList: React.FC = ({ onFocus={handleSeeAllFocus} onBlur={handleItemBlur} typography={typography} + posterSizes={posterSizes} /> )} diff --git a/components/home/StreamystatsPromotedWatchlists.tv.tsx b/components/home/StreamystatsPromotedWatchlists.tv.tsx index e2c5ddd7..1c5e69a4 100644 --- a/components/home/StreamystatsPromotedWatchlists.tv.tsx +++ b/components/home/StreamystatsPromotedWatchlists.tv.tsx @@ -11,11 +11,10 @@ import { FlatList, View, type ViewProps } from "react-native"; import { Text } from "@/components/common/Text"; import { getItemNavigation } from "@/components/common/TouchableItemRouter"; -import MoviePoster, { - TV_POSTER_WIDTH, -} from "@/components/posters/MoviePoster.tv"; +import MoviePoster from "@/components/posters/MoviePoster.tv"; import SeriesPoster from "@/components/posters/SeriesPoster.tv"; import { TVFocusablePoster } from "@/components/tv/TVFocusablePoster"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import { useScaledTVTypography } from "@/constants/TVTypography"; import useRouter from "@/hooks/useAppRouter"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; @@ -66,6 +65,7 @@ const WatchlistSection: React.FC = ({ ...props }) => { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const api = useAtomValue(apiAtom); const user = useAtomValue(userAtom); const { settings } = useSettings(); @@ -129,8 +129,8 @@ const WatchlistSection: React.FC = ({ const getItemLayout = useCallback( (_data: ArrayLike | null | undefined, index: number) => ({ - length: TV_POSTER_WIDTH + ITEM_GAP, - offset: (TV_POSTER_WIDTH + ITEM_GAP) * index, + length: posterSizes.poster + ITEM_GAP, + offset: (posterSizes.poster + ITEM_GAP) * index, index, }), [], @@ -139,7 +139,7 @@ const WatchlistSection: React.FC = ({ const renderItem = useCallback( ({ item }: { item: BaseItemDto }) => { return ( - + handleItemPress(item)} onFocus={() => onItemFocus?.(item)} @@ -182,11 +182,11 @@ const WatchlistSection: React.FC = ({ }} > {[1, 2, 3, 4, 5].map((i) => ( - + = ({ enabled = true, onItemFocus, ...props }) => { + const posterSizes = useScaledTVPosterSizes(); const api = useAtomValue(apiAtom); const user = useAtomValue(userAtom); const { settings } = useSettings(); @@ -316,11 +317,11 @@ export const StreamystatsPromotedWatchlists: React.FC< }} > {[1, 2, 3, 4, 5].map((i) => ( - + = ({ ...props }) => { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const api = useAtomValue(apiAtom); const user = useAtomValue(userAtom); const { settings } = useSettings(); @@ -190,8 +190,8 @@ export const StreamystatsRecommendations: React.FC = ({ const getItemLayout = useCallback( (_data: ArrayLike | null | undefined, index: number) => ({ - length: TV_POSTER_WIDTH + ITEM_GAP, - offset: (TV_POSTER_WIDTH + ITEM_GAP) * index, + length: posterSizes.poster + ITEM_GAP, + offset: (posterSizes.poster + ITEM_GAP) * index, index, }), [], @@ -200,7 +200,7 @@ export const StreamystatsRecommendations: React.FC = ({ const renderItem = useCallback( ({ item }: { item: BaseItemDto }) => { return ( - + handleItemPress(item)} onFocus={() => onItemFocus?.(item)} @@ -245,11 +245,11 @@ export const StreamystatsRecommendations: React.FC = ({ }} > {[1, 2, 3, 4, 5].map((i) => ( - + void; onPress: (item: BaseItemDto) => void; } const HeroCard: React.FC = React.memo( - ({ item, isFirst, onFocus, onPress }) => { + ({ item, isFirst, cardWidth, onFocus, onPress }) => { const api = useAtomValue(apiAtom); const [focused, setFocused] = useState(false); const scale = useRef(new Animated.Value(1)).current; @@ -129,8 +130,8 @@ const HeroCard: React.FC = React.memo( progress={progress} showWatchedIndicator={false} isFocused={focused} - width={CARD_WIDTH} - style={{ width: CARD_WIDTH }} + width={cardWidth} + style={{ width: cardWidth }} /> ); @@ -147,7 +148,7 @@ const HeroCard: React.FC = React.memo( > = ({ onItemFocus, }) => { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const api = useAtomValue(apiAtom); const insets = useSafeAreaInsets(); const router = useRouter(); @@ -354,11 +356,12 @@ export const TVHeroCarousel: React.FC = ({ ), - [handleCardFocus, handleCardPress], + [handleCardFocus, handleCardPress, posterSizes.heroCard], ); // Memoize keyExtractor diff --git a/components/persons/TVActorPage.tsx b/components/persons/TVActorPage.tsx index b731ab98..4f543be8 100644 --- a/components/persons/TVActorPage.tsx +++ b/components/persons/TVActorPage.tsx @@ -28,9 +28,8 @@ import { Text } from "@/components/common/Text"; import { getItemNavigation } from "@/components/common/TouchableItemRouter"; import { ItemCardText } from "@/components/ItemCardText"; import { Loader } from "@/components/Loader"; -import MoviePoster, { - TV_POSTER_WIDTH, -} from "@/components/posters/MoviePoster.tv"; +import MoviePoster from "@/components/posters/MoviePoster.tv"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import useRouter from "@/hooks/useAppRouter"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl"; @@ -103,6 +102,7 @@ export const TVActorPage: React.FC = ({ personId }) => { const router = useRouter(); const segments = useSegments(); const from = (segments as string[])[2] || "(home)"; + const posterSizes = useScaledTVPosterSizes(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); @@ -276,8 +276,8 @@ export const TVActorPage: React.FC = ({ personId }) => { // List item layout const getItemLayout = useCallback( (_data: ArrayLike | null | undefined, index: number) => ({ - length: TV_POSTER_WIDTH + ITEM_GAP, - offset: (TV_POSTER_WIDTH + ITEM_GAP) * index, + length: posterSizes.poster + ITEM_GAP, + offset: (posterSizes.poster + ITEM_GAP) * index, index, }), [], @@ -297,7 +297,7 @@ export const TVActorPage: React.FC = ({ personId }) => { > - + diff --git a/components/posters/MoviePoster.tv.tsx b/components/posters/MoviePoster.tv.tsx index 9d9bdcfa..ed047543 100644 --- a/components/posters/MoviePoster.tv.tsx +++ b/components/posters/MoviePoster.tv.tsx @@ -4,6 +4,7 @@ import { useAtom } from "jotai"; import { useMemo } from "react"; import { View } from "react-native"; import { WatchedIndicator } from "@/components/WatchedIndicator"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import { GlassPosterView, isGlassEffectAvailable, @@ -11,8 +12,6 @@ import { import { apiAtom } from "@/providers/JellyfinProvider"; import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl"; -export const TV_POSTER_WIDTH = 260; - type MoviePosterProps = { item: BaseItemDto; showProgress?: boolean; @@ -23,14 +22,15 @@ const MoviePoster: React.FC = ({ showProgress = false, }) => { const [api] = useAtom(apiAtom); + const posterSizes = useScaledTVPosterSizes(); const url = useMemo(() => { return getPrimaryImageUrl({ api, item, - width: 520, // 2x for quality on large screens + width: posterSizes.poster * 2, // 2x for quality on large screens }); - }, [api, item]); + }, [api, item, posterSizes.poster]); const progress = item.UserData?.PlayedPercentage || 0; const isWatched = item.UserData?.Played === true; @@ -52,8 +52,8 @@ const MoviePoster: React.FC = ({ progress={showProgress ? progress : 0} showWatchedIndicator={isWatched} isFocused={false} - width={TV_POSTER_WIDTH} - style={{ width: TV_POSTER_WIDTH }} + width={posterSizes.poster} + style={{ width: posterSizes.poster }} /> ); } @@ -65,7 +65,7 @@ const MoviePoster: React.FC = ({ position: "relative", borderRadius: 24, overflow: "hidden", - width: TV_POSTER_WIDTH, + width: posterSizes.poster, aspectRatio: 10 / 15, }} > diff --git a/components/posters/SeriesPoster.tv.tsx b/components/posters/SeriesPoster.tv.tsx index 79071868..125d9d3e 100644 --- a/components/posters/SeriesPoster.tv.tsx +++ b/components/posters/SeriesPoster.tv.tsx @@ -3,6 +3,7 @@ import { Image } from "expo-image"; import { useAtom } from "jotai"; import { useMemo } from "react"; import { View } from "react-native"; +import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes"; import { GlassPosterView, isGlassEffectAvailable, @@ -10,8 +11,6 @@ import { import { apiAtom } from "@/providers/JellyfinProvider"; import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl"; -export const TV_POSTER_WIDTH = 260; - type SeriesPosterProps = { item: BaseItemDto; showProgress?: boolean; @@ -19,17 +18,18 @@ type SeriesPosterProps = { const SeriesPoster: React.FC = ({ item }) => { const [api] = useAtom(apiAtom); + const posterSizes = useScaledTVPosterSizes(); const url = useMemo(() => { if (item.Type === "Episode") { - return `${api?.basePath}/Items/${item.SeriesId}/Images/Primary?fillHeight=780&quality=80&tag=${item.SeriesPrimaryImageTag}`; + return `${api?.basePath}/Items/${item.SeriesId}/Images/Primary?fillHeight=${posterSizes.poster * 3}&quality=80&tag=${item.SeriesPrimaryImageTag}`; } return getPrimaryImageUrl({ api, item, - width: 520, // 2x for quality on large screens + width: posterSizes.poster * 2, // 2x for quality on large screens }); - }, [api, item]); + }, [api, item, posterSizes.poster]); const blurhash = useMemo(() => { const key = item.ImageTags?.Primary as string; @@ -48,8 +48,8 @@ const SeriesPoster: React.FC = ({ item }) => { progress={0} showWatchedIndicator={false} isFocused={false} - width={TV_POSTER_WIDTH} - style={{ width: TV_POSTER_WIDTH }} + width={posterSizes.poster} + style={{ width: posterSizes.poster }} /> ); } @@ -58,7 +58,7 @@ const SeriesPoster: React.FC = ({ item }) => { return ( = ({ ...props }) => { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const flatListRef = useRef>(null); const [focusedCount, setFocusedCount] = useState(0); const prevFocusedCount = useRef(0); @@ -181,7 +179,7 @@ export const TVSearchSection: React.FC = ({ }, []); const itemWidth = - orientation === "horizontal" ? TV_LANDSCAPE_WIDTH : TV_POSTER_WIDTH; + orientation === "horizontal" ? posterSizes.landscape : posterSizes.poster; const getItemLayout = useCallback( (_data: ArrayLike | null | undefined, index: number) => ({ @@ -249,8 +247,8 @@ export const TVSearchSection: React.FC = ({ return ( = ({ refSetter, }) => { const typography = useScaledTVTypography(); + const posterSizes = useScaledTVPosterSizes(); const api = useAtomValue(apiAtom); const thumbnailUrl = useMemo(() => { @@ -68,7 +68,7 @@ export const TVEpisodeCard: React.FC = ({ }, [episode.ParentIndexNumber, episode.IndexNumber]); return ( - + = ({ > void; onBlur?: () => void; disabled?: boolean; + /** When true, the item remains focusable even when disabled (for navigation purposes) */ + focusableWhenDisabled?: boolean; /** Setter function for the ref (for focus guide destinations) */ refSetter?: (ref: View | null) => void; } @@ -31,6 +33,7 @@ export const TVFocusablePoster: React.FC = ({ onFocus: onFocusProp, onBlur: onBlurProp, disabled = false, + focusableWhenDisabled = false, refSetter, }) => { const [focused, setFocused] = useState(false); @@ -62,7 +65,7 @@ export const TVFocusablePoster: React.FC = ({ }} hasTVPreferredFocus={hasTVPreferredFocus && !disabled} disabled={disabled} - focusable={!disabled} + focusable={!disabled || focusableWhenDisabled} > = { + [TVTypographyScale.Small]: 0.95, + [TVTypographyScale.Default]: 1.0, + [TVTypographyScale.Large]: 1.05, + [TVTypographyScale.ExtraLarge]: 1.1, +}; + +/** + * Hook that returns scaled TV poster sizes based on user settings. + * Use this instead of the static TVPosterSizes constant for dynamic scaling. + * + * @example + * const posterSizes = useScaledTVPosterSizes(); + * + */ +export const useScaledTVPosterSizes = () => { + const { settings } = useSettings(); + const scale = + posterScaleMultipliers[settings.tvTypographyScale] ?? + posterScaleMultipliers[TVTypographyScale.Default]; + + return { + poster: Math.round(TVPosterSizes.poster * scale), + landscape: Math.round(TVPosterSizes.landscape * scale), + episode: Math.round(TVPosterSizes.episode * scale), + heroCard: Math.round(TVPosterSizes.heroCard * scale), + }; +}; diff --git a/translations/en.json b/translations/en.json index e015bec5..05663b32 100644 --- a/translations/en.json +++ b/translations/en.json @@ -128,11 +128,11 @@ "show_home_backdrop": "Dynamic Home Backdrop", "show_hero_carousel": "Hero Carousel", "show_series_poster_on_episode": "Show Series Poster on Episodes", - "text_size": "Text Size", - "text_size_small": "Small", - "text_size_default": "Default", - "text_size_large": "Large", - "text_size_extra_large": "Extra Large" + "display_size": "Display Size", + "display_size_small": "Small", + "display_size_default": "Default", + "display_size_large": "Large", + "display_size_extra_large": "Extra Large" }, "network": { "title": "Network", diff --git a/translations/sv.json b/translations/sv.json index 8788e617..a9d52daf 100644 --- a/translations/sv.json +++ b/translations/sv.json @@ -126,11 +126,11 @@ "show_home_backdrop": "Dynamisk hembakgrund", "show_hero_carousel": "Hjältekarusell", "show_series_poster_on_episode": "Visa serieaffisch på avsnitt", - "text_size": "Textstorlek", - "text_size_small": "Liten", - "text_size_default": "Standard", - "text_size_large": "Stor", - "text_size_extra_large": "Extra stor" + "display_size": "Visningsstorlek", + "display_size_small": "Liten", + "display_size_default": "Standard", + "display_size_large": "Stor", + "display_size_extra_large": "Extra stor" }, "network": { "title": "Nätverk",