Parental Overlay Localization Patch

This commit is contained in:
cyberalby2 2026-03-06 22:45:30 +01:00
parent 6bfade4a17
commit db9d12491d
3 changed files with 343 additions and 279 deletions

View file

@ -12,6 +12,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { parentalGuideService } from '../../../services/parentalGuideService'; import { parentalGuideService } from '../../../services/parentalGuideService';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { useTheme } from '../../../contexts/ThemeContext'; import { useTheme } from '../../../contexts/ThemeContext';
import { useTranslation } from 'react-i18next';
interface ParentalGuideOverlayProps { interface ParentalGuideOverlayProps {
imdbId: string | undefined; imdbId: string | undefined;
@ -49,12 +50,17 @@ const WarningItemView: React.FC<{
const animatedStyle = useAnimatedStyle(() => ({ const animatedStyle = useAnimatedStyle(() => ({
opacity: opacity.value, opacity: opacity.value,
})); }));
const { t } = useTranslation();
return ( return (
<Animated.View style={[styles.warningItem, animatedStyle]}> <Animated.View style={[styles.warningItem, animatedStyle]}>
<Text style={[styles.label, { fontSize }]}>{item.label}</Text> <Text style={[styles.label, { fontSize }]}>
{/* replace method added to support "Alcohol/Drugs" label */}
{t(`overlay_screen.${item.label.toLowerCase().replace('/', '_')}`)}
</Text>
<Text style={[styles.separator, { fontSize }]}>·</Text> <Text style={[styles.separator, { fontSize }]}>·</Text>
<Text style={[styles.severity, { fontSize }]}>{item.severity}</Text> <Text style={[styles.severity, { fontSize }]}>
{t(`overlay_screen.${item.severity.toLowerCase()}`)}
</Text>
</Animated.View> </Animated.View>
); );
}; };
@ -85,7 +91,13 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
const itemOpacity3 = useSharedValue(0); const itemOpacity3 = useSharedValue(0);
const itemOpacity4 = useSharedValue(0); const itemOpacity4 = useSharedValue(0);
const itemOpacities = [itemOpacity0, itemOpacity1, itemOpacity2, itemOpacity3, itemOpacity4]; const itemOpacities = [
itemOpacity0,
itemOpacity1,
itemOpacity2,
itemOpacity3,
itemOpacity4,
];
// Fetch parental guide data // Fetch parental guide data
useEffect(() => { useEffect(() => {
@ -115,8 +127,12 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
const severityOrder = { severe: 0, moderate: 1, mild: 2, none: 3 }; const severityOrder = { severe: 0, moderate: 1, mild: 2, none: 3 };
items.sort((a, b) => { items.sort((a, b) => {
const orderA = severityOrder[a.severity.toLowerCase() as keyof typeof severityOrder] ?? 3; const orderA =
const orderB = severityOrder[b.severity.toLowerCase() as keyof typeof severityOrder] ?? 3; severityOrder[a.severity.toLowerCase() as keyof typeof severityOrder] ??
3;
const orderB =
severityOrder[b.severity.toLowerCase() as keyof typeof severityOrder] ??
3;
return orderA - orderB; return orderA - orderB;
}); });
@ -152,19 +168,25 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
const reverseDelay = (count - 1 - i) * 40; const reverseDelay = (count - 1 - i) * 40;
itemOpacities[i].value = withDelay( itemOpacities[i].value = withDelay(
reverseDelay, reverseDelay,
withTiming(0, { duration: 100 }) withTiming(0, { duration: 100 }),
); );
} }
// Line shrinks after items are gone // Line shrinks after items are gone
const lineDelay = count * 40 + 50; const lineDelay = count * 40 + 50;
lineHeight.value = withDelay(lineDelay, withTiming(0, { lineHeight.value = withDelay(
lineDelay,
withTiming(0, {
duration: 200, duration: 200,
easing: Easing.in(Easing.cubic), easing: Easing.in(Easing.cubic),
})); }),
);
// Container fades out last // Container fades out last
containerOpacity.value = withDelay(lineDelay + 100, withTiming(0, { duration: 150 })); containerOpacity.value = withDelay(
lineDelay + 100,
withTiming(0, { duration: 150 }),
);
// Set invisible after all animations complete // Set invisible after all animations complete
fadeTimeoutRef.current = setTimeout(() => { fadeTimeoutRef.current = setTimeout(() => {
@ -175,14 +197,19 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
// When controls are hidden (shouldShow becomes true), show overlay if not already shown for this content // When controls are hidden (shouldShow becomes true), show overlay if not already shown for this content
// Only show if transitioning from false to true (controls just hidden) // Only show if transitioning from false to true (controls just hidden)
if (shouldShow && !prevShouldShowRef.current && warnings.length > 0 && !hasShownRef.current) { if (
shouldShow &&
!prevShouldShowRef.current &&
warnings.length > 0 &&
!hasShownRef.current
) {
hasShownRef.current = true; hasShownRef.current = true;
setIsVisible(true); setIsVisible(true);
const count = warnings.length; const count = warnings.length;
// Line height = (row height * count) + (gap * (count - 1)) // Line height = (row height * count) + (gap * (count - 1))
const gap = 2; // matches styles.itemsContainer gap const gap = 2; // matches styles.itemsContainer gap
const totalLineHeight = (count * ROW_HEIGHT) + ((count - 1) * gap); const totalLineHeight = count * ROW_HEIGHT + (count - 1) * gap;
// Container fade in // Container fade in
containerOpacity.value = withTiming(1, { duration: 300 }); containerOpacity.value = withTiming(1, { duration: 300 });
@ -197,7 +224,7 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
itemOpacities[i].value = withDelay( itemOpacities[i].value = withDelay(
400 + i * 80, // Start after line, stagger each 400 + i * 80, // Start after line, stagger each
withTiming(1, { duration: 200 }) withTiming(1, { duration: 200 }),
); );
} }
@ -208,19 +235,25 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
const reverseDelay = (count - 1 - i) * 60; const reverseDelay = (count - 1 - i) * 60;
itemOpacities[i].value = withDelay( itemOpacities[i].value = withDelay(
reverseDelay, reverseDelay,
withTiming(0, { duration: 150 }) withTiming(0, { duration: 150 }),
); );
} }
// Line shrinks after items are gone // Line shrinks after items are gone
const lineDelay = count * 60 + 100; const lineDelay = count * 60 + 100;
lineHeight.value = withDelay(lineDelay, withTiming(0, { lineHeight.value = withDelay(
lineDelay,
withTiming(0, {
duration: 300, duration: 300,
easing: Easing.in(Easing.cubic), easing: Easing.in(Easing.cubic),
})); }),
);
// Container fades out last // Container fades out last
containerOpacity.value = withDelay(lineDelay + 200, withTiming(0, { duration: 200 })); containerOpacity.value = withDelay(
lineDelay + 200,
withTiming(0, { duration: 200 }),
);
// Set invisible after all animations complete // Set invisible after all animations complete
fadeTimeoutRef.current = setTimeout(() => { fadeTimeoutRef.current = setTimeout(() => {
@ -286,9 +319,18 @@ export const ParentalGuideOverlay: React.FC<ParentalGuideOverlayProps> = ({
const safeTopOffset = containerPadding; const safeTopOffset = containerPadding;
return ( return (
<Animated.View style={[styles.container, { left: safeLeftOffset, top: safeTopOffset }]} pointerEvents="none"> <Animated.View
style={[styles.container, { left: safeLeftOffset, top: safeTopOffset }]}
pointerEvents="none"
>
{/* Vertical line - animates height */} {/* Vertical line - animates height */}
<Animated.View style={[styles.line, lineStyle, { backgroundColor: currentTheme.colors.primary, width: lineWidth }]} /> <Animated.View
style={[
styles.line,
lineStyle,
{ backgroundColor: currentTheme.colors.primary, width: lineWidth },
]}
/>
{/* Warning items */} {/* Warning items */}
<View style={styles.itemsContainer}> <View style={styles.itemsContainer}>

View file

@ -1531,6 +1531,17 @@
"library_desc": "Save favorites, track your progress, and sync with Trakt to keep everything organized across devices.", "library_desc": "Save favorites, track your progress, and sync with Trakt to keep everything organized across devices.",
"swipe_to_continue": "Swipe to continue", "swipe_to_continue": "Swipe to continue",
"skip": "Skip", "skip": "Skip",
"get_started":"Get Started" "get_started": "Get Started"
},
"overlay_screen": {
"nudity": "Nudity",
"violence": "Violence",
"profanity": "Profanity",
"alcohol": "Alcohol/Drugs",
"frightening": "Frightening",
"severe":"severe",
"moderate":"moderate",
"mild":"mild",
"none":"none"
} }
} }

View file

@ -1389,7 +1389,7 @@
"theme": { "theme": {
"title": "Temi App", "title": "Temi App",
"select_theme": "SELEZIONA TEMA", "select_theme": "SELEZIONA TEMA",
"create_custom": "Crea Tema Personalizado", "create_custom": "Crea Tema Personalizzato",
"options": "OPZIONI", "options": "OPZIONI",
"use_dominant_color": "Usa colore dominante dall'artwork", "use_dominant_color": "Usa colore dominante dall'artwork",
"categories": { "categories": {
@ -1532,5 +1532,16 @@
"swipe_to_continue": "Scorri per proseguire", "swipe_to_continue": "Scorri per proseguire",
"skip": "Salta", "skip": "Salta",
"get_started": "Inizia" "get_started": "Inizia"
},
"overlay_screen": {
"nudity": "Nudità",
"violence": "Violenza",
"profanity": "Volgarità",
"alcohol_drugs": "Alcol/Droga",
"frightening": "Paura",
"severe": "severa",
"moderate": "moderata",
"mild": "lieve",
"none": "nessuno"
} }
} }