Compare commits
	
		
			67 Commits
		
	
	
		
			v0.16.1-be
			...
			v1.0.2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 188d33199e | ||
|  | 24928261bb | ||
|  | 4c7bda8343 | ||
|  | d88709c999 | ||
|  | 29e1481a3b | ||
|  | 9d76359543 | ||
|  | f50e791221 | ||
|  | 7d08e5225c | ||
|  | 3842c1e2df | ||
|  | 21b1990991 | ||
|  | e278c9fb5a | ||
|  | bfa29bb7c2 | ||
|  | efa55a9696 | ||
|  | 8888cd6264 | ||
|  | 34e2c014e3 | ||
|  | 5d92a6d013 | ||
|  | fb03b2e95c | ||
|  | 5a1e09564c | ||
|  | 3783eba401 | ||
|  | a3530ce6bb | ||
|  | 27d8655d58 | ||
|  | fb845ce601 | ||
|  | dbd433df9d | ||
|  | badf32ff11 | ||
|  | 5e40f3264e | ||
|  | 71bb6d9410 | ||
|  | 731b682fc9 | ||
|  | c9751227a5 | ||
|  | 6ef2a26e94 | ||
|  | 5a8efa2388 | ||
|  | d25895fa28 | ||
|  | de09f3ece2 | ||
|  | 1135ffb30f | ||
|  | 5379cb31e8 | ||
|  | 7d41ab44b7 | ||
|  | b239f9bd05 | ||
|  | 1acc923ec2 | ||
|  | 8d6edfb91f | ||
|  | 491b62bb0d | ||
|  | d1413fc478 | ||
|  | 761f7039e9 | ||
|  | 59783a341d | ||
|  | 0100fa4236 | ||
|  | f6f9ed68dc | ||
|  | 8adb5e84d1 | ||
|  | 3940b65156 | ||
|  | 98e7f27eab | ||
|  | 7cf5cafd75 | ||
|  | d750e35273 | ||
|  | 1f12e12130 | ||
|  | 4a4fb4716e | ||
|  | efe6ec0d43 | ||
|  | 00d605e195 | ||
|  | f06de8d19f | ||
|  | fac335c849 | ||
|  | 5bef546ae4 | ||
|  | b690cd6a67 | ||
|  | dbee4a2657 | ||
|  | 67420f293b | ||
|  | d1d6b0792b | ||
|  | 2ec039ba0f | ||
|  | 4b9b9a10ad | ||
|  | 4193d8ada1 | ||
|  | 4701b090b0 | ||
|  | 8b0c85827c | ||
|   | 57e02d1c80 | ||
|  | 7987083555 | 
							
								
								
									
										1
									
								
								.flutter
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
							
								
								
									
										25
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -23,16 +23,27 @@ jobs: | ||||
|           gpg_private_key: ${{ secrets.PGP_KEY_BASE64 }} | ||||
|           passphrase: ${{ secrets.PGP_PASSPHRASE }} | ||||
|  | ||||
|       - name: Check submodule | ||||
|         id: check_submodule | ||||
|         run: | | ||||
|           SUBMODULE_COMMIT_LONG="$(git submodule status | head -1 | tail -c +2 | awk '{print $1}')" | ||||
|           FLUTTER_COMMIT_SHORT="$(flutter --version | head -2 | tail -1 | awk '{print $4}')" | ||||
|           echo "SUBMODULE_COMMIT_LONG=$SUBMODULE_COMMIT_LONG, FLUTTER_COMMIT_SHORT=$FLUTTER_COMMIT_SHORT" | ||||
|           if ! [[ "$SUBMODULE_COMMIT_LONG" =~ ^$FLUTTER_COMMIT_SHORT ]]; then | ||||
|             echo "Your submodule has not been updated!" | ||||
|             exit 1 | ||||
|           fi | ||||
|  | ||||
|       - name: Extract Version | ||||
|         id: extract_version       | ||||
|         run: | | ||||
|            VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10) | ||||
|            echo "version=$VERSION" >> $GITHUB_OUTPUT | ||||
|            if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi | ||||
|            echo "beta=$BETA" >> $GITHUB_OUTPUT | ||||
|            TAG="v$VERSION" | ||||
|            if [ $BETA == true ]; then TAG="$TAG"-beta; fi | ||||
|            echo "tag=$TAG" >> $GITHUB_OUTPUT | ||||
|           VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10) | ||||
|           echo "version=$VERSION" >> $GITHUB_OUTPUT | ||||
|           if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi | ||||
|           echo "beta=$BETA" >> $GITHUB_OUTPUT | ||||
|           TAG="v$VERSION" | ||||
|           if [ $BETA == true ]; then TAG="$TAG"-beta; fi | ||||
|           echo "tag=$TAG" >> $GITHUB_OUTPUT | ||||
|  | ||||
|       - name: Build APKs | ||||
|         run: | | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| [submodule ".flutter"] | ||||
| 	path = .flutter | ||||
| 	url = https://github.com/flutter/flutter/ | ||||
| @@ -108,3 +108,16 @@ dependencies { | ||||
|  | ||||
|     implementation "com.github.topjohnwu.libsu:core:5.2.2" | ||||
| } | ||||
|  | ||||
| ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3] | ||||
| import com.android.build.OutputFile | ||||
| android.applicationVariants.all { variant -> | ||||
|     variant.outputs.each { output -> | ||||
|         def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI)) | ||||
|         if (abiVersionCode != null) { | ||||
|             output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode | ||||
|         } else { | ||||
|             output.versionCodeOverride = variant.versionCode * 10 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -71,5 +71,7 @@ | ||||
|     <uses-permission | ||||
|         android:name="android.permission.WRITE_EXTERNAL_STORAGE" | ||||
|         android:maxSdkVersion="29" /> | ||||
|     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" | ||||
|         tools:node="remove" /> | ||||
|     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> | ||||
| </manifest> | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Instalirano", | ||||
|     "latest": "Najnoviji", | ||||
|     "invertRegEx": "Obrni regularni izraz", | ||||
|     "note": "Note", | ||||
|     "selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.", | ||||
|     "badDownload": "The APK could not be parsed (incompatible or partial download)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Želite li ukloniti aplikaciju?", | ||||
|         "other": "Želite li ukloniti aplikacije?" | ||||
|   | ||||
| @@ -292,12 +292,15 @@ | ||||
|     "useLatestAssetDateAsReleaseDate": "Použít poslední nahrané dílo jako datum vydání", | ||||
|     "defaultPseudoVersioningMethod": "Výchozí metoda pseudoverze", | ||||
|     "partialAPKHash": "Částečný hash APK", | ||||
|     "APKLinkHash": "APK Link Hash", | ||||
|     "APKLinkHash": "Odkaz APK Hash", | ||||
|     "directAPKLink": "Přímý odkaz APK", | ||||
|     "pseudoVersionInUse": "Pseudoverze se používá", | ||||
|     "installed": "Instalováno", | ||||
|     "latest": "Nejnovější", | ||||
|     "invertRegEx": "Invertovat regulární výraz", | ||||
|     "note": "Poznámka", | ||||
|     "selfHostedNote": "Rozbalovací seznam \"{}\" lze použít k dosažení vlastních/obvyklých instancí libovolného zdroje.", | ||||
|     "badDownload": "APK nelze analyzovat (nekompatibilní nebo částečné stažení)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Odstranit Apku?", | ||||
|         "other": "Odstranit Apky?" | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     "placeholder": "Platzhalter", | ||||
|     "someErrors": "Es traten einige Fehler auf", | ||||
|     "unexpectedError": "Unerwarteter Fehler", | ||||
|     "ok": "Okay", | ||||
|     "ok": "OK", | ||||
|     "and": "und", | ||||
|     "githubPATLabel": "GitHub Personal Access Token (Erhöht das Ratenlimit)", | ||||
|     "includePrereleases": "Vorabversionen einbeziehen", | ||||
| @@ -30,7 +30,7 @@ | ||||
|     "app": "App", | ||||
|     "appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zum Nachverfolgen.", | ||||
|     "youPickedTrackOnly": "Sie haben die Option „Nur Nachverfolgen“ gewählt.", | ||||
|     "trackOnlyAppDescription": "Die App wird auf Updates überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.", | ||||
|     "trackOnlyAppDescription": "Die App wird auf Aktualisierungen überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.", | ||||
|     "cancelled": "Abgebrochen", | ||||
|     "appAlreadyAdded": "App bereits hinzugefügt", | ||||
|     "alreadyUpToDateQuestion": "App bereits auf dem neuesten Stand?", | ||||
| @@ -86,7 +86,7 @@ | ||||
|     "author": "Autor", | ||||
|     "upToDateApps": "Apps mit aktueller Version", | ||||
|     "nonInstalledApps": "Nicht installierte Apps", | ||||
|     "importExport": "Import Export", | ||||
|     "importExport": "Import/Export", | ||||
|     "settings": "Einstellungen", | ||||
|     "exportedTo": "Exportiert zu {}", | ||||
|     "obtainiumExport": "Obtainium-Export", | ||||
| @@ -107,7 +107,7 @@ | ||||
|     "selectURL": "URL auswählen", | ||||
|     "selectURLs": "URLs auswählen", | ||||
|     "pick": "Auswählen", | ||||
|     "theme": "Thema", | ||||
|     "theme": "Theme", | ||||
|     "dark": "Dunkel", | ||||
|     "light": "Hell", | ||||
|     "followSystem": "System folgen", | ||||
| @@ -170,7 +170,7 @@ | ||||
|     "yesMarkUpdated": "Ja, als aktualisiert markieren", | ||||
|     "fdroid": "offizielles F-Droid-Repo", | ||||
|     "appIdOrName": "App ID oder Name", | ||||
|     "appId": "App-ID", | ||||
|     "appId": "App ID", | ||||
|     "appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden", | ||||
|     "reposHaveMultipleApps": "Repos können mehrere Apps enthalten", | ||||
|     "fdroidThirdPartyRepo": "F-Droid Drittparteienrepo", | ||||
| @@ -209,7 +209,7 @@ | ||||
|     "changes": "Änderungen", | ||||
|     "releaseDate": "Veröffentlichungsdatum", | ||||
|     "importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)", | ||||
|     "versionDetectionExplanation": "Versionszeichenfolge mit der vom Betriebssystem erkannten Version abgleichen", | ||||
|     "versionDetectionExplanation": "Abgleich der Versionsnummer mit der vom Betriebssystem erkannten Version", | ||||
|     "versionDetection": "Versionserkennung", | ||||
|     "standardVersionDetection": "Standardversionserkennung", | ||||
|     "groupByCategory": "Nach Kategorie gruppieren", | ||||
| @@ -226,7 +226,7 @@ | ||||
|     "tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln", | ||||
|     "removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps", | ||||
|     "pickHighestVersionCode": "Automatische Auswahl des APK mit höchstem Versionscode", | ||||
|     "checkUpdateOnDetailPage": "Nach Updates suchen, wenn eine App-Detailseite geöffnet wird", | ||||
|     "checkUpdateOnDetailPage": "Nach Aktualisierungen suchen, wenn eine App-Detailseite geöffnet wird", | ||||
|     "disablePageTransitions": "Animationen für Seitenübergänge deaktivieren", | ||||
|     "reversePageTransitions": "Umgekehrte Animationen für Seitenübergänge", | ||||
|     "minStarCount": "Minimale Anzahl von Sternen", | ||||
| @@ -238,7 +238,7 @@ | ||||
|     "filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern", | ||||
|     "customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')", | ||||
|     "appsPossiblyUpdated": "App Aktualisierungen wurden versucht", | ||||
|     "appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden", | ||||
|     "appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden", | ||||
|     "xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.", | ||||
|     "enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen", | ||||
|     "backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.", | ||||
| @@ -268,7 +268,7 @@ | ||||
|     "runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen", | ||||
|     "versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden", | ||||
|     "installing": "Installiere", | ||||
|     "skipUpdateNotifications": "Keine Benachrichtigung zu App-Updates geben", | ||||
|     "skipUpdateNotifications": "Keine Benachrichtigung zu App-Aktualisierungen geben", | ||||
|     "updatesAvailableNotifChannel": "Aktualisierungen verfügbar", | ||||
|     "appsUpdatedNotifChannel": "Apps aktualisiert", | ||||
|     "appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht", | ||||
| @@ -283,28 +283,31 @@ | ||||
|     "parallelDownloads": "Erlaube parallele Downloads", | ||||
|     "installMethod": "Installationsmethode", | ||||
|     "normal": "Normal", | ||||
|     "root": "Wurzel", | ||||
|     "root": "Root", | ||||
|     "shizukuBinderNotFound": "Kompatibler Shizukudienst wurde nicht gefunden", | ||||
|     "useSystemFont": "Verwende die Systemschriftart", | ||||
|     "systemFontError": "Fehler beim Laden der Systemschriftart: {}", | ||||
|     "useVersionCodeAsOSVersion": "Verwende die Appversion als erkannte Version vom Betriebssystem", | ||||
|     "requestHeader": "Request Header", | ||||
|     "useLatestAssetDateAsReleaseDate": "Den letzten Asset-Upload als Veröffentlichungsdatum verwenden", | ||||
|     "defaultPseudoVersioningMethod": "Standard-Pseudoversionierungsmethode", | ||||
|     "partialAPKHash": "Teilweiser APK-Hash", | ||||
|     "defaultPseudoVersioningMethod": "Standardmäßiges Verfahren zur Pseudo-Versionierung", | ||||
|     "partialAPKHash": "partieller APK-Hash", | ||||
|     "APKLinkHash": "APK-Link-Hash", | ||||
|     "directAPKLink": "Direkter APK-Link", | ||||
|     "pseudoVersionInUse": "Eine Pseudoversion ist im Einsatz", | ||||
|     "installed": "Eingerichtet", | ||||
|     "latest": "Neueste", | ||||
|     "invertRegEx": "Regulären Ausdruck umkehren", | ||||
|     "pseudoVersionInUse": "Es werden Pseudoversionen verwendet", | ||||
|     "installed": "Installiert", | ||||
|     "latest": "Neueste Version", | ||||
|     "invertRegEx": "Regulären Ausdruck  invertieren", | ||||
|     "note": "Hinweis", | ||||
|     "selfHostedNote": "Das „{}“-Dropdown-Menü kann verwendet werden, um selbst gehostete/angepasste Instanzen einer beliebigen Quelle zu erreichen.", | ||||
|     "badDownload": "Die APK konnte nicht geparst werden (inkompatibler oder teilweiser Download)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "App entfernen?", | ||||
|         "other": "Apps entfernen?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Zu viele Anfragen (Rate begrenzt) – versuchen Sie es in {} Minute erneut", | ||||
|         "other": "Zu viele Anfragen (Rate begrenzt) – versuchen Sie es in {} Minuten erneut" | ||||
|         "one": "Zu viele Anfragen (Rate begrenzt) – versuche es in {} Minute erneut", | ||||
|         "other": "Zu viele Anfragen (Rate begrenzt) – versuche es in {} Minuten erneut" | ||||
|     }, | ||||
|     "bgUpdateGotErrorRetryInMinutes": { | ||||
|         "one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant", | ||||
|   | ||||
| @@ -80,7 +80,6 @@ | ||||
|     "removeOutdatedFilter": "Remove Out-of-Date App Filter", | ||||
|     "showOutdatedOnly": "Show Out-of-Date Apps Only", | ||||
|     "filter": "Filter", | ||||
|     "filterActive": "Filter *", | ||||
|     "filterApps": "Filter Apps", | ||||
|     "appName": "App Name", | ||||
|     "author": "Author", | ||||
| @@ -298,6 +297,9 @@ | ||||
|     "installed": "Installed", | ||||
|     "latest": "Latest", | ||||
|     "invertRegEx": "Invert regular expression", | ||||
|     "note": "Note", | ||||
|     "selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.", | ||||
|     "badDownload": "The APK could not be parsed (incompatible or partial download)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Remove App?", | ||||
|         "other": "Remove Apps?" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|     "invalidURLForSource": "URL de la aplicación {} no válida", | ||||
|     "invalidURLForSource": "URL de la aplicación {} no es válida", | ||||
|     "noReleaseFound": "No se ha podido encontrar una versión válida", | ||||
|     "noVersionFound": "No se ha podido determinar la versión de la publicación", | ||||
|     "noVersionFound": "No se ha podido determinar la versión", | ||||
|     "urlMatchesNoSource": "La URL no coincide con ninguna fuente conocida", | ||||
|     "cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación", | ||||
|     "appIdMismatch": "La ID del paquete descargado no coincide con la ID de la aplicación instalada", | ||||
| @@ -11,7 +11,7 @@ | ||||
|     "unexpectedError": "Error inesperado", | ||||
|     "ok": "OK", | ||||
|     "and": "y", | ||||
|     "githubPATLabel": "Token GitHub de acceso personal\n(reduce tiempos de espera)", | ||||
|     "githubPATLabel": "Token de acceso personal a GitHub\n(reduce tiempos de espera)", | ||||
|     "includePrereleases": "Incluir versiones preliminares", | ||||
|     "fallbackToOlderReleases": "Retroceder a versiones previas", | ||||
|     "filterReleaseTitlesByRegEx": "Filtrar por título de versión", | ||||
| @@ -22,20 +22,20 @@ | ||||
|     "requiredInBrackets": "(Requerido)", | ||||
|     "dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN", | ||||
|     "colour": "Color", | ||||
|     "githubStarredRepos": "Repositorios favoritos GitHub", | ||||
|     "githubStarredRepos": "Repositorios favoritos en GitHub", | ||||
|     "uname": "Nombre de usuario", | ||||
|     "wrongArgNum": "Número de argumentos provistos inválido", | ||||
|     "xIsTrackOnly": "{} es de 'Sólo seguimiento'", | ||||
|     "xIsTrackOnly": "{} es de 'sólo seguimiento'", | ||||
|     "source": "Origen", | ||||
|     "app": "Aplicación", | ||||
|     "appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'solo seguimiento'.", | ||||
|     "youPickedTrackOnly": "Debe seleccionar la opción de 'solo seguimiento'.", | ||||
|     "trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargarla o actalizarla.", | ||||
|     "trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargar o actualizarla.", | ||||
|     "cancelled": "Cancelado", | ||||
|     "appAlreadyAdded": "Aplicación ya añadida", | ||||
|     "alreadyUpToDateQuestion": "¿Aplicación ya actualizada?", | ||||
|     "appAlreadyAdded": "Aplicación añadida anteriormente", | ||||
|     "alreadyUpToDateQuestion": "¿Aplicación actualizada previamente?", | ||||
|     "addApp": "Añadir Aplicación", | ||||
|     "appSourceURL": "URL de Origen de la Aplicación", | ||||
|     "appSourceURL": "URL de origen de la aplicación", | ||||
|     "error": "Error", | ||||
|     "add": "Añadir", | ||||
|     "searchSomeSourcesLabel": "Buscar (solo algunas fuentes)", | ||||
| @@ -71,11 +71,11 @@ | ||||
|     "pinToTop": "Fijar arriba", | ||||
|     "unpinFromTop": "Desfijar de arriba", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?", | ||||
|     "installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.", | ||||
|     "installStatusOfXWillBeResetExplanation": "Se restaurará el estado de instalación de las aplicaciones seleccionadas.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.", | ||||
|     "customLinkMessage": "Estos enlaces funcionan en dispositivos con Obtainium instalado", | ||||
|     "shareAppConfigLinks": "Compartir la configuración de la aplicación como enlace HTML", | ||||
|     "shareSelectedAppURLs": "Compartir URLs de las aplicaciones seleccionadas", | ||||
|     "resetInstallStatus": "Restaurar Estado de Instalación", | ||||
|     "resetInstallStatus": "Restaurar estado de instalación", | ||||
|     "more": "Más", | ||||
|     "removeOutdatedFilter": "Elimiar filtro de aplicaciones desactualizado", | ||||
|     "showOutdatedOnly": "Mostrar solo aplicaciones desactualizadas", | ||||
| @@ -123,7 +123,7 @@ | ||||
|     "neverManualOnly": "Nunca, solo manual", | ||||
|     "appearance": "Apariencia", | ||||
|     "showWebInAppView": "Mostrar vista de la web de origen", | ||||
|     "pinUpdates": "Fijar Actualizaciones al principio", | ||||
|     "pinUpdates": "Fijar actualizaciones al principio", | ||||
|     "updates": "Actualizaciones", | ||||
|     "sourceSpecific": "Fuente Específica", | ||||
|     "appSource": "Obtainium en GitHub", | ||||
| @@ -140,77 +140,77 @@ | ||||
|     "warning": "Aviso", | ||||
|     "sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es '{}' pero el paquete de la actualización viene de '{}'. ¿Desea continuar?", | ||||
|     "updatesAvailable": "Actualizaciones Disponibles", | ||||
|     "updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitorizadas por Obtainium", | ||||
|     "updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitoreadas por Obtainium", | ||||
|     "noNewUpdates": "No hay nuevas actualizaciones.", | ||||
|     "xHasAnUpdate": "{} tiene una actualización.", | ||||
|     "appsUpdated": "Aplicaciones Actualizadas", | ||||
|     "appsUpdated": "Aplicaciones actualizadas", | ||||
|     "appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano", | ||||
|     "xWasUpdatedToY": "{} ha sido actualizada a {}.", | ||||
|     "errorCheckingUpdates": "Error Buscando Actualizaciones", | ||||
|     "errorCheckingUpdates": "Error buscando ectualizaciones", | ||||
|     "errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla", | ||||
|     "appsRemoved": "Aplicaciones Eliminadas", | ||||
|     "appsRemoved": "Aplicaciones eliminadas", | ||||
|     "appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas", | ||||
|     "xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}", | ||||
|     "completeAppInstallation": "Instalación Completa de la Aplicación", | ||||
|     "completeAppInstallation": "Instalación completa de la aplicación", | ||||
|     "obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones", | ||||
|     "completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para terminar de instalar una aplicación", | ||||
|     "checkingForUpdates": "Buscando Actualizaciones", | ||||
|     "checkingForUpdates": "Buscando actualizaciones...", | ||||
|     "checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones", | ||||
|     "pleaseAllowInstallPerm": "Por favor, permita que Obtainium instale aplicaciones", | ||||
|     "trackOnly": "Solo Seguimiento", | ||||
|     "trackOnly": "Solo para seguimiento", | ||||
|     "errorWithHttpStatusCode": "Error {}", | ||||
|     "versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)", | ||||
|     "unknown": "Desconocido", | ||||
|     "none": "Ninguno", | ||||
|     "never": "Nunca", | ||||
|     "latestVersionX": "Última Versión: {}", | ||||
|     "installedVersionX": "Versión Instalada: {}", | ||||
|     "lastUpdateCheckX": "Última Comprobación: {}", | ||||
|     "latestVersionX": "Última versión: {}", | ||||
|     "installedVersionX": "Versión instalada: {}", | ||||
|     "lastUpdateCheckX": "Última comprobación: {}", | ||||
|     "remove": "Eliminar", | ||||
|     "yesMarkUpdated": "Sí, Marcar como Actualizada", | ||||
|     "yesMarkUpdated": "Sí, marcar como actualizada", | ||||
|     "fdroid": "Repositorio oficial F-Droid", | ||||
|     "appIdOrName": "ID o Nombre de la Aplicación", | ||||
|     "appId": "ID de la Aplicación", | ||||
|     "appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre", | ||||
|     "reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones", | ||||
|     "fdroidThirdPartyRepo": "Rpositorios de terceros F-Droid", | ||||
|     "fdroidThirdPartyRepo": "Repositorio de tercera parte F-Droid", | ||||
|     "steamMobile": "Móvil de vapor", | ||||
|     "steamChat": "Chat de vapor", | ||||
|     "install": "Instalar", | ||||
|     "markInstalled": "Marcar como Instalada", | ||||
|     "markInstalled": "Marcar como instalada", | ||||
|     "update": "Actualizar", | ||||
|     "markUpdated": "Marcar como Actualizada", | ||||
|     "additionalOptions": "Opciones Adicionales", | ||||
|     "disableVersionDetection": "Descativar Detección de Versiones", | ||||
|     "noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda no funcionar correctamente.", | ||||
|     "markUpdated": "Marcar como actualizada", | ||||
|     "additionalOptions": "Opciones adicionales", | ||||
|     "disableVersionDetection": "Desactivar la detección de versiones", | ||||
|     "noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda que no funcionar correctamente.", | ||||
|     "downloadingX": "Descargando {}", | ||||
|     "downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación", | ||||
|     "noAPKFound": "APK no encontrada", | ||||
|     "noAPKFound": "No se encontró el paquete de instalación APK", | ||||
|     "noVersionDetection": "Sin detección de versiones", | ||||
|     "categorize": "Catogorizar", | ||||
|     "categories": "Categorías", | ||||
|     "category": "Categoría", | ||||
|     "noCategory": "Sin Categoría", | ||||
|     "noCategories": "Sin Categorías", | ||||
|     "deleteCategoriesQuestion": "¿Eliminar Categorías?", | ||||
|     "categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin Categoría'.", | ||||
|     "addCategory": "Añadir Categoría", | ||||
|     "noCategory": "Sin categoría", | ||||
|     "noCategories": "Sin categorías", | ||||
|     "deleteCategoriesQuestion": "¿Eliminar categorías?", | ||||
|     "categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin categoría'.", | ||||
|     "addCategory": "Añadir categoría", | ||||
|     "label": "Nombre", | ||||
|     "language": "Idioma", | ||||
|     "copiedToClipboard": "Copiado al Portapapeles", | ||||
|     "storagePermissionDenied": "Permiso de Almacenamiento rechazado", | ||||
|     "copiedToClipboard": "Copiado al portapapeles", | ||||
|     "storagePermissionDenied": "Permiso de almacenamiento rechazado", | ||||
|     "selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.", | ||||
|     "filterAPKsByRegEx": "Filtrar por APKs", | ||||
|     "removeFromObtainium": "Eliminar de Obtainium", | ||||
|     "uninstallFromDevice": "Desinstalar del Dispositivo", | ||||
|     "uninstallFromDevice": "Desinstalar del dispositivo", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.", | ||||
|     "releaseDateAsVersion": "Por fecha de publicación", | ||||
|     "releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.", | ||||
|     "changes": "Cambios", | ||||
|     "releaseDate": "Fecha de Publicación", | ||||
|     "releaseDate": "Fecha de publicación", | ||||
|     "importFromURLsInFile": "Importar URLs desde archivo (como OPML)", | ||||
|     "versionDetectionExplanation": "Conciliar la cadena de versión con la versión detectada desde el sistema operativo", | ||||
|     "versionDetection": "Detección de Versiones", | ||||
|     "versionDetection": "Detección de versiones", | ||||
|     "standardVersionDetection": "Por versión", | ||||
|     "groupByCategory": "Agrupar por categoría", | ||||
|     "autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador (si es posible)", | ||||
| @@ -219,25 +219,25 @@ | ||||
|     "dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps en 'solo seguimiento'", | ||||
|     "dontShowAPKOriginWarnings": "No mostrar avisos sobre las fuentes de las APKs", | ||||
|     "moveNonInstalledAppsToBottom": "Mover Apps no instaladas al final", | ||||
|     "gitlabPATLabel": "Token GitLab de acceso personal\n(habilita la búsqueda y mejor detección de APKs)", | ||||
|     "gitlabPATLabel": "Token de acceso personal a GitLab\n(habilita la búsqueda y mejor detección de APKs)", | ||||
|     "about": "Acerca", | ||||
|     "requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en ajustes)", | ||||
|     "checkOnStart": "Comprobar actualizaciones al inicio", | ||||
|     "tryInferAppIdFromCode": "Intentar deducir la ID de la app por el código fuente", | ||||
|     "removeOnExternalUninstall": "Auto eliminar apps desinstaladas externamente", | ||||
|     "pickHighestVersionCode": "Auto selección versión superior del código APK", | ||||
|     "pickHighestVersionCode": "Auto selección de versión superior del paquete APK", | ||||
|     "checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la app", | ||||
|     "disablePageTransitions": "Deshabilitar animaciones de transición", | ||||
|     "reversePageTransitions": "Invertir animaciones de transición", | ||||
|     "minStarCount": "Número Mínimo de Estrellas", | ||||
|     "addInfoBelow": "Añadir esta información debajo.", | ||||
|     "addInfoInSettings": "Puede añadir esta información en Ajustes.", | ||||
|     "githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con una clave API.", | ||||
|     "gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin una clave API.", | ||||
|     "githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con un 'token de acceso personal'.", | ||||
|     "gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin un 'token de acceso personal'.", | ||||
|     "sortByLastLinkSegment": "Ordenar sólo por el último segmento del enlace", | ||||
|     "filterReleaseNotesByRegEx": "Filtrar por notas de versión (release notes)", | ||||
|     "customLinkFilterRegex": "Filtro personalizado de Enlace APK (por defecto '.apk$')", | ||||
|     "appsPossiblyUpdated": "Actualización de Apps intentada", | ||||
|     "customLinkFilterRegex": "Filtro personalizado de enlace APK (por defecto '.apk$')", | ||||
|     "appsPossiblyUpdated": "Actualización de apps intentada", | ||||
|     "appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones", | ||||
|     "xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.", | ||||
|     "enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano", | ||||
| @@ -250,16 +250,16 @@ | ||||
|     "intermediateLink": "Enlace intermedio", | ||||
|     "exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)", | ||||
|     "bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi", | ||||
|     "autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior", | ||||
|     "autoSelectHighestVersionCode": "Auto selección del paquete APK con versión más reciente", | ||||
|     "versionExtractionRegEx": "Versión de extracción regex", | ||||
|     "matchGroupToUse": "Grupo a usar para versión de extracción regex", | ||||
|     "highlightTouchTargets": "Resaltar objetivos menos obvios", | ||||
|     "pickExportDir": "Directorio para Exportar", | ||||
|     "autoExportOnChanges": "Auto Exportar cuando haya cambios", | ||||
|     "pickExportDir": "Directorio para exportar", | ||||
|     "autoExportOnChanges": "Auto exportar cuando haya cambios", | ||||
|     "includeSettings": "Incluir ajustes", | ||||
|     "filterVersionsByRegEx": "Filtrar por Versiones", | ||||
|     "trySelectingSuggestedVersionCode": "Pruebe seleccionando la versionCode APK sugerida", | ||||
|     "dontSortReleasesList": "Mantener el order de publicación desde API", | ||||
|     "filterVersionsByRegEx": "Filtrar por versiones", | ||||
|     "trySelectingSuggestedVersionCode": "Pruebe seleccionando la versión del paquete APK sugerida", | ||||
|     "dontSortReleasesList": "Mantener el order de publicación de la fuente original", | ||||
|     "reverseSort": "Orden inverso", | ||||
|     "takeFirstLink": "Usar primer enlace", | ||||
|     "skipSort": "Omitir orden", | ||||
| @@ -284,10 +284,10 @@ | ||||
|     "installMethod": "Método de instalación", | ||||
|     "normal": "Normal", | ||||
|     "root": "Raíz", | ||||
|     "shizukuBinderNotFound": "Shizuku no está operativo", | ||||
|     "useSystemFont": "Usar la fuente del sistema", | ||||
|     "systemFontError": "Error al cargar la fuente del sistema: {}", | ||||
|     "useVersionCodeAsOSVersion": "Usar el código de versión de la aplicación como versión detectada por el sistema operativo", | ||||
|     "shizukuBinderNotFound": "Shizuku no funciona", | ||||
|     "useSystemFont": "Usar la fuente de impresión del sistema", | ||||
|     "systemFontError": "Error al cargar la fuente de impresión del sistema: {}", | ||||
|     "useVersionCodeAsOSVersion": "Usar la versión de la aplicación como versión detectada por el sistema operativo", | ||||
|     "requestHeader": "Encabezado de solicitud", | ||||
|     "useLatestAssetDateAsReleaseDate": "Usar la última carga de recursos como fecha de lanzamiento", | ||||
|     "defaultPseudoVersioningMethod": "Método de pseudoversionado predeterminado", | ||||
| @@ -296,8 +296,11 @@ | ||||
|     "directAPKLink": "Enlace APK directo", | ||||
|     "pseudoVersionInUse": "Se está utilizando una pseudoversión", | ||||
|     "installed": "Instalado", | ||||
|     "latest": "El último", | ||||
|     "latest": "Versión más reciente", | ||||
|     "invertRegEx": "Invertir expresión regular", | ||||
|     "note": "Nota", | ||||
|     "selfHostedNote": "El desplegable \"{}\" puede utilizarse para acceder a instancias autoalojadas/personalizadas de cualquier fuente.", | ||||
|     "badDownload": "No se ha podido analizar el APK (incompatible o descarga parcial)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "¿Eliminar Aplicación?", | ||||
|         "other": "¿Eliminar Aplicaciones?" | ||||
|   | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "نصب شده است", | ||||
|     "latest": "آخرین", | ||||
|     "invertRegEx": "معکوس کردن عبارت منظم", | ||||
|     "note": "Note", | ||||
|     "selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.", | ||||
|     "badDownload": "The APK could not be parsed (incompatible or partial download)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "برنامه حذف شود؟", | ||||
|         "other": "برنامه ها حذف شوند؟" | ||||
|   | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Installée", | ||||
|     "latest": "Dernier", | ||||
|     "invertRegEx": "Inverser l'expression régulière", | ||||
|     "note": "Note", | ||||
|     "selfHostedNote": "La liste déroulante \"{}\" peut être utilisée pour accéder aux instances auto-hébergées/personnalisées de n'importe quelle source.", | ||||
|     "badDownload": "L'APK n'a pas pu être analysé (téléchargement incompatible ou partiel)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Supprimer l'application ?", | ||||
|         "other": "Supprimer les applications ?" | ||||
|   | ||||
| @@ -287,7 +287,7 @@ | ||||
|     "shizukuBinderNotFound": "A Shizuku nem fut", | ||||
|     "useSystemFont": "Használja a rendszer betűtípusát", | ||||
|     "systemFontError": "Hiba a rendszer betűtípusának betöltésekor: {}", | ||||
|     "useVersionCodeAsOSVersion": "Az app versionCode használata a rendszer által észlelt verzióként", | ||||
|     "useVersionCodeAsOSVersion": "Az app verziókód használata a rendszer által észlelt verzióként", | ||||
|     "requestHeader": "Kérelem fejléc", | ||||
|     "useLatestAssetDateAsReleaseDate": "Használja a legújabb tartalomfeltöltést megjelenési dátumként", | ||||
|     "defaultPseudoVersioningMethod": "Alapértelmezett álversziós módszer", | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Telepített", | ||||
|     "latest": "Legújabb", | ||||
|     "invertRegEx": "Invertált reguláris kifejezés", | ||||
|     "note": "Megjegyzés:", | ||||
|     "selfHostedNote": "A \"{}\" legördülő menü használható bármely forrás saját üzemeltetésű/egyéni példányainak eléréséhez.", | ||||
|     "badDownload": "Az APK-t nem lehetett elemezni (inkompatibilis vagy részleges letöltés)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Eltávolítja az alkalmazást?", | ||||
|         "other": "Eltávolítja az alkalmazást?" | ||||
|   | ||||
| @@ -111,7 +111,7 @@ | ||||
|     "dark": "Scuro", | ||||
|     "light": "Chiaro", | ||||
|     "followSystem": "Segui il sistema", | ||||
|     "useBlackTheme": "Usa il tema Nero puro", | ||||
|     "useBlackTheme": "Usa il tema nero puro", | ||||
|     "appSortBy": "App ordinate per", | ||||
|     "authorName": "Autore/Nome", | ||||
|     "nameAuthor": "Nome/Autore", | ||||
| @@ -283,13 +283,13 @@ | ||||
|     "parallelDownloads": "Permetti download paralleli", | ||||
|     "installMethod": "Metodo d'installazione", | ||||
|     "normal": "Normale", | ||||
|     "root": "Radice", | ||||
|     "root": "Root", | ||||
|     "shizukuBinderNotFound": "Shizuku non è in esecuzione", | ||||
|     "useSystemFont": "Utilizza il carattere di sistema", | ||||
|     "systemFontError": "Errore durante il caricamento del carattere di sistema: {}", | ||||
|     "useVersionCodeAsOSVersion": "Utilizza il codice versione dell'app come versione rilevata dal sistema operativo", | ||||
|     "useSystemFont": "Usa i caratteri di sistema", | ||||
|     "systemFontError": "Errore durante il caricamento dei caratteri di sistema: {}", | ||||
|     "useVersionCodeAsOSVersion": "Usa il codice versione dell'app come versione rilevata dal sistema operativo", | ||||
|     "requestHeader": "Intestazione della richiesta", | ||||
|     "useLatestAssetDateAsReleaseDate": "Utilizza l'ultimo caricamento della risorsa come data di rilascio", | ||||
|     "useLatestAssetDateAsReleaseDate": "Usa l'ultimo caricamento della risorsa come data di rilascio", | ||||
|     "defaultPseudoVersioningMethod": "Metodo di pseudoversione predefinito", | ||||
|     "partialAPKHash": "Hash APK parziale", | ||||
|     "APKLinkHash": "Hash collegamento APK", | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Installato", | ||||
|     "latest": "Ultimo", | ||||
|     "invertRegEx": "Inverti espressione regolare", | ||||
|     "note": "Nota", | ||||
|     "selfHostedNote": "Il menu a tendina \"{}\" può essere usato per raggiungere istanze autogestite/personali di qualsiasi fonte.", | ||||
|     "badDownload": "Non è stato possibile analizzare l'APK (download incompatibile o parziale).", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Rimuovere l'app?", | ||||
|         "other": "Rimuovere le app?" | ||||
|   | ||||
| @@ -168,14 +168,14 @@ | ||||
|     "lastUpdateCheckX": "最終アップデート確認: {}", | ||||
|     "remove": "削除", | ||||
|     "yesMarkUpdated": "はい、アップデート済みとしてマークします", | ||||
|     "fdroid": "F-ドロイドオフィシャル", | ||||
|     "fdroid": "F-Droid公式", | ||||
|     "appIdOrName": "アプリのIDまたは名前", | ||||
|     "appId": "アプリID", | ||||
|     "appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした", | ||||
|     "reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります", | ||||
|     "fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ", | ||||
|     "steamMobile": "スチームモバイル", | ||||
|     "steamChat": "スチームチャット", | ||||
|     "steamMobile": "Steamモバイル", | ||||
|     "steamChat": "Steamチャット", | ||||
|     "install": "インストール", | ||||
|     "markInstalled": "インストール済みとしてマークする", | ||||
|     "update": "アップデート", | ||||
| @@ -209,7 +209,7 @@ | ||||
|     "changes": "変更点", | ||||
|     "releaseDate": "リリース日", | ||||
|     "importFromURLsInFile": "ファイル(OPMLなど)内のURLからインポート", | ||||
|     "versionDetectionExplanation": "バージョン文字列とOSから検出されたバージョンを一致させます", | ||||
|     "versionDetectionExplanation": "バージョン文字列とOSから検出されたバージョンを照合する", | ||||
|     "versionDetection": "バージョン検出", | ||||
|     "standardVersionDetection": "標準のバージョン検出", | ||||
|     "groupByCategory": "カテゴリ別にグループ化する", | ||||
| @@ -283,21 +283,24 @@ | ||||
|     "parallelDownloads": "並行ダウンロードを許可する", | ||||
|     "installMethod": "インストール方法", | ||||
|     "normal": "通常", | ||||
|     "root": "根", | ||||
|     "root": "Root", | ||||
|     "shizukuBinderNotFound": "Shizukuが起動していません", | ||||
|     "useSystemFont": "システムフォントを使用する", | ||||
|     "systemFontError": "システムフォントの読み込みエラー: {}", | ||||
|     "useVersionCodeAsOSVersion": "アプリの versionCode を OS で検出されたバージョンとして使用する", | ||||
|     "useVersionCodeAsOSVersion": "アプリのバージョンコードをOSで検出されたバージョンとして使用する", | ||||
|     "requestHeader": "リクエストヘッダー", | ||||
|     "useLatestAssetDateAsReleaseDate": "最新のアセットアップロードをリリース日として使用", | ||||
|     "useLatestAssetDateAsReleaseDate": "最新のアセットアップロードをリリース日として使用する", | ||||
|     "defaultPseudoVersioningMethod": "デフォルトの疑似バージョン管理方法", | ||||
|     "partialAPKHash": "部分的なAPKハッシュ", | ||||
|     "APKLinkHash": "APKリンクハッシュ", | ||||
|     "directAPKLink": "APK ダイレクトリンク", | ||||
|     "partialAPKHash": "部分的なAPKのハッシュ", | ||||
|     "APKLinkHash": "APKリンクのハッシュ", | ||||
|     "directAPKLink": "APKのダイレクトリンク", | ||||
|     "pseudoVersionInUse": "疑似バージョンが使用されています", | ||||
|     "installed": "インストールされました", | ||||
|     "installed": "インストール済み", | ||||
|     "latest": "最新", | ||||
|     "invertRegEx": "正規表現を反転", | ||||
|     "note": "注", | ||||
|     "selfHostedNote": "ドロップダウン\"{}\"を使用すると、あらゆるソースのセルフホスト/カスタムインスタンスにアクセスできます。", | ||||
|     "badDownload": "APK を解析できませんでした(互換性がないか、部分的にダウンロードされています)。", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "アプリを削除しますか?", | ||||
|         "other": "アプリを削除しますか?" | ||||
|   | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Geïnstalleerd", | ||||
|     "latest": "Laatste", | ||||
|     "invertRegEx": "Reguliere expressie omkeren", | ||||
|     "note": "Opmerking", | ||||
|     "selfHostedNote": "De \"{}\" dropdown kan gebruikt worden om zelf gehoste/aangepaste instanties van elke bron te bereiken.", | ||||
|     "badDownload": "De APK kon niet worden verwerkt (incompatibele of gedeeltelijke download)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "App verwijderen?", | ||||
|         "other": "Apps verwijderen?" | ||||
|   | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Zainstalowano", | ||||
|     "latest": "Najnowszy", | ||||
|     "invertRegEx": "Odwróć wyrażenie regularne", | ||||
|     "note": "Uwaga", | ||||
|     "selfHostedNote": "Lista rozwijana \"{}\" może być używana do uzyskiwania dostępu do samodzielnie hostowanych / niestandardowych instancji dowolnego źródła.", | ||||
|     "badDownload": "Nie można przeanalizować pliku APK (niekompatybilny lub częściowo pobrany).", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Usunąć aplikację?", | ||||
|         "few": "Usunąć aplikacje?", | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     "invalidURLForSource": "URL {} inválida", | ||||
|     "noReleaseFound": "Não foi possível encontrar uma versão adequada", | ||||
|     "noVersionFound": "Não foi possível encontrar uma versão", | ||||
|     "urlMatchesNoSource": "URL não corresponde a uma fonte conhecida", | ||||
|     "urlMatchesNoSource": "A URL não corresponde a uma fonte conhecida", | ||||
|     "cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de um aplicativo", | ||||
|     "appIdMismatch": "ID do pacote baixado não é igual ao ID do aplicativo instalado", | ||||
|     "functionNotImplemented": "Esta classe não implementou essa função", | ||||
| @@ -11,10 +11,10 @@ | ||||
|     "unexpectedError": "Erro inesperado", | ||||
|     "ok": "OK", | ||||
|     "and": "e", | ||||
|     "githubPATLabel": "Token de acesso pessoal do GitHub (Reduz tempos de espera)", | ||||
|     "githubPATLabel": "Token de acesso pessoal do GitHub\n(Reduz tempos de espera)", | ||||
|     "includePrereleases": "Incluir pré-lançamentos", | ||||
|     "fallbackToOlderReleases": "Retornar para versões anteriores", | ||||
|     "filterReleaseTitlesByRegEx": "Filtrar títulos de versões por expressão regular", | ||||
|     "filterReleaseTitlesByRegEx": "Filtrar títulos de versões usando expressão regular", | ||||
|     "invalidRegEx": "Expressão regular inválida", | ||||
|     "noDescription": "Sem descrição", | ||||
|     "cancel": "Cancelar", | ||||
| @@ -22,7 +22,7 @@ | ||||
|     "requiredInBrackets": "(Necessário)", | ||||
|     "dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO", | ||||
|     "colour": "Cor", | ||||
|     "githubStarredRepos": "Favoritados no GitHub", | ||||
|     "githubStarredRepos": "repositórios favoritos no GitHub", | ||||
|     "uname": "Nome de usuário", | ||||
|     "wrongArgNum": "Número de argumentos errado", | ||||
|     "xIsTrackOnly": "{} é 'Apenas monitorar'", | ||||
| @@ -38,12 +38,12 @@ | ||||
|     "appSourceURL": "URL de origem do aplicativo", | ||||
|     "error": "Erro", | ||||
|     "add": "Adicionar", | ||||
|     "searchSomeSourcesLabel": "Procurar (Apenas algumas fontes)", | ||||
|     "searchSomeSourcesLabel": "Procurar (apenas algumas fontes)", | ||||
|     "search": "Procurar", | ||||
|     "additionalOptsFor": "Opções adicionais para {}", | ||||
|     "supportedSources": "Fontes compatíveis", | ||||
|     "trackOnlyInBrackets": "(Apenas monitorar)", | ||||
|     "searchableInBrackets": "(Pesquisável)", | ||||
|     "trackOnlyInBrackets": "(apenas monitorar)", | ||||
|     "searchableInBrackets": "(pesquisável)", | ||||
|     "appsString": "Aplicativos", | ||||
|     "noApps": "Não há aplicativos", | ||||
|     "noAppsForFilter": "Sem aplicativos para filtrar", | ||||
| @@ -70,12 +70,12 @@ | ||||
|     "markSelectedAppsUpdated": "Marcar aplicativos selecionados como Atualizados", | ||||
|     "pinToTop": "Fixar no topo", | ||||
|     "unpinFromTop": "Desafixar do topo", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação para aplicativos selecionados?", | ||||
|     "installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer aplicativo selecionado será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.", | ||||
|     "resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação nos aplicativos selecionados?", | ||||
|     "installStatusOfXWillBeResetExplanation": "O status de instalação de todos os aplicativos selecionados será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.", | ||||
|     "customLinkMessage": "Esses links funcionam em dispositivos com o Obtainium instalado", | ||||
|     "shareAppConfigLinks": "Compartilhar configuração do aplicativo como link HTML", | ||||
|     "shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados", | ||||
|     "resetInstallStatus": "Reiniciar status de Iistalação", | ||||
|     "resetInstallStatus": "Reiniciar status de instalação", | ||||
|     "more": "Mais", | ||||
|     "removeOutdatedFilter": "Remover filtro de aplicativos desatualizados", | ||||
|     "showOutdatedOnly": "Mostrar apenas aplicativos desatualizados", | ||||
| @@ -84,20 +84,20 @@ | ||||
|     "filterApps": "Filtrar aplicativos", | ||||
|     "appName": "Nome do aplicativo", | ||||
|     "author": "Autor", | ||||
|     "upToDateApps": "Aplicativos tualizados", | ||||
|     "upToDateApps": "Aplicativos atualizados", | ||||
|     "nonInstalledApps": "Aplicativos não instalados", | ||||
|     "importExport": "Importar/Exportar", | ||||
|     "settings": "Configurações", | ||||
|     "exportedTo": "Exportado para {}", | ||||
|     "obtainiumExport": "Exportar Obtainium", | ||||
|     "invalidInput": "Input Inválido", | ||||
|     "obtainiumExport": "Exportar dados do Obtainium", | ||||
|     "invalidInput": "Entrada inválida", | ||||
|     "importedX": "Importado {}", | ||||
|     "obtainiumImport": "Importar Obtainium", | ||||
|     "obtainiumImport": "Importar dados do Obtainium", | ||||
|     "importFromURLList": "Importar de lista de URLs", | ||||
|     "searchQuery": "Pesquisa", | ||||
|     "appURLList": "Lista de URLs de aplicativos", | ||||
|     "line": "Linha", | ||||
|     "searchX": "Pesquisa {}", | ||||
|     "searchX": "Pesquisar na/o {}", | ||||
|     "noResults": "Nenhum resultado encontrado", | ||||
|     "importX": "Importar {}", | ||||
|     "importedAppsIdDisclaimer": "Aplicativos Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do aplicativo.\n\nAfeta apenas métodos de importação de URL e de terceiros.", | ||||
| @@ -110,8 +110,8 @@ | ||||
|     "theme": "Tema", | ||||
|     "dark": "Escuro", | ||||
|     "light": "Claro", | ||||
|     "followSystem": "Seguir o sistema", | ||||
|     "useBlackTheme": "Usar tema preto completamente escuro", | ||||
|     "followSystem": "Padrão do sistema", | ||||
|     "useBlackTheme": "Usar tema preto AMOLED", | ||||
|     "appSortBy": "Classificar aplicativo por", | ||||
|     "authorName": "Autor/Nome", | ||||
|     "nameAuthor": "Nome/Autor", | ||||
| @@ -122,10 +122,10 @@ | ||||
|     "bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo-plano", | ||||
|     "neverManualOnly": "Nunca - apenas manual", | ||||
|     "appearance": "Aparência", | ||||
|     "showWebInAppView": "Mostrar página da internet em informações do aplicativo", | ||||
|     "showWebInAppView": "Mostrar página web do aplicativo em informações do aplicativo", | ||||
|     "pinUpdates": "Fixar atualizações no topo da janela de aplicativos", | ||||
|     "updates": "Atualizações", | ||||
|     "sourceSpecific": "Específico a fonte", | ||||
|     "sourceSpecific": "Token de acesso", | ||||
|     "appSource": "Fonte do aplicativo", | ||||
|     "noLogs": "Sem logs", | ||||
|     "appLogs": "Logs do aplicativo", | ||||
| @@ -151,7 +151,7 @@ | ||||
|     "appsRemoved": "Aplicativos removidos", | ||||
|     "appsRemovedNotifDescription": "Notifica o usuário quando um ou mais aplicativos foram removidos devido a erros de carregamento", | ||||
|     "xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}", | ||||
|     "completeAppInstallation": "Instalação do aplicativo completa", | ||||
|     "completeAppInstallation": "Instalação do aplicativo concluída", | ||||
|     "obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar os aplicativos", | ||||
|     "completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um aplicativo", | ||||
|     "checkingForUpdates": "Verificando atualizações", | ||||
| @@ -167,15 +167,15 @@ | ||||
|     "installedVersionX": "Versão instalada: {}", | ||||
|     "lastUpdateCheckX": "Última verificação de atualizações: {}", | ||||
|     "remove": "Remover", | ||||
|     "yesMarkUpdated": "Sim, marcar como Atualizado", | ||||
|     "yesMarkUpdated": "Sim, marcar como atualizado", | ||||
|     "fdroid": "Oficial F-Droid", | ||||
|     "appIdOrName": "ID do aplicativo ou nome", | ||||
|     "appId": "ID do aplicativo", | ||||
|     "appWithIdOrNameNotFound": "Nenhum aplicativo foi encontrado com esse ID ou nome", | ||||
|     "reposHaveMultipleApps": "Repositórios podem conter multiplos aplicativos", | ||||
|     "reposHaveMultipleApps": "Repositórios podem conter múltiplos aplicativos", | ||||
|     "fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid", | ||||
|     "steamMobile": "Vapor Móvel", | ||||
|     "steamChat": "Bate-papo Steam", | ||||
|     "steamMobile": "Steam para celular", | ||||
|     "steamChat": "Chat do Steam", | ||||
|     "install": "Instalar", | ||||
|     "markInstalled": "Marcar instalado", | ||||
|     "update": "Atualizar", | ||||
| @@ -191,7 +191,7 @@ | ||||
|     "categories": "Categorias", | ||||
|     "category": "Categoria", | ||||
|     "noCategory": "Sem categoria", | ||||
|     "noCategories": "Sem categoria", | ||||
|     "noCategories": "Sem categorias", | ||||
|     "deleteCategoriesQuestion": "Deletar  categorias?", | ||||
|     "categoryDeleteWarning": "Todos os aplicativos em categorias removidas serão descategorizados.", | ||||
|     "addCategory": "Adicionar categoria", | ||||
| @@ -200,15 +200,15 @@ | ||||
|     "copiedToClipboard": "Copiado para a área de transferência", | ||||
|     "storagePermissionDenied": "Permissão de armazenamento negada", | ||||
|     "selectedCategorizeWarning": "Isso vai substituir qualquer configuração de categoria para os aplicativos selecionados.", | ||||
|     "filterAPKsByRegEx": "Filtrar APKs por expressão regular", | ||||
|     "filterAPKsByRegEx": "Filtrar APKs usando expressão regular", | ||||
|     "removeFromObtainium": "Remover do Obtainium", | ||||
|     "uninstallFromDevice": "Desinstalar do dispositivo", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicativos com detecção de versão desativada.", | ||||
|     "releaseDateAsVersion": "Usar data de lançamento como versão", | ||||
|     "releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.", | ||||
|     "changes": "Mudanças", | ||||
|     "changes": "Alterações", | ||||
|     "releaseDate": "Data de lançamento", | ||||
|     "importFromURLsInFile": "Importar de URLs em arquivo (como OPML)", | ||||
|     "importFromURLsInFile": "Importar de URLs em arquivo (formato OPML)", | ||||
|     "versionDetectionExplanation": "Reconciliar string de versão com versão detectada no sistema operacional", | ||||
|     "versionDetection": "Detecção de versão", | ||||
|     "standardVersionDetection": "Detecção de versão padrão", | ||||
| @@ -216,10 +216,10 @@ | ||||
|     "autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível", | ||||
|     "overrideSource": "Substituir fonte", | ||||
|     "dontShowAgain": "Não mostrar isso novamente", | ||||
|     "dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas Monitorar'", | ||||
|     "dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas monitorar'", | ||||
|     "dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK", | ||||
|     "moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos", | ||||
|     "gitlabPATLabel": "Token de Acesso Pessoal do Gitlab\n(Ativa pesquisa e melhora a descoberta de APKs)", | ||||
|     "gitlabPATLabel": "Token de acesso pessoal do Gitlab\n(Ativa pesquisa e melhora a descoberta de APKs)", | ||||
|     "about": "Sobre", | ||||
|     "requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)", | ||||
|     "checkOnStart": "Verificar se há atualizações ao iniciar", | ||||
| @@ -228,7 +228,7 @@ | ||||
|     "pickHighestVersionCode": "Auto-selecionar o maior número de versão do APK", | ||||
|     "checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de um aplicativo", | ||||
|     "disablePageTransitions": "Desativar animações de transição de página", | ||||
|     "reversePageTransitions": "Reverter animações de transição de página", | ||||
|     "reversePageTransitions": "Animações de transição de página invertidas", | ||||
|     "minStarCount": "Contagem mínima de estrelas", | ||||
|     "addInfoBelow": "Adicionar essa informação abaixo.", | ||||
|     "addInfoInSettings": "Adicionar essa informação nas configurações.", | ||||
| @@ -254,7 +254,7 @@ | ||||
|     "versionExtractionRegEx": "Regex de extração de versão", | ||||
|     "matchGroupToUse": "Grupo correspondente a ser usado no Regex de extração de versão", | ||||
|     "highlightTouchTargets": "Realçar áreas sensíveis ao toque que são menos óbvias", | ||||
|     "pickExportDir": "Escolher diretório para a exportação", | ||||
|     "pickExportDir": "Escolher diretório para exportação", | ||||
|     "autoExportOnChanges": "Auto-exportar em mudanças", | ||||
|     "includeSettings": "Incluir configurações", | ||||
|     "filterVersionsByRegEx": "Filtrar versões por expressão regular", | ||||
| @@ -272,18 +272,18 @@ | ||||
|     "updatesAvailableNotifChannel": "Atualizações disponíveis", | ||||
|     "appsUpdatedNotifChannel": "Aplicativos atualizados", | ||||
|     "appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de aplicativos", | ||||
|     "errorCheckingUpdatesNotifChannel": "Erro ao Procurar por Atualizações", | ||||
|     "errorCheckingUpdatesNotifChannel": "Erro ao procurar por atualizações", | ||||
|     "appsRemovedNotifChannel": "Aplicativos removidos", | ||||
|     "downloadingXNotifChannel": "Baixando {}", | ||||
|     "completeAppInstallationNotifChannel": "Instalação completa do aplicativo", | ||||
|     "checkingForUpdatesNotifChannel": "Checando por Atualizações", | ||||
|     "onlyCheckInstalledOrTrackOnlyApps": "Apenas checar aplicativos instalados e 'Apenas Seguir' por updates", | ||||
|     "checkingForUpdatesNotifChannel": "Checando por atualizações", | ||||
|     "onlyCheckInstalledOrTrackOnlyApps": "Apenas verificar atualizações de aplicativos instalados e 'Apenas monitorar'", | ||||
|     "supportFixedAPKURL": "Suporte a APK com URLs fixas", | ||||
|     "selectX": "Selecionar {}", | ||||
|     "parallelDownloads": "Permitir downloads paralelos", | ||||
|     "installMethod": "Método de instalação", | ||||
|     "normal": "Normal", | ||||
|     "root": "Raiz", | ||||
|     "root": "Root", | ||||
|     "shizukuBinderNotFound": "O Shizuku não está rodando", | ||||
|     "useSystemFont": "Usar fonte padrão do sistema", | ||||
|     "systemFontError": "Erro ao carregar a fonte do sistema: {}", | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Instalado", | ||||
|     "latest": "Mais recente", | ||||
|     "invertRegEx": "Inverter expressão regular", | ||||
|     "note": "Nota", | ||||
|     "selfHostedNote": "O menu suspenso \"{}\" pode ser usado para acessar instâncias auto-hospedadas/personalizadas de qualquer fonte.", | ||||
|     "badDownload": "Não foi possível analisar o APK (transferência incompatível ou parcial)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Remover aplicativo?", | ||||
|         "other": "Remover aplicativos?" | ||||
| @@ -339,15 +342,15 @@ | ||||
|         "other": "Foram limpos {n} logs (antes = {antes}, depois = {depois})" | ||||
|     }, | ||||
|     "xAndNMoreUpdatesAvailable": { | ||||
|         "one": "{} e 1 outro aplicativo possui atualizações.", | ||||
|         "one": "{} e um outro aplicativo possui atualizações.", | ||||
|         "other": "{} e {} outros aplicativo possuem atualizações." | ||||
|     }, | ||||
|     "xAndNMoreUpdatesInstalled": { | ||||
|         "one": "{} e um outro aplicativo foi atualizado.", | ||||
|         "one": "{} e um outro aplicativo foram atualizado.", | ||||
|         "other": "{} e {} outros aplicativos foram atualizados." | ||||
|     }, | ||||
|     "xAndNMoreUpdatesPossiblyInstalled": { | ||||
|         "one": "{} e 1 outro aplicativo pode ter sido atualizado.", | ||||
|         "one": "{} e um outro aplicativo podem ter sido atualizados.", | ||||
|         "other": "{} e {} outros aplicativos podem ter sido atualizados." | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Установлен", | ||||
|     "latest": "Последний", | ||||
|     "invertRegEx": "Инвертировать регулярное выражение", | ||||
|     "note": "Примечание", | ||||
|     "selfHostedNote": "Выпадающий список \"{}\" можно использовать для доступа к самостоятельно размещенным/настроенным экземплярам любого источника.", | ||||
|     "badDownload": "APK не удалось разобрать (несовместимая или неполная загрузка)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Удалить приложение?", | ||||
|         "other": "Удалить приложения?" | ||||
|   | ||||
| @@ -1,30 +1,119 @@ | ||||
| // Take one (hardcoded) translation file and ensure that all other translation files have the same keys in the same order | ||||
| // Then report which other translation files have identical items | ||||
| // Then report which other translation files have identical items (or auto-translate them if a DeepL API key is provided) | ||||
|  | ||||
| const fs = require('fs') | ||||
| const https = require('https') | ||||
|  | ||||
| const translationsDir = __dirname | ||||
| const templateFile = `${translationsDir}/en.json` | ||||
| const otherFiles = fs.readdirSync(translationsDir).map(f => { | ||||
|     return `${translationsDir}/${f}` | ||||
| }).filter(f => f.endsWith('.json') && f != templateFile) | ||||
| const deeplAPIKey = process.argv[2] | ||||
|  | ||||
| const templateTranslation = require(templateFile) | ||||
| const neverAutoTranslate = { | ||||
|     steamMobile: ['*'], | ||||
|     steamChat: ['*'], | ||||
|     root: ['*'], | ||||
|     obtainiumExportHyphenatedLowercase: ['*'], | ||||
|     theme: ['de'], | ||||
|     appId: ['de'] | ||||
| } | ||||
|  | ||||
| otherFiles.forEach(file => { | ||||
|     const thisTranslationOriginal = require(file) | ||||
|     const thisTranslationNew = {} | ||||
|     Object.keys(templateTranslation).forEach(k => { | ||||
|         thisTranslationNew[k] = thisTranslationOriginal[k] || templateTranslation[k] | ||||
|     }) | ||||
|     fs.writeFileSync(file, `${JSON.stringify(thisTranslationNew, null, '    ')}\n`) | ||||
| }); | ||||
|  | ||||
| otherFiles.forEach(file => { | ||||
|     const thisTranslation = require(file) | ||||
|     Object.keys(templateTranslation).forEach(k => { | ||||
|         if (JSON.stringify(thisTranslation[k]) == JSON.stringify(templateTranslation[k])) { | ||||
|             console.log(`${file} :::: ${k} :::: ${JSON.stringify(thisTranslation[k])}`) | ||||
| const translateText = async (text, targetLang, authKey) => { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         const postData = `text=${encodeURIComponent(text)}&target_lang=${encodeURIComponent(targetLang)}&source_lang=EN` | ||||
|         const options = { | ||||
|             hostname: 'api-free.deepl.com', | ||||
|             port: 443, | ||||
|             path: '/v2/translate', | ||||
|             method: 'POST', | ||||
|             headers: { | ||||
|                 'Authorization': `DeepL-Auth-Key ${authKey}`, | ||||
|                 'Content-Type': 'application/x-www-form-urlencoded', | ||||
|                 'Content-Length': Buffer.byteLength(postData) | ||||
|             } | ||||
|         } | ||||
|         const req = https.request(options, (res) => { | ||||
|             let responseData = '' | ||||
|             res.on('data', (chunk) => { | ||||
|                 responseData += chunk | ||||
|             }) | ||||
|             res.on('end', () => { | ||||
|                 try { | ||||
|                     const jsonResponse = JSON.parse(responseData) | ||||
|                     resolve(jsonResponse) | ||||
|                 } catch (error) { | ||||
|                     reject(error) | ||||
|                 } | ||||
|             }) | ||||
|         }) | ||||
|         req.on('error', (error) => { | ||||
|             reject(error) | ||||
|         }) | ||||
|         req.write(postData) | ||||
|         req.end() | ||||
|     }) | ||||
| }); | ||||
| } | ||||
|  | ||||
| const main = async () => { | ||||
|     const translationsDir = __dirname | ||||
|     const templateFile = `${translationsDir}/en.json` | ||||
|     const otherFiles = fs.readdirSync(translationsDir).map(f => { | ||||
|         return `${translationsDir}/${f}` | ||||
|     }).filter(f => f.endsWith('.json') && f != templateFile) | ||||
|  | ||||
|     const templateTranslation = require(templateFile) | ||||
|  | ||||
|  | ||||
|     otherFiles.forEach(file => { | ||||
|         const thisTranslationOriginal = require(file) | ||||
|         const thisTranslationNew = {} | ||||
|         Object.keys(templateTranslation).forEach(k => { | ||||
|             thisTranslationNew[k] = thisTranslationOriginal[k] || templateTranslation[k] | ||||
|         }) | ||||
|         fs.writeFileSync(file, `${JSON.stringify(thisTranslationNew, null, '    ')}\n`) | ||||
|     }) | ||||
|  | ||||
|     for (let i in otherFiles) { | ||||
|         const file = otherFiles[i] | ||||
|         const thisTranslation = require(file) | ||||
|         const translationKeys = Object.keys(templateTranslation) | ||||
|         for (let j in translationKeys) { | ||||
|             const k = translationKeys[j] | ||||
|             if (JSON.stringify(thisTranslation[k]) == JSON.stringify(templateTranslation[k])) { | ||||
|                 const lang = file.split('/').pop().split('.')[0] | ||||
|                 if (!neverAutoTranslate[k] || (neverAutoTranslate[k].indexOf('*') < 0 && neverAutoTranslate[k].indexOf(lang) < 0)) { | ||||
|                     const reportLine = `${file} :::: ${k} :::: ${JSON.stringify(thisTranslation[k])}` | ||||
|                     if (deeplAPIKey) { | ||||
|                         const translateFunc = async (str) => { | ||||
|                             const response = await translateText(str, lang, deeplAPIKey) | ||||
|                             if (response.translations) { | ||||
|                                 return response.translations[0].text | ||||
|                             } else { | ||||
|                                 throw JSON.stringify(response) | ||||
|                             } | ||||
|                         } | ||||
|                         try { | ||||
|                             if (typeof templateTranslation[k] == 'string') { | ||||
|                                 thisTranslation[k] = await translateFunc(thisTranslation[k]) | ||||
|                             } else { | ||||
|                                 const subKeys = Object.keys(templateTranslation[k]) | ||||
|                                 for (let n in subKeys) { | ||||
|                                     const kk = subKeys[n] | ||||
|                                     thisTranslation[k][kk] = await translateFunc(thisTranslation[k][kk]) | ||||
|                                 } | ||||
|                             } | ||||
|                         } catch (e) { | ||||
|                             if (typeof e == 'string') { | ||||
|                                 console.log(`${reportLine} :::: ${e}`) | ||||
|                             } else { | ||||
|                                 throw e | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                         console.log(reportLine) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         fs.writeFileSync(file, `${JSON.stringify(thisTranslation, null, '    ')}\n`) | ||||
|     } | ||||
| } | ||||
|  | ||||
| main().catch(e => console.error) | ||||
|   | ||||
| @@ -288,16 +288,19 @@ | ||||
|     "useSystemFont": "Använd systemteckensnittet", | ||||
|     "systemFontError": "Fel vid laddning av systemteckensnittet: {}", | ||||
|     "useVersionCodeAsOSVersion": "Använd appversionskoden som OS-upptäckt version", | ||||
|     "requestHeader": "Request header", | ||||
|     "requestHeader": "Rubrik för begäran", | ||||
|     "useLatestAssetDateAsReleaseDate": "Använd senaste tillgångsuppladdning som releasedatum", | ||||
|     "defaultPseudoVersioningMethod": "Standard pseudoversionsmetod", | ||||
|     "partialAPKHash": "Delvis APK-hash", | ||||
|     "APKLinkHash": "APK Link Hash", | ||||
|     "APKLinkHash": "APK-länk Hash", | ||||
|     "directAPKLink": "Direkt APK-länk", | ||||
|     "pseudoVersionInUse": "En pseudoversion används", | ||||
|     "installed": "Installerad", | ||||
|     "latest": "Senast", | ||||
|     "invertRegEx": "Invertera reguljärt uttryck", | ||||
|     "note": "Anmärkning", | ||||
|     "selfHostedNote": "Rullgardinsmenyn \"{}\" kan användas för att nå självhostade/anpassade instanser av valfri källa.", | ||||
|     "badDownload": "APK kunde inte analyseras (inkompatibel eller partiell nedladdning)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Ta Bort App?", | ||||
|         "other": "Ta Bort Appar?" | ||||
|   | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "Kurulmuş", | ||||
|     "latest": "En sonuncu", | ||||
|     "invertRegEx": "Normal ifadeyi ters çevir", | ||||
|     "note": "Not", | ||||
|     "selfHostedNote": "\"{}\" açılır menüsü, herhangi bir kaynağın kendi kendine barındırılan/özel örneklerine ulaşmak için kullanılabilir.", | ||||
|     "badDownload": "APK ayrıştırılamadı (uyumsuz veya kısmi indirme)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Uygulamayı Kaldır?", | ||||
|         "other": "Uygulamaları Kaldır?" | ||||
|   | ||||
| @@ -25,11 +25,11 @@ | ||||
|     "githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub", | ||||
|     "uname": "Tên người dùng", | ||||
|     "wrongArgNum": "Số lượng đối số được cung cấp sai", | ||||
|     "xIsTrackOnly": "{}là Chỉ-Theo dõi", | ||||
|     "xIsTrackOnly": "{} là Chỉ theo dõi", | ||||
|     "source": "Nguồn", | ||||
|     "app": "Ứng dụng", | ||||
|     "appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ-Theo dõi'.", | ||||
|     "youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ-Theo dõi'.", | ||||
|     "appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ theo dõi'.", | ||||
|     "youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ theo dõi'.", | ||||
|     "trackOnlyAppDescription": "Ứng dụng sẽ được theo dõi để cập nhật, nhưng Obtainium sẽ không thể tải xuống hoặc cài đặt nó.", | ||||
|     "cancelled": "Đã hủy", | ||||
|     "appAlreadyAdded": "Ứng dụng được thêm rồi", | ||||
| @@ -42,7 +42,7 @@ | ||||
|     "search": "Tìm kiếm", | ||||
|     "additionalOptsFor": "Tùy chọn bổ sung cho {}", | ||||
|     "supportedSources": "Nguồn được hỗ trợ", | ||||
|     "trackOnlyInBrackets": "(Chỉ-Theo dõi)", | ||||
|     "trackOnlyInBrackets": "(Chỉ theo dõi)", | ||||
|     "searchableInBrackets": "(Có thể tìm kiếm)", | ||||
|     "appsString": "Ứng dụng", | ||||
|     "noApps": "Không có ứng dụng", | ||||
| @@ -60,7 +60,7 @@ | ||||
|     "removeSelectedApps": "Xóa ứng dụng đã chọn", | ||||
|     "updateX": "Cập nhật {}", | ||||
|     "installX": "Cài đặt {}", | ||||
|     "markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ-Theo dõi)\nnhư là đã cập nhật", | ||||
|     "markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ theo dõi)\nnhư là đã cập nhật", | ||||
|     "changeX": "Thay đổi {}", | ||||
|     "installUpdateApps": "Cài đặt/Cập nhật ứng dụng", | ||||
|     "installUpdateSelectedApps": "Cài đặt/Cập nhật ứng dụng đã chọn", | ||||
| @@ -157,7 +157,7 @@ | ||||
|     "checkingForUpdates": "Đang kiểm tra cập nhật", | ||||
|     "checkingForUpdatesNotifDescription": "Thông báo tạm thời xuất hiện khi kiểm tra bản cập nhật", | ||||
|     "pleaseAllowInstallPerm": "Vui lòng cho phép Obtainium cài đặt Ứng dụng", | ||||
|     "trackOnly": "Chỉ-Theo dõi", | ||||
|     "trackOnly": "Chỉ theo dõi", | ||||
|     "errorWithHttpStatusCode": "Lỗi {}", | ||||
|     "versionCorrectionDisabled": "Tính năng sửa phiên bản bị vô hiệu hóa (plugin dường như không hoạt động)", | ||||
|     "unknown": "Không xác định", | ||||
| @@ -216,7 +216,7 @@ | ||||
|     "autoApkFilterByArch": "Cố gắng lọc APK theo kiến trúc CPU nếu có thể", | ||||
|     "overrideSource": "Ghi đè nguồn", | ||||
|     "dontShowAgain": "Đừng hiển thị thông tin này nữa", | ||||
|     "dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ-Theo dõi'", | ||||
|     "dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ theo dõi'", | ||||
|     "dontShowAPKOriginWarnings": "Không hiển thị cảnh báo nguồn gốc APK", | ||||
|     "moveNonInstalledAppsToBottom": "Chuyển Ứng dụng chưa được cài đặt xuống cuối danh sách", | ||||
|     "gitlabPATLabel": "GitLab Token\n(Cho phép tìm kiếm và lọc APK tốt hơn)", | ||||
| @@ -277,13 +277,13 @@ | ||||
|     "downloadingXNotifChannel": "Đang tải xuống {}", | ||||
|     "completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng", | ||||
|     "checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật", | ||||
|     "onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra cập nhật các ứng dụng đã cài đặt và Chỉ-Theo dõi", | ||||
|     "onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra cập nhật các ứng dụng đã cài đặt và Chỉ theo dõi", | ||||
|     "supportFixedAPKURL": "Hỗ trợ URL APK cố định", | ||||
|     "selectX": "Lựa chọn {}", | ||||
|     "parallelDownloads": "Cho phép tải đa luồng", | ||||
|     "installMethod": "Phương thức cài đặt", | ||||
|     "normal": "Mặc định", | ||||
|     "root": "Nguồn gốc", | ||||
|     "root": "Root", | ||||
|     "shizukuBinderNotFound": "Shizuku chưa khởi động", | ||||
|     "useSystemFont": "Sử dụng phông chữ hệ thống", | ||||
|     "systemFontError": "Lỗi tải phông chữ hệ thống: {}", | ||||
| @@ -295,9 +295,12 @@ | ||||
|     "APKLinkHash": "Băm liên kết APK", | ||||
|     "directAPKLink": "Liên kết APK trực tiếp", | ||||
|     "pseudoVersionInUse": "Phiên bản giả đang được sử dụng", | ||||
|     "installed": "Cài đặt", | ||||
|     "latest": "Muộn nhất", | ||||
|     "installed": "Đã cài đặt", | ||||
|     "latest": "Mới nhất", | ||||
|     "invertRegEx": "Đảo ngược biểu thức chính quy", | ||||
|     "note": "Note", | ||||
|     "selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.", | ||||
|     "badDownload": "The APK could not be parsed (incompatible or partial download)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Gỡ ứng dụng?", | ||||
|         "other": "Gỡ ứng dụng?" | ||||
|   | ||||
| @@ -283,7 +283,7 @@ | ||||
|     "parallelDownloads": "启用并行下载", | ||||
|     "installMethod": "安装方式", | ||||
|     "normal": "常规", | ||||
|     "root": "根", | ||||
|     "root": "root", | ||||
|     "shizukuBinderNotFound": "未发现兼容的 Shizuku 服务", | ||||
|     "useSystemFont": "使用系统字体", | ||||
|     "systemFontError": "加载系统字体出错:{}", | ||||
| @@ -298,6 +298,9 @@ | ||||
|     "installed": "已安装", | ||||
|     "latest": "最新的", | ||||
|     "invertRegEx": "反转正则表达式", | ||||
|     "note": "备注", | ||||
|     "selfHostedNote": "{}\"下拉菜单可用于访问任何来源的自托管/自定义实例。", | ||||
|     "badDownload": "无法解析 APK(不兼容或部分下载)", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "是否删除应用?", | ||||
|         "other": "是否删除应用?" | ||||
|   | ||||
							
								
								
									
										4
									
								
								build.sh
									
									
									
									
									
								
							
							
						
						| @@ -7,6 +7,10 @@ trap "cd "$CURR_DIR"" EXIT | ||||
| if [ -z "$1" ]; then | ||||
|     git fetch && git merge origin/main && git push # Typically run after a PR to main, so bring dev up to date | ||||
| fi | ||||
| cd .flutter | ||||
| git fetch | ||||
| git checkout "$(flutter --version | head -2 | tail -1 | awk '{print $4}')" # Ensure included Flutter submodule version equals my environment | ||||
| cd .. | ||||
| rm ./build/app/outputs/flutter-apk/* 2>/dev/null                                       # Get rid of older builds if any | ||||
| flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal # Build (both split and combined APKs) | ||||
| for file in ./build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done | ||||
|   | ||||
							
								
								
									
										54
									
								
								fastlane/metadata/android/en-US/full_description.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,54 @@ | ||||
| <p>Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.</p> | ||||
| <p>Read the <a href="https://github.com/ImranR98/Obtainium/wiki">Wiki</a></p> | ||||
| <p> | ||||
| 	<b>Currently supported App sources:</b> | ||||
| </p> | ||||
| <ul> | ||||
| 	<li> | ||||
| 		<p>Open Source - General:</p> | ||||
| 		<ul> | ||||
| 			<li>GitHub</li> | ||||
| 			<li>GitLab</li> | ||||
| 			<li>Codeberg</li> | ||||
| 			<li>F-Droid</li> | ||||
| 			<li>Third Party F-Droid Repos</li> | ||||
| 			<li>IzzyOnDroid</li> | ||||
| 			<li>SourceForge</li> | ||||
| 			<li>SourceHut</li> | ||||
| 		</ul> | ||||
| 	</li> | ||||
| 	<li> | ||||
| 		<p>Other - General:</p> | ||||
| 		<ul> | ||||
| 			<li>APKPure</li> | ||||
| 			<li>Aptoide</li> | ||||
| 			<li>Uptodowng</li> | ||||
| 			<li>APKMirror (Track-Only)</li> | ||||
| 			<li>Huawei AppGallery</li> | ||||
| 			<li>Jenkins Jobs</li> | ||||
| 		</ul> | ||||
| 	</li> | ||||
| 	<li> | ||||
| 		<p>Open Source - App-Specific:</p> | ||||
| 		<ul> | ||||
| 			<li>Mullvad</li> | ||||
| 			<li>Signal</li> | ||||
| 			<li>VLC</li> | ||||
| 		</ul> | ||||
| 	</li> | ||||
| 	<li> | ||||
| 		<p>Other - App-Specific:</p> | ||||
| 		<ul> | ||||
| 			<li>WhatsApp</li> | ||||
| 			<li>Telegram App</li> | ||||
| 			<li>Neutron Code</li> | ||||
| 		</ul> | ||||
| 	</li> | ||||
| 	<li><p>"HTML" (Fallback): Any other URL that returns an HTML page with links to APK files</p></li> | ||||
| </ul> | ||||
| <p> | ||||
| 	<b>Limitations:</b> | ||||
| </p> | ||||
| <p> | ||||
| 	For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable. | ||||
| </p> | ||||
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/featureGraphic.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						| After Width: | Height: | Size: 66 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 44 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 234 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 238 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 140 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 139 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 118 KiB | 
							
								
								
									
										
											BIN
										
									
								
								fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 262 KiB | 
							
								
								
									
										1
									
								
								fastlane/metadata/android/en-US/short_description.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| Get android app updates directly from the source | ||||
							
								
								
									
										1
									
								
								fastlane/metadata/android/en-US/title.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| Obtainium | ||||
| @@ -3,6 +3,8 @@ import 'dart:convert'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/app_sources/github.dart'; | ||||
| import 'package:obtainium/app_sources/gitlab.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| @@ -95,17 +97,32 @@ class FDroid extends AppSource { | ||||
|           details.names.author = | ||||
|               authorLines.first.split(': ').sublist(1).join(': '); | ||||
|         } | ||||
|         var changelogUrls = lines.where((l) => l.startsWith('Changelog: ')); | ||||
|         var changelogUrls = lines | ||||
|             .where((l) => l.startsWith('Changelog: ')) | ||||
|             .map((e) => e.split(' ').sublist(1).join(' ')); | ||||
|         if (changelogUrls.isNotEmpty) { | ||||
|           details.changeLog = changelogUrls.first; | ||||
|           details.changeLog = (await sourceRequest( | ||||
|                   details.changeLog! | ||||
|                       .split(': ') | ||||
|                       .sublist(1) | ||||
|                       .join(': ') | ||||
|                       .replaceFirst('/blob/', '/raw/'), | ||||
|                   additionalSettings)) | ||||
|               .body; | ||||
|           bool isGitHub = false; | ||||
|           bool isGitLab = false; | ||||
|           try { | ||||
|             GitHub().sourceSpecificStandardizeURL(details.changeLog!); | ||||
|             isGitHub = true; | ||||
|           } catch (e) { | ||||
|             // | ||||
|           } | ||||
|           try { | ||||
|             GitLab().sourceSpecificStandardizeURL(details.changeLog!); | ||||
|             isGitLab = true; | ||||
|           } catch (e) { | ||||
|             // | ||||
|           } | ||||
|           if ((isGitHub || isGitLab) && | ||||
|               (details.changeLog?.indexOf('/blob/') ?? -1) >= 0) { | ||||
|             details.changeLog = (await sourceRequest( | ||||
|                     details.changeLog!.replaceFirst('/blob/', '/raw/'), | ||||
|                     additionalSettings)) | ||||
|                 .body; | ||||
|           } | ||||
|         } | ||||
|       } catch (e) { | ||||
|         // Fail silently | ||||
|   | ||||
| @@ -319,7 +319,7 @@ class HTML extends AppSource { | ||||
|     version ??= | ||||
|         additionalSettings['defaultPseudoVersioningMethod'] == 'APKLinkHash' | ||||
|             ? rel.hashCode.toString() | ||||
|             : (await checkPartialDownloadHashDynamc(rel)).toString(); | ||||
|             : (await checkPartialDownloadHashDynamic(rel)).toString(); | ||||
|     return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(), | ||||
|         AppNames(uri.host, tr('app'))); | ||||
|   } | ||||
|   | ||||
| @@ -10,16 +10,23 @@ class SourceForge extends AppSource { | ||||
|  | ||||
|   @override | ||||
|   String sourceSpecificStandardizeURL(String url) { | ||||
|     RegExp standardUrlRegExB = RegExp( | ||||
|         '^https?://(www\\.)?${getSourceRegex(hosts)}/p/[^/]+', | ||||
|         caseSensitive: false); | ||||
|     RegExpMatch? match = standardUrlRegExB.firstMatch(url); | ||||
|     var sourceRegex = getSourceRegex(hosts); | ||||
|     RegExp standardUrlRegExC = | ||||
|         RegExp('^https?://(www\\.)?$sourceRegex/p/.+', caseSensitive: false); | ||||
|     RegExpMatch? match = standardUrlRegExC.firstMatch(url); | ||||
|     if (match != null) { | ||||
|       url = | ||||
|           'https://${Uri.parse(match.group(0)!).host}/projects/${url.substring(Uri.parse(match.group(0)!).host.length + '/projects/'.length + 1)}'; | ||||
|     } | ||||
|     RegExp standardUrlRegExB = RegExp( | ||||
|         '^https?://(www\\.)?$sourceRegex/projects/[^/]+', | ||||
|         caseSensitive: false); | ||||
|     match = standardUrlRegExB.firstMatch(url); | ||||
|     if (match != null && match.group(0) == url) { | ||||
|       url = '$url/files'; | ||||
|     } | ||||
|     RegExp standardUrlRegExA = RegExp( | ||||
|         '^https?://(www\\.)?${getSourceRegex(hosts)}/projects/[^/]+', | ||||
|         '^https?://(www\\.)?$sourceRegex/projects/[^/]+/files(/.+)?', | ||||
|         caseSensitive: false); | ||||
|     match = standardUrlRegExA.firstMatch(url); | ||||
|     if (match == null) { | ||||
| @@ -33,38 +40,79 @@ class SourceForge extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     Response res = | ||||
|         await sourceRequest('$standardUrl/rss?path=/', additionalSettings); | ||||
|     var standardUri = Uri.parse(standardUrl); | ||||
|     if (standardUri.pathSegments.length == 2) { | ||||
|       standardUrl = '$standardUrl/files'; | ||||
|       standardUri = Uri.parse(standardUrl); | ||||
|     } | ||||
|     Response res = await sourceRequest( | ||||
|         '${standardUri.origin}/${standardUri.pathSegments.sublist(0, 2).join('/')}/rss?path=/', | ||||
|         additionalSettings); | ||||
|     if (res.statusCode == 200) { | ||||
|       var parsedHtml = parse(res.body); | ||||
|       var allDownloadLinks = | ||||
|           parsedHtml.querySelectorAll('guid').map((e) => e.innerHtml).toList(); | ||||
|       var allDownloadLinks = parsedHtml | ||||
|           .querySelectorAll('guid') | ||||
|           .map((e) => e.innerHtml) | ||||
|           .where((element) => element.startsWith(standardUrl)) | ||||
|           .toList(); | ||||
|       getVersion(String url) { | ||||
|         try { | ||||
|           var tokens = url.split('/'); | ||||
|           var fi = tokens.indexOf('files'); | ||||
|           return tokens[tokens[fi + 2] == 'download' ? fi - 1 : fi + 1]; | ||||
|           var segments = url | ||||
|               .substring(standardUrl.length) | ||||
|               .split('/') | ||||
|               .where((element) => element.isNotEmpty) | ||||
|               .toList() | ||||
|               .reversed | ||||
|               .toList() | ||||
|               .sublist(1) | ||||
|               .reversed | ||||
|               .toList(); | ||||
|           segments = segments.length > 1 | ||||
|               ? segments.reversed.toList().sublist(1).reversed.toList() | ||||
|               : segments; | ||||
|           var version = segments.isNotEmpty ? segments.join('/') : null; | ||||
|           if (version != null) { | ||||
|             try { | ||||
|               var extractedVersion = extractVersion( | ||||
|                   additionalSettings['versionExtractionRegEx'] as String?, | ||||
|                   additionalSettings['matchGroupToUse'] as String?, | ||||
|                   version); | ||||
|               if (extractedVersion != null) { | ||||
|                 version = extractedVersion; | ||||
|               } | ||||
|             } catch (e) { | ||||
|               if (e is NoVersionError) { | ||||
|                 version = null; | ||||
|               } else { | ||||
|                 rethrow; | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|           return version; | ||||
|         } catch (e) { | ||||
|           return null; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       String? version = getVersion(allDownloadLinks[0]); | ||||
|       var apkUrlListAllReleases = allDownloadLinks | ||||
|           .where((element) => element.toLowerCase().endsWith('.apk/download')) | ||||
|           .where((element) => getVersion(element) != null) | ||||
|           .toList(); | ||||
|       if (apkUrlListAllReleases.isEmpty) { | ||||
|         throw NoReleasesError(); | ||||
|       } | ||||
|       String? version = getVersion(apkUrlListAllReleases[0]); | ||||
|       if (version == null) { | ||||
|         throw NoVersionError(); | ||||
|       } | ||||
|       var apkUrlListAllReleases = allDownloadLinks | ||||
|           .where((element) => element.toLowerCase().endsWith('.apk/download')) | ||||
|           .toList(); | ||||
|  | ||||
|       var apkUrlList = | ||||
|           apkUrlListAllReleases // This can be used skipped for fallback support later | ||||
|               .where((element) => getVersion(element) == version) | ||||
|               .toList(); | ||||
|       return APKDetails( | ||||
|           version, | ||||
|           getApkUrlsFromUrls(apkUrlList), | ||||
|           AppNames( | ||||
|               name, standardUrl.substring(standardUrl.lastIndexOf('/') + 1))); | ||||
|       var segments = standardUrl.split('/'); | ||||
|       return APKDetails(version, getApkUrlsFromUrls(apkUrlList), | ||||
|           AppNames(name, segments[segments.indexOf('files') - 1])); | ||||
|     } else { | ||||
|       throw getObtainiumHttpError(res); | ||||
|     } | ||||
|   | ||||
| @@ -39,6 +39,15 @@ class SourceHut extends AppSource { | ||||
|     String standardUrl, | ||||
|     Map<String, dynamic> additionalSettings, | ||||
|   ) async { | ||||
|     if (standardUrl.endsWith('/refs')) { | ||||
|       standardUrl = standardUrl | ||||
|           .split('/') | ||||
|           .reversed | ||||
|           .toList() | ||||
|           .sublist(1) | ||||
|           .reversed | ||||
|           .join('/'); | ||||
|     } | ||||
|     Uri standardUri = Uri.parse(standardUrl); | ||||
|     String appName = standardUri.pathSegments.last; | ||||
|     bool fallbackToOlderReleases = | ||||
|   | ||||
| @@ -184,7 +184,6 @@ class _ObtainiumState extends State<Obtainium> { | ||||
|                     [], | ||||
|                     0, | ||||
|                     { | ||||
|                       'includePrereleases': true, | ||||
|                       'versionDetection': true, | ||||
|                       'apkFilterRegEx': 'fdroid', | ||||
|                       'invertAPKFilter': true | ||||
|   | ||||
| @@ -530,7 +530,20 @@ class AddAppPageState extends State<AddAppPage> { | ||||
|                                                 ? TextDecoration.underline | ||||
|                                                 : TextDecoration.none), | ||||
|                                       ))), | ||||
|                             ) | ||||
|                             ), | ||||
|                             const SizedBox( | ||||
|                               height: 16, | ||||
|                             ), | ||||
|                             Text( | ||||
|                               '${tr('note')}:', | ||||
|                               style: | ||||
|                                   const TextStyle(fontWeight: FontWeight.bold), | ||||
|                             ), | ||||
|                             const SizedBox( | ||||
|                               height: 4, | ||||
|                             ), | ||||
|                             Text(tr('selfHostedNote', | ||||
|                                 args: [tr('overrideSource')])), | ||||
|                           ], | ||||
|                         ); | ||||
|                       }, | ||||
|   | ||||
| @@ -205,6 +205,12 @@ class _AppPageState extends State<AppPage> { | ||||
|               textAlign: TextAlign.center, | ||||
|               style: Theme.of(context).textTheme.displayLarge, | ||||
|             ), | ||||
|             Text(tr('byX', args: [app?.app.author ?? tr('unknown')]), | ||||
|                 textAlign: TextAlign.center, | ||||
|                 style: Theme.of(context).textTheme.headlineMedium), | ||||
|             const SizedBox( | ||||
|               height: 24, | ||||
|             ), | ||||
|             GestureDetector( | ||||
|                 onTap: () { | ||||
|                   if (app?.app.url != null) { | ||||
| @@ -219,15 +225,12 @@ class _AppPageState extends State<AppPage> { | ||||
|                   )); | ||||
|                 }, | ||||
|                 child: Text( | ||||
|                   tr('byX', args: [app?.app.author ?? tr('unknown')]), | ||||
|                   app?.app.url ?? '', | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: Theme.of(context).textTheme.headlineMedium!.copyWith( | ||||
|                   style: Theme.of(context).textTheme.labelSmall!.copyWith( | ||||
|                       decoration: TextDecoration.underline, | ||||
|                       fontStyle: FontStyle.italic), | ||||
|                 )), | ||||
|             const SizedBox( | ||||
|               height: 8, | ||||
|             ), | ||||
|             Text( | ||||
|               app?.app.id ?? '', | ||||
|               textAlign: TextAlign.center, | ||||
| @@ -347,18 +350,6 @@ class _AppPageState extends State<AppPage> { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     getResetInstallStatusButton() => TextButton( | ||||
|         onPressed: app?.app == null || updating | ||||
|             ? null | ||||
|             : () { | ||||
|                 app!.app.installedVersion = null; | ||||
|                 appsProvider.saveApps([app.app]); | ||||
|               }, | ||||
|         child: Text( | ||||
|           tr('resetInstallStatus'), | ||||
|           textAlign: TextAlign.center, | ||||
|         )); | ||||
|  | ||||
|     getInstallOrUpdateButton() => TextButton( | ||||
|         onPressed: !updating && | ||||
|                 (app?.app.installedVersion == null || | ||||
| @@ -403,16 +394,6 @@ class _AppPageState extends State<AppPage> { | ||||
|                 child: Row( | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||
|                     children: [ | ||||
|                       if (app?.app.installedVersion != null && | ||||
|                           app?.app.installedVersion != app?.app.latestVersion && | ||||
|                           !isVersionDetectionStandard && | ||||
|                           !trackOnly) | ||||
|                         IconButton( | ||||
|                             onPressed: app?.downloadProgress != null || updating | ||||
|                                 ? null | ||||
|                                 : showMarkUpdatedDialog, | ||||
|                             tooltip: tr('markUpdated'), | ||||
|                             icon: const Icon(Icons.done)), | ||||
|                       if (source != null && | ||||
|                           source.combinedAppSpecificSettingFormItems.isNotEmpty) | ||||
|                         IconButton( | ||||
| @@ -458,14 +439,30 @@ class _AppPageState extends State<AppPage> { | ||||
|                             }, | ||||
|                             icon: const Icon(Icons.more_horiz), | ||||
|                             tooltip: tr('more')), | ||||
|                       if (app?.app.installedVersion != null && | ||||
|                           app?.app.installedVersion != app?.app.latestVersion && | ||||
|                           !isVersionDetectionStandard && | ||||
|                           !trackOnly) | ||||
|                         IconButton( | ||||
|                             onPressed: app?.downloadProgress != null || updating | ||||
|                                 ? null | ||||
|                                 : showMarkUpdatedDialog, | ||||
|                             tooltip: tr('markUpdated'), | ||||
|                             icon: const Icon(Icons.done)), | ||||
|                       if ((!isVersionDetectionStandard || trackOnly) && | ||||
|                           app?.app.installedVersion != null && | ||||
|                           app?.app.installedVersion == app?.app.latestVersion) | ||||
|                         IconButton( | ||||
|                             onPressed: app?.app == null || updating | ||||
|                                 ? null | ||||
|                                 : () { | ||||
|                                     app!.app.installedVersion = null; | ||||
|                                     appsProvider.saveApps([app.app]); | ||||
|                                   }, | ||||
|                             icon: const Icon(Icons.restore_rounded), | ||||
|                             tooltip: tr('resetInstallStatus')), | ||||
|                       const SizedBox(width: 16.0), | ||||
|                       Expanded( | ||||
|                           child: (!isVersionDetectionStandard || trackOnly) && | ||||
|                                   app?.app.installedVersion != null && | ||||
|                                   app?.app.installedVersion == | ||||
|                                       app?.app.latestVersion | ||||
|                               ? getResetInstallStatusButton() | ||||
|                               : getInstallOrUpdateButton()), | ||||
|                       Expanded(child: getInstallOrUpdateButton()), | ||||
|                       const SizedBox(width: 16.0), | ||||
|                       IconButton( | ||||
|                         onPressed: app?.downloadProgress != null || updating | ||||
|   | ||||
| @@ -358,6 +358,16 @@ class AppsPageState extends State<AppsPage> { | ||||
|       String? changesUrl = | ||||
|           appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url); | ||||
|       String? changeLog = listedApps[appIndex].app.changeLog; | ||||
|       if (changeLog?.split('\n').length == 1) { | ||||
|         if (RegExp( | ||||
|                 '(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?') | ||||
|             .hasMatch(changeLog!)) { | ||||
|           if (changesUrl == null) { | ||||
|             changesUrl = changeLog; | ||||
|             changeLog = null; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       return (changeLog == null && changesUrl == null) | ||||
|           ? null | ||||
|           : () { | ||||
| @@ -1028,7 +1038,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|           IconButton( | ||||
|               color: Theme.of(context).colorScheme.primary, | ||||
|               style: const ButtonStyle(visualDensity: VisualDensity.compact), | ||||
|               tooltip: isFilterOff ? tr('filter') : tr('filterActive'), | ||||
|               tooltip: '${tr('filter')}${isFilterOff ? '' : ' *'}', | ||||
|               onPressed: isFilterOff | ||||
|                   ? showFilterDialog | ||||
|                   : () { | ||||
|   | ||||
| @@ -167,7 +167,7 @@ String hashListOfLists(List<List<int>> data) { | ||||
|   return hash.hashCode.toString(); | ||||
| } | ||||
|  | ||||
| Future<String> checkPartialDownloadHashDynamc(String url, | ||||
| Future<String> checkPartialDownloadHashDynamic(String url, | ||||
|     {int startingSize = 1024, | ||||
|     int lowerLimit = 128, | ||||
|     Map<String, String>? headers}) async { | ||||
| @@ -243,7 +243,9 @@ Future<File> downloadFile( | ||||
|       tempDownloadedFile.deleteSync(recursive: true); | ||||
|       throw response.reasonPhrase ?? tr('unexpectedError'); | ||||
|     } | ||||
|     tempDownloadedFile.renameSync(downloadedFile.path); | ||||
|     if (tempDownloadedFile.existsSync()) { | ||||
|       tempDownloadedFile.renameSync(downloadedFile.path); | ||||
|     } | ||||
|   } else { | ||||
|     client.close(); | ||||
|   } | ||||
| @@ -530,9 +532,18 @@ class AppsProvider with ChangeNotifier { | ||||
|       {bool needsBGWorkaround = false}) async { | ||||
|     var newInfo = | ||||
|         await pm.getPackageArchiveInfo(archiveFilePath: file.file.path); | ||||
|     if (newInfo == null) { | ||||
|       try { | ||||
|         file.file.deleteSync(recursive: true); | ||||
|       } catch (e) { | ||||
|         // | ||||
|       } finally { | ||||
|         throw ObtainiumError(tr('badDownload')); | ||||
|       } | ||||
|     } | ||||
|     PackageInfo? appInfo = await getInstalledInfo(apps[file.appId]!.app.id); | ||||
|     if (appInfo != null && | ||||
|         newInfo!.versionCode! < appInfo.versionCode! && | ||||
|         newInfo.versionCode! < appInfo.versionCode! && | ||||
|         !(await canDowngradeApps())) { | ||||
|       throw DowngradeError(); | ||||
|     } | ||||
|   | ||||
| @@ -829,8 +829,9 @@ class SourceProvider { | ||||
|     APKDetails apk = | ||||
|         await source.getLatestAPKDetails(standardUrl, additionalSettings); | ||||
|  | ||||
|     if (source.runtimeType != HTML().runtimeType) { | ||||
|       // HTML does it separately | ||||
|     if (source.runtimeType != | ||||
|             HTML().runtimeType && // Some sources do it separately | ||||
|         source.runtimeType != SourceForge().runtimeType) { | ||||
|       String? extractedVersion = extractVersion( | ||||
|           additionalSettings['versionExtractionRegEx'] as String?, | ||||
|           additionalSettings['matchGroupToUse'] as String?, | ||||
|   | ||||
							
								
								
									
										48
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						| @@ -22,10 +22,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: android_package_manager | ||||
|       sha256: e52ca607b9f19f95d5dae4211ed8fa93e67093f22ac570db47489c5bca512940 | ||||
|       sha256: "2de859fae7226a7de1c1ff9a2308f1967599408800330501a1ce97927c051153" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.7.0" | ||||
|     version: "0.7.1" | ||||
|   animations: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -190,10 +190,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: device_info_plus | ||||
|       sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" | ||||
|       sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "9.1.1" | ||||
|     version: "9.1.2" | ||||
|   device_info_plus_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -307,10 +307,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_local_notifications | ||||
|       sha256: "66cc2fe16bf4bca71d795939763ad3f1830ad85772dc3b1561613c501859826d" | ||||
|       sha256: c18f1de98fe0bb9dd5ba91e1330d4febc8b6a7de6aae3ffe475ef423723e72f3 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "16.3.1+1" | ||||
|     version: "16.3.2" | ||||
|   flutter_local_notifications_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -410,10 +410,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: image | ||||
|       sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" | ||||
|       sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.1.4" | ||||
|     version: "4.1.6" | ||||
|   intl: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -482,10 +482,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: mime | ||||
|       sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e | ||||
|       sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.4" | ||||
|     version: "1.0.5" | ||||
|   nested: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -650,10 +650,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: share_plus | ||||
|       sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd | ||||
|       sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "7.2.1" | ||||
|     version: "7.2.2" | ||||
|   share_plus_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -751,18 +751,18 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: sqflite | ||||
|       sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" | ||||
|       sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.0" | ||||
|     version: "2.3.2" | ||||
|   sqflite_common: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sqflite_common | ||||
|       sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 | ||||
|       sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.5.0+2" | ||||
|     version: "2.5.3" | ||||
|   stack_trace: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -831,10 +831,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: url_launcher | ||||
|       sha256: d25bb0ca00432a5e1ee40e69c36c85863addf7cc45e433769d61bed3fe81fd96 | ||||
|       sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.3" | ||||
|     version: "6.2.4" | ||||
|   url_launcher_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -919,26 +919,26 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: webview_flutter | ||||
|       sha256: "71e1bfaef41016c8d5954291df5e9f8c6172f1f6ff3af01b5656456ddb11f94c" | ||||
|       sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.4.4" | ||||
|     version: "4.5.0" | ||||
|   webview_flutter_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_android | ||||
|       sha256: "161af93c2abaf94ef2192bffb53a3658b2d721a3bf99b69aa1e47814ee18cc96" | ||||
|       sha256: "4ea3c4e1b8ed590162b15b8a61b41b1ef3ff179a314627c16ce40c086d94b8af" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.13.2" | ||||
|     version: "3.14.0" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_platform_interface | ||||
|       sha256: "80b40ae4fb959957eef9fa8970b6c9accda9f49fc45c2b75154696a8e8996cfe" | ||||
|       sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.9.1" | ||||
|     version: "2.10.0" | ||||
|   webview_flutter_wkwebview: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ||||
| # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html | ||||
| # In Windows, build-name is used as the major, minor, and patch parts | ||||
| # of the product and file versions while build-number is used as the build suffix. | ||||
| version: 0.16.1+249 # When changing this, update the tag in main() accordingly | ||||
| version: 1.0.2+252 # When changing this, update the tag in main() accordingly | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=3.0.0 <4.0.0' | ||||
|   | ||||