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})` : ''}