diff --git a/mobile/src/components/BugReportFAB.tsx b/mobile/src/components/BugReportFAB.tsx index 2f50846..d4e11cf 100644 --- a/mobile/src/components/BugReportFAB.tsx +++ b/mobile/src/components/BugReportFAB.tsx @@ -87,11 +87,11 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { const submit = useCallback(async () => { if (!client) { - Alert.alert('Bug report', 'Brak połączenia z backendem.'); + Alert.alert('Bug report', 'No connection to the backend.'); return; } if (!message.trim()) { - Alert.alert('Bug report', 'Wpisz krótki opis.'); + Alert.alert('Bug report', 'Enter a short description.'); return; } setSubmitting(true); @@ -136,9 +136,9 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { setOpen(false); setMessage(''); setScreenshot(null); - Alert.alert('Bug report', 'Wysłano. Dzięki!'); + Alert.alert('Bug report', 'Sent. Thanks!'); } catch (e) { - Alert.alert('Bug report', `Nie udało się wysłać: ${(e as Error).message}`); + Alert.alert('Bug report', `Failed to send: ${(e as Error).message}`); } finally { setSubmitting(false); } @@ -164,7 +164,7 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { > setOpen(false)} /> - Zgłoś buga + Report a bug {screenshot ? ( @@ -174,7 +174,7 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { resizeMode="contain" /> - Dołącz screenshot + Attach screenshot ) : ( - Screenshot niedostępny (capture fail). Wyślę sam tekst. + Screenshot unavailable (capture failed). Sending text only. )} @@ -196,7 +196,7 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { style={styles.input} value={message} onChangeText={setMessage} - placeholder="Co jest nie tak? Tytuł sceny / co próbujesz zrobić / co zobaczyłeś" + placeholder="What's wrong? Scene title / what you're trying to do / what you saw" placeholderTextColor={theme.mutedDim} multiline autoFocus @@ -208,7 +208,7 @@ export function BugReportFAB({ client, appVersion, navRef }: Props) { onPress={() => setOpen(false)} disabled={submitting} > - Anuluj + Cancel ) : ( - Wyślij + Send )} diff --git a/mobile/src/lib/applock.ts b/mobile/src/lib/applock.ts index 0ba894c..1b2e1ba 100644 --- a/mobile/src/lib/applock.ts +++ b/mobile/src/lib/applock.ts @@ -44,7 +44,7 @@ export async function setTimeoutSeconds(s: number): Promise { } export async function setPin(pin: string): Promise { - if (!/^\d{4,8}$/.test(pin)) throw new Error('PIN musi mieć 4-8 cyfr'); + if (!/^\d{4,8}$/.test(pin)) throw new Error('PIN must be 4-8 digits'); await SecureStore.setItemAsync(PIN_KEY, pin); } @@ -66,8 +66,8 @@ export async function biometricAvailable(): Promise { export async function authenticateBiometric(): Promise { const res = await LocalAuthentication.authenticateAsync({ - promptMessage: 'Odblokuj Goon', - cancelLabel: 'Użyj PIN', + promptMessage: 'Unlock Goon', + cancelLabel: 'Use PIN', disableDeviceFallback: true, }); return res.success; diff --git a/mobile/src/lib/doodstream.ts b/mobile/src/lib/doodstream.ts index 8e63c19..ee51fec 100644 --- a/mobile/src/lib/doodstream.ts +++ b/mobile/src/lib/doodstream.ts @@ -103,7 +103,7 @@ export async function resolveDoodStream( // 30x → resolve Location, kontynuuj. Inaczej traktuj jako final. if (r.status >= 300 && r.status < 400) { const loc = r.headers.get('location'); - if (!loc) return { error: `redirect ${r.status} bez Location` }; + if (!loc) return { error: `redirect ${r.status} without Location` }; try { currentUrl = new URL(loc, currentUrl).toString(); } catch { @@ -147,7 +147,7 @@ async function _continueAfterFetch( // Tu odsyłamy do WebView fallback. Sam `turnstile` w HTML nie wystarcza — // pełna strona playera ZAWIERA opcjonalny turnstile container. if (html.length < 2000 || /challenge-platform/i.test(html)) { - return { error: 'captcha_gate (mobile IP też zablokowane?)' }; + return { error: 'captcha_gate (mobile IP also blocked?)' }; } return { error: 'no_pass_md5_in_html' }; } diff --git a/mobile/src/screens/AppLockScreen.tsx b/mobile/src/screens/AppLockScreen.tsx index 5bb4c5b..38f5e24 100644 --- a/mobile/src/screens/AppLockScreen.tsx +++ b/mobile/src/screens/AppLockScreen.tsx @@ -99,9 +99,9 @@ export function AppLockScreen({ onUnlock, onLogout }: Props) { g - Goon zablokowany + Goon locked - {bioEnabled && bioAvailable ? 'Użyj odcisku lub PIN-u' : 'Wpisz PIN'} + {bioEnabled && bioAvailable ? 'Use fingerprint or PIN' : 'Enter PIN'} @@ -148,7 +148,7 @@ export function AppLockScreen({ onUnlock, onLogout }: Props) { {onLogout ? ( - Wyloguj się + Log out ) : null} diff --git a/mobile/src/screens/AppLockSettingsScreen.tsx b/mobile/src/screens/AppLockSettingsScreen.tsx index 391eb2a..2621703 100644 --- a/mobile/src/screens/AppLockSettingsScreen.tsx +++ b/mobile/src/screens/AppLockSettingsScreen.tsx @@ -26,8 +26,8 @@ import { theme } from '../theme'; import { PinEntry } from './PinEntry'; const TIMEOUT_OPTIONS: { label: string; seconds: number }[] = [ - { label: 'Natychmiast', seconds: 0 }, - { label: '30 sek', seconds: 30 }, + { label: 'Immediately', seconds: 0 }, + { label: '30 sec', seconds: 30 }, { label: '1 min', seconds: 60 }, { label: '5 min', seconds: 300 }, { label: '15 min', seconds: 900 }, @@ -63,7 +63,7 @@ export function AppLockSettingsScreen() { if (stage === 'enter-current') { return ( { setStage('menu'); @@ -72,7 +72,7 @@ export function AppLockSettingsScreen() { onSubmit={async (candidate) => { const ok = await verifyPin(candidate); if (!ok) { - setErrorText('Nieprawidłowy PIN'); + setErrorText('Incorrect PIN'); return; } setErrorText(null); @@ -85,7 +85,7 @@ export function AppLockSettingsScreen() { if (stage === 'set-new') { return ( { setStage('menu'); @@ -94,7 +94,7 @@ export function AppLockSettingsScreen() { }} onSubmit={async (candidate) => { if (candidate.length < 4) { - setErrorText('Min. 4 cyfry'); + setErrorText('Min. 4 digits'); return; } setNewPin(candidate); @@ -108,7 +108,7 @@ export function AppLockSettingsScreen() { if (stage === 'confirm-new') { return ( { setStage('menu'); @@ -117,7 +117,7 @@ export function AppLockSettingsScreen() { }} onSubmit={async (candidate) => { if (candidate !== newPin) { - setErrorText('PIN-y nie pasują'); + setErrorText('PINs do not match'); return; } await setPin(newPin); @@ -126,7 +126,7 @@ export function AppLockSettingsScreen() { setErrorText(null); setStage('menu'); await refresh(); - Alert.alert('Gotowe', 'PIN zapisany. Blokada włączona.'); + Alert.alert('Done', 'PIN saved. App lock enabled.'); }} /> ); @@ -135,7 +135,7 @@ export function AppLockSettingsScreen() { if (stage === 'disable-confirm') { return ( { setStage('menu'); @@ -144,7 +144,7 @@ export function AppLockSettingsScreen() { onSubmit={async (candidate) => { const ok = await verifyPin(candidate); if (!ok) { - setErrorText('Nieprawidłowy PIN'); + setErrorText('Incorrect PIN'); return; } await setEnabled(false); @@ -161,13 +161,13 @@ export function AppLockSettingsScreen() { return ( - Blokada aplikacji + App lock - Włączona + Enabled - {settings.enabled ? 'Wymaga PIN przy starcie i po przerwie' : 'Wyłączona'} + {settings.enabled ? 'Requires PIN at startup and after a break' : 'Disabled'} - Zmień PIN + Change PIN ) : null} @@ -202,14 +202,14 @@ export function AppLockSettingsScreen() { {settings.enabled ? ( <> - Biometria + Biometrics - Odcisk / Face Unlock + Fingerprint / Face Unlock {bioAvailable - ? 'Szybsze odblokowywanie, PIN jako fallback' - : 'Brak biometrii w urządzeniu'} + ? 'Faster unlocking, PIN as fallback' + : 'No biometrics on this device'} - Czas do zablokowania - Po wyjściu z aplikacji ile czekać przed lockiem + Time until lock + How long to wait after leaving the app before locking {TIMEOUT_OPTIONS.map((opt) => { const active = settings.timeoutSeconds === opt.seconds; @@ -253,16 +253,16 @@ export function AppLockSettingsScreen() { - Aplikacja jest również ukryta na liście ostatnich aplikacji i blokuje zrzuty ekranu. + The app is also hidden in the recent apps list and blocks screenshots. - O aplikacji + About - Wersja - Bieżący JS bundle (OTA-updated) + Version + Current JS bundle (OTA-updated) {APP_VERSION} diff --git a/mobile/src/screens/MovieDetailScreen.tsx b/mobile/src/screens/MovieDetailScreen.tsx index e648f2e..b5e5cbd 100644 --- a/mobile/src/screens/MovieDetailScreen.tsx +++ b/mobile/src/screens/MovieDetailScreen.tsx @@ -200,7 +200,7 @@ function WatchChip({ if (parts.length > 1) { Alert.alert( title, - 'Film składa się z kilku części. Wybierz którą zacząć.', + 'This movie has several parts. Choose which one to start.', [ ...parts.map((p) => ({ text: ((p.raw as any).part_label as string) ?? p.quality ?? 'Part', @@ -216,7 +216,7 @@ function WatchChip({ }); }, })), - { text: 'Anuluj', style: 'cancel' as const }, + { text: 'Cancel', style: 'cancel' as const }, ], ); return; @@ -246,36 +246,36 @@ function WatchChip({ const onLongPress = () => { Alert.alert( pb.origin, - 'Co zrobić z tym linkiem?', + 'What do you want to do with this link?', [ { - text: 'Otwórz w przeglądarce (diagnostyka)', + text: 'Open in browser (diagnostics)', onPress: async () => { try { const url = pb.page_url || pb.embed_url; if (url) { await Linking.openURL(url); } else { - Alert.alert('Brak URL', 'Ten playback nie ma page_url do otworzenia.'); + Alert.alert('No URL', 'This playback has no page_url to open.'); } } catch (e) { - Alert.alert('Nie udało się otworzyć', e instanceof Error ? e.message : String(e)); + Alert.alert('Could not open', e instanceof Error ? e.message : String(e)); } }, }, { - text: 'Oznacz jako nieprawidłowy', + text: 'Mark as invalid', style: 'destructive', onPress: async () => { try { await client.markMoviePlaybackDead(movieId, pb.id); queryClient.invalidateQueries({ queryKey: ['movie', movieId] }); } catch (e) { - Alert.alert('Nie udało się', e instanceof Error ? e.message : String(e)); + Alert.alert('Failed', e instanceof Error ? e.message : String(e)); } }, }, - { text: 'Anuluj', style: 'cancel' }, + { text: 'Cancel', style: 'cancel' }, ], ); }; diff --git a/mobile/src/screens/PinEntry.tsx b/mobile/src/screens/PinEntry.tsx index faeecb6..0dc93cd 100644 --- a/mobile/src/screens/PinEntry.tsx +++ b/mobile/src/screens/PinEntry.tsx @@ -57,14 +57,14 @@ export function PinEntry({ title, error, onSubmit, onCancel }: Props) { - Anuluj + Cancel onSubmit(pin)} > - Dalej + Next diff --git a/mobile/src/screens/PlaybackQualityModal.tsx b/mobile/src/screens/PlaybackQualityModal.tsx index ebf4a2e..79819b9 100644 --- a/mobile/src/screens/PlaybackQualityModal.tsx +++ b/mobile/src/screens/PlaybackQualityModal.tsx @@ -50,7 +50,7 @@ export function PlaybackQualityModal({ > e.stopPropagation()}> - Wybierz jakość + Select quality {sorted.map((link, i) => { const px = qualityToInt(link.quality); return ( @@ -70,7 +70,7 @@ export function PlaybackQualityModal({ ); })} - Anuluj + Cancel diff --git a/mobile/src/screens/PlayerScreen.tsx b/mobile/src/screens/PlayerScreen.tsx index 725ad85..a01ad37 100644 --- a/mobile/src/screens/PlayerScreen.tsx +++ b/mobile/src/screens/PlayerScreen.tsx @@ -606,7 +606,7 @@ function NativeVideoPlayer({ params }: { params: RouteParams }) { Playback failed - {playerError?.message ?? 'Stream nie odpalił się.'} + {playerError?.message ?? 'The stream did not start.'} nav.goBack()}> Back @@ -1129,7 +1129,7 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) { return ( - Resolwuję bezpośredni link... + Resolving direct link... ); } @@ -1153,7 +1153,7 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) { borderRadius: 8, }} > - Otwórz w WebView + Open in WebView ); @@ -1197,7 +1197,7 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) { }) } > - ▶ Otwórz w native playerze + ▶ Open in native player {extractedUrl.slice(0, 80)} )} diff --git a/mobile/src/screens/SceneDetailScreen.tsx b/mobile/src/screens/SceneDetailScreen.tsx index 98064b5..9952f6b 100644 --- a/mobile/src/screens/SceneDetailScreen.tsx +++ b/mobile/src/screens/SceneDetailScreen.tsx @@ -521,7 +521,7 @@ function PlaybackButton({ const embedLinks = unique.filter((l) => !l.stream_url && !!l.embed_url); if (directLinks.length === 0 && embedLinks.length === 0) { - Alert.alert('No stream', 'porn-app nie zwrócił żadnego stream URL — fallback do strony.'); + Alert.alert('No stream', 'porn-app did not return any stream URL — falling back to the page.'); await openUrl(source.page_url); return; } @@ -567,8 +567,8 @@ function PlaybackButton({ // lub tube page 404/410). Refresh sceny żeby ten button zniknął z listy. if (e instanceof ApiError && e.status === 410) { Alert.alert( - 'Link martwy', - 'Tube usunął ten film. Oznaczyliśmy źródło, więcej go nie zobaczysz.', + 'Dead link', + 'The tube removed this video. We marked the source, you won\'t see it again.', ); queryClient.invalidateQueries({ queryKey: ['scene', sceneId] }); queryClient.invalidateQueries({ queryKey: ['scenes'] }); @@ -579,8 +579,8 @@ function PlaybackButton({ // następne kliknięcie pewnie zadziała. if (e instanceof ApiError && e.status === 503) { Alert.alert( - 'Spróbuj ponownie', - 'Chwilowy problem z hosterem. Kliknij Play ponownie za moment.', + 'Try again', + 'Temporary problem with the host. Tap Play again in a moment.', ); return; } @@ -638,25 +638,25 @@ function PlaybackButton({ const onLongPress = () => { Alert.alert( label, - 'Co zrobić z tym linkiem?', + 'What do you want to do with this link?', [ { - text: 'Otwórz w przeglądarce (diagnostyka)', + text: 'Open in browser (diagnostics)', onPress: async () => { try { const url = source.page_url || source.embed_url || source.stream_url; if (url) { await Linking.openURL(url); } else { - Alert.alert('Brak URL', 'Ten playback nie ma page_url do otworzenia.'); + Alert.alert('No URL', 'This playback has no page_url to open.'); } } catch (e) { - Alert.alert('Nie udało się otworzyć', e instanceof Error ? e.message : String(e)); + Alert.alert('Could not open', e instanceof Error ? e.message : String(e)); } }, }, { - text: 'Oznacz jako nieprawidłowy', + text: 'Mark as invalid', style: 'destructive', onPress: async () => { try { @@ -664,11 +664,11 @@ function PlaybackButton({ queryClient.invalidateQueries({ queryKey: ['scene', sceneId] }); queryClient.invalidateQueries({ queryKey: ['scenes'] }); } catch (e) { - Alert.alert('Nie udało się', e instanceof Error ? e.message : String(e)); + Alert.alert('Failed', e instanceof Error ? e.message : String(e)); } }, }, - { text: 'Anuluj', style: 'cancel' }, + { text: 'Cancel', style: 'cancel' }, ], ); }; diff --git a/mobile/src/screens/ScenesFilterModal.tsx b/mobile/src/screens/ScenesFilterModal.tsx index bd66af0..0686dfe 100644 --- a/mobile/src/screens/ScenesFilterModal.tsx +++ b/mobile/src/screens/ScenesFilterModal.tsx @@ -168,7 +168,7 @@ export function ScenesFilterModal({ style={styles.search} value={draft.origin} onChangeText={(v) => setDraft({ ...draft, origin: v })} - placeholder="np. hqporner, porntrex, xnxx — puste = wszystkie" + placeholder="e.g. hqporner, porntrex, xnxx — empty = all" placeholderTextColor={theme.muted} autoCapitalize="none" autoCorrect={false} diff --git a/mobile/src/screens/SiteScenesScreen.tsx b/mobile/src/screens/SiteScenesScreen.tsx index 9e8c4ce..cf1c3ac 100644 --- a/mobile/src/screens/SiteScenesScreen.tsx +++ b/mobile/src/screens/SiteScenesScreen.tsx @@ -82,12 +82,12 @@ export function SiteScenesScreen() { onPress={() => setFilterOpen(true)} > - Tagi{selectedTags.length > 0 ? ` ${selectedTags.length}` : ''} + Tags{selectedTags.length > 0 ? ` ${selectedTags.length}` : ''} {selectedTags.length > 0 ? ( setSelectedTags([])}> - Wyczyść + Clear ) : null} @@ -181,14 +181,14 @@ function TagPickerModal({ - Filtruj po tagach + Filter by tags Brak wyników + No results ) : null} )} @@ -226,7 +226,7 @@ function TagPickerModal({ - Wyczyść + Clear onApply(selected)} > - Zastosuj{selected.length > 0 ? ` (${selected.length})` : ''} + Apply{selected.length > 0 ? ` (${selected.length})` : ''}