Compare commits
192 Commits
Author | SHA1 | Date | |
---|---|---|---|
19a4489d4f | |||
98e4ae1921 | |||
cdff0613b3 | |||
0f803b963f | |||
b9fffed625 | |||
918091ba54 | |||
09d729261c | |||
e50e9e40fc | |||
3f1ca3b957 | |||
8d95da46b1 | |||
a11de53c97 | |||
cf088f15ae | |||
6055ae6a69 | |||
2223616f2a | |||
dab5e4246b | |||
39b76a41cf | |||
377ebeeca2 | |||
c88f55af5a | |||
2dca74edb2 | |||
e35fd1e01e | |||
86be6a77d7 | |||
7d2f215b80 | |||
7d9a641e24 | |||
e0c69b9cf4 | |||
5f971dcddb | |||
b539f0a926 | |||
db82fe7b8f | |||
81c9f4ad47 | |||
092f81cb00 | |||
fc4596c0bc | |||
f6587ae8da | |||
0a30bf6d8e | |||
8bec3cf053 | |||
43efc044d7 | |||
63e71624f9 | |||
3edaa143e4 | |||
798bddd17f | |||
4f27e25a23 | |||
a2a1f48310 | |||
c246548436 | |||
f67cfeb231 | |||
9758f8b391 | |||
c45aaff610 | |||
bd32a10de7 | |||
ef83be07cd | |||
9fe33737eb | |||
51c7acf466 | |||
d8490c289d | |||
1d73d4dcbb | |||
6d28d15d4f | |||
3890785fc7 | |||
38d34c810b | |||
b2d6752b80 | |||
47a6e0dc7c | |||
5b6299496f | |||
369127806f | |||
ee9b0e710c | |||
6e0819b0a7 | |||
d8c9cd6579 | |||
ae2dad01ff | |||
3345b26fa9 | |||
99e3b8ac4f | |||
7d01141db5 | |||
195c0d1f11 | |||
6b8bb095d4 | |||
fd9bf721d4 | |||
1a058a1959 | |||
f78beff5d8 | |||
39757a3416 | |||
e7ff0bb2cb | |||
b1bd488f22 | |||
8201348995 | |||
c1ddc57126 | |||
b41b9d3109 | |||
add2574c25 | |||
c702dbcefe | |||
19710cf7ad | |||
104a19bc32 | |||
b02de232ee | |||
971b5dbf11 | |||
eb805bd30a | |||
21ec90c11c | |||
51ed211b3d | |||
34848794e4 | |||
ab3489f60d | |||
12c878eaba | |||
6c3a496be1 | |||
b2742dd904 | |||
1d69e42949 | |||
9ce74beda5 | |||
26bca6df64 | |||
7a7a6f78e9 | |||
8d062f0296 | |||
8d54824a7d | |||
20d3c4141e | |||
2f54de6d7c | |||
849a565177 | |||
972d7d6634 | |||
2a3251aacb | |||
fa6ed32ad5 | |||
53caa88d6f | |||
5dab58cc89 | |||
e061e99451 | |||
a282080fea | |||
0b812b508a | |||
e639758b15 | |||
f159c0bd44 | |||
950bf28289 | |||
ecf4326b47 | |||
98182d9873 | |||
c7c6731732 | |||
b62b60d9df | |||
3e41913153 | |||
6b4943349a | |||
2f60835801 | |||
3de2121ed8 | |||
e1c80229ab | |||
e9feaf0d8b | |||
3175597a2a | |||
6af1748a78 | |||
c9aed8dfc4 | |||
9c3bdafa47 | |||
d4e857f7f4 | |||
74d6ffcfb3 | |||
10634e8ed2 | |||
2317c5e0d3 | |||
e9dfb494f7 | |||
71ca60244b | |||
94ab83ff75 | |||
2aa91ed535 | |||
23a72f1c83 | |||
12a25d5cbc | |||
eb57d150b4 | |||
5dc8461341 | |||
5cf751caa8 | |||
526ebdfc32 | |||
48f2164fe0 | |||
0d4bc44aa4 | |||
e78961f0ab | |||
6784c6c0c6 | |||
b6ef153bfa | |||
6a3805723d | |||
ebc6671d62 | |||
c87fc6f242 | |||
b23db43620 | |||
b2b10739e3 | |||
32cc97a18a | |||
4815e5180e | |||
14325eb710 | |||
7607b747c1 | |||
774bc07663 | |||
4e43001276 | |||
46835d0876 | |||
48d8eb214d | |||
988f9a6f9f | |||
51f0d745c9 | |||
ae72302f4c | |||
e157bda0eb | |||
adf05abfac | |||
6a8cf0096d | |||
5e6e3f457c | |||
044bd7f8a0 | |||
902f29fdcf | |||
6c6f256976 | |||
fbf2330c0f | |||
86ac573edd | |||
6a07138389 | |||
23c2664ead | |||
4200e1d954 | |||
3eb3cf25bf | |||
1b039fb5a5 | |||
6a4a15ab4d | |||
a70c6ef1ed | |||
85e5dddd34 | |||
d9c29c6751 | |||
0fd496e660 | |||
b3af899ba1 | |||
9a58643088 | |||
b223522801 | |||
375caa4511 | |||
92ebbd9138 | |||
a3c2761aba | |||
68e38259bd | |||
4aed749b44 | |||
e135476d86 | |||
7490942fad | |||
794be438b0 | |||
f2f055ad83 | |||
4c94f278e2 | |||
22acb6a2dd | |||
d32befb832 | |||
94d8295992 |
15
.dockerignore
Normal file
@ -0,0 +1,15 @@
|
||||
LICENSE.md
|
||||
README.md
|
||||
analysis_options.yaml
|
||||
android
|
||||
assets
|
||||
build
|
||||
build.sh
|
||||
data
|
||||
docker
|
||||
fastlane
|
||||
lib
|
||||
pubspec.lock
|
||||
pubspec.yaml
|
||||
sign.sh
|
||||
test
|
2
.flutter
16
.github/workflows/fastlane.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Validate Fastlane metadata
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
go:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate Fastlane Supply Metadata
|
||||
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2
|
4
.github/workflows/release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
|
||||
- name: Flutter Doctor
|
||||
id: flutter_doctor
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Build APKs
|
||||
run: |
|
||||
sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle
|
||||
sed -i 's/signingConfig = signingConfigs.getByName("release")//g' android/app/build.gradle.kts
|
||||
flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal
|
||||
for file in build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done
|
||||
flutter build apk --flavor fdroid -t lib/main_fdroid.dart && flutter build apk --split-per-abi --flavor fdroid -t lib/main_fdroid.dart
|
||||
|
3
.gitignore
vendored
@ -48,4 +48,5 @@ app.*.map.json
|
||||
/android/app/.cxx
|
||||
|
||||
# Custom
|
||||
TODO.txt
|
||||
TODO.txt
|
||||
data
|
@ -1,6 +1,6 @@
|
||||
#  Obtainium
|
||||
<div align="center"><a href="https://github.com/Safouene1/support-palestine-banner/blob/master/Markdown-pages/Support.md"><img src="https://raw.githubusercontent.com/Safouene1/support-palestine-banner/master/banner-support.svg" alt="Support Palestine" style="width: 100%;"></a></div>
|
||||
|
||||
[](https://techforpalestine.org/learn-more)
|
||||
#  Obtainium
|
||||
|
||||
Get Android app updates straight from the source.
|
||||
|
||||
@ -8,6 +8,7 @@ Obtainium allows you to install and update apps directly from their releases pag
|
||||
|
||||
More info:
|
||||
- [Obtainium Wiki](https://wiki.obtainium.imranr.dev/) ([repository](https://github.com/ImranR98/Obtainium-Wiki))
|
||||
- [Obtainium 101](https://www.youtube.com/watch?v=0MF_v2OBncw) - Tutorial video
|
||||
- [AppVerifier](https://github.com/soupslurpr/AppVerifier) - App verification tool (recommended, integrates with Obtainium)
|
||||
- [apps.obtainium.imranr.dev](https://apps.obtainium.imranr.dev/) - Crowdsourced app configurations ([repository](https://github.com/ImranR98/apps.obtainium.imranr.dev))
|
||||
- [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0) - Original motivation for this app
|
||||
@ -29,6 +30,8 @@ Currently supported App sources:
|
||||
- [Uptodown](https://uptodown.com/)
|
||||
- [Huawei AppGallery](https://appgallery.huawei.com/)
|
||||
- [Tencent App Store](https://sj.qq.com/)
|
||||
- [CoolApk](https://coolapk.com/)
|
||||
- [vivo App Store (CN)](https://h5.appstore.vivo.com.cn/)
|
||||
- [RuStore](https://rustore.ru/)
|
||||
- Jenkins Jobs
|
||||
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
||||
@ -60,7 +63,7 @@ Or, contribute some configurations to the website by creating a PR at [this repo
|
||||
|
||||
Verification info:
|
||||
- Package ID: `dev.imranr.obtainium`
|
||||
- SHA-256 Hash of Signing Certificate: `B3:53:60:1F:6A:1D:5F:D6:60:3A:E2:F5:0B:E8:0C:F3:01:36:7B:86:B6:AB:8B:1F:66:24:3D:A9:6C:D5:73:62`
|
||||
- SHA-256 hash of signing certificate: `B3:53:60:1F:6A:1D:5F:D6:60:3A:E2:F5:0B:E8:0C:F3:01:36:7B:86:B6:AB:8B:1F:66:24:3D:A9:6C:D5:73:62`
|
||||
- Note: The above signature is also valid for the F-Droid flavour of Obtainium, thanks to [reproducible builds](https://f-droid.org/docs/Reproducible_Builds/).
|
||||
- [PGP Public Key](https://keyserver.ubuntu.com/pks/lookup?search=contact%40imranr.dev&fingerprint=on&op=index) (to verify APK hashes)
|
||||
|
||||
|
@ -1,105 +0,0 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.imranr.obtainium"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "dev.imranr.obtainium"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = 24
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
}
|
||||
|
||||
flavorDimensions "flavor"
|
||||
|
||||
productFlavors {
|
||||
normal {
|
||||
dimension "flavor"
|
||||
applicationIdSuffix ""
|
||||
}
|
||||
fdroid {
|
||||
dimension "flavor"
|
||||
applicationIdSuffix ".fdroid"
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
107
android/app/build.gradle.kts
Normal file
@ -0,0 +1,107 @@
|
||||
import java.io.FileInputStream
|
||||
import java.util.Properties
|
||||
import com.android.build.api.variant.FilterConfiguration.FilterType.*
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
val localProperties = Properties()
|
||||
val localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.reader(Charsets.UTF_8).use { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
var flutterVersionCode = localProperties.getProperty("flutter.versionCode") ?: "1"
|
||||
var flutterVersionName = localProperties.getProperty("flutter.versionName") ?: "1.0"
|
||||
|
||||
val keystoreProperties = Properties()
|
||||
val keystorePropertiesFile = rootProject.file("key.properties")
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.imranr.obtainium"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = "27.0.12077973" // 'flutter.ndkVersion' produces warnings (TODO can/should we switch back?)
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "dev.imranr.obtainium"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = 24
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutterVersionCode.toInt()
|
||||
versionName = flutterVersionName
|
||||
}
|
||||
|
||||
flavorDimensions("flavor")
|
||||
|
||||
productFlavors {
|
||||
create("normal") {
|
||||
dimension = "flavor"
|
||||
applicationIdSuffix = ""
|
||||
}
|
||||
create("fdroid") {
|
||||
dimension = "flavor"
|
||||
applicationIdSuffix = ".fdroid"
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
keyAlias = keystoreProperties["keyAlias"].toString()
|
||||
keyPassword = keystoreProperties["keyPassword"].toString()
|
||||
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
|
||||
storePassword = keystoreProperties["storePassword"].toString()
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
}
|
||||
getByName("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val abiCodes = mapOf("x86_64" to 1, "armeabi-v7a" to 2, "arm64-v8a" to 3)
|
||||
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
variant.outputs.forEach { output ->
|
||||
val name = output.filters.find { it.filterType == ABI }?.identifier
|
||||
val baseAbiCode = abiCodes[name] ?: 0
|
||||
if (baseAbiCode != null) {
|
||||
output.versionCode.set(baseAbiCode + ((output.versionCode.get() ?: 0) * 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
@ -61,6 +61,13 @@
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<service
|
||||
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="false" />
|
||||
<meta-data
|
||||
android:name="dev.imranr.obtainium.service.NOTIFICATION_ICON"
|
||||
android:resource="@drawable/ic_notification" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
@ -69,10 +76,13 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.ENFORCE_UPDATE_OWNERSHIP"/>
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
@ -2,4 +2,4 @@ package dev.imranr.obtainium
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity()
|
||||
class MainActivity : FlutterActivity()
|
||||
|
@ -1,22 +0,0 @@
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
// [required] background_fetch
|
||||
url "${project(':background_fetch').projectDir}/libs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = "../build"
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(":app")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
25
android/build.gradle.kts
Normal file
@ -0,0 +1,25 @@
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
// [required] background_fetch
|
||||
url = uri("${project(":background_fetch").projectDir}/libs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
|
||||
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||
|
||||
subprojects {
|
||||
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(":app")
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||
|
@ -1,25 +0,0 @@
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.1.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
25
android/settings.gradle.kts
Normal file
@ -0,0 +1,25 @@
|
||||
pluginManagement {
|
||||
val flutterSdkPath = run {
|
||||
val properties = java.util.Properties()
|
||||
file("local.properties").inputStream().use { properties.load(it) }
|
||||
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||
flutterSdkPath
|
||||
}
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.7.3" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
BIN
assets/graphics/icon-512x512.png
Normal file
After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 418 KiB After Width: | Height: | Size: 346 KiB |
Before Width: | Height: | Size: 418 KiB After Width: | Height: | Size: 354 KiB |
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 265 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 227 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 264 KiB |
396
assets/translations/ar.json
Normal file
@ -0,0 +1,396 @@
|
||||
{
|
||||
"invalidURLForSource": "رابط تطبيق {} غير صالح",
|
||||
"noReleaseFound": "تعذر العثور على إصدار مناسب",
|
||||
"noVersionFound": "تعذر تحديد إصدار التطبيق",
|
||||
"urlMatchesNoSource": "الرابط لا يتطابق مع مصدر معروف",
|
||||
"cantInstallOlderVersion": "لا يمكن تثبيت إصدار أقدم من التطبيق",
|
||||
"appIdMismatch": "معرّف الحزمة المحملة لا يتطابق مع معرّف التطبيق الحالي",
|
||||
"functionNotImplemented": "هذه الوظيفة غير مطبقة في هذا الصنف",
|
||||
"placeholder": "عنصر نائب",
|
||||
"someErrors": "حدثت بعض الأخطاء",
|
||||
"unexpectedError": "خطأ غير متوقع",
|
||||
"ok": "موافق",
|
||||
"and": "و",
|
||||
"githubPATLabel": "رمز الوصول الشخصي لـ GitHub (يزيد من الحد المسموح)",
|
||||
"includePrereleases": "تضمين الإصدارات الأولية",
|
||||
"fallbackToOlderReleases": "الرجوع إلى الإصدارات الأقدم",
|
||||
"filterReleaseTitlesByRegEx": "تصفية عناوين الإصدارات باستخدام تعبير نمطي",
|
||||
"invalidRegEx": "تعبير نمطي غير صالح",
|
||||
"noDescription": "لا يوجد وصف",
|
||||
"cancel": "إلغاء",
|
||||
"continue": "متابعة",
|
||||
"requiredInBrackets": "(مطلوب)",
|
||||
"dropdownNoOptsError": "خطأ: يجب أن يحتوي القائمة المنسدلة على خيار واحد على الأقل",
|
||||
"colour": "لون",
|
||||
"standard": "قياسي",
|
||||
"custom": "مخصص",
|
||||
"useMaterialYou": "استخدم Material You",
|
||||
"githubStarredRepos": "مستودعات GitHub المفضلة",
|
||||
"uname": "اسم المستخدم",
|
||||
"wrongArgNum": "عدد وسائط غير صحيح",
|
||||
"xIsTrackOnly": "{} للتعقب فقط",
|
||||
"source": "المصدر",
|
||||
"app": "التطبيق",
|
||||
"appsFromSourceAreTrackOnly": "تطبيقات هذا المصدر للتعقب فقط.",
|
||||
"youPickedTrackOnly": "لقد اخترت خيار 'التعقب فقط'.",
|
||||
"trackOnlyAppDescription": "سيتم تعقب التطبيق للتحديثات، لكن Obtainium لن يتمكن من تنزيله أو تثبيته.",
|
||||
"cancelled": "تم الإلغاء",
|
||||
"appAlreadyAdded": "التطبيق مضاف بالفعل",
|
||||
"alreadyUpToDateQuestion": "التطبيق محدث بالفعل؟",
|
||||
"addApp": "إضافة تطبيق",
|
||||
"appSourceURL": "رابط مصدر التطبيق",
|
||||
"error": "خطأ",
|
||||
"add": "إضافة",
|
||||
"searchSomeSourcesLabel": "بحث (بعض المصادر فقط)",
|
||||
"search": "بحث",
|
||||
"additionalOptsFor": "خيارات إضافية لـ {}",
|
||||
"supportedSources": "المصادر المدعومة",
|
||||
"trackOnlyInBrackets": "(للتعقب فقط)",
|
||||
"searchableInBrackets": "(قابل للبحث)",
|
||||
"appsString": "التطبيقات",
|
||||
"noApps": "لا توجد تطبيقات",
|
||||
"noAppsForFilter": "لا توجد تطبيقات تطابق الفلتر",
|
||||
"byX": "بواسطة {}",
|
||||
"percentProgress": "التقدم: {}%",
|
||||
"pleaseWait": "يرجى الانتظار",
|
||||
"updateAvailable": "تحديث متاح",
|
||||
"notInstalled": "غير مثبت",
|
||||
"pseudoVersion": "إصدار وهمي",
|
||||
"selectAll": "تحديد الكل",
|
||||
"deselectX": "إلغاء تحديد {}",
|
||||
"xWillBeRemovedButRemainInstalled": "سيتم إزالة {} من Obtainium لكنه سيظل مثبتًا على الجهاز.",
|
||||
"removeSelectedAppsQuestion": "إزالة التطبيقات المحددة؟",
|
||||
"removeSelectedApps": "إزالة التطبيقات المحددة",
|
||||
"updateX": "تحديث {}",
|
||||
"installX": "تثبيت {}",
|
||||
"markXTrackOnlyAsUpdated": "تعليم {}\n(للتعقب فقط)\nكمحدث",
|
||||
"changeX": "تغيير {}",
|
||||
"installUpdateApps": "تثبيت/تحديث التطبيقات",
|
||||
"installUpdateSelectedApps": "تثبيت/تحديث التطبيقات المحددة",
|
||||
"markXSelectedAppsAsUpdated": "تعليم {} تطبيقات محددة كمحدثة؟",
|
||||
"no": "لا",
|
||||
"yes": "نعم",
|
||||
"markSelectedAppsUpdated": "تعليم التطبيقات المحددة كمحدثة",
|
||||
"pinToTop": "تثبيت في الأعلى",
|
||||
"unpinFromTop": "إلغاء التثبيت من الأعلى",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "إعادة تعيين حالة التثبيت للتطبيقات المحددة؟",
|
||||
"installStatusOfXWillBeResetExplanation": "سيتم إعادة تعيين حالة التثبيت لأي تطبيقات محددة.\n\nقد يساعد هذا عندما يكون إصدار التطبيق المعروض في Obtainium غير صحيح بسبب فشل التحديثات أو مشاكل أخرى.",
|
||||
"customLinkMessage": "هذه الروابط تعمل على الأجهزة التي بها Obtainium مثبتًا",
|
||||
"shareAppConfigLinks": "مشاركة تكوين التطبيق كرابط HTML",
|
||||
"shareSelectedAppURLs": "مشاركة روابط التطبيقات المحددة",
|
||||
"resetInstallStatus": "إعادة تعيين حالة التثبيت",
|
||||
"more": "المزيد",
|
||||
"removeOutdatedFilter": "إزالة فلتر التطبيقات غير المحدثة",
|
||||
"showOutdatedOnly": "عرض التطبيقات غير المحدثة فقط",
|
||||
"filter": "تصفية",
|
||||
"filterApps": "تصفية التطبيقات",
|
||||
"appName": "اسم التطبيق",
|
||||
"author": "المؤلف",
|
||||
"upToDateApps": "تطبيقات محدثة",
|
||||
"nonInstalledApps": "تطبيقات غير مثبتة",
|
||||
"importExport": "استيراد/تصدير",
|
||||
"settings": "الإعدادات",
|
||||
"exportedTo": "تم التصدير إلى {}",
|
||||
"obtainiumExport": "تصدير Obtainium",
|
||||
"invalidInput": "إدخال غير صالح",
|
||||
"importedX": "تم استيراد {}",
|
||||
"obtainiumImport": "استيراد Obtainium",
|
||||
"importFromURLList": "استيراد من قائمة الروابط",
|
||||
"searchQuery": "استعلام البحث",
|
||||
"appURLList": "قائمة روابط التطبيقات",
|
||||
"line": "سطر",
|
||||
"searchX": "بحث {}",
|
||||
"noResults": "لا توجد نتائج",
|
||||
"importX": "استيراد {}",
|
||||
"importedAppsIdDisclaimer": "قد تظهر التطبيقات المستوردة بشكل غير صحيح كـ \"غير مثبتة\".\nلإصلاح هذا، قم بإعادة تثبيتها عبر Obtainium.\nهذا لا يؤثر على بيانات التطبيق.\n\nينطبق فقط على طرق الاستيراد عبر الروابط أو الطرف الثالث.",
|
||||
"importErrors": "أخطاء الاستيراد",
|
||||
"importedXOfYApps": "تم استيراد {} من {} تطبيقات.",
|
||||
"followingURLsHadErrors": "حدثت أخطاء في الروابط التالية:",
|
||||
"selectURL": "اختر رابطًا",
|
||||
"selectURLs": "اختر روابط",
|
||||
"pick": "اختر",
|
||||
"theme": "المظهر",
|
||||
"dark": "مظلم",
|
||||
"light": "فاتح",
|
||||
"followSystem": "اتباع النظام",
|
||||
"followSystemThemeExplanation": "اتباع مظهر النظام ممكن فقط باستخدام تطبيقات طرف ثالث",
|
||||
"useBlackTheme": "استخدام المظهر الأسود الداكن بالكامل",
|
||||
"appSortBy": "ترتيب التطبيقات حسب",
|
||||
"authorName": "المؤلف/الاسم",
|
||||
"nameAuthor": "الاسم/المؤلف",
|
||||
"asAdded": "كما تمت إضافتها",
|
||||
"appSortOrder": "ترتيب التطبيقات",
|
||||
"ascending": "تصاعدي",
|
||||
"descending": "تنازلي",
|
||||
"bgUpdateCheckInterval": "فترة التحقق من التحديثات في الخلفية",
|
||||
"neverManualOnly": "أبدًا - يدوي فقط",
|
||||
"appearance": "المظهر",
|
||||
"showWebInAppView": "عرض صفحة الويب المصدر في عرض التطبيق",
|
||||
"pinUpdates": "تثبيت التحديثات في أعلى قائمة التطبيقات",
|
||||
"updates": "التحديثات",
|
||||
"sourceSpecific": "خاص بالمصدر",
|
||||
"appSource": "مصدر التطبيق",
|
||||
"noLogs": "لا توجد سجلات",
|
||||
"appLogs": "سجلات التطبيق",
|
||||
"close": "إغلاق",
|
||||
"share": "مشاركة",
|
||||
"appNotFound": "التطبيق غير موجود",
|
||||
"obtainiumExportHyphenatedLowercase": "تصدير-obtainium",
|
||||
"pickAnAPK": "اختر ملف APK",
|
||||
"appHasMoreThanOnePackage": "{} يحتوي على أكثر من حزمة:",
|
||||
"deviceSupportsXArch": "جهازك يدعم بنية المعالج {}.",
|
||||
"deviceSupportsFollowingArchs": "جهازك يدعم بنى المعالج التالية:",
|
||||
"warning": "تحذير",
|
||||
"sourceIsXButPackageFromYPrompt": "مصدر التطبيق هو '{}' لكن الحزمة تأتي من '{}'. هل تتابع؟",
|
||||
"updatesAvailable": "تحديثات متاحة",
|
||||
"updatesAvailableNotifDescription": "يخطر المستخدم بوجود تحديثات لواحد أو أكثر من التطبيقات التي يتعقبها Obtainium",
|
||||
"noNewUpdates": "لا توجد تحديثات جديدة.",
|
||||
"xHasAnUpdate": "{} لديه تحديث.",
|
||||
"appsUpdated": "تم تحديث التطبيقات",
|
||||
"appsNotUpdated": "فشل تحديث التطبيقات",
|
||||
"appsUpdatedNotifDescription": "يخطر المستخدم بأن تحديثات لواحد أو أكثر من التطبيقات تم تطبيقها في الخلفية",
|
||||
"xWasUpdatedToY": "تم تحديث {} إلى {}.",
|
||||
"xWasNotUpdatedToY": "فشل تحديث {} إلى {}.",
|
||||
"errorCheckingUpdates": "خطأ في التحقق من التحديثات",
|
||||
"errorCheckingUpdatesNotifDescription": "إشعار يظهر عند فشل التحقق من التحديثات في الخلفية",
|
||||
"appsRemoved": "تمت إزالة التطبيقات",
|
||||
"appsRemovedNotifDescription": "يخطر المستخدم بأنه تمت إزالة واحد أو أكثر من التطبيقات بسبب أخطاء أثناء تحميلها",
|
||||
"xWasRemovedDueToErrorY": "تمت إزالة {} بسبب هذا الخطأ: {}",
|
||||
"completeAppInstallation": "إكمال تثبيت التطبيق",
|
||||
"obtainiumMustBeOpenToInstallApps": "يجب فتح Obtainium لتثبيت التطبيقات",
|
||||
"completeAppInstallationNotifDescription": "يطلب من المستخدم العودة إلى Obtainium لإكمال تثبيت التطبيق",
|
||||
"checkingForUpdates": "جاري التحقق من التحديثات",
|
||||
"checkingForUpdatesNotifDescription": "إشعار مؤقت يظهر عند التحقق من التحديثات",
|
||||
"pleaseAllowInstallPerm": "يرجى السماح لـ Obtainium بتثبيت التطبيقات",
|
||||
"trackOnly": "للتعقب فقط",
|
||||
"errorWithHttpStatusCode": "خطأ {}",
|
||||
"versionCorrectionDisabled": "تعطيل تصحيح الإصدار (يبدو أن الملحق لا يعمل)",
|
||||
"unknown": "غير معروف",
|
||||
"none": "لا شيء",
|
||||
"never": "أبدًا",
|
||||
"latestVersionX": "الأحدث: {}",
|
||||
"installedVersionX": "المثبت: {}",
|
||||
"lastUpdateCheckX": "آخر تحقق من التحديثات: {}",
|
||||
"remove": "إزالة",
|
||||
"yesMarkUpdated": "نعم، تعليم كمحدث",
|
||||
"fdroid": "F-Droid الرسمي",
|
||||
"appIdOrName": "معرّف التطبيق أو الاسم",
|
||||
"appId": "معرّف التطبيق",
|
||||
"appWithIdOrNameNotFound": "لم يتم العثور على تطبيق بهذا المعرّف أو الاسم",
|
||||
"reposHaveMultipleApps": "قد تحتوي المستودعات على عدة تطبيقات",
|
||||
"fdroidThirdPartyRepo": "مستودع F-Droid طرف ثالث",
|
||||
"install": "تثبيت",
|
||||
"markInstalled": "تعليم كمثبت",
|
||||
"update": "تحديث",
|
||||
"markUpdated": "تعليم كمحدث",
|
||||
"additionalOptions": "خيارات إضافية",
|
||||
"disableVersionDetection": "تعطيل اكتشاف الإصدار",
|
||||
"noVersionDetectionExplanation": "يجب استخدام هذا الخيار فقط للتطبيقات التي لا يعمل فيها اكتشاف الإصدار بشكل صحيح.",
|
||||
"downloadingX": "جاري تنزيل {}",
|
||||
"downloadX": "تنزيل {}",
|
||||
"downloadedX": "تم تنزيل {}",
|
||||
"releaseAsset": "ملف الإصدار",
|
||||
"downloadNotifDescription": "يخطر المستخدم بالتقدم في تنزيل التطبيق",
|
||||
"noAPKFound": "لم يتم العثور على ملف APK",
|
||||
"noVersionDetection": "لا يوجد اكتشاف للإصدار",
|
||||
"categorize": "تصنيف",
|
||||
"categories": "التصنيفات",
|
||||
"category": "تصنيف",
|
||||
"noCategory": "بدون تصنيف",
|
||||
"noCategories": "لا توجد تصنيفات",
|
||||
"deleteCategoriesQuestion": "حذف التصنيفات؟",
|
||||
"categoryDeleteWarning": "سيتم تعيين جميع التطبيقات في التصنيفات المحذوفة كغير مصنفة.",
|
||||
"addCategory": "إضافة تصنيف",
|
||||
"label": "تسمية",
|
||||
"language": "اللغة",
|
||||
"copiedToClipboard": "تم النسخ إلى الحافظة",
|
||||
"storagePermissionDenied": "تم رفض إذن التخزين",
|
||||
"selectedCategorizeWarning": "سيؤدي هذا إلى استبدال أي إعدادات تصنيف موجودة للتطبيقات المحددة.",
|
||||
"filterAPKsByRegEx": "تصفية ملفات APK باستخدام تعبير نمطي",
|
||||
"removeFromObtainium": "إزالة من Obtainium",
|
||||
"uninstallFromDevice": "إلغاء التثبيت من الجهاز",
|
||||
"onlyWorksWithNonVersionDetectApps": "يعمل فقط مع التطبيقات التي تم تعطيل اكتشاف الإصدار لها.",
|
||||
"releaseDateAsVersion": "استخدام تاريخ الإصدار كسلسلة إصدار",
|
||||
"releaseTitleAsVersion": "استخدام عنوان الإصدار كسلسلة إصدار",
|
||||
"releaseDateAsVersionExplanation": "يجب استخدام هذا الخيار فقط للتطبيقات التي لا يعمل فيها اكتشاف الإصدار بشكل صحيح، ولكن تاريخ الإصدار متاح.",
|
||||
"changes": "التغييرات",
|
||||
"releaseDate": "تاريخ الإصدار",
|
||||
"importFromURLsInFile": "استيراد من روابط في ملف (مثل OPML)",
|
||||
"versionDetectionExplanation": "تنسيق سلسلة الإصدار مع الإصدار المكتشف من النظام",
|
||||
"versionDetection": "اكتشاف الإصدار",
|
||||
"standardVersionDetection": "اكتشاف الإصدار القياسي",
|
||||
"groupByCategory": "التجميع حسب التصنيف",
|
||||
"autoApkFilterByArch": "محاولة تصفية ملفات APK حسب بنية المعالج إذا أمكن",
|
||||
"autoLinkFilterByArch": "محاولة تصفية الروابط حسب بنية المعالج إذا أمكن",
|
||||
"overrideSource": "تجاوز المصدر",
|
||||
"dontShowAgain": "عدم إظهار هذا مرة أخرى",
|
||||
"dontShowTrackOnlyWarnings": "عدم إظهار تحذيرات 'للتعقب فقط'",
|
||||
"dontShowAPKOriginWarnings": "عدم إظهار تحذيرات مصدر APK",
|
||||
"moveNonInstalledAppsToBottom": "نقل التطبيقات غير المثبتة إلى أسفل قائمة التطبيقات",
|
||||
"gitlabPATLabel": "رمز الوصول الشخصي لـ GitLab",
|
||||
"about": "حول",
|
||||
"requiresCredentialsInSettings": "{} يحتاج إلى بيانات اعتماد إضافية (في الإعدادات)",
|
||||
"checkOnStart": "التحقق من التحديثات عند البدء",
|
||||
"tryInferAppIdFromCode": "محاولة استنتاج معرّف التطبيق من الكود المصدري",
|
||||
"removeOnExternalUninstall": "إزالة التطبيقات المثبتة خارجيًا تلقائيًا",
|
||||
"pickHighestVersionCode": "تحديد أعلى إصدار تلقائيًا",
|
||||
"checkUpdateOnDetailPage": "التحقق من التحديثات عند فتح صفحة التفاصيل",
|
||||
"disablePageTransitions": "تعطيل انتقالات الصفحات",
|
||||
"reversePageTransitions": "عكس انتقالات الصفحات",
|
||||
"minStarCount": "الحد الأدنى لعدد النجوم",
|
||||
"addInfoBelow": "أضف هذه المعلومات أدناه.",
|
||||
"addInfoInSettings": "أضف هذه المعلومات في الإعدادات.",
|
||||
"githubSourceNote": "يمكن تجنب الحد المسموح لـ GitHub باستخدام مفتاح API.",
|
||||
"sortByLastLinkSegment": "الفرز حسب الجزء الأخير من الرابط فقط",
|
||||
"filterReleaseNotesByRegEx": "تصفية ملاحظات الإصدار باستخدام تعبير نمطي",
|
||||
"customLinkFilterRegex": "تصفية روابط APK المخصصة باستخدام تعبير نمطي (افتراضيًا '.apk$')",
|
||||
"appsPossiblyUpdated": "تمت محاولة تحديث التطبيقات",
|
||||
"appsPossiblyUpdatedNotifDescription": "يخطر المستخدم بأن تحديثات لواحد أو أكثر من التطبيقات قد تم تطبيقها في الخلفية",
|
||||
"xWasPossiblyUpdatedToY": "{} قد تم تحديثه إلى {}.",
|
||||
"enableBackgroundUpdates": "تمكين التحديثات في الخلفية",
|
||||
"backgroundUpdateReqsExplanation": "قد لا تكون التحديثات في الخلفية ممكنة لجميع التطبيقات.",
|
||||
"backgroundUpdateLimitsExplanation": "يمكن تحديد نجاح التثبيت في الخلفية فقط عند فتح Obtainium.",
|
||||
"verifyLatestTag": "التحقق من علامة 'الأحدث'",
|
||||
"intermediateLinkRegex": "تصفية للوصول إلى رابط 'وسيط'",
|
||||
"filterByLinkText": "تصفية الروابط حسب نص الرابط",
|
||||
"intermediateLinkNotFound": "لم يتم العثور على رابط وسيط",
|
||||
"intermediateLink": "رابط وسيط",
|
||||
"exemptFromBackgroundUpdates": "إعفاء من التحديثات في الخلفية (إذا تم تمكينها)",
|
||||
"bgUpdatesOnWiFiOnly": "تعطيل التحديثات في الخلفية عند عدم الاتصال بشبكة Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "تعطيل التحديثات في الخلفية عند عدم الشحن",
|
||||
"autoSelectHighestVersionCode": "تحديد أعلى إصدار تلقائيًا",
|
||||
"versionExtractionRegEx": "تعبير نمطي لاستخراج الإصدار",
|
||||
"trimVersionString": "قص سلسلة الإصدار باستخدام تعبير نمطي",
|
||||
"matchGroupToUseForX": "مجموعة المطابقة لاستخدامها لـ \"{}\"",
|
||||
"matchGroupToUse": "مجموعة المطابقة لاستخدامها في تعبير استخراج الإصدار",
|
||||
"highlightTouchTargets": "تمييز أهداف اللمس الأقل وضوحًا",
|
||||
"pickExportDir": "اختر دليل التصدير",
|
||||
"autoExportOnChanges": "تصدير تلقائي عند التغييرات",
|
||||
"includeSettings": "تضمين الإعدادات",
|
||||
"filterVersionsByRegEx": "تصفية الإصدارات باستخدام تعبير نمطي",
|
||||
"trySelectingSuggestedVersionCode": "محاولة تحديد الإصدار المقترح تلقائيًا",
|
||||
"dontSortReleasesList": "الحفاظ على ترتيب الإصدارات من API",
|
||||
"reverseSort": "فرز عكسي",
|
||||
"takeFirstLink": "أخذ أول رابط",
|
||||
"skipSort": "تخطي الفرز",
|
||||
"debugMenu": "قائمة التصحيح",
|
||||
"bgTaskStarted": "بدأت المهمة في الخلفية - تحقق من السجلات.",
|
||||
"runBgCheckNow": "تشغيل التحقق من التحديثات في الخلفية الآن",
|
||||
"versionExtractWholePage": "تطبيق تعبير استخراج الإصدار على الصفحة بأكملها",
|
||||
"installing": "جاري التثبيت",
|
||||
"skipUpdateNotifications": "تخطي إشعارات التحديث",
|
||||
"updatesAvailableNotifChannel": "تحديثات متاحة",
|
||||
"appsUpdatedNotifChannel": "تم تحديث التطبيقات",
|
||||
"appsPossiblyUpdatedNotifChannel": "تمت محاولة تحديث التطبيقات",
|
||||
"errorCheckingUpdatesNotifChannel": "خطأ في التحقق من التحديثات",
|
||||
"appsRemovedNotifChannel": "تمت إزالة التطبيقات",
|
||||
"downloadingXNotifChannel": "جاري تنزيل {}",
|
||||
"completeAppInstallationNotifChannel": "إكمال تثبيت التطبيق",
|
||||
"checkingForUpdatesNotifChannel": "جاري التحقق من التحديثات",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "التحقق فقط من التطبيقات المثبتة أو للتعقب فقط",
|
||||
"supportFixedAPKURL": "دعم روابط APK الثابتة",
|
||||
"selectX": "اختر {}",
|
||||
"parallelDownloads": "السماح بالتنزيل المتوازي",
|
||||
"useShizuku": "استخدم Shizuku أو Sui للتثبيت",
|
||||
"shizukuBinderNotFound": "خدمة Shizuku غير قيد التشغيل",
|
||||
"shizukuOld": "إصدار قديم من Shizuku (<11) - قم بتحديثه",
|
||||
"shizukuOldAndroidWithADB": "Shizuku يعمل على إصدار أندرويد < 8.1 مع ADB - قم بتحديث أندرويد أو استخدم Sui بدلاً من ذلك",
|
||||
"shizukuPretendToBeGooglePlay": "تعيين Google Play كمصدر التثبيت (إذا تم استخدام Shizuku)",
|
||||
"useSystemFont": "استخدم خط النظام",
|
||||
"useVersionCodeAsOSVersion": "استخدم versionCode كإصدار مكتشف من النظام",
|
||||
"requestHeader": "رأس الطلب",
|
||||
"useLatestAssetDateAsReleaseDate": "استخدام أحدث تاريخ تحميل كتاريخ إصدار",
|
||||
"defaultPseudoVersioningMethod": "طريقة الإصدار الوهمي الافتراضية",
|
||||
"partialAPKHash": "جزء من تجزئة APK",
|
||||
"APKLinkHash": "تجزئة رابط APK",
|
||||
"directAPKLink": "رابط APK مباشر",
|
||||
"pseudoVersionInUse": "يتم استخدام إصدار وهمي",
|
||||
"installed": "مثبت",
|
||||
"latest": "الأحدث",
|
||||
"invertRegEx": "عكس التعبير النمطي",
|
||||
"note": "ملاحظة",
|
||||
"selfHostedNote": "يمكن استخدام القائمة المنسدلة \"{}\" للوصول إلى نسخ مخصصة/مستضافة ذاتيًا لأي مصدر.",
|
||||
"badDownload": "تعذر تحليل ملف APK (تنزيل غير متوافق أو غير مكتمل)",
|
||||
"beforeNewInstallsShareToAppVerifier": "مشاركة التطبيقات الجديدة مع AppVerifier (إذا كان متاحًا)",
|
||||
"appVerifierInstructionToast": "شارك مع AppVerifier، ثم عد هنا عندما تكون جاهزًا.",
|
||||
"wiki": "المساعدة/ويكي",
|
||||
"crowdsourcedConfigsLabel": "تكوينات التطبيقات المقدمة من الجمهور (استخدم على مسؤوليتك)",
|
||||
"crowdsourcedConfigsShort": "تكوينات التطبيقات المقدمة من الجمهور",
|
||||
"allowInsecure": "السماح بطلبات HTTP غير الآمنة",
|
||||
"stayOneVersionBehind": "البقاء بإصدار واحد خلف الأحدث",
|
||||
"useFirstApkOfVersion": "التحديد التلقائي الأول من بين عدة ملفات APK",
|
||||
"refreshBeforeDownload": "تحديث تفاصيل التطبيق قبل التنزيل",
|
||||
"tencentAppStore": "متجر Tencent للتطبيقات",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "متجر vivo للتطبيقات (CN)",
|
||||
"name": "الاسم",
|
||||
"smartname": "الاسم (ذكي)",
|
||||
"sortMethod": "طريقة الفرز",
|
||||
"welcome": "مرحبًا",
|
||||
"documentationLinksNote": "تحتوي صفحة Obtainium على GitHub المرتبطة أدناه على روابط لمقاطع فيديو، مقالات، مناقشات وموارد أخرى ستساعدك على فهم كيفية استخدام التطبيق.",
|
||||
"batteryOptimizationNote": "لاحظ أن التنزيلات في الخلفية قد تعمل بشكل أكثر موثوقية إذا قمت بتعطيل تحسينات بطارية النظام لـ Obtainium.",
|
||||
"fileDeletionError": "فشل حذف الملف (حاول حذفه يدويًا ثم حاول مرة أخرى): \"{}\"",
|
||||
"foregroundService": "الحصول على خدمة الحصول على خدمة المقدمة",
|
||||
"foregroundServiceExplanation": "استخدام خدمة مقدمة للتحقق من التحديثات (أكثر موثوقية وتستهلك طاقة أكبر)",
|
||||
"fgServiceNotice": "هذا الإشعار مطلوب للتحقق من التحديث في الخلفية (يمكن إخفاؤه في إعدادات نظام التشغيل)",
|
||||
"removeAppQuestion": {
|
||||
"one": "إزالة التطبيق؟",
|
||||
"other": "إزالة التطبيقات؟"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "طلبات كثيرة جدًا (تم تحديد المعدل) - حاول مرة أخرى بعد {} دقيقة",
|
||||
"other": "طلبات كثيرة جدًا (تم تحديد المعدل) - حاول مرة أخرى بعد {} دقائق"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "واجه التحقق من التحديثات في الخلفية {}، سيتم جدولة محاولة أخرى بعد {} دقيقة",
|
||||
"other": "واجه التحقق من التحديثات في الخلفية {}، سيتم جدولة محاولة أخرى بعد {} دقائق"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "وجد التحقق من التحديثات في الخلفية {} تحديث - سيتم إخطار المستخدم إذا لزم الأمر",
|
||||
"other": "وجد التحقق من التحديثات في الخلفية {} تحديثات - سيتم إخطار المستخدم إذا لزم الأمر"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} تطبيق",
|
||||
"other": "{} تطبيقات"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} رابط",
|
||||
"other": "{} روابط"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} دقيقة",
|
||||
"other": "{} دقائق"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} ساعة",
|
||||
"other": "{} ساعات"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} يوم",
|
||||
"other": "{} أيام"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "تم مسح {n} سجل (قبل = {before}, بعد = {after})",
|
||||
"other": "تم مسح {n} سجلات (قبل = {before}, بعد = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} وتطبيق واحد آخر لديه تحديثات.",
|
||||
"other": "{} و{} تطبيقات أخرى لديها تحديثات."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} وتطبيق واحد آخر تم تحديثه.",
|
||||
"other": "{} و{} تطبيقات أخرى تم تحديثها."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "فشل تحديث {} وتطبيق واحد آخر.",
|
||||
"other": "فشل تحديث {} و{} تطبيقات أخرى."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} وتطبيق واحد آخر قد تم تحديثه.",
|
||||
"other": "{} و{} تطبيقات أخرى قد تم تحديثها."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Detekcija standardne verzije",
|
||||
"groupByCategory": "Grupiši po kategoriji",
|
||||
"autoApkFilterByArch": "Pokušajte filtrirati APK-ove po arhitekturi procesora ako je moguće",
|
||||
"autoLinkFilterByArch": "Attempt to filter links by CPU architecture if possible",
|
||||
"overrideSource": "Premosti izvor",
|
||||
"dontShowAgain": "Ne prikazuj ovo ponovo",
|
||||
"dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "Intermediate veza nije nađena",
|
||||
"intermediateLink": "Intermediate veza",
|
||||
"exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)",
|
||||
"bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste naWi-Fi-ju",
|
||||
"bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na Wi-Fi-ju",
|
||||
"bgUpdatesWhileChargingOnly": "Disable background updates when not charging",
|
||||
"autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a",
|
||||
"versionExtractionRegEx": "RegEx ekstrakcija verzije",
|
||||
@ -314,11 +315,24 @@
|
||||
"appVerifierInstructionToast": "Dijeli sa AppVerifier-om, zatim se vratite kada ste spremni.",
|
||||
"wiki": "Pomoć/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Konfiguracije aplikacije obezbeđene pomoću velikog broja ljudi (crowdsourcing) (koristite na svoju odgovornost)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configs",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configurations",
|
||||
"allowInsecure": "Allow insecure HTTP requests",
|
||||
"stayOneVersionBehind": "Stay one version behind latest",
|
||||
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
|
||||
"refreshBeforeDownload": "Refresh app details before download",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Name",
|
||||
"smartname": "Name (Smart)",
|
||||
"sortMethod": "Sort Method",
|
||||
"welcome": "Welcome",
|
||||
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.",
|
||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
||||
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Želite li ukloniti aplikaciju?",
|
||||
"other": "Želite li ukloniti aplikacije?"
|
||||
|
396
assets/translations/ca.json
Normal file
@ -0,0 +1,396 @@
|
||||
{
|
||||
"invalidURLForSource": "L'URL de l'aplicació {} no es vàlid",
|
||||
"noReleaseFound": "No s'ha pogut trobar una versió adequada",
|
||||
"noVersionFound": "No s'ha pogut determinar la versió",
|
||||
"urlMatchesNoSource": "L'URL no coincideix amb cap font coneguda",
|
||||
"cantInstallOlderVersion": "No és possible instal·lar una versió més antiga de l'aplicació",
|
||||
"appIdMismatch": "L'ID del paquet descarregat no coincideix amb l'ID de l'aplicació instal·lada",
|
||||
"functionNotImplemented": "Aquesta classe no ha implentat aquesta funció",
|
||||
"placeholder": "Espai reservat",
|
||||
"someErrors": "S'han produït alguns errors",
|
||||
"unexpectedError": "Error inesperat",
|
||||
"ok": "Accepta",
|
||||
"and": "i",
|
||||
"githubPATLabel": "Token d'accés personal a GitHub (augmenta el límit d'accés)",
|
||||
"includePrereleases": "Inclou les versions preliminars",
|
||||
"fallbackToOlderReleases": "Torna a les versions anteriors",
|
||||
"filterReleaseTitlesByRegEx": "Filtra el títol de la versió per una expressió regular",
|
||||
"invalidRegEx": "Expressió regular invàlida",
|
||||
"noDescription": "Sense descripció",
|
||||
"cancel": "Cancel·la",
|
||||
"continue": "Continua",
|
||||
"requiredInBrackets": "(requerit)",
|
||||
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE HA DE TENIR ALMENYS UNA OPCIÓ",
|
||||
"colour": "Color",
|
||||
"standard": "Estàndard",
|
||||
"custom": "Personalitzat",
|
||||
"useMaterialYou": "Usa 'Material You'",
|
||||
"githubStarredRepos": "Repositoris favorits de GitHub",
|
||||
"uname": "Nom d'usuari",
|
||||
"wrongArgNum": "Nombre d'arguments proveït invàlid",
|
||||
"xIsTrackOnly": "{} és només per a seguiment",
|
||||
"source": "Font",
|
||||
"app": "Aplicació",
|
||||
"appsFromSourceAreTrackOnly": "Les aplicacions d'aquesta font són només per a seguiment.",
|
||||
"youPickedTrackOnly": "Has seleccionat l'opció 'només per a seguiment'.",
|
||||
"trackOnlyAppDescription": "Farem el seguiment de les actualitzacions per a l'aplicació, però Obtainium no podrà descarregar-la ni actualitzar-la.",
|
||||
"cancelled": "Cancel·lat",
|
||||
"appAlreadyAdded": "Aplicació ja afegida",
|
||||
"alreadyUpToDateQuestion": "Aplicació ja actualitzada?",
|
||||
"addApp": "Afegeix l'aplicació",
|
||||
"appSourceURL": "URL font de l'aplicació",
|
||||
"error": "Error",
|
||||
"add": "Afegeix",
|
||||
"searchSomeSourcesLabel": "Cerca (només algunes fonts)",
|
||||
"search": "Cerca",
|
||||
"additionalOptsFor": "Opcions addicionals per a {}",
|
||||
"supportedSources": "Fonts suportades",
|
||||
"trackOnlyInBrackets": "(només per a seguiment)",
|
||||
"searchableInBrackets": "(permet la cerca)",
|
||||
"appsString": "Aplicacions",
|
||||
"noApps": "No hi ha aplicacions",
|
||||
"noAppsForFilter": "No hi ha aplicacions per filtrar",
|
||||
"byX": "Per: {}",
|
||||
"percentProgress": "Progrés: {} %",
|
||||
"pleaseWait": "Espera...",
|
||||
"updateAvailable": "Actualització disponible",
|
||||
"notInstalled": "No instal·lat",
|
||||
"pseudoVersion": "pseudo-versió",
|
||||
"selectAll": "Selecciona-ho tot",
|
||||
"deselectX": "Desselecciona {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} s'eliminarà d'Obtainium però romandrà instal·lada al dispositiu.",
|
||||
"removeSelectedAppsQuestion": "Elimino les aplicacions seleccionades?",
|
||||
"removeSelectedApps": "Elimina les aplicacions seleccionades",
|
||||
"updateX": "Actualitza {}",
|
||||
"installX": "Instal·la {}",
|
||||
"markXTrackOnlyAsUpdated": "Marca {}\n(només per a seguiment)\ncom a actualitzada",
|
||||
"changeX": "Canvia {}",
|
||||
"installUpdateApps": "Instal·la/actualitza les aplicacions",
|
||||
"installUpdateSelectedApps": "Instal·la/actualitza les aplicacions seleccionades",
|
||||
"markXSelectedAppsAsUpdated": "Marco {} les aplicaciones seleccionades com a actualitzades?",
|
||||
"no": "No",
|
||||
"yes": "Sí",
|
||||
"markSelectedAppsUpdated": "Marca les aplicacions seleccionades com a actualitzades",
|
||||
"pinToTop": "Ancora-la al capdamunt",
|
||||
"unpinFromTop": "Desancora-la del capdamunt",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Restableixo l'estat d'instal·lació per a les aplicacions seleccionades?",
|
||||
"installStatusOfXWillBeResetExplanation": "Es restablirà l'estat d'instal·lació de les aplicacions seleccionades.\n\nAçò pot ser útil quan la versió de l'aplicació mostrada per Obtainium és incorrecta a conseqüència d'una actualització no reeixida o d'algun altre problema.",
|
||||
"customLinkMessage": "Aquests enllaços funcionen en dispositius amb Obtainium instal·lat",
|
||||
"shareAppConfigLinks": "Comparteix la configuració de l'aplicació com a enllaç HTML",
|
||||
"shareSelectedAppURLs": "Comparteix els URL de les aplicacions seleccionades",
|
||||
"resetInstallStatus": "Restableix l'estat de la instal·lació",
|
||||
"more": "Més",
|
||||
"removeOutdatedFilter": "Elimina el filtre d'aplicacions desactualitzades",
|
||||
"showOutdatedOnly": "Mostra només les aplicacions desactualitzades",
|
||||
"filter": "Filtra",
|
||||
"filterApps": "Filtra les aplicacions",
|
||||
"appName": "Nom de l'aplicació",
|
||||
"author": "Autor",
|
||||
"upToDateApps": "Aplicacions actualizades",
|
||||
"nonInstalledApps": "Aplicacions no instal·lades",
|
||||
"importExport": "Importa/exporta",
|
||||
"settings": "Paràmetres",
|
||||
"exportedTo": "Exportat a {}",
|
||||
"obtainiumExport": "Exporta Obtainium",
|
||||
"invalidInput": "Entrada no vàlida",
|
||||
"importedX": "Importat {}",
|
||||
"obtainiumImport": "Importa Obtainium",
|
||||
"importFromURLList": "Importa des de la llista d'URL",
|
||||
"searchQuery": "Terme de cerca",
|
||||
"appURLList": "Llista d'URL d'aplicacions",
|
||||
"line": "Línia",
|
||||
"searchX": "Cerca {}",
|
||||
"noResults": "No hi ha resultats",
|
||||
"importX": "Importa des de {}",
|
||||
"importedAppsIdDisclaimer": "Les aplicacions importades podrien mostrar-se incorrectament com a «No instal·lada».\nPer solventar-ho reinstal·la-les a través d'Obtainium.\nAixò no hauria d'afectar les dades de les aplicacions.\n\nNomés afecta els URL i els mètodes d'importació de tercers.",
|
||||
"importErrors": "Errors d'importació",
|
||||
"importedXOfYApps": "{} de {} aplicacions importades.",
|
||||
"followingURLsHadErrors": "Els següents URLs han tingut problemes:",
|
||||
"selectURL": "Selecciona URL",
|
||||
"selectURLs": "Selecciona URLs",
|
||||
"pick": "Escull",
|
||||
"theme": "Tema",
|
||||
"dark": "Fosc",
|
||||
"light": "Clar",
|
||||
"followSystem": "Segueix el sistema",
|
||||
"followSystemThemeExplanation": "Seguir el tema del sistema només és possible si uses aplicacions de tercers",
|
||||
"useBlackTheme": "Fes servir el negre pur en el tema fosc",
|
||||
"appSortBy": "Ordena les aplicacions per",
|
||||
"authorName": "Autor/nom",
|
||||
"nameAuthor": "Nom/Autor",
|
||||
"asAdded": "Per l'ordre en què es van afegir",
|
||||
"appSortOrder": "Per ordre de classificació",
|
||||
"ascending": "Ascendent",
|
||||
"descending": "Descendent",
|
||||
"bgUpdateCheckInterval": "Comprova les actualitzacions en segon pla",
|
||||
"neverManualOnly": "Mai, només manual",
|
||||
"appearance": "Aparença",
|
||||
"showWebInAppView": "Mostra el web d'origen en la vista de l'aplicació",
|
||||
"pinUpdates": "Ancora les actualitzacions al capdamunt de les aplicacions",
|
||||
"updates": "Actualitzacions",
|
||||
"sourceSpecific": "Font específica",
|
||||
"appSource": "Font de l'aplicació",
|
||||
"noLogs": "Cap registre",
|
||||
"appLogs": "Registres de l'aplicació",
|
||||
"close": "Tanca",
|
||||
"share": "Comparteix",
|
||||
"appNotFound": "No s'ha trobat l'aplicació",
|
||||
"obtainiumExportHyphenatedLowercase": "Exportació d'Obtainium",
|
||||
"pickAnAPK": "Escull una APK",
|
||||
"appHasMoreThanOnePackage": "{} té més d'un paquet:",
|
||||
"deviceSupportsXArch": "Aquest dispositiu admet l'aquitectura de CPU: {}.",
|
||||
"deviceSupportsFollowingArchs": "Aquest dispositiu admet les següents arquitectures de CPU:",
|
||||
"warning": "Avís",
|
||||
"sourceIsXButPackageFromYPrompt": "La font de l'aplicació és «{}» però el paquet de l'actualització ve de «{}». Vols continuar?",
|
||||
"updatesAvailable": "Actualitzacions disponibles",
|
||||
"updatesAvailableNotifDescription": "Notifica l'usuari que hi ha actualitzacions per a una o més aplicacions seguides per Obtainium",
|
||||
"noNewUpdates": "No hi ha noves actualitzacions.",
|
||||
"xHasAnUpdate": "{} té una actualització.",
|
||||
"appsUpdated": "Aplicacions actualitzades",
|
||||
"appsNotUpdated": "Error en actualitzar les aplicacions",
|
||||
"appsUpdatedNotifDescription": "Notifica l'usuari que una o més aplicacions s'han actualitzat en segon pla",
|
||||
"xWasUpdatedToY": "{} s'ha actualitzat a {}.",
|
||||
"xWasNotUpdatedToY": "Error en actualitzar {} a {}.",
|
||||
"errorCheckingUpdates": "Error en cercar actualitzacions",
|
||||
"errorCheckingUpdatesNotifDescription": "Una notificació que es mostra quan la comprovació d'actualizacions en segon pla ha fallat",
|
||||
"appsRemoved": "Aplicacions suprimides",
|
||||
"appsRemovedNotifDescription": "Notifica l'usuari que una o més aplicacions s'han suprimit per errors en carregar-les",
|
||||
"xWasRemovedDueToErrorY": "{} s'ha suprimit per aquest error: {}",
|
||||
"completeAppInstallation": "Instal·lació completa de l'aplicació",
|
||||
"obtainiumMustBeOpenToInstallApps": "Obtainium ha d'estar obert per poder instal·lar aplicacions",
|
||||
"completeAppInstallationNotifDescription": "Demana l'usuari de tornar a Obtainium per acabar d'instal·lar una aplicació",
|
||||
"checkingForUpdates": "S'estan cercant actualitzacions...",
|
||||
"checkingForUpdatesNotifDescription": "Notificació temporal que apareix en cercar actualitzacions",
|
||||
"pleaseAllowInstallPerm": "Permet que Obtainium instal·li aplicacions",
|
||||
"trackOnly": "Només per a seguiment",
|
||||
"errorWithHttpStatusCode": "Error {}",
|
||||
"versionCorrectionDisabled": "Correcció de versions desactivada (el plugin sembla que no funciona)",
|
||||
"unknown": "Desconegut",
|
||||
"none": "Cap",
|
||||
"never": "Mai",
|
||||
"latestVersionX": "Última versió: {}",
|
||||
"installedVersionX": "Versió instal·lada: {}",
|
||||
"lastUpdateCheckX": "Última comprovació d'actualització: {}",
|
||||
"remove": "Suprimeix",
|
||||
"yesMarkUpdated": "Sí, marca com a actualitzada",
|
||||
"fdroid": "Repositori oficial F-Droid",
|
||||
"appIdOrName": "ID o nom de l'aplicació",
|
||||
"appId": "ID de l'aplicació",
|
||||
"appWithIdOrNameNotFound": "No s'han trobat aplicacions amb aquest ID o nom",
|
||||
"reposHaveMultipleApps": "Els repositoris poden contenir diverses aplicacions",
|
||||
"fdroidThirdPartyRepo": "Repositori F-Droid de tercers",
|
||||
"install": "Instal·la",
|
||||
"markInstalled": "Marca com a instal·lada",
|
||||
"update": "Actualitza",
|
||||
"markUpdated": "Marca com a actualitzada",
|
||||
"additionalOptions": "Opcions addicionals",
|
||||
"disableVersionDetection": "Desactiva la detecció de versions",
|
||||
"noVersionDetectionExplanation": "Només has d'usar aquesta opció en les aplicacions en què la detecció de versions no funcioni correctament.",
|
||||
"downloadingX": "Descarregant {}",
|
||||
"downloadX": "Descarrega {}",
|
||||
"downloadedX": "Descarregada {}",
|
||||
"releaseAsset": "Recurs actualitzat",
|
||||
"downloadNotifDescription": "Notifica l'usuari del progrés de la descàrrega d'una aplicació",
|
||||
"noAPKFound": "No s'ha trobat l'APK",
|
||||
"noVersionDetection": "No s'han detectat versions",
|
||||
"categorize": "Categoritza",
|
||||
"categories": "Categories",
|
||||
"category": "Categoria",
|
||||
"noCategory": "No hi ha la categoria",
|
||||
"noCategories": "No hi ha les categories",
|
||||
"deleteCategoriesQuestion": "Suprimeixo les categories?",
|
||||
"categoryDeleteWarning": "Totes les aplicacions de les categories suprimides es marcaran com a no categoritzades.",
|
||||
"addCategory": "Afegeix una categoria",
|
||||
"label": "Nom",
|
||||
"language": "Idioma",
|
||||
"copiedToClipboard": "Copiat al porta-retalls",
|
||||
"storagePermissionDenied": "Permís d'emmagatzematge denegat",
|
||||
"selectedCategorizeWarning": "Açò substituirà els paràmetres de categorització per a les aplicacions selecionades.",
|
||||
"filterAPKsByRegEx": "Filtra les APKs per l'expressió regular",
|
||||
"removeFromObtainium": "Suprimeix d'Obtainium",
|
||||
"uninstallFromDevice": "Desinstal·la del dispositiu",
|
||||
"onlyWorksWithNonVersionDetectApps": "Només funciona per a aplicacions amb la detecció de versions desactivada.",
|
||||
"releaseDateAsVersion": "Usa la data de llançament com a cadena de la versió",
|
||||
"releaseTitleAsVersion": "Usa el títol com a cadena de la versió",
|
||||
"releaseDateAsVersionExplanation": "Aquest opció només s'hauria d'usar per a aplicacions en què la detecció de la versió no funciona correctament però disposem de la data de publicació.",
|
||||
"changes": "Canvis",
|
||||
"releaseDate": "Data de publicació",
|
||||
"importFromURLsInFile": "Importa els URLs des d'un fitxer (com ara OPML)",
|
||||
"versionDetectionExplanation": "Concilia la cadena de la versió amb la versió detectada del Sistema Operatiu",
|
||||
"versionDetection": "Detecció de la versió",
|
||||
"standardVersionDetection": "Detecció de la versió estàndard",
|
||||
"groupByCategory": "Agrupa per categories",
|
||||
"autoApkFilterByArch": "Intenta filtrar les APKs per l'aquitectura de la CPU, si és possible",
|
||||
"autoLinkFilterByArch": "Intenta filtrar els enllaços per l'aquitectura de la CPU, si és possible",
|
||||
"overrideSource": "Força la font",
|
||||
"dontShowAgain": "No ho tornis a mostrar",
|
||||
"dontShowTrackOnlyWarnings": "No mostris avisos de les aplicacions 'només per a seguiment'",
|
||||
"dontShowAPKOriginWarnings": "No mostris avisos dels orígens de les APKs",
|
||||
"moveNonInstalledAppsToBottom": "Desplaça les aplicacions no instal·lades al capdavall de les aplicacions",
|
||||
"gitlabPATLabel": "Token d'accés personal a GitLab",
|
||||
"about": "Quant a",
|
||||
"requiresCredentialsInSettings": "{} requereix credencials addicionals (a Paràmetres)",
|
||||
"checkOnStart": "Comprova si hi ha actualitzacions en iniciar Obtainium",
|
||||
"tryInferAppIdFromCode": "Intenta deduir l'ID de l'aplicació des del codi font",
|
||||
"removeOnExternalUninstall": "Suprimeix de forma automàtica les aplicacions desinstal·lades externament",
|
||||
"pickHighestVersionCode": "Selecciona de forma automàtica la versió superior de l'APK",
|
||||
"checkUpdateOnDetailPage": "Comprova les actualitzacions en obrir la pàgina de detalls de l'aplicació",
|
||||
"disablePageTransitions": "Inhabilita les animacions de transició de pàgina",
|
||||
"reversePageTransitions": "Inverteix les animacions de transició de pàgina",
|
||||
"minStarCount": "Nombre mínim d'estrelles",
|
||||
"addInfoBelow": "Afegeix aquesta informació a sota.",
|
||||
"addInfoInSettings": "Afegeix aquesta informació a Paràmetres.",
|
||||
"githubSourceNote": "La limitació de peticions a GitHub es pot evitar amb una clau API.",
|
||||
"sortByLastLinkSegment": "Ordena per 'només el darrer fragment de l'enllaç'",
|
||||
"filterReleaseNotesByRegEx": "Filtra les notes de la publicació de la versió per una expressió regular",
|
||||
"customLinkFilterRegex": "Filtre personalitzat de l'enllaç de l'APK per una expressió regular (Per_defecte '.apk$')",
|
||||
"appsPossiblyUpdated": "S'ha intentat l'actualització de l'aplicació",
|
||||
"appsPossiblyUpdatedNotifDescription": "Notifica l'usuari que les actualitzacions per a una o més aplicacions podrien haver-se fet en segon pla",
|
||||
"xWasPossiblyUpdatedToY": "{} podria haver-se actualitzat a {}.",
|
||||
"enableBackgroundUpdates": "Habilita les actualizacions en segon pla",
|
||||
"backgroundUpdateReqsExplanation": "Les actualitzacions en segon pla és possible que no estiguin disponibles per a totes les aplicacions.",
|
||||
"backgroundUpdateLimitsExplanation": "Les instal·lacions en segon pla reexides només es poden comprovar amb Obtainium obert.",
|
||||
"verifyLatestTag": "Comprova l'etiqueta 'Latest' (última versió)",
|
||||
"intermediateLinkRegex": "Filtra per un enllaç 'intermediari' per anar-hi",
|
||||
"filterByLinkText": "Filtra els enllaços pel text de l'enllaç",
|
||||
"intermediateLinkNotFound": "No s'ha trobat l'enllaç intermediari",
|
||||
"intermediateLink": "Enllaç intermediari",
|
||||
"exemptFromBackgroundUpdates": "Exempta d'actualitzacions en segon pla (si han estat habilitades)",
|
||||
"bgUpdatesOnWiFiOnly": "Inhabilita les actualitzacions en segon pla sense Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Inhabilita les actualitzacions en segon pla quan no s'estigui carregant el mòbil",
|
||||
"autoSelectHighestVersionCode": "Selecciona de forma automàtica la versió més recent de l'APK",
|
||||
"versionExtractionRegEx": "Extracció de la cadena de la versió amb una expressió regular",
|
||||
"trimVersionString": "Retalla la cadena de la versió amb una expressió regular",
|
||||
"matchGroupToUseForX": "Grup de coincidència a usar per a \"{}\"",
|
||||
"matchGroupToUse": "Grup de coincidència a usar per a l'extracció de la cadena de la versió amb una expressió regular",
|
||||
"highlightTouchTargets": "Ressalta els elements de selecció menys obvis",
|
||||
"pickExportDir": "Selecciona el directori d'exportació",
|
||||
"autoExportOnChanges": "Exporta automàticament quan hi hagi canvis",
|
||||
"includeSettings": "Inclou paràmetres",
|
||||
"filterVersionsByRegEx": "Filtra les versions per una expressió regular",
|
||||
"trySelectingSuggestedVersionCode": "Prova a seleccionar la versió de l'APK suggerida",
|
||||
"dontSortReleasesList": "Mantén l'ordre de publicació de l'API",
|
||||
"reverseSort": "Ordre invers",
|
||||
"takeFirstLink": "Usa el primer enllaç",
|
||||
"skipSort": "Omet l'ordre",
|
||||
"debugMenu": "Menú de depuració",
|
||||
"bgTaskStarted": "S'ha iniciat la tasca en segon pla (revisa-ho als registres).",
|
||||
"runBgCheckNow": "Executa la comprovació d'actualitzacions en segon pla",
|
||||
"versionExtractWholePage": "Aplica l'extracció de la cadena de la versió amb una expressió regular a tota la pàgina",
|
||||
"installing": "Instal·lant",
|
||||
"skipUpdateNotifications": "No notifiquis les actualitzacions",
|
||||
"updatesAvailableNotifChannel": "Actualitzacions disponibles",
|
||||
"appsUpdatedNotifChannel": "Aplicacions actualitzades",
|
||||
"appsPossiblyUpdatedNotifChannel": "S'ha intentat actualitzar l'aplicació",
|
||||
"errorCheckingUpdatesNotifChannel": "Error en cercar actualitzacions",
|
||||
"appsRemovedNotifChannel": "Aplicacions suprimides",
|
||||
"downloadingXNotifChannel": "Descarregant {}",
|
||||
"completeAppInstallationNotifChannel": "Instal·lació finalitzada",
|
||||
"checkingForUpdatesNotifChannel": "S'estan cercant actualitzacions",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Comprova les actualitzacions només per a aplicacions instal·lades o en seguiment",
|
||||
"supportFixedAPKURL": "Suport per als URLs fixos de l'APK",
|
||||
"selectX": "Selecciona {}",
|
||||
"parallelDownloads": "Permet les descàrregues paralel·les",
|
||||
"useShizuku": "Usa Shizuku o Sui per instal·lar",
|
||||
"shizukuBinderNotFound": "Shizuku no s'està executant",
|
||||
"shizukuOld": "Versió antiga de Shizuku (<11) - Actualitza-la",
|
||||
"shizukuOldAndroidWithADB": "Shizuku s'executa en Android < 8.1 amb ADB - Actualitza Android o usa Sui com a alternativa",
|
||||
"shizukuPretendToBeGooglePlay": "Defineix Google Play com a font d'instal·lació (si uses Shizuku)",
|
||||
"useSystemFont": "Usa la font del sistema",
|
||||
"useVersionCodeAsOSVersion": "Usa la versió de l'aplicació com a versió detectada del Sistema Operatiu",
|
||||
"requestHeader": "Capçalera de sol·licitud",
|
||||
"useLatestAssetDateAsReleaseDate": "Usa el darrer recurs carregat com a data de llançament",
|
||||
"defaultPseudoVersioningMethod": "Mètode de pseudo-versionat predeterminat",
|
||||
"partialAPKHash": "Hash de l'APK parcial",
|
||||
"APKLinkHash": "Hash de l'enllaç de l'APK",
|
||||
"directAPKLink": "Enllaç de l'APK directe",
|
||||
"pseudoVersionInUse": "S'està usant una pseudo-versió",
|
||||
"installed": "Instal·lada",
|
||||
"latest": "Versió més recent",
|
||||
"invertRegEx": "Inverteix l'expressió regular",
|
||||
"note": "Nota",
|
||||
"selfHostedNote": "El desplegable «{}» es pot usar per accedir a instàncies autoallotjades/personalitzades de qualsevol font.",
|
||||
"badDownload": "L'APK no s'ha pogut analitzar (incompatible o descàrrega parcial)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Comparteix les aplicacions noves amb AppVerifier (si està instal·lat)",
|
||||
"appVerifierInstructionToast": "Comparteix amb AppVerifier i torna aquí quan estigui fet.",
|
||||
"wiki": "Ajuda/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Configuració de les aplicacions crowdsourcing (usa-ho sota la teva responsabilitat)",
|
||||
"crowdsourcedConfigsShort": "Configuració de les aplicacions crowdsourcing",
|
||||
"allowInsecure": "Permet les sol·licituds HTTP insegures",
|
||||
"stayOneVersionBehind": "Roman a la versió anterior a l'última",
|
||||
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
|
||||
"refreshBeforeDownload": "Actualitza les dades de l'aplicació abans de descarregar-la",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nom",
|
||||
"smartname": "Nom (smart)",
|
||||
"sortMethod": "Mètode d'ordenació",
|
||||
"welcome": "Benvinguda",
|
||||
"documentationLinksNote": "La pàgina GitHub d'Obtainium enllaçada a sota conté enllaços a vídeos, articles, debats i altres recursos que t'ajudaran a entendre com usar l'aplicació.",
|
||||
"batteryOptimizationNote": "Tingues present que les descàrregues en segon pla funcionaran millor si inhabilites l'optimització de bateria per a Obtainium.",
|
||||
"fileDeletionError": "No s'ha pogut suprimir el fitxer (intenta suprimir-lo manualment i torna-ho a provar): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "¿Suprimeixo l'aplicació?",
|
||||
"other": "¿Suprimeixo les aplicacions?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Massa peticions (límit excedit), torna-hi en {} minut",
|
||||
"other": "Massa peticions (límit excedit), torna-hi en {} minuts"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "La comprovació d'actualitzacions en segon pla ha trobat un {}, es tornarà a provar en {} minut",
|
||||
"other": "La comprovació d'actualitzacions en segon pla ha trobat un {}, es tornarà a provar en {} minuts"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "La comprovació d'actualitzacions en segon pla ha trobat {} actualització, t'ho notificarem si cal",
|
||||
"other": "La comprovació d'actualitzacions en segon pla ha trobat {} actualitzacions, t'ho notificarem si cal"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} Aplicació",
|
||||
"other": "{} Aplicacions"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL",
|
||||
"other": "{} URLs"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} Minut",
|
||||
"other": "{} Minuts"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} Hora",
|
||||
"other": "{} Hores"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} Dia",
|
||||
"other": "{} Dies"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "Suprimit {n} registre (anterior a = {before}, posterior a = {after})",
|
||||
"other": "Suprimits {n} registres (anteriors a = {before}, posteriors a = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} i 1 aplicació més tenen actualitzacions.",
|
||||
"other": "{} i {} aplicacions més tenen actualitzacions."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} i 1 aplicació més s'han actualitzat.",
|
||||
"other": "{} i {} aplicacions més s'han actualitzat."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "Error en actualitzar {} i 1 aplicació més.",
|
||||
"other": "No s'ha pogut actualizar {} i {} aplicacions més."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} i 1 aplicació més podrien haver estat actualizades.",
|
||||
"other": "{} i {} aplicacions més podrien haver estat actualitzades."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
|
||||
"cancelled": "Zrušeno",
|
||||
"appAlreadyAdded": "Aplikace již přidána",
|
||||
"alreadyUpToDateQuestion": "App already up to date?",
|
||||
"alreadyUpToDateQuestion": "Aplikace je již aktualizovaná?",
|
||||
"addApp": "Přidat aplikaci",
|
||||
"appSourceURL": "Zdrojová adresa URL aplikace",
|
||||
"error": "Chyba",
|
||||
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Standardní detekce verze",
|
||||
"groupByCategory": "Seskupit podle kategorie",
|
||||
"autoApkFilterByArch": "Pokud je to možné, pokuste se filtrovat soubory APK podle architektury procesoru",
|
||||
"autoLinkFilterByArch": "Pokus o filtrování odkazů podle architektury procesoru, pokud je to možné.",
|
||||
"overrideSource": "Přepsat zdroj",
|
||||
"dontShowAgain": "Nezobrazovat znovu",
|
||||
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Konfigurace aplikací s využitím crowdsourcingu",
|
||||
"allowInsecure": "Povolení nezabezpečených požadavků HTTP",
|
||||
"stayOneVersionBehind": "Zůstaňte o jednu verzi pozadu za nejnovější",
|
||||
"useFirstApkOfVersion": "Automatický výběr prvního z více souborů APK",
|
||||
"refreshBeforeDownload": "Obnovení údajů o aplikaci před stažením",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Název",
|
||||
"smartname": "Název (Smart)",
|
||||
"sortMethod": "Metoda třídění",
|
||||
"welcome": "Vítejte na",
|
||||
"documentationLinksNote": "Níže odkazovaná stránka Obtainium GitHub obsahuje odkazy na videa, články, diskuse a další zdroje, které vám pomohou pochopit, jak aplikaci používat.",
|
||||
"batteryOptimizationNote": "Všimněte si, že stahování na pozadí může fungovat spolehlivěji, pokud vypnete optimalizaci baterie operačního systému pro Obtainium.",
|
||||
"fileDeletionError": "Soubor se nepodařilo odstranit (zkuste jej odstranit ručně a pak to zkuste znovu): \"{}\"",
|
||||
"foregroundService": "Služba Obtainium na popředí",
|
||||
"foregroundServiceExplanation": "Použití služby v popředí pro kontrolu aktualizací (spolehlivější, spotřebovává více energie).",
|
||||
"fgServiceNotice": "Toto oznámení je nutné pro kontrolu aktualizací na pozadí (lze jej skrýt v nastavení operačního systému).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Odstranit Apku?",
|
||||
"other": "Odstranit Apky?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Standard versionsregistrering",
|
||||
"groupByCategory": "Gruppér efter kategori",
|
||||
"autoApkFilterByArch": "Forsøg at filtrere APK'er efter CPU-arkitektur, hvis muligt",
|
||||
"autoLinkFilterByArch": "Forsøg at filtrere links efter CPU-arkitektur, hvis det er muligt",
|
||||
"overrideSource": "Tilsidesæt kilde",
|
||||
"dontShowAgain": "Vis ikke igen",
|
||||
"dontShowTrackOnlyWarnings": "Vis ikke 'Følg Kun'-advarsler",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Crowdsourcede app-konfigurationer",
|
||||
"allowInsecure": "Tillad usikre HTTP-anmodninger",
|
||||
"stayOneVersionBehind": "Forbliv én version bagud den seneste",
|
||||
"useFirstApkOfVersion": "Vælg automatisk den første af flere APK'er",
|
||||
"refreshBeforeDownload": "Opdater app-detaljer før download",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Navn",
|
||||
"smartname": "Navn (Smart)",
|
||||
"sortMethod": "Sorteringsmetode",
|
||||
"welcome": "Velkommen",
|
||||
"documentationLinksNote": "Obtainiums GitHub-side, som der linkes til nedenfor, indeholder links til videoer, artikler, diskussioner og andre ressourcer, som kan hjælpe dig med at forstå, hvordan du bruger appen.",
|
||||
"batteryOptimizationNote": "Bemærk, at baggrundsdownloads kan fungere mere pålideligt, hvis du deaktiverer OS-batterioptimering for Obtainium.",
|
||||
"fileDeletionError": "Kunne ikke slette filen (prøv at slette den manuelt og prøv igen): \"{}\"",
|
||||
"foregroundService": "Obtainium forgrundstjeneste",
|
||||
"foregroundServiceExplanation": "Brug en forgrundstjeneste til opdateringskontrol (mere pålidelig, bruger mere strøm)",
|
||||
"fgServiceNotice": "Denne meddelelse er nødvendig for baggrundsopdateringskontrol (den kan skjules i OS-indstillingerne).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Fjern app?",
|
||||
"other": "Fjern apps?"
|
||||
|
@ -19,7 +19,7 @@
|
||||
"noDescription": "Keine Beschreibung",
|
||||
"cancel": "Abbrechen",
|
||||
"continue": "Weiter",
|
||||
"requiredInBrackets": "(wird benötigt)",
|
||||
"requiredInBrackets": "(notwendig)",
|
||||
"dropdownNoOptsError": "FEHLER: DROPDOWN MUSS MINDESTENS EINE OPTION HABEN",
|
||||
"colour": "Farbe",
|
||||
"standard": "Standard",
|
||||
@ -32,7 +32,7 @@
|
||||
"source": "Quelle",
|
||||
"app": "App",
|
||||
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zur Nachverfolgung.",
|
||||
"youPickedTrackOnly": "Du hast die Option „Nur nachverfolgen“ gewählt.",
|
||||
"youPickedTrackOnly": "Sie haben die Option „Nur nachverfolgen“ gewählt.",
|
||||
"trackOnlyAppDescription": "Die App wird auf neue verfügbare Versionen überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
|
||||
"cancelled": "Abgebrochen",
|
||||
"appAlreadyAdded": "App bereits hinzugefügt",
|
||||
@ -46,7 +46,7 @@
|
||||
"additionalOptsFor": "Zusatzoptionen für {}",
|
||||
"supportedSources": "Unterstützte Quellen",
|
||||
"trackOnlyInBrackets": "(Nur nachverfolgen)",
|
||||
"searchableInBrackets": "(Durchsuchbar)",
|
||||
"searchableInBrackets": "(durchsuchbar)",
|
||||
"appsString": "Apps",
|
||||
"noApps": "Keine Apps",
|
||||
"noAppsForFilter": "Keine Apps für ausgewählten Filter",
|
||||
@ -98,26 +98,26 @@
|
||||
"importFromURLList": "Aus URL-Liste importieren",
|
||||
"searchQuery": "Suchanfrage",
|
||||
"appURLList": "App-URL-Liste",
|
||||
"line": "Linie",
|
||||
"line": "Zeile",
|
||||
"searchX": "{} suchen",
|
||||
"noResults": "Keine Ergebnisse gefunden",
|
||||
"importX": "{} importieren",
|
||||
"importedAppsIdDisclaimer": "Importierte Apps werden manchmal fälschlicherweise als „Nicht installiert“ angezeigt. Um dies zu beheben, installiere sie erneut über Obtainium. Dies hat keine Auswirkungen auf App-Daten. Es betrifft nur URL- und Drittanbieter-Importmethoden.",
|
||||
"importedAppsIdDisclaimer": "Importierte Apps werden manchmal fälschlicherweise als „Nicht installiert“ angezeigt. Um dies zu beheben, installieren Sie sie erneut über Obtainium. Dies hat keine Auswirkungen auf App-Daten. Es betrifft nur URL- und Drittanbieter-Importmethoden.",
|
||||
"importErrors": "Importfehler",
|
||||
"importedXOfYApps": "{} von {} Apps importiert.",
|
||||
"followingURLsHadErrors": "Bei folgenden URLs traten Fehler auf:",
|
||||
"selectURL": "URL auswählen",
|
||||
"selectURLs": "URLs auswählen",
|
||||
"pick": "Auswählen",
|
||||
"theme": "Design",
|
||||
"theme": "Erscheinungsbild",
|
||||
"dark": "Dunkel",
|
||||
"light": "Hell",
|
||||
"followSystem": "Systemstandard",
|
||||
"followSystemThemeExplanation": "Das Abrufen des Systemdesigns ist unter Android < 10 nur mit Hilfe von Drittanbieterapps möglich",
|
||||
"useBlackTheme": "Rein schwarzen Hintergrund verwenden",
|
||||
"appSortBy": "App sortieren nach",
|
||||
"authorName": "Hrsg./Name",
|
||||
"nameAuthor": "Name/Hrsg.",
|
||||
"authorName": "Herausgebende/Name",
|
||||
"nameAuthor": "Name/Herausgebende",
|
||||
"asAdded": "Wie hinzugefügt",
|
||||
"appSortOrder": "App sortieren nach",
|
||||
"ascending": "Aufsteigend",
|
||||
@ -138,12 +138,12 @@
|
||||
"obtainiumExportHyphenatedLowercase": "Obtainium-Export",
|
||||
"pickAnAPK": "APK auswählen",
|
||||
"appHasMoreThanOnePackage": "{} verfügt über mehr als ein Paket:",
|
||||
"deviceSupportsXArch": "Dein Gerät unterstützt die CPU-Architektur {}.",
|
||||
"deviceSupportsFollowingArchs": "Dein Gerät unterstützt die folgenden CPU-Architekturen:",
|
||||
"deviceSupportsXArch": "Ihr Gerät unterstützt die CPU-Architektur {}.",
|
||||
"deviceSupportsFollowingArchs": "Ihr Gerät unterstützt die folgenden CPU-Architekturen:",
|
||||
"warning": "Warnung",
|
||||
"sourceIsXButPackageFromYPrompt": "Die App-Quelle ist '{}', aber das Release-Paket stammt von '{}'. Fortfahren?",
|
||||
"updatesAvailable": "Aktualisierungen verfügbar",
|
||||
"updatesAvailableNotifDescription": "Benachrichtigt, wenn Aktualisierungen für eine oder mehrere von Obtainium verfolgte Apps verfügbar sind",
|
||||
"updatesAvailableNotifDescription": "Benachrichtigt, wenn Aktualisierungen für eine oder mehrere von Obtainium überwachte Apps verfügbar sind",
|
||||
"noNewUpdates": "Keine neuen Aktualisierungen.",
|
||||
"xHasAnUpdate": "{} hat eine Aktualisierung.",
|
||||
"appsUpdated": "App wurde aktualisiert",
|
||||
@ -161,7 +161,7 @@
|
||||
"completeAppInstallationNotifDescription": "Aufforderung zu Obtainium zurückzukehren, um die Installation einer App abzuschließen",
|
||||
"checkingForUpdates": "Nach Aktualisierungen suchen",
|
||||
"checkingForUpdatesNotifDescription": "Vorübergehende Benachrichtigung, die bei der Suche nach Aktualisierungen angezeigt wird",
|
||||
"pleaseAllowInstallPerm": "Bitte erlaube Obtainium die Installation von Apps",
|
||||
"pleaseAllowInstallPerm": "Bitte erlauben Sie Obtainium die Installation von Apps",
|
||||
"trackOnly": "Nur nachverfolgen",
|
||||
"errorWithHttpStatusCode": "Fehler {}",
|
||||
"versionCorrectionDisabled": "Versionskorrektur deaktiviert (Plugin scheint nicht zu funktionieren)",
|
||||
@ -189,8 +189,8 @@
|
||||
"downloadingX": "{} wird heruntergeladen",
|
||||
"downloadX": "{} herunterladen",
|
||||
"downloadedX": "{} heruntergeladen",
|
||||
"releaseAsset": "Release-Asset",
|
||||
"downloadNotifDescription": "Zeigt den Fortschritt beim Herunterladen einer App",
|
||||
"releaseAsset": "Release-Anhang",
|
||||
"downloadNotifDescription": "Fortschrittsanzeige beim Herunterladen einer App",
|
||||
"noAPKFound": "Keine APK gefunden",
|
||||
"noVersionDetection": "Keine Versionserkennung",
|
||||
"categorize": "Kategorisieren",
|
||||
@ -221,15 +221,16 @@
|
||||
"standardVersionDetection": "Standardversionserkennung",
|
||||
"groupByCategory": "Nach Kategorie gruppieren",
|
||||
"autoApkFilterByArch": "Nach Möglichkeit versuchen, APKs nach CPU-Architektur zu filtern",
|
||||
"autoLinkFilterByArch": "Nach Möglichkeit versuchen, APKs nach CPU-Architektur zu filtern",
|
||||
"overrideSource": "Quelle überschreiben",
|
||||
"dontShowAgain": "Nicht noch einmal anzeigen",
|
||||
"dontShowTrackOnlyWarnings": "Warnung für „Nur nachverfolgen“ nicht anzeigen",
|
||||
"dontShowTrackOnlyWarnings": "Warnung für ‚Nur nachverfolgen‘ nicht anzeigen",
|
||||
"dontShowAPKOriginWarnings": "Warnung für APK-Herkunft nicht anzeigen",
|
||||
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der App-Ansicht verschieben",
|
||||
"gitlabPATLabel": "Persönlicher Zugangstoken für GitLab",
|
||||
"about": "Über",
|
||||
"about": "Weitere Informationen",
|
||||
"requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
|
||||
"checkOnStart": "Einmalig beim Start überprüfen",
|
||||
"checkOnStart": "Einmalig beim Start auf Aktualisierungen prüfen",
|
||||
"tryInferAppIdFromCode": "Versuchen, die App-ID aus dem Quellcode zu ermitteln",
|
||||
"removeOnExternalUninstall": "Extern deinstallierte Apps automatische entfernen",
|
||||
"pickHighestVersionCode": "Automatische Auswahl des APK mit höchstem Versionscode",
|
||||
@ -245,7 +246,7 @@
|
||||
"customLinkFilterRegex": "Benutzerdefinierter APK-Linkfilter durch regulären Ausdruck (Standard '.apk$')",
|
||||
"appsPossiblyUpdated": "App-Aktualisierungen wurden versucht",
|
||||
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt, dass möglicherweise eine oder mehrere Apps im Hintergrund aktualisiert wurden",
|
||||
"xWasPossiblyUpdatedToY": "{} wurde eventuell auf Version {} aktualisiert.",
|
||||
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise auf Version {} aktualisiert.",
|
||||
"enableBackgroundUpdates": "Hintergrundaktualisierungen aktivieren",
|
||||
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist unter Umständen nicht für alle Apps möglich.",
|
||||
"backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
|
||||
@ -286,19 +287,19 @@
|
||||
"downloadingXNotifChannel": "{} herunterladen",
|
||||
"completeAppInstallationNotifChannel": "App-Installation abschließen",
|
||||
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Nur installierte und mit „Nur nachverfolgen“ markierte Apps auf Aktualisierungen prüfen",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Nur mit „Nur nachverfolgen“ markierte und installierte Apps auf Aktualisierungen prüfen",
|
||||
"supportFixedAPKURL": "Unterstützung von festen APK-URLs",
|
||||
"selectX": "{} wählen",
|
||||
"parallelDownloads": "Parallele Downloads erlauben",
|
||||
"useShizuku": "Shizuku oder Sui zur Installation verwenden",
|
||||
"shizukuBinderNotFound": "Kompatibler Shizuku-Dienst wurde nicht gefunden",
|
||||
"shizukuOld": "Veraltete Shizuku-Version (< 11) - bitte aktualisiere sie",
|
||||
"shizukuOldAndroidWithADB": "Shizuku läuft auf Android < 8.1 mit ADB - aktualisiere die Android-Version oder verwende stattdessen Sui",
|
||||
"shizukuOld": "Veraltete Shizuku-Version (< 11) – bitte aktualisieren Sie sie",
|
||||
"shizukuOldAndroidWithADB": "Shizuku läuft auf Android < 8.1 mit ADB – aktualisieren Sie die Android-Version oder verwenden Sie stattdessen Sui",
|
||||
"shizukuPretendToBeGooglePlay": "(Mittels Shizuku) Google Play als Installationsquelle registrieren",
|
||||
"useSystemFont": "Systemschriftart verwenden",
|
||||
"useVersionCodeAsOSVersion": "Versionscode (versionCode) als erkannte Version vom Betriebssystem verwenden",
|
||||
"useVersionCodeAsOSVersion": "Versionscode (versionCode) als vom Betriebssystem erkannte Version verwenden",
|
||||
"requestHeader": "Kopfzeile („Header“) anfordern",
|
||||
"useLatestAssetDateAsReleaseDate": "Letzten Asset-Upload als Veröffentlichungsdatum verwenden",
|
||||
"useLatestAssetDateAsReleaseDate": "Letzten Upload eines Release-Anhangs als Veröffentlichungsdatum verwenden",
|
||||
"defaultPseudoVersioningMethod": "Standardmäßiges Verfahren zur Pseudo-Versionierung",
|
||||
"partialAPKHash": "Partieller APK-Hash",
|
||||
"APKLinkHash": "APK-Link-Hash",
|
||||
@ -317,15 +318,28 @@
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App-Konfigurationen",
|
||||
"allowInsecure": "Unsichere HTTP-Anfragen zulassen",
|
||||
"stayOneVersionBehind": "Eine Version hinter der neuesten Version bleiben",
|
||||
"useFirstApkOfVersion": "Automatische Auswahl der ersten von mehreren APKs",
|
||||
"refreshBeforeDownload": "App-Details vor dem Download aktualisieren",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (China)",
|
||||
"name": "Name",
|
||||
"smartname": "Name (Smart)",
|
||||
"sortMethod": "Sortierverfahren",
|
||||
"welcome": "Willkommen",
|
||||
"documentationLinksNote": "Die unten verlinkte GitHub-Seite von Obtainium enthält Links zu Videos, Artikeln, Diskussionen und anderen Ressourcen, die Ihnen helfen werden, die Verwendung der App zu verstehen.",
|
||||
"batteryOptimizationNote": "Beachten Sie, dass Downloads im Hintergrund möglicherweise zuverlässiger funktionieren, wenn Sie die Batterieoptimierung des Betriebssystems für Obtainium deaktivieren.",
|
||||
"fileDeletionError": "Die Datei konnte nicht gelöscht werden (versuchen Sie, sie manuell zu löschen und versuchen Sie es dann erneut): \"{}\"",
|
||||
"foregroundService": "Obtainium-Vordergrunddienst",
|
||||
"foregroundServiceExplanation": "Verwendung eines Dienstes im Vordergrund für die Aktualisierungsprüfung (zuverlässiger, verbraucht mehr Strom)",
|
||||
"fgServiceNotice": "Diese Benachrichtigung ist für die Prüfung von Updates im Hintergrund erforderlich (sie kann in den Betriebssystemeinstellungen ausgeblendet werden)",
|
||||
"removeAppQuestion": {
|
||||
"one": "App entfernen?",
|
||||
"other": "Apps entfernen?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Zu viele Anfragen (Rate begrenzt) – versuche es in {} Minute erneut",
|
||||
"other": "Zu viele Anfragen (Rate begrenzt) – versuche es in {} Minuten erneut"
|
||||
"one": "Zu viele Anfragen (Rate begrenzt) – versuchen Sie es in {} Minute erneut",
|
||||
"other": "Zu viele Anfragen (Rate begrenzt) – versuchen Sie es in {} Minuten erneut"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Norma versiodetekto",
|
||||
"groupByCategory": "Grupigi per kategorio",
|
||||
"autoApkFilterByArch": "Provi filtri APKj per CPU-arkitekturo se ebla",
|
||||
"autoLinkFilterByArch": "Attempt to filter links by CPU architecture if possible",
|
||||
"overrideSource": "Substitui la fonton",
|
||||
"dontShowAgain": "Ne plu montri",
|
||||
"dontShowTrackOnlyWarnings": "Ne plu montri 'Nur sekvita' avertojn",
|
||||
@ -314,11 +315,24 @@
|
||||
"appVerifierInstructionToast": "Diskonigu kun AppVerifier, poste revenu ĉi tie kiam preta.",
|
||||
"wiki": "Helpo/Vikio",
|
||||
"crowdsourcedConfigsLabel": "Komunumaj apo-agordoj (uzu kun singardo)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configs",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configurations",
|
||||
"allowInsecure": "Allow insecure HTTP requests",
|
||||
"stayOneVersionBehind": "Stay one version behind latest",
|
||||
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
|
||||
"refreshBeforeDownload": "Refresh app details before download",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Name",
|
||||
"smartname": "Name (Smart)",
|
||||
"sortMethod": "Sort Method",
|
||||
"welcome": "Welcome",
|
||||
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.",
|
||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
||||
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Forigi la aplikaĵon?",
|
||||
"other": "Forigi la aplikaĵojn?"
|
||||
|
@ -1,110 +1,110 @@
|
||||
{
|
||||
"invalidURLForSource": "Not a valid {} App URL",
|
||||
"invalidURLForSource": "Not a valid {} app URL",
|
||||
"noReleaseFound": "Could not find a suitable release",
|
||||
"noVersionFound": "Could not determine release version",
|
||||
"urlMatchesNoSource": "URL does not match a known source",
|
||||
"cantInstallOlderVersion": "Cannot install an older version of an App",
|
||||
"appIdMismatch": "Downloaded package ID does not match existing App ID",
|
||||
"cantInstallOlderVersion": "Cannot install an older version of an app",
|
||||
"appIdMismatch": "Downloaded package ID does not match existing app ID",
|
||||
"functionNotImplemented": "This class has not implemented this function",
|
||||
"placeholder": "Placeholder",
|
||||
"someErrors": "Some Errors Occurred",
|
||||
"unexpectedError": "Unexpected Error",
|
||||
"someErrors": "Some errors occurred",
|
||||
"unexpectedError": "Unexpected error",
|
||||
"ok": "Okay",
|
||||
"and": "and",
|
||||
"githubPATLabel": "GitHub Personal Access Token (Increases Rate Limit)",
|
||||
"githubPATLabel": "GitHub personal access token (increases rate limit)",
|
||||
"includePrereleases": "Include prereleases",
|
||||
"fallbackToOlderReleases": "Fallback to older releases",
|
||||
"filterReleaseTitlesByRegEx": "Filter Release Titles by Regular Expression",
|
||||
"filterReleaseTitlesByRegEx": "Filter release titles by regular expression",
|
||||
"invalidRegEx": "Invalid regular expression",
|
||||
"noDescription": "No description",
|
||||
"cancel": "Cancel",
|
||||
"continue": "Continue",
|
||||
"requiredInBrackets": "(Required)",
|
||||
"requiredInBrackets": "(required)",
|
||||
"dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT",
|
||||
"colour": "Colour",
|
||||
"standard": "Standard",
|
||||
"custom": "Custom",
|
||||
"useMaterialYou": "Use Material You",
|
||||
"githubStarredRepos": "GitHub Starred Repos",
|
||||
"githubStarredRepos": "GitHub starred repos",
|
||||
"uname": "Username",
|
||||
"wrongArgNum": "Wrong number of arguments provided",
|
||||
"xIsTrackOnly": "{} is Track-Only",
|
||||
"xIsTrackOnly": "{} is track-only",
|
||||
"source": "Source",
|
||||
"app": "App",
|
||||
"appsFromSourceAreTrackOnly": "Apps from this source are 'Track-Only'.",
|
||||
"youPickedTrackOnly": "You have selected the 'Track-Only' option.",
|
||||
"trackOnlyAppDescription": "The App will be tracked for updates, but Obtainium will not be able to download or install it.",
|
||||
"appsFromSourceAreTrackOnly": "Apps from this source are 'track-only'.",
|
||||
"youPickedTrackOnly": "You have selected the 'track-only' option.",
|
||||
"trackOnlyAppDescription": "The app will be tracked for updates, but Obtainium will not be able to download or install it.",
|
||||
"cancelled": "Cancelled",
|
||||
"appAlreadyAdded": "App already added",
|
||||
"alreadyUpToDateQuestion": "App Already up to Date?",
|
||||
"addApp": "Add App",
|
||||
"appSourceURL": "App Source URL",
|
||||
"alreadyUpToDateQuestion": "App already up to date?",
|
||||
"addApp": "Add app",
|
||||
"appSourceURL": "App source URL",
|
||||
"error": "Error",
|
||||
"add": "Add",
|
||||
"searchSomeSourcesLabel": "Search (Some Sources Only)",
|
||||
"searchSomeSourcesLabel": "Search (some sources only)",
|
||||
"search": "Search",
|
||||
"additionalOptsFor": "Additional Options for {}",
|
||||
"supportedSources": "Supported Sources",
|
||||
"trackOnlyInBrackets": "(Track-Only)",
|
||||
"searchableInBrackets": "(Searchable)",
|
||||
"additionalOptsFor": "Additional options for {}",
|
||||
"supportedSources": "Supported sources",
|
||||
"trackOnlyInBrackets": "(track-only)",
|
||||
"searchableInBrackets": "(searchable)",
|
||||
"appsString": "Apps",
|
||||
"noApps": "No Apps",
|
||||
"noAppsForFilter": "No Apps for Filter",
|
||||
"noApps": "No apps",
|
||||
"noAppsForFilter": "No apps for filter",
|
||||
"byX": "By {}",
|
||||
"percentProgress": "Progress: {}%",
|
||||
"pleaseWait": "Please Wait",
|
||||
"updateAvailable": "Update Available",
|
||||
"notInstalled": "Not Installed",
|
||||
"pleaseWait": "Please wait",
|
||||
"updateAvailable": "Update available",
|
||||
"notInstalled": "Not installed",
|
||||
"pseudoVersion": "pseudo-version",
|
||||
"selectAll": "Select All",
|
||||
"selectAll": "Select all",
|
||||
"deselectX": "Deselect {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.",
|
||||
"removeSelectedAppsQuestion": "Remove Selected Apps?",
|
||||
"removeSelectedApps": "Remove Selected Apps",
|
||||
"removeSelectedAppsQuestion": "Remove selected apps?",
|
||||
"removeSelectedApps": "Remove selected apps",
|
||||
"updateX": "Update {}",
|
||||
"installX": "Install {}",
|
||||
"markXTrackOnlyAsUpdated": "Mark {}\n(Track-Only)\nas Updated",
|
||||
"markXTrackOnlyAsUpdated": "Mark {}\n(track-only)\nas updated",
|
||||
"changeX": "Change {}",
|
||||
"installUpdateApps": "Install/Update Apps",
|
||||
"installUpdateSelectedApps": "Install/Update Selected Apps",
|
||||
"markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?",
|
||||
"installUpdateApps": "Install/update apps",
|
||||
"installUpdateSelectedApps": "Install/update selected apps",
|
||||
"markXSelectedAppsAsUpdated": "Mark {} selected apps as updated?",
|
||||
"no": "No",
|
||||
"yes": "Yes",
|
||||
"markSelectedAppsUpdated": "Mark Selected Apps as Updated",
|
||||
"markSelectedAppsUpdated": "Mark selected apps as updated",
|
||||
"pinToTop": "Pin to top",
|
||||
"unpinFromTop": "Unpin from top",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Reset Install Status for Selected Apps?",
|
||||
"installStatusOfXWillBeResetExplanation": "The install status of any selected Apps will be reset.\n\nThis can help when the App version shown in Obtainium is incorrect due to failed updates or other issues.",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Reset install status for selected apps?",
|
||||
"installStatusOfXWillBeResetExplanation": "The install status of any selected apps will be reset.\n\nThis can help when the app version shown in Obtainium is incorrect due to failed updates or other issues.",
|
||||
"customLinkMessage": "These links work on devices with Obtainium installed",
|
||||
"shareAppConfigLinks": "Share app configuration as HTML link",
|
||||
"shareSelectedAppURLs": "Share Selected App URLs",
|
||||
"resetInstallStatus": "Reset Install Status",
|
||||
"shareSelectedAppURLs": "Share selected app URLs",
|
||||
"resetInstallStatus": "Reset install status",
|
||||
"more": "More",
|
||||
"removeOutdatedFilter": "Remove Out-of-Date App Filter",
|
||||
"showOutdatedOnly": "Show Out-of-Date Apps Only",
|
||||
"removeOutdatedFilter": "Remove out-of-date app filter",
|
||||
"showOutdatedOnly": "Show out-of-date apps only",
|
||||
"filter": "Filter",
|
||||
"filterApps": "Filter Apps",
|
||||
"appName": "App Name",
|
||||
"filterApps": "Filter apps",
|
||||
"appName": "App name",
|
||||
"author": "Author",
|
||||
"upToDateApps": "Up to Date Apps",
|
||||
"nonInstalledApps": "Non-Installed Apps",
|
||||
"importExport": "Import/Export",
|
||||
"upToDateApps": "Up to date apps",
|
||||
"nonInstalledApps": "Non-installed apps",
|
||||
"importExport": "Import/export",
|
||||
"settings": "Settings",
|
||||
"exportedTo": "Exported to {}",
|
||||
"obtainiumExport": "Obtainium Export",
|
||||
"obtainiumExport": "Obtainium export",
|
||||
"invalidInput": "Invalid input",
|
||||
"importedX": "Imported {}",
|
||||
"obtainiumImport": "Obtainium Import",
|
||||
"importFromURLList": "Import from URL List",
|
||||
"searchQuery": "Search Query",
|
||||
"appURLList": "App URL List",
|
||||
"obtainiumImport": "Obtainium import",
|
||||
"importFromURLList": "Import from URL list",
|
||||
"searchQuery": "Search query",
|
||||
"appURLList": "App URL list",
|
||||
"line": "Line",
|
||||
"searchX": "Search {}",
|
||||
"noResults": "No results found",
|
||||
"importX": "Import {}",
|
||||
"importedAppsIdDisclaimer": "Imported Apps may incorrectly show as \"Not Installed\".\nTo fix this, re-install them through Obtainium.\nThis should not affect App data.\n\nOnly affects URL and third-party import methods.",
|
||||
"importErrors": "Import Errors",
|
||||
"importedXOfYApps": "{} of {} Apps imported.",
|
||||
"importedAppsIdDisclaimer": "Imported apps may incorrectly show as \"not installed\".\nTo fix this, re-install them through Obtainium.\nThis should not affect app data.\n\nOnly affects URL and third-party import methods.",
|
||||
"importErrors": "Import errors",
|
||||
"importedXOfYApps": "{} of {} apps imported.",
|
||||
"followingURLsHadErrors": "The following URLs had errors:",
|
||||
"selectURL": "Select URL",
|
||||
"selectURLs": "Select URLs",
|
||||
@ -112,26 +112,26 @@
|
||||
"theme": "Theme",
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
"followSystem": "Follow System",
|
||||
"followSystem": "Follow system",
|
||||
"followSystemThemeExplanation": "Following system theme is possible only by using third-party applications",
|
||||
"useBlackTheme": "Use pure black dark theme",
|
||||
"appSortBy": "App Sort By",
|
||||
"authorName": "Author/Name",
|
||||
"nameAuthor": "Name/Author",
|
||||
"asAdded": "As Added",
|
||||
"appSortOrder": "App Sort Order",
|
||||
"appSortBy": "App sort by",
|
||||
"authorName": "Author/name",
|
||||
"nameAuthor": "Name/author",
|
||||
"asAdded": "As added",
|
||||
"appSortOrder": "App sort order",
|
||||
"ascending": "Ascending",
|
||||
"descending": "Descending",
|
||||
"bgUpdateCheckInterval": "Background Update Checking Interval",
|
||||
"neverManualOnly": "Never - Manual Only",
|
||||
"bgUpdateCheckInterval": "Background update checking interval",
|
||||
"neverManualOnly": "Never - manual only",
|
||||
"appearance": "Appearance",
|
||||
"showWebInAppView": "Show Source webpage in App view",
|
||||
"pinUpdates": "Pin updates to top of Apps view",
|
||||
"showWebInAppView": "Show source webpage in app view",
|
||||
"pinUpdates": "Pin updates to top of apps view",
|
||||
"updates": "Updates",
|
||||
"sourceSpecific": "Source-Specific",
|
||||
"appSource": "App Source",
|
||||
"noLogs": "No Logs",
|
||||
"appLogs": "App Logs",
|
||||
"sourceSpecific": "Source-specific",
|
||||
"appSource": "App source",
|
||||
"noLogs": "No logs",
|
||||
"appLogs": "App logs",
|
||||
"close": "Close",
|
||||
"share": "Share",
|
||||
"appNotFound": "App not found",
|
||||
@ -141,28 +141,28 @@
|
||||
"deviceSupportsXArch": "Your device supports the {} CPU architecture.",
|
||||
"deviceSupportsFollowingArchs": "Your device supports the following CPU architectures:",
|
||||
"warning": "Warning",
|
||||
"sourceIsXButPackageFromYPrompt": "The App source is '{}' but the release package comes from '{}'. Continue?",
|
||||
"updatesAvailable": "Updates Available",
|
||||
"updatesAvailableNotifDescription": "Notifies the user that updates are available for one or more Apps tracked by Obtainium",
|
||||
"sourceIsXButPackageFromYPrompt": "The app source is '{}' but the release package comes from '{}'. Continue?",
|
||||
"updatesAvailable": "Updates available",
|
||||
"updatesAvailableNotifDescription": "Notifies the user that updates are available for one or more apps tracked by Obtainium",
|
||||
"noNewUpdates": "No new updates.",
|
||||
"xHasAnUpdate": "{} has an update.",
|
||||
"appsUpdated": "Apps Updated",
|
||||
"appsUpdated": "Apps updated",
|
||||
"appsNotUpdated": "Failed to update applications",
|
||||
"appsUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were applied in the background",
|
||||
"appsUpdatedNotifDescription": "Notifies the user that updates to one or more apps were applied in the background",
|
||||
"xWasUpdatedToY": "{} was updated to {}.",
|
||||
"xWasNotUpdatedToY": "Failed to update {} to {}.",
|
||||
"errorCheckingUpdates": "Error Checking for Updates",
|
||||
"errorCheckingUpdates": "Error checking for updates",
|
||||
"errorCheckingUpdatesNotifDescription": "A notification that shows when background update checking fails",
|
||||
"appsRemoved": "Apps Removed",
|
||||
"appsRemovedNotifDescription": "Notifies the user that one or more Apps were removed due to errors while loading them",
|
||||
"appsRemoved": "Apps removed",
|
||||
"appsRemovedNotifDescription": "Notifies the user that one or more apps were removed due to errors while loading them",
|
||||
"xWasRemovedDueToErrorY": "{} was removed due to this error: {}",
|
||||
"completeAppInstallation": "Complete App Installation",
|
||||
"obtainiumMustBeOpenToInstallApps": "Obtainium must be open to install Apps",
|
||||
"completeAppInstallationNotifDescription": "Asks the user to return to Obtainium to finish installing an App",
|
||||
"checkingForUpdates": "Checking for Updates",
|
||||
"completeAppInstallation": "Complete app installation",
|
||||
"obtainiumMustBeOpenToInstallApps": "Obtainium must be open to install apps",
|
||||
"completeAppInstallationNotifDescription": "Asks the user to return to Obtainium to finish installing an app",
|
||||
"checkingForUpdates": "Checking for updates",
|
||||
"checkingForUpdatesNotifDescription": "Transient notification that appears when checking for updates",
|
||||
"pleaseAllowInstallPerm": "Please allow Obtainium to install Apps",
|
||||
"trackOnly": "Track-Only",
|
||||
"pleaseAllowInstallPerm": "Please allow Obtainium to install apps",
|
||||
"trackOnly": "Track-only",
|
||||
"errorWithHttpStatusCode": "Error {}",
|
||||
"versionCorrectionDisabled": "Version correction disabled (plugin doesn't seem to work)",
|
||||
"unknown": "Unknown",
|
||||
@ -170,123 +170,124 @@
|
||||
"never": "Never",
|
||||
"latestVersionX": "Latest: {}",
|
||||
"installedVersionX": "Installed: {}",
|
||||
"lastUpdateCheckX": "Last Update Check: {}",
|
||||
"lastUpdateCheckX": "Last update check: {}",
|
||||
"remove": "Remove",
|
||||
"yesMarkUpdated": "Yes, Mark as Updated",
|
||||
"fdroid": "F-Droid Official",
|
||||
"appIdOrName": "App ID or Name",
|
||||
"yesMarkUpdated": "Yes, mark as updated",
|
||||
"fdroid": "F-Droid official",
|
||||
"appIdOrName": "App ID or name",
|
||||
"appId": "App ID",
|
||||
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
||||
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
||||
"appWithIdOrNameNotFound": "No app was found with that ID or name",
|
||||
"reposHaveMultipleApps": "Repos may contain multiple apps",
|
||||
"fdroidThirdPartyRepo": "F-Droid third-party repo",
|
||||
"install": "Install",
|
||||
"markInstalled": "Mark Installed",
|
||||
"markInstalled": "Mark installed",
|
||||
"update": "Update",
|
||||
"markUpdated": "Mark Updated",
|
||||
"additionalOptions": "Additional Options",
|
||||
"disableVersionDetection": "Disable Version Detection",
|
||||
"noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.",
|
||||
"markUpdated": "Mark updated",
|
||||
"additionalOptions": "Additional options",
|
||||
"disableVersionDetection": "Disable version detection",
|
||||
"noVersionDetectionExplanation": "This option should only be used for apps where version detection does not work correctly.",
|
||||
"downloadingX": "Downloading {}",
|
||||
"downloadX": "Download {}",
|
||||
"downloadedX": "Downloaded {}",
|
||||
"releaseAsset": "Release Asset",
|
||||
"downloadNotifDescription": "Notifies the user of the progress in downloading an App",
|
||||
"releaseAsset": "Release asset",
|
||||
"downloadNotifDescription": "Notifies the user of the progress in downloading an app",
|
||||
"noAPKFound": "No APK found",
|
||||
"noVersionDetection": "No version detection",
|
||||
"categorize": "Categorize",
|
||||
"categories": "Categories",
|
||||
"category": "Category",
|
||||
"noCategory": "No Category",
|
||||
"noCategories": "No Categories",
|
||||
"deleteCategoriesQuestion": "Delete Categories?",
|
||||
"categoryDeleteWarning": "All Apps in deleted categories will be set to uncategorized.",
|
||||
"addCategory": "Add Category",
|
||||
"noCategory": "No category",
|
||||
"noCategories": "No categories",
|
||||
"deleteCategoriesQuestion": "Delete categories?",
|
||||
"categoryDeleteWarning": "All apps in deleted categories will be set to uncategorized.",
|
||||
"addCategory": "Add category",
|
||||
"label": "Label",
|
||||
"language": "Language",
|
||||
"copiedToClipboard": "Copied to Clipboard",
|
||||
"copiedToClipboard": "Copied to clipboard",
|
||||
"storagePermissionDenied": "Storage permission denied",
|
||||
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
|
||||
"filterAPKsByRegEx": "Filter APKs by Regular Expression",
|
||||
"selectedCategorizeWarning": "This will replace any existing category settings for the selected apps.",
|
||||
"filterAPKsByRegEx": "Filter APKs by regular expression",
|
||||
"removeFromObtainium": "Remove from Obtainium",
|
||||
"uninstallFromDevice": "Uninstall from Device",
|
||||
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
|
||||
"uninstallFromDevice": "Uninstall from device",
|
||||
"onlyWorksWithNonVersionDetectApps": "Only works for apps with version detection disabled.",
|
||||
"releaseDateAsVersion": "Use release date as version string",
|
||||
"releaseTitleAsVersion": "Use release title as version string",
|
||||
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
|
||||
"releaseDateAsVersionExplanation": "This option should only be used for apps where version detection does not work correctly, but a release date is available.",
|
||||
"changes": "Changes",
|
||||
"releaseDate": "Release Date",
|
||||
"importFromURLsInFile": "Import from URLs in File (like OPML)",
|
||||
"releaseDate": "Release date",
|
||||
"importFromURLsInFile": "Import from URLs in file (like OPML)",
|
||||
"versionDetectionExplanation": "Reconcile version string with version detected from OS",
|
||||
"versionDetection": "Version Detection",
|
||||
"versionDetection": "Version detection",
|
||||
"standardVersionDetection": "Standard version detection",
|
||||
"groupByCategory": "Group by Category",
|
||||
"groupByCategory": "Group by category",
|
||||
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
|
||||
"overrideSource": "Override Source",
|
||||
"autoLinkFilterByArch": "Attempt to filter links by CPU architecture if possible",
|
||||
"overrideSource": "Override source",
|
||||
"dontShowAgain": "Don't show this again",
|
||||
"dontShowTrackOnlyWarnings": "Don't show 'Track-Only' warnings",
|
||||
"dontShowTrackOnlyWarnings": "Don't show 'track-only' warnings",
|
||||
"dontShowAPKOriginWarnings": "Don't show APK origin warnings",
|
||||
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
|
||||
"gitlabPATLabel": "GitLab Personal Access Token",
|
||||
"moveNonInstalledAppsToBottom": "Move non-installed apps to bottom of apps view",
|
||||
"gitlabPATLabel": "GitLab personal access token",
|
||||
"about": "About",
|
||||
"requiresCredentialsInSettings": "{} needs additional credentials (in Settings)",
|
||||
"checkOnStart": "Check for updates on startup",
|
||||
"tryInferAppIdFromCode": "Try inferring App ID from source code",
|
||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
|
||||
"tryInferAppIdFromCode": "Try inferring app ID from source code",
|
||||
"removeOnExternalUninstall": "Automatically remove externally uninstalled apps",
|
||||
"pickHighestVersionCode": "Auto-select highest version code APK",
|
||||
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
|
||||
"checkUpdateOnDetailPage": "Check for updates on opening an app detail page",
|
||||
"disablePageTransitions": "Disable page transition animations",
|
||||
"reversePageTransitions": "Reverse page transition animations",
|
||||
"minStarCount": "Minimum Star Count",
|
||||
"minStarCount": "Minimum star count",
|
||||
"addInfoBelow": "Add this info below.",
|
||||
"addInfoInSettings": "Add this info in the Settings.",
|
||||
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
|
||||
"sortByLastLinkSegment": "Sort by only the last segment of the link",
|
||||
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
|
||||
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
|
||||
"appsPossiblyUpdated": "App Updates Attempted",
|
||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
|
||||
"filterReleaseNotesByRegEx": "Filter release notes by regular expression",
|
||||
"customLinkFilterRegex": "Custom APK link filter by regular expression (default '.apk$')",
|
||||
"appsPossiblyUpdated": "App updates attempted",
|
||||
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more apps were potentially applied in the background",
|
||||
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
|
||||
"enableBackgroundUpdates": "Enable background updates",
|
||||
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
|
||||
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
|
||||
"verifyLatestTag": "Verify the 'latest' tag",
|
||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit",
|
||||
"intermediateLinkRegex": "Filter for an 'intermediate' link to visit",
|
||||
"filterByLinkText": "Filter links by link text",
|
||||
"intermediateLinkNotFound": "Intermediate link not found",
|
||||
"intermediateLink": "Intermediate link",
|
||||
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
|
||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not onWi-Fi",
|
||||
"bgUpdatesOnWiFiOnly": "Disable background updates when not on Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Disable background updates when not charging",
|
||||
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
|
||||
"versionExtractionRegEx": "Version String Extraction RegEx",
|
||||
"trimVersionString": "Trim Version String With RegEx",
|
||||
"matchGroupToUseForX": "Match Group to Use for \"{}\"",
|
||||
"matchGroupToUse": "Match Group to Use for Version String Extraction RegEx",
|
||||
"versionExtractionRegEx": "Version string extraction RegEx",
|
||||
"trimVersionString": "Trim version string with RegEx",
|
||||
"matchGroupToUseForX": "Match group to use for \"{}\"",
|
||||
"matchGroupToUse": "Match group to use for version string extraction RegEx",
|
||||
"highlightTouchTargets": "Highlight less obvious touch targets",
|
||||
"pickExportDir": "Pick Export Directory",
|
||||
"autoExportOnChanges": "Auto-export on changes",
|
||||
"pickExportDir": "Pick export directory",
|
||||
"autoExportOnChanges": "Automatically export on changes",
|
||||
"includeSettings": "Include settings",
|
||||
"filterVersionsByRegEx": "Filter Versions by Regular Expression",
|
||||
"filterVersionsByRegEx": "Filter versions by regular expression",
|
||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||
"dontSortReleasesList": "Retain release order from API",
|
||||
"reverseSort": "Reverse sorting",
|
||||
"takeFirstLink": "Take first link",
|
||||
"skipSort": "Skip sorting",
|
||||
"debugMenu": "Debug Menu",
|
||||
"debugMenu": "Debug menu",
|
||||
"bgTaskStarted": "Background task started - check logs.",
|
||||
"runBgCheckNow": "Run Background Update Check Now",
|
||||
"runBgCheckNow": "Run background update check now",
|
||||
"versionExtractWholePage": "Apply version string extraction Regex to entire page",
|
||||
"installing": "Installing",
|
||||
"skipUpdateNotifications": "Skip update notifications",
|
||||
"updatesAvailableNotifChannel": "Updates Available",
|
||||
"appsUpdatedNotifChannel": "Apps Updated",
|
||||
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
|
||||
"errorCheckingUpdatesNotifChannel": "Error Checking for Updates",
|
||||
"appsRemovedNotifChannel": "Apps Removed",
|
||||
"updatesAvailableNotifChannel": "Updates available",
|
||||
"appsUpdatedNotifChannel": "Apps updated",
|
||||
"appsPossiblyUpdatedNotifChannel": "App updates attempted",
|
||||
"errorCheckingUpdatesNotifChannel": "Error checking for updates",
|
||||
"appsRemovedNotifChannel": "Apps removed",
|
||||
"downloadingXNotifChannel": "Downloading {}",
|
||||
"completeAppInstallationNotifChannel": "Complete App Installation",
|
||||
"checkingForUpdatesNotifChannel": "Checking for Updates",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||
"completeAppInstallationNotifChannel": "Complete app installation",
|
||||
"checkingForUpdatesNotifChannel": "Checking for updates",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and track-only apps for updates",
|
||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||
"selectX": "Select {}",
|
||||
"parallelDownloads": "Allow parallel downloads",
|
||||
@ -299,29 +300,42 @@
|
||||
"useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version",
|
||||
"requestHeader": "Request header",
|
||||
"useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date",
|
||||
"defaultPseudoVersioningMethod": "Default Pseudo-Versioning Method",
|
||||
"partialAPKHash": "Partial APK Hash",
|
||||
"APKLinkHash": "APK Link Hash",
|
||||
"directAPKLink": "Direct APK Link",
|
||||
"pseudoVersionInUse": "A Pseudo-Version is in Use",
|
||||
"defaultPseudoVersioningMethod": "Default pseudo-versioning method",
|
||||
"partialAPKHash": "Partial APK hash",
|
||||
"APKLinkHash": "APK link hash",
|
||||
"directAPKLink": "Direct APK link",
|
||||
"pseudoVersionInUse": "A pseudo-version is in use",
|
||||
"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)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Share new apps with AppVerifier (if available)",
|
||||
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
|
||||
"wiki": "Help/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configs",
|
||||
"crowdsourcedConfigsLabel": "Crowdsourced app configurations (use at your own risk)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced app configurations",
|
||||
"allowInsecure": "Allow insecure HTTP requests",
|
||||
"stayOneVersionBehind": "Stay one version behind latest",
|
||||
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
|
||||
"refreshBeforeDownload": "Refresh app details before download",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Name",
|
||||
"smartname": "Name (smart)",
|
||||
"sortMethod": "Sort method",
|
||||
"welcome": "Welcome",
|
||||
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.",
|
||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you switch to the \"foreground service\" in the Obtainium settings and/or disable battery optimization for Obtainium in your OS settings.",
|
||||
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Remove App?",
|
||||
"other": "Remove Apps?"
|
||||
"one": "Remove app?",
|
||||
"other": "Remove apps?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Too many requests (rate limited) - try again in {} minute",
|
||||
@ -379,4 +393,4 @@
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
||||
}
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Por versión",
|
||||
"groupByCategory": "Agrupar por categorías",
|
||||
"autoApkFilterByArch": "Filtrar APK por arquitectura del procesador (si es posible)",
|
||||
"autoLinkFilterByArch": "Intente filtrar los enlaces por arquitectura de CPU si es posible",
|
||||
"overrideSource": "Forzar desde la fuente",
|
||||
"dontShowAgain": "No mostrar de nuevo",
|
||||
"dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps 'solo para seguimiento'",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "Enlace intermedio no encontrado",
|
||||
"intermediateLink": "Enlace intermedio",
|
||||
"exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)",
|
||||
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sinWi-Fi",
|
||||
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Desactiva las actualizaciones en segundo plano cuando no estés cargando",
|
||||
"autoSelectHighestVersionCode": "Auto selección del paquete APK con versión más reciente",
|
||||
"versionExtractionRegEx": "Versión de extracción RegEx",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Configuración de aplicaciones por crowdsourcing",
|
||||
"allowInsecure": "Permitir peticiones HTTP inseguras",
|
||||
"stayOneVersionBehind": "Mantenerse una versión por detrás de la última",
|
||||
"useFirstApkOfVersion": "Auto-selección del primero de múltiples APKs",
|
||||
"refreshBeforeDownload": "Actualiza los datos de la aplicación antes de descargarla",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nombre",
|
||||
"smartname": "Nombre (Smart)",
|
||||
"sortMethod": "Método de clasificación",
|
||||
"welcome": "Bienvenido",
|
||||
"documentationLinksNote": "La página GitHub de Obtainium enlazada a continuación contiene enlaces a vídeos, artículos, debates y otros recursos que te ayudarán a entender cómo utilizar la aplicación.",
|
||||
"batteryOptimizationNote": "Ten en cuenta que las descargas en segundo plano pueden funcionar de forma más fiable si desactivas las optimizaciones de batería del sistema operativo para Obtainium.",
|
||||
"fileDeletionError": "No se ha podido eliminar el archivo (intente eliminarlo manualmente y vuelva a intentarlo): \"{}\"",
|
||||
"foregroundService": "Obtainium servicio en primer plano",
|
||||
"foregroundServiceExplanation": "Utilizar un servicio en primer plano para comprobar las actualizaciones (más fiable, consume más energía).",
|
||||
"fgServiceNotice": "Esta notificación es necesaria para la comprobación de actualizaciones en segundo plano (puede ocultarse en la configuración del sistema operativo).",
|
||||
"removeAppQuestion": {
|
||||
"one": "¿Eliminar aplicación?",
|
||||
"other": "¿Eliminar aplicaciones?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "تشخیص نسخه استاندارد",
|
||||
"groupByCategory": "گروه بر اساس دسته",
|
||||
"autoApkFilterByArch": "در صورت امکان سعی کنید APKها را بر اساس معماری CPU فیلتر کنید",
|
||||
"autoLinkFilterByArch": "Attempt to filter links by CPU architecture if possible",
|
||||
"overrideSource": "نادیده گرفتن منبع",
|
||||
"dontShowAgain": "دوباره این را نشان نده",
|
||||
"dontShowTrackOnlyWarnings": "هشدار 'فقط ردیابی' را نشان ندهید",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "لینک میانی پیدا نشد",
|
||||
"intermediateLink": "پیوند میانی",
|
||||
"exemptFromBackgroundUpdates": "معاف از بهروزرسانیهای پسزمینه (در صورت فعال بودن)",
|
||||
"bgUpdatesOnWiFiOnly": "بهروزرسانیهای پسزمینه را در صورت عدم اتصال بهWi-Fi غیرفعال کنید",
|
||||
"bgUpdatesOnWiFiOnly": "بهروزرسانیهای پسزمینه را در صورت عدم اتصال به Wi-Fi غیرفعال کنید",
|
||||
"bgUpdatesWhileChargingOnly": "بهروزرسانیهای پسزمینه را هنگام شارژ نشدن غیرفعال کنید",
|
||||
"autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK",
|
||||
"versionExtractionRegEx": "نسخه استخراج RegEx",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "تنظیمات برنامه های مشارکت جمعی",
|
||||
"allowInsecure": "درخواست های HTTP ناامن را مجاز کنید",
|
||||
"stayOneVersionBehind": "یک نسخه از آخرین نسخه پشت سر بگذارید",
|
||||
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
|
||||
"refreshBeforeDownload": "قبل از دانلود، جزئیات برنامه را بازخوانی کنید",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Name",
|
||||
"smartname": "Name (Smart)",
|
||||
"sortMethod": "Sort Method",
|
||||
"welcome": "Welcome",
|
||||
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.",
|
||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
||||
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "برنامه حذف شود؟",
|
||||
"other": "برنامه ها حذف شوند؟"
|
||||
|
@ -9,11 +9,11 @@
|
||||
"placeholder": "Espace réservé",
|
||||
"someErrors": "Des erreurs sont survenues",
|
||||
"unexpectedError": "Erreur inattendue",
|
||||
"ok": "Ok",
|
||||
"ok": "OK",
|
||||
"and": "et",
|
||||
"githubPATLabel": "Jeton d'accès personnel GitHub (augmente la limite de débit)",
|
||||
"includePrereleases": "Inclure les versions préliminaires",
|
||||
"fallbackToOlderReleases": "Retour aux anciennes versions",
|
||||
"fallbackToOlderReleases": "Revenir aux anciennes versions",
|
||||
"filterReleaseTitlesByRegEx": "Filtrer les titres de version par expression régulière",
|
||||
"invalidRegEx": "Expression régulière invalide",
|
||||
"noDescription": "Aucune description",
|
||||
@ -30,13 +30,13 @@
|
||||
"wrongArgNum": "Nombre incorrect des arguments fournis",
|
||||
"xIsTrackOnly": "{} en Suivi uniquement",
|
||||
"source": "source",
|
||||
"app": "Application",
|
||||
"app": "Appli",
|
||||
"appsFromSourceAreTrackOnly": "Les applications de cette source sont en 'Suivi uniquement'.",
|
||||
"youPickedTrackOnly": "Vous avez sélectionné l'option 'Suivi uniquement'.",
|
||||
"trackOnlyAppDescription": "L'application sera suivie pour les mises à jour, mais Obtainium ne pourra pas la télécharger ou l'installer.",
|
||||
"cancelled": "Annulé",
|
||||
"appAlreadyAdded": "Application déjà ajoutée",
|
||||
"alreadyUpToDateQuestion": "L'application est à jour?",
|
||||
"alreadyUpToDateQuestion": "L'application est déjà à jour?",
|
||||
"addApp": "Ajouter appli",
|
||||
"appSourceURL": "URL source de l'application",
|
||||
"error": "Erreur",
|
||||
@ -47,30 +47,30 @@
|
||||
"supportedSources": "Sources prises en charge",
|
||||
"trackOnlyInBrackets": "(Suivi uniquement)",
|
||||
"searchableInBrackets": "(Interrogeable)",
|
||||
"appsString": "Applications",
|
||||
"appsString": "Applis",
|
||||
"noApps": "Aucune application",
|
||||
"noAppsForFilter": "Aucune application à filtrer",
|
||||
"noAppsForFilter": "Aucune application correspondant au filtre",
|
||||
"byX": "Par {}",
|
||||
"percentProgress": "Progression : {}%",
|
||||
"pleaseWait": "Veuillez patienter",
|
||||
"updateAvailable": "Mise à jour disponible",
|
||||
"notInstalled": "Non installé",
|
||||
"notInstalled": "Non installée",
|
||||
"pseudoVersion": "Version fictive",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"deselectX": "Déselectionner {}",
|
||||
"deselectX": "Désélectionner {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} sera supprimée d'Obtainium mais restera installée sur l'appareil.",
|
||||
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
|
||||
"removeSelectedApps": "Supprimer les applications sélectionnées",
|
||||
"updateX": "Mettre à jour {}",
|
||||
"installX": "Installer {}",
|
||||
"markXTrackOnlyAsUpdated": "Marquer {}\n(Suivi uniquement)\ncomme étant à jour",
|
||||
"changeX": "Changer {}",
|
||||
"changeX": "Modifier {}",
|
||||
"installUpdateApps": "Installer/Mettre à jour les applications",
|
||||
"installUpdateSelectedApps": "Installer/Mettre à jour les applications sélectionnées",
|
||||
"markXSelectedAppsAsUpdated": "Marquer les {} applications sélectionnées comme étant à jour ?",
|
||||
"no": "Non",
|
||||
"yes": "Oui",
|
||||
"markSelectedAppsUpdated": "Marquer les application sélectionnées comme étant à jour",
|
||||
"markSelectedAppsUpdated": "Marquer les applications sélectionnées comme étant à jour",
|
||||
"pinToTop": "Épingler en haut",
|
||||
"unpinFromTop": "Désépingler du haut",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Réinitialiser l'état d'installation des applications sélectionnées ?",
|
||||
@ -88,7 +88,7 @@
|
||||
"author": "Auteur",
|
||||
"upToDateApps": "Applications à jour",
|
||||
"nonInstalledApps": "Applications non installées",
|
||||
"importExport": "Importer/exporter",
|
||||
"importExport": "Import/Export",
|
||||
"settings": "Paramètres",
|
||||
"exportedTo": "Exporté vers {}",
|
||||
"obtainiumExport": "Exporter la configuration d'Obtainium",
|
||||
@ -97,24 +97,24 @@
|
||||
"obtainiumImport": "Importer la configuration sur Obtainium",
|
||||
"importFromURLList": "Importer depuis une liste d'URL",
|
||||
"searchQuery": "Requête de recherche",
|
||||
"appURLList": "Liste d'URL de l'application",
|
||||
"appURLList": "Liste d'URL des applications",
|
||||
"line": "Ligne",
|
||||
"searchX": "Sélectionner {}",
|
||||
"searchX": "Rechercher {}",
|
||||
"noResults": "Aucun résultat",
|
||||
"importX": "Importation de {}",
|
||||
"importedAppsIdDisclaimer": "Les applications importées peuvent s'afficher de manière incorrecte comme étant \"Non installées\".\nPour résoudre ce problème, réinstallez-les via Obtainium.\nCela n'affectera pas les données des applications.\n\nN'affecte que les méthodes d'importation d'URL et par des tiers.",
|
||||
"importedAppsIdDisclaimer": "Les applications importées peuvent s'afficher de manière incorrecte comme étant \"Non installées\".\nPour résoudre ce problème, réinstallez-les via Obtainium.\nCela n'affectera pas les données des applications.\n\nCela n'affecte que les méthodes d'importation d'URL et par des tiers.",
|
||||
"importErrors": "Erreurs lors de l'importation",
|
||||
"importedXOfYApps": "{} applications sur {} ont été importés.",
|
||||
"followingURLsHadErrors": "Les URL suivants comportent des erreurs :",
|
||||
"importedXOfYApps": "{} applications sur {} ont été importées.",
|
||||
"followingURLsHadErrors": "Les URL suivantes comportent des erreurs :",
|
||||
"selectURL": "Sélectionner l'URL",
|
||||
"selectURLs": "Sélectionner les URL",
|
||||
"pick": "Choisir",
|
||||
"theme": "Thème",
|
||||
"dark": "Sombre",
|
||||
"light": "Clair",
|
||||
"followSystem": "Correspondre au système",
|
||||
"followSystemThemeExplanation": "Correspondre au thème du système est possible en utilisant des applications tierces.",
|
||||
"useBlackTheme": "Utiliser un thème Noir",
|
||||
"followSystem": "Suivre le système",
|
||||
"followSystemThemeExplanation": "Suivre le thème du système est possible en utilisant des applications tierces.",
|
||||
"useBlackTheme": "Utiliser un thème noir",
|
||||
"appSortBy": "Trier les applications par",
|
||||
"authorName": "Auteur/Nom",
|
||||
"nameAuthor": "Nom/Auteur",
|
||||
@ -136,10 +136,10 @@
|
||||
"share": "Partager",
|
||||
"appNotFound": "Application introuvable",
|
||||
"obtainiumExportHyphenatedLowercase": "export-obtainium",
|
||||
"pickAnAPK": "Selectionner un APK",
|
||||
"appHasMoreThanOnePackage": "{} a plus d'un paquet:",
|
||||
"pickAnAPK": "Sélectionner un APK",
|
||||
"appHasMoreThanOnePackage": "{} a plus d'un paquet :",
|
||||
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture CPU {}.",
|
||||
"deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivants: ",
|
||||
"deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivantes : ",
|
||||
"warning": "Avertissement",
|
||||
"sourceIsXButPackageFromYPrompt": "La source de l'application est '{}' mais le paquet de mise à jour provient de '{}'. Continuer ?",
|
||||
"updatesAvailable": "Mises à jour disponibles",
|
||||
@ -149,7 +149,7 @@
|
||||
"appsUpdated": "Applications mises à jour",
|
||||
"appsNotUpdated": "Échec de la mise à jour des applications",
|
||||
"appsUpdatedNotifDescription": "Notifie à l'utilisateur que des mises à jour d'une ou plusieurs applications ont été installées en arrière-plan.",
|
||||
"xWasUpdatedToY": "{} a été mis à jour en {}.",
|
||||
"xWasUpdatedToY": "{} a été mise à jour vers {}.",
|
||||
"xWasNotUpdatedToY": "Échec de la mise à jour de {} vers {}.",
|
||||
"errorCheckingUpdates": "Erreur lors de la recherche de mises à jour",
|
||||
"errorCheckingUpdatesNotifDescription": "Notifie l'utilisateur lorsque la recherche de mises à jour en arrière-plan échoue.",
|
||||
@ -180,26 +180,26 @@
|
||||
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
|
||||
"fdroidThirdPartyRepo": "Dépôt tiers F-Droid",
|
||||
"install": "Installer",
|
||||
"markInstalled": "Marquer comme étant installé",
|
||||
"markInstalled": "Marquer comme installée",
|
||||
"update": "Mettre à jour",
|
||||
"markUpdated": "Marquer comme étant à jour",
|
||||
"markUpdated": "Marquer comme à jour",
|
||||
"additionalOptions": "Options supplémentaires",
|
||||
"disableVersionDetection": "Désactiver la détection de la version",
|
||||
"noVersionDetectionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de la version ne fonctionne pas correctement.",
|
||||
"downloadingX": "Téléchargement {}",
|
||||
"downloadingX": "Téléchargement de {}",
|
||||
"downloadX": "Télécharger {}",
|
||||
"downloadedX": "Téléchargé {}",
|
||||
"releaseAsset": "Version active",
|
||||
"releaseAsset": "Élément de version",
|
||||
"downloadNotifDescription": "Notifie l'utilisateur sur l'avancement du téléchargement d'une application",
|
||||
"noAPKFound": "Aucun APK trouvé",
|
||||
"noVersionDetection": "Aucune version trouvée",
|
||||
"noVersionDetection": "Aucune détection de version",
|
||||
"categorize": "Catégoriser",
|
||||
"categories": "Catégories",
|
||||
"category": "Catégorie",
|
||||
"noCategory": "Aucune catégorie",
|
||||
"noCategories": "Aucune catégories",
|
||||
"deleteCategoriesQuestion": "Supprimer les catégories?",
|
||||
"categoryDeleteWarning": "Toutes les applications des catégories supprimées seront définies comme non catégorisées .",
|
||||
"noCategories": "Aucune catégorie",
|
||||
"deleteCategoriesQuestion": "Supprimer les catégories ?",
|
||||
"categoryDeleteWarning": "Toutes les applications des catégories supprimées seront définies comme non catégorisées.",
|
||||
"addCategory": "Ajouter une catégorie",
|
||||
"label": "Nom",
|
||||
"language": "Langue",
|
||||
@ -209,26 +209,27 @@
|
||||
"filterAPKsByRegEx": "Filtrer les APK par expression régulière",
|
||||
"removeFromObtainium": "Supprimer d'Obtainium",
|
||||
"uninstallFromDevice": "Désinstaller de l'appareil",
|
||||
"onlyWorksWithNonVersionDetectApps": "Ne fonctionne que pour les applications dont la détection de la version est désactivée.",
|
||||
"onlyWorksWithNonVersionDetectApps": "Ne fonctionne qu'avec les applications dont la détection de la version est désactivée.",
|
||||
"releaseDateAsVersion": "Utiliser la date de sortie comme version",
|
||||
"releaseTitleAsVersion": "Utiliser le titre de la version comme chaîne de caractères de la version",
|
||||
"releaseTitleAsVersion": "Utiliser le titre de la version comme chaîne de version",
|
||||
"releaseDateAsVersionExplanation": "Cette option ne doit être utilisée que pour les applications pour lesquelles la détection de la version ne fonctionne pas correctement, mais dont une date de sortie est disponible.",
|
||||
"changes": "Modifications",
|
||||
"releaseDate": "Date de sortie",
|
||||
"importFromURLsInFile": "Importer à partir des URLs d'un fichier (comme OPML)",
|
||||
"versionDetectionExplanation": "Reporter la chaîne de version selon la version détectée par le système d'exploitation",
|
||||
"versionDetectionExplanation": "Réconcilier la chaîne de version avec la version détectée par le système d'exploitation",
|
||||
"versionDetection": "Détection de la version",
|
||||
"standardVersionDetection": "Détection de la version standard",
|
||||
"groupByCategory": "Grouper par catégorie",
|
||||
"autoApkFilterByArch": "Essayer de filtrer les APKs par architecture CPU si possible",
|
||||
"autoLinkFilterByArch": "Essayer de filtrer les liens en fonction de l'architecture de l'unité centrale si possible",
|
||||
"overrideSource": "Remplacer la source",
|
||||
"dontShowAgain": "Ne plus afficher",
|
||||
"dontShowTrackOnlyWarnings": "Ne plus afficher les erreurs 'Suivi uniquement'",
|
||||
"dontShowAPKOriginWarnings": "Ne plus afficher les erreurs sur l'origine de l'APK",
|
||||
"dontShowTrackOnlyWarnings": "Ne plus afficher les avertissements 'Suivi uniquement'",
|
||||
"dontShowAPKOriginWarnings": "Ne plus afficher les avertissements sur l'origine de l'APK",
|
||||
"moveNonInstalledAppsToBottom": "Déplacer les applications non installées vers le bas de la vue Applications",
|
||||
"gitlabPATLabel": "Jeton d'accès personnel GitLab",
|
||||
"about": "À propos",
|
||||
"requiresCredentialsInSettings": "{} a besoin d'un complément d'information (dans les Paramètres)",
|
||||
"requiresCredentialsInSettings": "{} a besoin d'informations d'identification supplémentaires (dans les Paramètres)",
|
||||
"checkOnStart": "Rechercher les mises à jour au démarrage",
|
||||
"tryInferAppIdFromCode": "Essayer de déduire l'identifiant de l'application à partir du code source",
|
||||
"removeOnExternalUninstall": "Supprimer automatiquement les applications désinstallées en externe",
|
||||
@ -243,67 +244,67 @@
|
||||
"sortByLastLinkSegment": "Trier par le dernier segment du lien",
|
||||
"filterReleaseNotesByRegEx": "Filtrer les notes de version par expression régulière",
|
||||
"customLinkFilterRegex": "Filtre de lien APK personnalisé par expression régulière (par défaut '.apk$')",
|
||||
"appsPossiblyUpdated": "Tentative de mise à jour des applications",
|
||||
"appsPossiblyUpdated": "Tentatives de mise à jour d'applications",
|
||||
"appsPossiblyUpdatedNotifDescription": "Notifie à l'utilisateur que des mises à jour d'une ou plusieurs applications ont potentiellement été appliquées en arrière-plan",
|
||||
"xWasPossiblyUpdatedToY": "{} peut être mis à jour en {}.",
|
||||
"xWasPossiblyUpdatedToY": "{} a peut-être été mise à jour vers {}.",
|
||||
"enableBackgroundUpdates": "Activer les mises à jour en arrière-plan",
|
||||
"backgroundUpdateReqsExplanation": "Les mises à jour en arrière-plan peuvent ne pas être possibles pour toutes les applications.",
|
||||
"backgroundUpdateLimitsExplanation": "Le résultat d'une installation en arrière-plan ne peut être déterminé qu'à l'ouverture d'Obtainium.",
|
||||
"backgroundUpdateLimitsExplanation": "Le succès d'une installation en arrière-plan ne peut être déterminé qu'à l'ouverture d'Obtainium.",
|
||||
"verifyLatestTag": "Vérifier la balise 'latest'",
|
||||
"intermediateLinkRegex": "Filtrer un lien 'intermédiaire' à visiter",
|
||||
"filterByLinkText": "Filtrer les liens par texte du lien",
|
||||
"intermediateLinkNotFound": "Lien intermédiaire introuvable",
|
||||
"intermediateLink": "Lien intermédiaire",
|
||||
"exemptFromBackgroundUpdates": "Exclure de la mise à jour en arrière-plan (si activé)",
|
||||
"bgUpdatesOnWiFiOnly": "Désactiver les mises à jour en arrière-plan lorsque vous n'êtes pas enWi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Désactiver les mises à jour en arrière-plan lorsque le véhicule n'est pas en charge",
|
||||
"exemptFromBackgroundUpdates": "Exclure des mises à jour en arrière-plan (si activées)",
|
||||
"bgUpdatesOnWiFiOnly": "Désactiver les mises à jour en arrière-plan lorsque vous n'êtes pas en Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Désactiver les mises à jour en arrière-plan lorsque l'appareil n'est pas en charge",
|
||||
"autoSelectHighestVersionCode": "Sélectionner automatiquement la version la plus récente du code APK",
|
||||
"versionExtractionRegEx": "Extraire la version par Expression régulière",
|
||||
"trimVersionString": "Découper la version par Expression régulière",
|
||||
"versionExtractionRegEx": "Expression régulière d'extraction de version",
|
||||
"trimVersionString": "Découper la chaîne de version avec une expression régulière",
|
||||
"matchGroupToUseForX": "Groupe de correspondance à utiliser pour \"{}\"",
|
||||
"matchGroupToUse": "Groupe de correspondance à utiliser pour l'extraction de la version par Expression régulière",
|
||||
"highlightTouchTargets": "Mettre en évidence les touches moins évidentes",
|
||||
"pickExportDir": "Selectionner le dossier d'exportation",
|
||||
"matchGroupToUse": "Groupe de correspondance à utiliser pour l'extraction de la version par expression régulière",
|
||||
"highlightTouchTargets": "Mettre en évidence les zones tactiles moins évidentes",
|
||||
"pickExportDir": "Sélectionner le dossier d'exportation",
|
||||
"autoExportOnChanges": "Exporter automatiquement lors de modifications",
|
||||
"includeSettings": "Inclure les paramètres",
|
||||
"filterVersionsByRegEx": "Filtrer les versions par expression régulière",
|
||||
"trySelectingSuggestedVersionCode": "Essayer de sélectionner la version suggérée du code APK",
|
||||
"dontSortReleasesList": "Conserver l'ordre de la version de l'API",
|
||||
"trySelectingSuggestedVersionCode": "Essayer de sélectionner le code de version APK suggéré",
|
||||
"dontSortReleasesList": "Conserver l'ordre de version de l'API",
|
||||
"reverseSort": "Tri inversé",
|
||||
"takeFirstLink": "Utiliser le premier lien",
|
||||
"skipSort": "Ignorer le tri",
|
||||
"debugMenu": "Menu de déboggage",
|
||||
"debugMenu": "Menu de débogage",
|
||||
"bgTaskStarted": "Tâche en arrière-plan démarrée - vérifier les journaux.",
|
||||
"runBgCheckNow": "Exécuter la recherche de mise à jour en arrière-plan maintenant",
|
||||
"versionExtractWholePage": "Appliquer l'extraction de la version par expression régulière à l'ensemble de la page",
|
||||
"runBgCheckNow": "Exécuter la recherche de mises à jour en arrière-plan maintenant",
|
||||
"versionExtractWholePage": "Appliquer l'expression régulière d'extraction de version à l'ensemble de la page",
|
||||
"installing": "Installation",
|
||||
"skipUpdateNotifications": "Ignorer les notifications de mise à jour",
|
||||
"skipUpdateNotifications": "Désactiver les notifications de mise à jour",
|
||||
"updatesAvailableNotifChannel": "Mises à jour disponibles",
|
||||
"appsUpdatedNotifChannel": "Applications mises à jour",
|
||||
"appsPossiblyUpdatedNotifChannel": "Essayer de mettre à jour les applications",
|
||||
"appsPossiblyUpdatedNotifChannel": "Tentatives de mise à jour d'applications",
|
||||
"errorCheckingUpdatesNotifChannel": "Erreur lors de la recherche de mises à jour",
|
||||
"appsRemovedNotifChannel": "Applications supprimées",
|
||||
"downloadingXNotifChannel": "Téléchargement {}",
|
||||
"completeAppInstallationNotifChannel": "Installation complète de l'application",
|
||||
"downloadingXNotifChannel": "Téléchargement de {}",
|
||||
"completeAppInstallationNotifChannel": "Terminer l'installation de l'application",
|
||||
"checkingForUpdatesNotifChannel": "Recherche de mises à jour",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Rechercher uniquement les mises à jour des applications installées et des applications 'Suivi uniquement'",
|
||||
"supportFixedAPKURL": "Prise en charge des URL APK fixes",
|
||||
"selectX": "Sélectionner {}",
|
||||
"parallelDownloads": "Autoriser les téléchargements simultanés",
|
||||
"useShizuku": "Utiliser Shizuku ou Sui pour l'installation",
|
||||
"shizukuBinderNotFound": "Le service Shizuku est introuvable",
|
||||
"shizukuBinderNotFound": "Le service Shizuku n'est pas en cours d'exécution",
|
||||
"shizukuOld": "Ancienne version de Shizuku (<11) - veuillez le mettre à jour",
|
||||
"shizukuOldAndroidWithADB": "Shizuku fonctionne sur Android < 8.1 avec ADB - veuillez mettre à jour Android ou utiliser Sui à la place",
|
||||
"shizukuPretendToBeGooglePlay": "Définir Google Play comme source d'installation (si Shizuku est utilisé)",
|
||||
"useSystemFont": "Utiliser la police du système",
|
||||
"useVersionCodeAsOSVersion": "Utiliser le code de version de l'application détectée par le système d'exploitation",
|
||||
"requestHeader": "Intitulé de la demande",
|
||||
"useLatestAssetDateAsReleaseDate": "Utiliser le dernier élément mis en ligne comme date de sortie",
|
||||
"defaultPseudoVersioningMethod": "Méthode de version fictive par défaut",
|
||||
"requestHeader": "En-tête de requête",
|
||||
"useLatestAssetDateAsReleaseDate": "Utiliser la date du dernier élément mis en ligne comme date de sortie",
|
||||
"defaultPseudoVersioningMethod": "Méthode de versionnage fictif par défaut",
|
||||
"partialAPKHash": "Hash partiel de l'APK",
|
||||
"APKLinkHash": "Hash du lien APK",
|
||||
"directAPKLink": "Lien direct de l'APK",
|
||||
"pseudoVersionInUse": "Version fictive utilisé",
|
||||
"pseudoVersionInUse": "Une version fictive est utilisée",
|
||||
"installed": "Installée",
|
||||
"latest": "Dernière version",
|
||||
"invertRegEx": "Inverser l'expression régulière",
|
||||
@ -313,27 +314,40 @@
|
||||
"beforeNewInstallsShareToAppVerifier": "Partager les nouvelles applications avec AppVerifier (si disponible)",
|
||||
"appVerifierInstructionToast": "Partagez avec AppVerifier, puis revenez ici lorsque tout est prêt.",
|
||||
"wiki": "Aide/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Configuration d'applis communautaires (à utiliser à vos risques et périls)",
|
||||
"crowdsourcedConfigsShort": "Applis communautaires",
|
||||
"crowdsourcedConfigsLabel": "Configurations d'applications communautaires (à utiliser à vos risques et périls)",
|
||||
"crowdsourcedConfigsShort": "Applications communautaires",
|
||||
"allowInsecure": "Autoriser les requêtes HTTP non sécurisées",
|
||||
"stayOneVersionBehind": "Rester à une version de la dernière",
|
||||
"stayOneVersionBehind": "Rester une version en arrière de la dernière",
|
||||
"useFirstApkOfVersion": "Sélection automatique du premier APK parmi plusieurs",
|
||||
"refreshBeforeDownload": "Actualiser les détails de l'application avant de la télécharger",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nom",
|
||||
"smartname": "Nom (Smart)",
|
||||
"sortMethod": "Méthode de tri",
|
||||
"welcome": "Bienvenue",
|
||||
"documentationLinksNote": "La page GitHub d'Obtainium, dont le lien figure ci-dessous, contient des liens vers des vidéos, des articles, des discussions et d'autres ressources qui vous aideront à comprendre comment utiliser l'application.",
|
||||
"batteryOptimizationNote": "Notez que les téléchargements en arrière-plan peuvent fonctionner de manière plus fiable si vous désactivez les optimisations de la batterie du système d'exploitation pour Obtainium.",
|
||||
"fileDeletionError": "Échec de la suppression du fichier (essayez de le supprimer manuellement puis réessayez) : \"{}\"",
|
||||
"foregroundService": "Service d'avant-plan Obtainium",
|
||||
"foregroundServiceExplanation": "Utiliser un service de premier plan pour la vérification des mises à jour (plus fiable, consomme plus d'énergie)",
|
||||
"fgServiceNotice": "Cette notification est nécessaire pour la vérification des mises à jour en arrière-plan (elle peut être masquée dans les paramètres du système d'exploitation).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Supprimer l'application ?",
|
||||
"other": "Supprimer les applications ?"
|
||||
"one": "Supprimer l'application ?",
|
||||
"other": "Supprimer les applications ?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Trop de requêtes (taux limité) - réessayez dans {} minute",
|
||||
"other": "Trop de requêtes (taux limité) - réessayez dans {} minutes"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "La recherche de mise à jour en arrière-plan a rencontré un {}, une nouvelle tentative programmée dans {} minute",
|
||||
"other": "La recherche de mise à jour en arrière-plan a rencontré un {}, une nouvelle tentative programmée dans {} minutes"
|
||||
"one": "La recherche de mise à jour en arrière-plan a rencontré une erreur {}, une nouvelle tentative programmée dans {} minute",
|
||||
"other": "La recherche de mise à jour en arrière-plan a rencontré une erreur {}, une nouvelle tentative programmée dans {} minutes"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "La recherche de mises à jour en arrière-plan à trouvée {} mise à jour - l'utilisateur sera notifié si nécessaire",
|
||||
"other": "La recherche de mises à jour en arrière-plan à trouvée {} mises à jour - l'utilisateur sera notifié si nécessaire"
|
||||
"one": "La recherche de mises à jour en arrière-plan a trouvé {} mise à jour - l'utilisateur sera notifié si nécessaire",
|
||||
"other": "La recherche de mises à jour en arrière-plan a trouvé {} mises à jour - l'utilisateur sera notifié si nécessaire"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} Application",
|
||||
@ -341,7 +355,7 @@
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL",
|
||||
"other": "{} URL"
|
||||
"other": "{} URLs"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} Minute",
|
||||
@ -365,7 +379,7 @@
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} et 1 autre application ont été mises à jour.",
|
||||
"other": "{} et {} autres applications ont étés mis à jour."
|
||||
"other": "{} et {} autres applications ont été mises à jour."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "Échec de la mise à jour de {} et 1 autre application.",
|
||||
@ -373,7 +387,7 @@
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} et 1 autre application ont peut-être été mises à jour.",
|
||||
"other": "{} et {} autres applications ont peut-être étés mis à jour."
|
||||
"other": "{} et {} autres applications ont peut-être été mises à jour."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
|
@ -28,11 +28,11 @@
|
||||
"githubStarredRepos": "Csillagozott GitHub tárolók",
|
||||
"uname": "Felhasználónév",
|
||||
"wrongArgNum": "A megadott argumentumok száma nem megfelelő",
|
||||
"xIsTrackOnly": "A(z) {} csak nyomonkövethető",
|
||||
"xIsTrackOnly": "A(z) {} csak nyomon követhető",
|
||||
"source": "Forrás",
|
||||
"app": "Alkalmazás",
|
||||
"appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások „csak nyomonkövethetők”.",
|
||||
"youPickedTrackOnly": "„Csak nyomonkövetés” opciót választotta.",
|
||||
"appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások „csak nyomon követhetők”.",
|
||||
"youPickedTrackOnly": "„Csak nyomon követés” opciót választotta.",
|
||||
"trackOnlyAppDescription": "Az alkalmazás frissítéseit nyomon követi, de az Obtainium nem tudja letölteni vagy telepíteni.",
|
||||
"cancelled": "Visszavonva",
|
||||
"appAlreadyAdded": "Az alkalmazás már hozzá van adva",
|
||||
@ -45,7 +45,7 @@
|
||||
"search": "Keresés",
|
||||
"additionalOptsFor": "További lehetőségek a következőhöz: {}",
|
||||
"supportedSources": "Támogatott források",
|
||||
"trackOnlyInBrackets": "(Csak nyomonkövetés)",
|
||||
"trackOnlyInBrackets": "(Csak nyomon követés)",
|
||||
"searchableInBrackets": "(Kereshető)",
|
||||
"appsString": "Alkalmazások",
|
||||
"noApps": "Nincsenek alkalmazások",
|
||||
@ -63,7 +63,7 @@
|
||||
"removeSelectedApps": "A kiválasztott alkalmazások eltávolítása",
|
||||
"updateX": "{} frissítése",
|
||||
"installX": "{} telepítése",
|
||||
"markXTrackOnlyAsUpdated": "Megjelölés: {}\n(Csak nyomonkövetés)\nFrissítettként",
|
||||
"markXTrackOnlyAsUpdated": "Megjelölés: {}\n(Csak nyomon követés)\nFrissítettként",
|
||||
"changeX": "{}-változás",
|
||||
"installUpdateApps": "Alkalmazások telepítése/frissítése",
|
||||
"installUpdateSelectedApps": "A kiválasztott alkalmazások telepítése/frissítése",
|
||||
@ -88,13 +88,13 @@
|
||||
"author": "Szerző",
|
||||
"upToDateApps": "Naprakész alkalmazások",
|
||||
"nonInstalledApps": "Nem telepített alkalmazások",
|
||||
"importExport": "Import/Export",
|
||||
"importExport": "Adatmozgatás",
|
||||
"settings": "Beállítások",
|
||||
"exportedTo": "Exportálva ide: {}",
|
||||
"obtainiumExport": "Obtainium adatok exportálása",
|
||||
"obtainiumExport": "Obtainium-adatok exportálása",
|
||||
"invalidInput": "Hibás bemenet",
|
||||
"importedX": "Importálva innen: {}",
|
||||
"obtainiumImport": "Obtainium adatok importálása",
|
||||
"obtainiumImport": "Obtainium-adatok importálása",
|
||||
"importFromURLList": "Importálás webcímlistából",
|
||||
"searchQuery": "Keresési lekérdezés",
|
||||
"appURLList": "Alkalmazás-webcímlista",
|
||||
@ -143,7 +143,7 @@
|
||||
"warning": "Figyelem",
|
||||
"sourceIsXButPackageFromYPrompt": "Az alkalmazás forrása a(z) „{}” tároló, de a kiadási csomag innen származik: „{}”. Folytatja?",
|
||||
"updatesAvailable": "Frissítések érhetők el",
|
||||
"updatesAvailableNotifDescription": "Értesíti a felhasználót, hogy egy vagy több, az Obtainium által nyomonkövetett alkalmazáshoz frissítések állnak rendelkezésre",
|
||||
"updatesAvailableNotifDescription": "Értesíti a felhasználót, hogy egy vagy több, az Obtainium által nyomon követett alkalmazáshoz frissítések állnak rendelkezésre",
|
||||
"noNewUpdates": "Nincsenek új frissítések.",
|
||||
"xHasAnUpdate": "A(z) {} frissítést kapott.",
|
||||
"appsUpdated": "Alkalmazások frissítve",
|
||||
@ -154,15 +154,15 @@
|
||||
"errorCheckingUpdates": "Hiba a frissítések ellenőrzésekor",
|
||||
"errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, amikor a frissítések ellenőrzése a háttérben nem sikerül",
|
||||
"appsRemoved": "Alkalmazások eltávolítva",
|
||||
"appsRemovedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás betöltés közbeni hiba miatt eltávolításra került",
|
||||
"xWasRemovedDueToErrorY": "A(z) {} eltávolításra került a következő hiba miatt: {}",
|
||||
"appsRemovedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás egy betöltés közbeni hiba miatt el lesz(nek) távolítva",
|
||||
"xWasRemovedDueToErrorY": "A(z) {} el lett távolítva a következő hiba miatt: {}",
|
||||
"completeAppInstallation": "Teljes alkalmazástelepítés",
|
||||
"obtainiumMustBeOpenToInstallApps": "Az alkalmazások telepítéséhez az Obtainiumnak megnyitva kell lennie",
|
||||
"completeAppInstallationNotifDescription": "Megkéri a felhasználót, hogy térjen vissza az Obtainiumhoz, hogy befejezze az alkalmazás telepítését",
|
||||
"checkingForUpdates": "Frissítések ellenőrzése",
|
||||
"checkingForUpdatesNotifDescription": "Átmeneti értesítés, amely a frissítések ellenőrzésekor jelenik meg",
|
||||
"pleaseAllowInstallPerm": "Engedélyezze az Obtainiumnak az alkalmazások telepítését",
|
||||
"trackOnly": "Csak nyomonkövetés",
|
||||
"trackOnly": "Csak nyomon követés",
|
||||
"errorWithHttpStatusCode": "Hiba {}",
|
||||
"versionCorrectionDisabled": "Verzió-korrekció letiltva (úgy tűnik, hogy a bővítmény nem működik)",
|
||||
"unknown": "Ismeretlen",
|
||||
@ -220,10 +220,11 @@
|
||||
"versionDetection": "Verzió-érzékelés",
|
||||
"standardVersionDetection": "Alapértelmezett verzió-érzékelés",
|
||||
"groupByCategory": "Csoportosítás kategória alapján",
|
||||
"autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-kat",
|
||||
"autoApkFilterByArch": "Ha lehetséges, próbálja meg az APK-kat CPU-architektúra szerint szűrni",
|
||||
"autoLinkFilterByArch": "Ha lehetséges, próbálja meg a hivatkozásokat CPU-architektúra szerint szűrni",
|
||||
"overrideSource": "Forrás felülírása",
|
||||
"dontShowAgain": "Ne jelenítse meg ezt többé",
|
||||
"dontShowTrackOnlyWarnings": "Ne jelenítse meg a „Csak nyomonkövetés” figyelmeztetést",
|
||||
"dontShowTrackOnlyWarnings": "Ne jelenítse meg a „Csak nyomon követés” figyelmeztetést",
|
||||
"dontShowAPKOriginWarnings": "Ne jelenítse meg az APK eredetére vonatkozó figyelmeztetéseket",
|
||||
"moveNonInstalledAppsToBottom": "Helyezze át a nem telepített alkalmazásokat az alkalmazásnézet aljára",
|
||||
"gitlabPATLabel": "GitLab személyes hozzáférési token",
|
||||
@ -261,7 +262,7 @@
|
||||
"versionExtractionRegEx": "Verzió-karakterlánc kivonatolása reguláris kifejezéssel",
|
||||
"trimVersionString": "Verzió-karakterlánc levágása reguláris kifejezéssel",
|
||||
"matchGroupToUseForX": "A(z) „{}” esetén használandó csoport egyeztetése",
|
||||
"matchGroupToUse": "Verziókarakterlánc-kivonatoláshoz használandó csoport reguláris kifejezéssel való egyeztetése",
|
||||
"matchGroupToUse": "A verzió-karakterlánc kivonatolásához használandó csoport reguláris kifejezéssel való egyeztetése",
|
||||
"highlightTouchTargets": "A kevésbé nyilvánvaló érintési pontok kiemelése",
|
||||
"pickExportDir": "Válassza ki a könyvtárat, ahová exportálni szeretne",
|
||||
"autoExportOnChanges": "Automatikus exportálás a változtatások után",
|
||||
@ -286,7 +287,7 @@
|
||||
"downloadingXNotifChannel": "A(z) {} letöltése",
|
||||
"completeAppInstallationNotifChannel": "Teljes alkalmazás telepítés",
|
||||
"checkingForUpdatesNotifChannel": "Frissítések ellenőrzése",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak nyomonkövethető alkalmazások frissítéseinek ellenőrzése",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak nyomon követhető alkalmazások frissítéseinek ellenőrzése",
|
||||
"supportFixedAPKURL": "Támogatja a rögzített APK webcímeket",
|
||||
"selectX": "{} kiválasztása",
|
||||
"parallelDownloads": "Párhuzamos letöltések engedélyezése",
|
||||
@ -294,7 +295,7 @@
|
||||
"shizukuBinderNotFound": "A Shizuku szolgáltatás nem fut",
|
||||
"shizukuOld": "Régi Shizuku verzió (<11) - frissítse",
|
||||
"shizukuOldAndroidWithADB": "A Shizuku csak Android < 8.1 ADB-vel fut - frissítse az Androidot vagy használja a Sui-t helyette",
|
||||
"shizukuPretendToBeGooglePlay": "Állítsa be a Google Playt telepítési forrásként (ha Shizukut használ)",
|
||||
"shizukuPretendToBeGooglePlay": "A Google Play beállítása telepítési forrásként (ha Shizukut használ)",
|
||||
"useSystemFont": "A rendszer betűtípusának használata",
|
||||
"useVersionCodeAsOSVersion": "Az alkalmazás verziókódjának használata a rendszer által érzékelt verzióként",
|
||||
"requestHeader": "Kérelemfejléc",
|
||||
@ -313,12 +314,25 @@
|
||||
"beforeNewInstallsShareToAppVerifier": "Új alkalmazások megosztása az AppVerifierrel (ha elérhető)",
|
||||
"appVerifierInstructionToast": "Ossza meg az AppVerifierrel, majd térjen vissza ide, ha kész.",
|
||||
"wiki": "Súgó/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Crowdsource-ből származó alkalmazások beállítása (saját felelősségére használja)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configs",
|
||||
"crowdsourcedConfigsLabel": "Közreműködők által összeállított alkalmazásbeállítások (saját felelősségére használja)",
|
||||
"crowdsourcedConfigsShort": "Alkalmazáslista",
|
||||
"allowInsecure": "Nem biztonságos HTTP-kérések engedélyezése",
|
||||
"stayOneVersionBehind": "Maradjon egy verzióval a legújabb mögött",
|
||||
"useFirstApkOfVersion": "A legelső APK automatikus kiválasztása, ha több APK is található",
|
||||
"refreshBeforeDownload": "Az alkalmazás adatainak frissítése a letöltés előtt",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"tencentAppStore": "Tencent Appstore",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Név",
|
||||
"smartname": "Név (Okos)",
|
||||
"sortMethod": "Rendezési eljárás",
|
||||
"welcome": "Üdvözöljük!",
|
||||
"documentationLinksNote": "Az alábbi hivatkozás az Obtainium GitHub oldalára vezet, amely további videók, cikkek, beszélgetések és egyéb források hivatkozásait tartalmazza, amelyek segítenek megérteni az alkalmazás használatát.",
|
||||
"batteryOptimizationNote": "Megjegyzés: A háttérfrissítések megbízhatóbban működhetnek, ha kikapcsolja a rendszer akkumulátor-optimalizálását az Obtainium számára.",
|
||||
"fileDeletionError": "Nem sikerült törölni a fájlt (próbálja meg kézzel törölni, majd próbálja meg újra): \"{}\"",
|
||||
"foregroundService": "Obtainium új szolgáltatás",
|
||||
"foregroundServiceExplanation": "Frissítésellenőrzéshez előtérben lévő szolgáltatás használata (megbízhatóbb, de több energiát fogyaszt)",
|
||||
"fgServiceNotice": "Ez az értesítés a háttérben történő frissítésellenőrzéshez szükséges (az operációs rendszer beállításaiban elrejthető).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Eltávolítja az alkalmazást?",
|
||||
"other": "Eltávolítja az alkalmazásokat?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Pendeteksi versi standar",
|
||||
"groupByCategory": "Kelompokkan berdasarkan kategori",
|
||||
"autoApkFilterByArch": "Coba filter APK berdasarkan arsitektur CPU jika memungkinkan",
|
||||
"autoLinkFilterByArch": "Mencoba memfilter tautan berdasarkan arsitektur CPU jika memungkinkan",
|
||||
"overrideSource": "Ganti sumber",
|
||||
"dontShowAgain": "Jangan tampilkan lagi",
|
||||
"dontShowTrackOnlyWarnings": "Jangan tampilkan peringatan 'Pelacakan Saja'",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "Tautan perantara tidak ditemukan",
|
||||
"intermediateLink": "Tautan perantara",
|
||||
"exemptFromBackgroundUpdates": "Dikecualikan dari pembaruan latar belakang (jika diaktifkan)",
|
||||
"bgUpdatesOnWiFiOnly": "Nonaktifkan pembaruan latar belakang saat tidak menggunakanWi-Fi",
|
||||
"bgUpdatesOnWiFiOnly": "Nonaktifkan pembaruan latar belakang saat tidak menggunakan Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Menonaktifkan pembaruan latar belakang saat tidak mengisi daya",
|
||||
"autoSelectHighestVersionCode": "Pilih otomatis APK dengan versi kode tertinggi",
|
||||
"versionExtractionRegEx": "Reguler ekspresi terkait ekstraksi versi string",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Konfigurasi Aplikasi Crowdsourced",
|
||||
"allowInsecure": "Izinkan permintaan HTTP yang tidak aman",
|
||||
"stayOneVersionBehind": "Tetap satu versi di belakang versi terbaru",
|
||||
"useFirstApkOfVersion": "Pilih otomatis yang pertama dari beberapa APK",
|
||||
"refreshBeforeDownload": "Segarkan detail aplikasi sebelum mengunduh",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nama",
|
||||
"smartname": "Nama (Cerdas)",
|
||||
"sortMethod": "Metode Penyortiran",
|
||||
"welcome": "Selamat datang.",
|
||||
"documentationLinksNote": "Halaman GitHub Obtainium yang ditautkan di bawah ini berisi tautan ke video, artikel, diskusi, dan sumber daya lain yang akan membantu Anda memahami cara menggunakan aplikasi.",
|
||||
"batteryOptimizationNote": "Perhatikan bahwa unduhan latar belakang dapat bekerja lebih andal jika Anda menonaktifkan optimasi baterai OS untuk Obtainium.",
|
||||
"fileDeletionError": "Gagal menghapus file (coba hapus secara manual, lalu coba lagi): \"{}\"",
|
||||
"foregroundService": "Layanan latar depan Obtainium",
|
||||
"foregroundServiceExplanation": "Gunakan layanan latar depan untuk pemeriksaan pembaruan (lebih dapat diandalkan, menghabiskan lebih banyak daya)",
|
||||
"fgServiceNotice": "Pemberitahuan ini diperlukan untuk pemeriksaan pembaruan latar belakang (dapat disembunyikan dalam pengaturan OS)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Hapus aplikasi?",
|
||||
"other": "Hapus aplikasi?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Rilevamento di versione standard",
|
||||
"groupByCategory": "Raggruppa per categoria",
|
||||
"autoApkFilterByArch": "Tenta di filtrare gli APK in base all'architettura della CPU, se possibile",
|
||||
"autoLinkFilterByArch": "Tentare di filtrare i collegamenti in base all'architettura della CPU, se possibile.",
|
||||
"overrideSource": "Sovrascrivi fonte",
|
||||
"dontShowAgain": "Non mostrarlo più",
|
||||
"dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "Link intermedio non trovato",
|
||||
"intermediateLink": "Collegamento intermedio",
|
||||
"exemptFromBackgroundUpdates": "Esente da aggiornamenti in secondo piano (se attivo)",
|
||||
"bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa ilWi-Fi",
|
||||
"bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa il Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Disabilita gli aggiornamenti in background quando non è in carica",
|
||||
"autoSelectHighestVersionCode": "Auto-seleziona APK con versionCode più alto",
|
||||
"versionExtractionRegEx": "RegEx di estrazione versione",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Configurazioni di app in crowdsourcing",
|
||||
"allowInsecure": "Consentire le richieste HTTP non sicure",
|
||||
"stayOneVersionBehind": "Rimanere una versione indietro rispetto alla più recente",
|
||||
"useFirstApkOfVersion": "Selezione automatica del primo di più APK",
|
||||
"refreshBeforeDownload": "Aggiornare i dettagli dell'app prima del download",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nome",
|
||||
"smartname": "Nome (intelligente)",
|
||||
"sortMethod": "Metodo di ordinamento",
|
||||
"welcome": "Benvenuti",
|
||||
"documentationLinksNote": "La pagina GitHub di Obtainium collegata qui sotto contiene collegamenti a video, articoli, discussioni e altre risorse che vi aiuteranno a capire come utilizzare l'applicazione.",
|
||||
"batteryOptimizationNote": "Si noti che i download in background potrebbero funzionare in modo più affidabile se si disabilita l'ottimizzazione della batteria del sistema operativo per Obtainium.",
|
||||
"fileDeletionError": "Errore nell'eliminazione del file (provare a cancellarlo manualmente e poi riprovare): \"{}\"",
|
||||
"foregroundService": "Servizio Obtainium in primo piano",
|
||||
"foregroundServiceExplanation": "Utilizzare un servizio in primo piano per il controllo degli aggiornamenti (più affidabile, consuma più energia)",
|
||||
"fgServiceNotice": "Questa notifica è necessaria per il controllo degli aggiornamenti in background (può essere nascosta nelle impostazioni del sistema operativo).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Rimuovere l'app?",
|
||||
"other": "Rimuovere le app?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "標準のバージョン検出",
|
||||
"groupByCategory": "カテゴリ別にグループ化する",
|
||||
"autoApkFilterByArch": "可能であれば、CPUアーキテクチャによるAPKのフィルタリングを試みる",
|
||||
"autoLinkFilterByArch": "可能であれば、CPUアーキテクチャによるリンクのフィルタリングを試みる。",
|
||||
"overrideSource": "ソースの上書き",
|
||||
"dontShowAgain": "二度と表示しない",
|
||||
"dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "クラウドソーシングによるアプリの設定",
|
||||
"allowInsecure": "安全でないHTTPリクエストを許可する",
|
||||
"stayOneVersionBehind": "最新のバージョンから1つ前のものを使用する",
|
||||
"useFirstApkOfVersion": "複数のAPKから最初のAPKを自動選択する",
|
||||
"refreshBeforeDownload": "ダウンロード前にアプリの詳細を更新する",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "クールApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "名称",
|
||||
"smartname": "名前(スマート)",
|
||||
"sortMethod": "ソート方法",
|
||||
"welcome": "ようこそ",
|
||||
"documentationLinksNote": "以下のリンクにあるObtainium GitHubページには、ビデオ、記事、ディスカッション、その他のリソースへのリンクがあり、アプリの使い方を理解するのに役立ちます。",
|
||||
"batteryOptimizationNote": "ObtainiumのOSバッテリー最適化を無効にすると、バックグラウンドダウンロードがより確実に動作するようになります。",
|
||||
"fileDeletionError": "ファイルの削除に失敗しました(手動で削除してから再試行してください):\"{}\"",
|
||||
"foregroundService": "フォアグラウンド・サービス",
|
||||
"foregroundServiceExplanation": "更新チェックにフォアグラウンド・サービスを使用する(より信頼性が高いが、より電力を消費する)",
|
||||
"fgServiceNotice": "この通知は、バックグラウンドでアップデートをチェックするために必要です(OSの設定で非表示にできます)。",
|
||||
"removeAppQuestion": {
|
||||
"one": "アプリを削除しますか?",
|
||||
"other": "アプリを削除しますか?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "표준 버전 감지",
|
||||
"groupByCategory": "카테고리별 그룹화",
|
||||
"autoApkFilterByArch": "가능한 경우 CPU 아키텍처별로 APK 필터링 시도",
|
||||
"autoLinkFilterByArch": "가능하면 CPU 아키텍처별로 링크를 필터링해 보세요.",
|
||||
"overrideSource": "소스 재정의",
|
||||
"dontShowAgain": "다시 표시하지 않기",
|
||||
"dontShowTrackOnlyWarnings": "'추적 전용' 경고 표시 안 함",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "크라우드소싱 앱 구성",
|
||||
"allowInsecure": "안전하지 않은 HTTP 요청 허용",
|
||||
"stayOneVersionBehind": "최신 버전보다 한 버전 뒤에 머무르기",
|
||||
"useFirstApkOfVersion": "여러 앱 중 첫 번째 앱 자동 선택",
|
||||
"refreshBeforeDownload": "다운로드 전에 앱 세부 정보 새로 고침",
|
||||
"tencentAppStore": "텐센트 앱 스토어",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "이름",
|
||||
"smartname": "이름(스마트)",
|
||||
"sortMethod": "정렬 방법",
|
||||
"welcome": "환영",
|
||||
"documentationLinksNote": "아래에 링크된 Obtainium 깃허브 페이지에는 앱 사용 방법을 이해하는 데 도움이 되는 동영상, 기사, 토론 및 기타 리소스에 대한 링크가 포함되어 있습니다.",
|
||||
"batteryOptimizationNote": "Obtainium의 OS 배터리 최적화를 비활성화하면 백그라운드 다운로드가 더 안정적으로 작동할 수 있습니다.",
|
||||
"fileDeletionError": "파일을 삭제하지 못했습니다(수동으로 삭제한 후 다시 시도하세요): \"{}\"",
|
||||
"foregroundService": "오베티움 포그라운드 서비스",
|
||||
"foregroundServiceExplanation": "업데이트 확인을 위해 포그라운드 서비스 사용(안정성 향상, 전력 소비량 증가)",
|
||||
"fgServiceNotice": "이 알림은 백그라운드 업데이트 확인에 필요합니다(OS 설정에서 숨길 수 있음).",
|
||||
"removeAppQuestion": {
|
||||
"one": "앱을 제거하시겠습니까?",
|
||||
"other": "앱을 제거하시겠습니까?"
|
||||
|
396
assets/translations/ml.json
Normal file
@ -0,0 +1,396 @@
|
||||
{
|
||||
"invalidURLForSource": "ഒരു സാധുവായ {} ആപ്പ് URL അല്ല",
|
||||
"noReleaseFound": "അനുയോജ്യമായ റിലീസ് കണ്ടെത്താനായില്ല",
|
||||
"noVersionFound": "റിലീസ് പതിപ്പ് നിർണ്ണയിക്കാനായില്ല",
|
||||
"urlMatchesNoSource": "URL അറിയപ്പെടുന്ന ഒരു ഉറവിടവുമായും യോജിക്കുന്നില്ല",
|
||||
"cantInstallOlderVersion": "ആപ്പിന്റെ പഴയ പതിപ്പ് ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല.",
|
||||
"appIdMismatch": "ഡൗൺലോഡ് ചെയ്ത പാക്കേജ് ഐഡി നിലവിലുള്ള ആപ്പ് ഐഡിയുമായി ചേരുന്നില്ല",
|
||||
"functionNotImplemented": "ഈ ക്ലാസ് ഈ ഫംഗ്ഷൻ നടപ്പിലാക്കിയിട്ടില്ല",
|
||||
"placeholder": "സ്ഥലസൂചകം",
|
||||
"someErrors": "ചില പിശകുകൾ സംഭവിച്ചു",
|
||||
"unexpectedError": "പ്രതീക്ഷിക്കാത്ത പിശക്",
|
||||
"ok": "ശരി",
|
||||
"and": "കൂടാതെ",
|
||||
"githubPATLabel": "GitHub പേഴ്സണൽ ആക്സസ് ടോക്കൺ (നിരക്ക് പരിധി വർദ്ധിപ്പിക്കുന്നു)",
|
||||
"includePrereleases": "പ്രീ-റിലീസുകൾ ഉൾപ്പെടുത്തുക",
|
||||
"fallbackToOlderReleases": "പഴയ റിലീസുകളിലേക്ക് മടങ്ങുക",
|
||||
"filterReleaseTitlesByRegEx": "റിലീസ് ശീർഷകങ്ങൾ റെഗുലർ എക്സ്പ്രഷൻ ഉപയോഗിച്ച് ഫിൽട്ടർ ചെയ്യുക",
|
||||
"invalidRegEx": "സാധുവായ റെഗുലർ എക്സ്പ്രഷൻ അല്ല",
|
||||
"noDescription": "വിവരണം ലഭ്യമല്ല",
|
||||
"cancel": "റദ്ദാക്കുക",
|
||||
"continue": "തുടരുക",
|
||||
"requiredInBrackets": "(ആവശ്യമാണ്)",
|
||||
"dropdownNoOptsError": "തെറ്റ്: ഡ്രോപ്പ്ഡൗണിന് കുറഞ്ഞത് ഒരു ഓപ്ഷനെങ്കിലും ഉണ്ടായിരിക്കണം",
|
||||
"colour": "നിറം",
|
||||
"standard": "സാധാരണ",
|
||||
"custom": "ഇഷ്ടാനുസൃതം",
|
||||
"useMaterialYou": "മെറ്റീരിയൽ യൂ ഉപയോഗിക്കുക",
|
||||
"githubStarredRepos": "GitHub സ്റ്റാർ ചെയ്ത റെപ്പോസിറ്ററികൾ",
|
||||
"uname": "ഉപയോക്തൃനാമം",
|
||||
"wrongArgNum": "തെറ്റായ എണ്ണം ആർഗ്യുമെന്റുകൾ നൽകി",
|
||||
"xIsTrackOnly": "{} ട്രാക്ക്-മാത്രം ആണ്",
|
||||
"source": "ഉറവിടം",
|
||||
"app": "ആപ്പ്",
|
||||
"appsFromSourceAreTrackOnly": "ഈ ഉറവിടത്തിൽ നിന്നുള്ള ആപ്പുകൾ 'ട്രാക്ക്-മാത്രം' ആണ്.",
|
||||
"youPickedTrackOnly": "നിങ്ങൾ 'ട്രാക്ക്-മാത്രം' ഓപ്ഷൻ തിരഞ്ഞെടുത്തു.",
|
||||
"trackOnlyAppDescription": "ആപ്പ് അപ്ഡേറ്റുകൾക്കായി ട്രാക്ക് ചെയ്യപ്പെടും, പക്ഷേ ഒബ്റ്റേനിയത്തിന് അത് ഡൗൺലോഡ് ചെയ്യാനോ ഇൻസ്റ്റാൾ ചെയ്യാനോ കഴിയില്ല.",
|
||||
"cancelled": "റദ്ദാക്കി",
|
||||
"appAlreadyAdded": "ആപ്പ് ഇതിനകം കൂട്ടിച്ചേർത്തിട്ടുണ്ട്",
|
||||
"alreadyUpToDateQuestion": "ആപ്പ് ഇതിനകം ഏറ്റവും പുതിയ പതിപ്പിലാണോ?",
|
||||
"addApp": "ആപ്പ് ചേർക്കുക",
|
||||
"appSourceURL": "ആപ്പ് ഉറവിട URL",
|
||||
"error": "പിശക്",
|
||||
"add": "ചേർക്കുക",
|
||||
"searchSomeSourcesLabel": "തിരയുക (ചില ഉറവിടങ്ങളിൽ മാത്രം)",
|
||||
"search": "തിരയുക",
|
||||
"additionalOptsFor": "{} നായുള്ള അധിക ഓപ്ഷനുകൾ",
|
||||
"supportedSources": "പിന്തുണയ്ക്കുന്ന ഉറവിടങ്ങൾ",
|
||||
"trackOnlyInBrackets": "(ട്രാക്ക്-മാത്രം)",
|
||||
"searchableInBrackets": "(തിരയാവുന്നത്)",
|
||||
"appsString": "ആപ്പുകൾ",
|
||||
"noApps": "ആപ്പുകളൊന്നുമില്ല",
|
||||
"noAppsForFilter": "ഈ ഫിൽട്ടറിനായി ആപ്പുകളൊന്നുമില്ല",
|
||||
"byX": "{} വഴി",
|
||||
"percentProgress": "പുരോഗതി: {}%",
|
||||
"pleaseWait": "ദയവായി കാത്തിരിക്കുക",
|
||||
"updateAvailable": "അപ്ഡേറ്റ് ലഭ്യമാണ്",
|
||||
"notInstalled": "ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല",
|
||||
"pseudoVersion": "സ്യൂഡോ-പതിപ്പ്",
|
||||
"selectAll": "എല്ലാം തിരഞ്ഞെടുക്കുക",
|
||||
"deselectX": "{} തിരഞ്ഞെടുക്കൽ റദ്ദാക്കുക",
|
||||
"xWillBeRemovedButRemainInstalled": "{} ഒബ്റ്റേനിയത്തിൽ നിന്ന് നീക്കം ചെയ്യപ്പെടും, പക്ഷേ ഉപകരണത്തിൽ ഇൻസ്റ്റാൾ ചെയ്ത നിലയിൽ തുടരും.",
|
||||
"removeSelectedAppsQuestion": "തിരഞ്ഞെടുത്ത ആപ്പുകൾ നീക്കം ചെയ്യണോ?",
|
||||
"removeSelectedApps": "തിരഞ്ഞെടുത്ത ആപ്പുകൾ നീക്കം ചെയ്യുക",
|
||||
"updateX": "{} അപ്ഡേറ്റ് ചെയ്യുക",
|
||||
"installX": "{} ഇൻസ്റ്റാൾ ചെയ്യുക",
|
||||
"markXTrackOnlyAsUpdated": "{}\n(ട്രാക്ക്-മാത്രം)\nഅപ്ഡേറ്റ് ചെയ്തതായി അടയാളപ്പെടുത്തുക",
|
||||
"changeX": "{} മാറ്റുക",
|
||||
"installUpdateApps": "ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക/അപ്ഡേറ്റ് ചെയ്യുക",
|
||||
"installUpdateSelectedApps": "തിരഞ്ഞെടുത്ത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക/അപ്ഡേറ്റ് ചെയ്യുക",
|
||||
"markXSelectedAppsAsUpdated": "{} തിരഞ്ഞെടുത്ത ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്തതായി അടയാളപ്പെടുത്തണോ?",
|
||||
"no": "ഇല്ല",
|
||||
"yes": "അതെ",
|
||||
"markSelectedAppsUpdated": "തിരഞ്ഞെടുത്ത ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്തതായി അടയാളപ്പെടുത്തുക",
|
||||
"pinToTop": "മുകളിൽ പിൻ ചെയ്യുക",
|
||||
"unpinFromTop": "മുകളിൽ നിന്ന് അൺപിൻ ചെയ്യുക",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "തിരഞ്ഞെടുത്ത ആപ്പുകളുടെ ഇൻസ്റ്റാൾ സ്റ്റാറ്റസ് റീസെറ്റ് ചെയ്യണോ?",
|
||||
"installStatusOfXWillBeResetExplanation": "തിരഞ്ഞെടുത്ത ഏതെങ്കിലും ആപ്പുകളുടെ ഇൻസ്റ്റാൾ സ്റ്റാറ്റസ് റീസെറ്റ് ചെയ്യപ്പെടും.\n\nപരാജയപ്പെട്ട അപ്ഡേറ്റുകളോ മറ്റ് പ്രശ്നങ്ങളോ കാരണം ഒബ്റ്റേനിയത്തിൽ കാണിക്കുന്ന ആപ്പ് പതിപ്പ് തെറ്റായിരിക്കുമ്പോൾ ഇത് സഹായകമാകും.",
|
||||
"customLinkMessage": "ഈ ലിങ്കുകൾ ഒബ്റ്റേനിയം ഇൻസ്റ്റാൾ ചെയ്ത ഉപകരണങ്ങളിൽ പ്രവർത്തിക്കും",
|
||||
"shareAppConfigLinks": "ആപ്പ് കോൺഫിഗറേഷൻ HTML ലിങ്കായി പങ്കിടുക",
|
||||
"shareSelectedAppURLs": "തിരഞ്ഞെടുത്ത ആപ്പ് URL-കൾ പങ്കിടുക",
|
||||
"resetInstallStatus": "ഇൻസ്റ്റാൾ സ്റ്റാറ്റസ് റീസെറ്റ് ചെയ്യുക",
|
||||
"more": "കൂടുതൽ",
|
||||
"removeOutdatedFilter": "കാലഹരണപ്പെട്ട ആപ്പ് ഫിൽട്ടർ നീക്കം ചെയ്യുക",
|
||||
"showOutdatedOnly": "കാലഹരണപ്പെട്ട ആപ്പുകൾ മാത്രം കാണിക്കുക",
|
||||
"filter": "ഫിൽട്ടർ",
|
||||
"filterApps": "ആപ്പുകൾ ഫിൽട്ടർ ചെയ്യുക",
|
||||
"appName": "ആപ്പിന്റെ പേര്",
|
||||
"author": "ഡെവലപ്പർ",
|
||||
"upToDateApps": "ഏറ്റവും പുതിയ പതിപ്പിലുള്ള ആപ്പുകൾ",
|
||||
"nonInstalledApps": "ഇൻസ്റ്റാൾ ചെയ്യാത്ത ആപ്പുകൾ",
|
||||
"importExport": "ഇംപോർട്ട്/എക്സ്പോർട്ട്",
|
||||
"settings": "ക്രമീകരണങ്ങൾ",
|
||||
"exportedTo": "{} ലേക്ക് എക്സ്പോർട്ട് ചെയ്തു",
|
||||
"obtainiumExport": "ഒബ്റ്റേനിയം എക്സ്പോർട്ട്",
|
||||
"invalidInput": "തെറ്റായ ഇൻപുട്ട്",
|
||||
"importedX": "{} ഇംപോർട്ട് ചെയ്തു",
|
||||
"obtainiumImport": "ഒബ്റ്റേനിയം ഇംപോർട്ട്",
|
||||
"importFromURLList": "URL ലിസ്റ്റിൽ നിന്ന് ഇംപോർട്ട് ചെയ്യുക",
|
||||
"searchQuery": "തിരയൽ ചോദ്യം",
|
||||
"appURLList": "ആപ്പ് URL ലിസ്റ്റ്",
|
||||
"line": "വരി",
|
||||
"searchX": "{} തിരയുക",
|
||||
"noResults": "ഫലങ്ങളൊന്നും കണ്ടെത്തിയില്ല",
|
||||
"importX": "{} ഇംപോർട്ട് ചെയ്യുക",
|
||||
"importedAppsIdDisclaimer": "ഇംപോർട്ട് ചെയ്ത ആപ്പുകൾ \"ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല\" എന്ന് തെറ്റായി കാണിച്ചേക്കാം.\nഇത് ശരിയാക്കാൻ, ഒബ്റ്റേനിയം വഴി അവ വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുക.\nഇത് ആപ്പ് ഡാറ്റയെ ബാധിക്കില്ല.\n\nURL, മൂന്നാം കക്ഷി ഇംപോർട്ട് രീതികളെ മാത്രം ബാധിക്കുന്നു.",
|
||||
"importErrors": "ഇംപോർട്ട് പിശകുകൾ",
|
||||
"importedXOfYApps": "{} ആപ്പുകളിൽ {} എണ്ണം ഇംപോർട്ട് ചെയ്തു.",
|
||||
"followingURLsHadErrors": "ഇനിപ്പറയുന്ന URL-കളിൽ പിശകുകൾ ഉണ്ടായിരുന്നു:",
|
||||
"selectURL": "URL തിരഞ്ഞെടുക്കുക",
|
||||
"selectURLs": "URL-കൾ തിരഞ്ഞെടുക്കുക",
|
||||
"pick": "തിരഞ്ഞെടുക്കുക",
|
||||
"theme": "തീം",
|
||||
"dark": "ഇരുണ്ട",
|
||||
"light": "ഇളം",
|
||||
"followSystem": "സിസ്റ്റം പിന്തുടരുക",
|
||||
"followSystemThemeExplanation": "സിസ്റ്റം തീം പിന്തുടരുന്നത് തേർഡ് പാർട്ടി ആപ്ലിക്കേഷനുകൾ ഉപയോഗിച്ച് മാത്രമേ സാധ്യമാകൂ",
|
||||
"useBlackTheme": "തനി കറുപ്പ് തീം ഉപയോഗിക്കുക",
|
||||
"appSortBy": "ആപ്പ് അടുക്കേണ്ട രീതി",
|
||||
"authorName": "ഡെവലപ്പർ/പേര്",
|
||||
"nameAuthor": "പേര്/ഡെവലപ്പർ",
|
||||
"asAdded": "ചേർത്ത ക്രമത്തിൽ",
|
||||
"appSortOrder": "ആപ്പ് അടുക്കേണ്ട ക്രമം",
|
||||
"ascending": "ആരോഹണ ക്രമത്തിൽ",
|
||||
"descending": "അവരോഹണ ക്രമത്തിൽ",
|
||||
"bgUpdateCheckInterval": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധന ഇടവേള",
|
||||
"neverManualOnly": "ഒരിക്കലുമില്ല - സ്വമേധയാ മാത്രം",
|
||||
"appearance": "രൂപം",
|
||||
"showWebInAppView": "ആപ്പ് കാഴ്ചയിൽ ഉറവിട വെബ്പേജ് കാണിക്കുക",
|
||||
"pinUpdates": "അപ്ഡേറ്റുകൾ ആപ്പ് കാഴ്ചയുടെ മുകളിൽ പിൻ ചെയ്യുക",
|
||||
"updates": "അപ്ഡേറ്റുകൾ",
|
||||
"sourceSpecific": "ഉറവിടം അടിസ്ഥാനമാക്കി",
|
||||
"appSource": "ആപ്പ് ഉറവിടം",
|
||||
"noLogs": "ലോഗുകളൊന്നുമില്ല",
|
||||
"appLogs": "ആപ്പ് ലോഗുകൾ",
|
||||
"close": "അടയ്ക്കുക",
|
||||
"share": "പങ്കിടുക",
|
||||
"appNotFound": "ആപ്പ് കണ്ടെത്തിയില്ല",
|
||||
"obtainiumExportHyphenatedLowercase": "ഒബ്റ്റേനിയം-എക്സ്പോർട്ട്",
|
||||
"pickAnAPK": "ഒരു APK തിരഞ്ഞെടുക്കുക",
|
||||
"appHasMoreThanOnePackage": "{}-ന് ഒന്നിൽ കൂടുതൽ പാക്കേജുകൾ ഉണ്ട്:",
|
||||
"deviceSupportsXArch": "നിങ്ങളുടെ ഉപകരണം {} CPU ആർക്കിടെക്ചറിനെ പിന്തുണയ്ക്കുന്നു.",
|
||||
"deviceSupportsFollowingArchs": "നിങ്ങളുടെ ഉപകരണം ഇനിപ്പറയുന്ന CPU ആർക്കിടെക്ചറുകളെ പിന്തുണയ്ക്കുന്നു:",
|
||||
"warning": "മുന്നറിയിപ്പ്",
|
||||
"sourceIsXButPackageFromYPrompt": "ആപ്പ് ഉറവിടം '{}' ആണ്, പക്ഷേ റിലീസ് പാക്കേജ് '{}'-ൽ നിന്നാണ് വരുന്നത്. തുടരണോ?",
|
||||
"updatesAvailable": "അപ്ഡേറ്റുകൾ ലഭ്യമാണ്",
|
||||
"updatesAvailableNotifDescription": "ഒബ്റ്റേനിയം ട്രാക്ക് ചെയ്യുന്ന ഒന്നോ അതിലധികമോ ആപ്പുകൾക്ക് അപ്ഡേറ്റുകൾ ലഭ്യമാണെന്ന് ഉപയോക്താവിനെ അറിയിക്കുന്നു",
|
||||
"noNewUpdates": "പുതിയ അപ്ഡേറ്റുകളൊന്നുമില്ല.",
|
||||
"xHasAnUpdate": "{}-ന് ഒരു അപ്ഡേറ്റ് ഉണ്ട്.",
|
||||
"appsUpdated": "ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്തു",
|
||||
"appsNotUpdated": "ആപ്ലിക്കേഷനുകൾ അപ്ഡേറ്റ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു",
|
||||
"appsUpdatedNotifDescription": "ഒന്നോ അതിലധികമോ ആപ്പുകളിലേക്കുള്ള അപ്ഡേറ്റുകൾ ബാക്ക്ഗ്രൗണ്ടിൽ പ്രയോഗിച്ചു എന്ന് ഉപയോക്താവിനെ അറിയിക്കുന്നു",
|
||||
"xWasUpdatedToY": "{} {} ലേക്ക് അപ്ഡേറ്റ് ചെയ്തു.",
|
||||
"xWasNotUpdatedToY": "{} {} ലേക്ക് അപ്ഡേറ്റ് ചെയ്യാൻ പരാജയപ്പെട്ടു.",
|
||||
"errorCheckingUpdates": "അപ്ഡേറ്റുകൾ പരിശോധിക്കുന്നതിൽ പിശക്",
|
||||
"errorCheckingUpdatesNotifDescription": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധന പരാജയപ്പെടുമ്പോൾ കാണിക്കുന്ന അറിയിപ്പ്",
|
||||
"appsRemoved": "ആപ്പുകൾ നീക്കം ചെയ്തു",
|
||||
"appsRemovedNotifDescription": "ഒന്നോ അതിലധികമോ ആപ്പുകൾ ലോഡ് ചെയ്യുന്നതിൽ പിശകുകൾ കാരണം നീക്കം ചെയ്തു എന്ന് ഉപയോക്താവിനെ അറിയിക്കുന്നു",
|
||||
"xWasRemovedDueToErrorY": "{} ഈ പിശക് കാരണം നീക്കം ചെയ്തു: {}",
|
||||
"completeAppInstallation": "ആപ്പ് ഇൻസ്റ്റാളേഷൻ പൂർത്തിയാക്കുക",
|
||||
"obtainiumMustBeOpenToInstallApps": "ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ ഒബ്റ്റേനിയം തുറന്നിരിക്കണം",
|
||||
"completeAppInstallationNotifDescription": "ഒരു ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യുന്നത് പൂർത്തിയാക്കാൻ ഒബ്റ്റേനിയത്തിലേക്ക് മടങ്ങാൻ ഉപയോക്താവിനോട് ആവശ്യപ്പെടുന്നു",
|
||||
"checkingForUpdates": "അപ്ഡേറ്റുകൾ പരിശോധിക്കുന്നു",
|
||||
"checkingForUpdatesNotifDescription": "അപ്ഡേറ്റുകൾ പരിശോധിക്കുമ്പോൾ ദൃശ്യമാകുന്ന താൽക്കാലിക അറിയിപ്പ്",
|
||||
"pleaseAllowInstallPerm": "ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ ഒബ്റ്റേനിയത്തിന് അനുമതി നൽകുക",
|
||||
"trackOnly": "ട്രാക്ക്-മാത്രം",
|
||||
"errorWithHttpStatusCode": "പിശക് {}",
|
||||
"versionCorrectionDisabled": "പതിപ്പ് തിരുത്തൽ പ്രവർത്തനരഹിതമാക്കി (പ്ലഗിൻ പ്രവർത്തിക്കുന്നതായി തോന്നുന്നില്ല)",
|
||||
"unknown": "അജ്ഞാതം",
|
||||
"none": "ഒന്നുമില്ല",
|
||||
"never": "ഒരിക്കലുമില്ല",
|
||||
"latestVersionX": "ഏറ്റവും പുതിയത്: {}",
|
||||
"installedVersionX": "ഇൻസ്റ്റാൾ ചെയ്തത്: {}",
|
||||
"lastUpdateCheckX": "അവസാന അപ്ഡേറ്റ് പരിശോധന: {}",
|
||||
"remove": "നീക്കം ചെയ്യുക",
|
||||
"yesMarkUpdated": "അതെ, അപ്ഡേറ്റ് ചെയ്തതായി അടയാളപ്പെടുത്തുക",
|
||||
"fdroid": "എഫ്-ഡ്രോയിഡ് ഒഫീഷ്യൽ",
|
||||
"appIdOrName": "ആപ്പ് ഐഡി അല്ലെങ്കിൽ പേര്",
|
||||
"appId": "ആപ്പ് ഐഡി",
|
||||
"appWithIdOrNameNotFound": "ആ ഐഡിയിലോ പേരിലോ ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല",
|
||||
"reposHaveMultipleApps": "റെപ്പോകളിൽ ഒന്നിലധികം ആപ്പുകൾ അടങ്ങിയിരിക്കാം",
|
||||
"fdroidThirdPartyRepo": "എഫ്-ഡ്രോയിഡ് തേർഡ് പാർട്ടി റെപ്പോ",
|
||||
"install": "ഇൻസ്റ്റാൾ ചെയ്യുക",
|
||||
"markInstalled": "ഇൻസ്റ്റാൾ ചെയ്തതായി അടയാളപ്പെടുത്തുക",
|
||||
"update": "അപ്ഡേറ്റ് ചെയ്യുക",
|
||||
"markUpdated": "അപ്ഡേറ്റ് ചെയ്തതായി അടയാളപ്പെടുത്തുക",
|
||||
"additionalOptions": "കൂടുതൽ ഓപ്ഷനുകൾ",
|
||||
"disableVersionDetection": "പതിപ്പ് കണ്ടെത്തൽ പ്രവർത്തനരഹിതമാക്കുക",
|
||||
"noVersionDetectionExplanation": "പതിപ്പ് കണ്ടെത്തൽ ശരിയായി പ്രവർത്തിക്കാത്ത ആപ്പുകൾക്ക് മാത്രമേ ഈ ഓപ്ഷൻ ഉപയോഗിക്കാവൂ.",
|
||||
"downloadingX": "{} ഡൗൺലോഡ് ചെയ്യുന്നു",
|
||||
"downloadX": "{} ഡൗൺലോഡ് ചെയ്യുക",
|
||||
"downloadedX": "{} ഡൗൺലോഡ് ചെയ്തു",
|
||||
"releaseAsset": "റിലീസ് അസറ്റ്",
|
||||
"downloadNotifDescription": "ഒരു ആപ്പ് ഡൗൺലോഡ് ചെയ്യുന്നതിലെ പുരോഗതി ഉപയോക്താവിനെ അറിയിക്കുന്നു",
|
||||
"noAPKFound": "APK കണ്ടെത്തിയില്ല",
|
||||
"noVersionDetection": "പതിപ്പ് കണ്ടെത്തൽ ഇല്ല",
|
||||
"categorize": "വിഭാഗീകരിക്കുക",
|
||||
"categories": "വിഭാഗങ്ങൾ",
|
||||
"category": "വിഭാഗം",
|
||||
"noCategory": "വിഭാഗമൊന്നുമില്ല",
|
||||
"noCategories": "വിഭാഗങ്ങളൊന്നുമില്ല",
|
||||
"deleteCategoriesQuestion": "വിഭാഗങ്ങൾ ഇല്ലാതാക്കണോ?",
|
||||
"categoryDeleteWarning": "ഇല്ലാതാക്കിയ വിഭാഗങ്ങളിലെ എല്ലാ ആപ്പുകളും വിഭാഗരഹിതമായി ക്രമീകരിക്കും.",
|
||||
"addCategory": "വിഭാഗം ചേർക്കുക",
|
||||
"label": "ലേബൽ",
|
||||
"language": "ഭാഷ",
|
||||
"copiedToClipboard": "ക്ലിപ്ബോർഡിലേക്ക് പകർത്തി",
|
||||
"storagePermissionDenied": "സ്റ്റോറേജ് അനുമതി നിഷേധിച്ചു",
|
||||
"selectedCategorizeWarning": "ഇത് തിരഞ്ഞെടുത്ത ആപ്പുകൾക്കായി നിലവിലുള്ള ഏതൊരു വിഭാഗ ക്രമീകരണങ്ങളെയും മാറ്റിസ്ഥാപിക്കും.",
|
||||
"filterAPKsByRegEx": "റെഗുലർ എക്സ്പ്രഷൻ ഉപയോഗിച്ച് APK-കൾ ഫിൽട്ടർ ചെയ്യുക",
|
||||
"removeFromObtainium": "ഒബ്റ്റേനിയത്തിൽ നിന്ന് നീക്കം ചെയ്യുക",
|
||||
"uninstallFromDevice": "ഉപകരണത്തിൽ നിന്ന് അൺഇൻസ്റ്റാൾ ചെയ്യുക",
|
||||
"onlyWorksWithNonVersionDetectApps": "പതിപ്പ് കണ്ടെത്തൽ പ്രവർത്തനരഹിതമാക്കിയ ആപ്പുകൾക്ക് മാത്രമേ ഇത് പ്രവർത്തിക്കൂ.",
|
||||
"releaseDateAsVersion": "റിലീസ് തീയതി പതിപ്പ് സ്ട്രിംഗായി ഉപയോഗിക്കുക",
|
||||
"releaseTitleAsVersion": "റിലീസ് ശീർഷകം പതിപ്പ് സ്ട്രിംഗായി ഉപയോഗിക്കുക",
|
||||
"releaseDateAsVersionExplanation": "പതിപ്പ് കണ്ടെത്തൽ ശരിയായി പ്രവർത്തിക്കാത്തതും എന്നാൽ റിലീസ് തീയതി ലഭ്യമായതുമായ ആപ്പുകൾക്ക് മാത്രമേ ഈ ഓപ്ഷൻ ഉപയോഗിക്കാവൂ.",
|
||||
"changes": "മാറ്റങ്ങൾ",
|
||||
"releaseDate": "റിലീസ് തീയതി",
|
||||
"importFromURLsInFile": "ഫയലിലെ URL-കളിൽ നിന്ന് ഇംപോർട്ട് ചെയ്യുക (OPML പോലെ)",
|
||||
"versionDetectionExplanation": "കണ്ടെത്തിയ പതിപ്പ് സ്ട്രിംഗ് OS-ൽ നിന്ന് കണ്ടെത്തിയ പതിപ്പുമായി പൊരുത്തപ്പെടുത്തുക",
|
||||
"versionDetection": "പതിപ്പ് കണ്ടെത്തൽ",
|
||||
"standardVersionDetection": "സാധാരണ പതിപ്പ് കണ്ടെത്തൽ",
|
||||
"groupByCategory": "വിഭാഗം തിരിച്ച് ഗ്രൂപ്പ് ചെയ്യുക",
|
||||
"autoApkFilterByArch": "സാധ്യമെങ്കിൽ CPU ആർക്കിടെക്ചർ അനുസരിച്ച് APK-കൾ ഫിൽട്ടർ ചെയ്യാൻ ശ്രമിക്കുക",
|
||||
"autoLinkFilterByArch": "സാധ്യമെങ്കിൽ CPU ആർക്കിടെക്ചർ അനുസരിച്ച് ലിങ്കുകൾ ഫിൽട്ടർ ചെയ്യാൻ ശ്രമിക്കുക",
|
||||
"overrideSource": "ഉറവിടം അസാധുവാക്കുക",
|
||||
"dontShowAgain": "ഇത് വീണ്ടും കാണിക്കരുത്",
|
||||
"dontShowTrackOnlyWarnings": "'ട്രാക്ക്-മാത്രം' മുന്നറിയിപ്പുകൾ കാണിക്കരുത്",
|
||||
"dontShowAPKOriginWarnings": "APK ഉറവിട മുന്നറിയിപ്പുകൾ കാണിക്കരുത്",
|
||||
"moveNonInstalledAppsToBottom": "ഇൻസ്റ്റാൾ ചെയ്യാത്ത ആപ്പുകൾ ആപ്പ് കാഴ്ചയുടെ താഴേക്ക് മാറ്റുക",
|
||||
"gitlabPATLabel": "GitLab പേഴ്സണൽ ആക്സസ് ടോക്കൺ",
|
||||
"about": "വിവരങ്ങൾ",
|
||||
"requiresCredentialsInSettings": "{}-ന് അധിക ക്രെഡൻഷ്യലുകൾ ആവശ്യമാണ് (ക്രമീകരണങ്ങളിൽ)",
|
||||
"checkOnStart": "തുടങ്ങുമ്പോൾ അപ്ഡേറ്റുകൾക്കായി പരിശോധിക്കുക",
|
||||
"tryInferAppIdFromCode": "സോഴ്സ് കോഡിൽ നിന്ന് ആപ്പ് ഐഡി അനുമാനിക്കാൻ ശ്രമിക്കുക",
|
||||
"removeOnExternalUninstall": "പുറത്ത് നിന്ന് അൺഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സ്വയമേവ നീക്കം ചെയ്യുക",
|
||||
"pickHighestVersionCode": "ഏറ്റവും ഉയർന്ന പതിപ്പ് കോഡ് APK സ്വയമേവ തിരഞ്ഞെടുക്കുക",
|
||||
"checkUpdateOnDetailPage": "ഒരു ആപ്പ് വിശദാംശ പേജ് തുറക്കുമ്പോൾ അപ്ഡേറ്റുകൾക്കായി പരിശോധിക്കുക",
|
||||
"disablePageTransitions": "പേജ് ട്രാൻസിഷൻ ആനിമേഷനുകൾ പ്രവർത്തനരഹിതമാക്കുക",
|
||||
"reversePageTransitions": "പേജ് ട്രാൻസിഷൻ ആനിമേഷനുകൾ വിപരീതമാക്കുക",
|
||||
"minStarCount": "കുറഞ്ഞ നക്ഷത്രങ്ങളുടെ എണ്ണം",
|
||||
"addInfoBelow": "ഈ വിവരങ്ങൾ താഴെ ചേർക്കുക.",
|
||||
"addInfoInSettings": "ഈ വിവരങ്ങൾ ക്രമീകരണങ്ങളിൽ ചേർക്കുക.",
|
||||
"githubSourceNote": "API കീ ഉപയോഗിച്ച് GitHub നിരക്ക് പരിധി ഒഴിവാക്കാം.",
|
||||
"sortByLastLinkSegment": "ലിങ്കിന്റെ അവസാന ഭാഗം മാത്രം ഉപയോഗിച്ച് അടുക്കുക",
|
||||
"filterReleaseNotesByRegEx": "റെഗുലർ എക്സ്പ്രഷൻ ഉപയോഗിച്ച് റിലീസ് നോട്ടുകൾ ഫിൽട്ടർ ചെയ്യുക",
|
||||
"customLinkFilterRegex": "റെഗുലർ എക്സ്പ്രഷൻ ഉപയോഗിച്ചുള്ള കസ്റ്റം APK ലിങ്ക് ഫിൽട്ടർ (സ്ഥിരസ്ഥിതി '.apk$')",
|
||||
"appsPossiblyUpdated": "ആപ്പ് അപ്ഡേറ്റുകൾ ശ്രമിച്ചു",
|
||||
"appsPossiblyUpdatedNotifDescription": "ഒന്നോ അതിലധികമോ ആപ്പുകളിലേക്കുള്ള അപ്ഡേറ്റുകൾ ബാക്ക്ഗ്രൗണ്ടിൽ പ്രയോഗിക്കാൻ സാധ്യതയുണ്ടെന്ന് ഉപയോക്താവിനെ അറിയിക്കുന്നു",
|
||||
"xWasPossiblyUpdatedToY": "{} {} ലേക്ക് അപ്ഡേറ്റ് ചെയ്തിരിക്കാം.",
|
||||
"enableBackgroundUpdates": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റുകൾ പ്രവർത്തനക്ഷമമാക്കുക",
|
||||
"backgroundUpdateReqsExplanation": "എല്ലാ ആപ്പുകൾക്കും ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റുകൾ സാധ്യമായെന്ന് വരില്ല.",
|
||||
"backgroundUpdateLimitsExplanation": "ബാക്ക്ഗ്രൗണ്ട് ഇൻസ്റ്റാളിന്റെ വിജയം ഒബ്റ്റേനിയം തുറക്കുമ്പോൾ മാത്രമേ നിർണ്ണയിക്കാൻ കഴിയൂ.",
|
||||
"verifyLatestTag": "'ഏറ്റവും പുതിയ' ടാഗ് പരിശോധിക്കുക",
|
||||
"intermediateLinkRegex": "സന്ദർശിക്കാൻ ഒരു 'ഇന്റർമീഡിയറ്റ്' ലിങ്കിനായി ഫിൽട്ടർ ചെയ്യുക",
|
||||
"filterByLinkText": "ലിങ്ക് ടെക്സ്റ്റ് ഉപയോഗിച്ച് ലിങ്കുകൾ ഫിൽട്ടർ ചെയ്യുക",
|
||||
"intermediateLinkNotFound": "ഇന്റർമീഡിയറ്റ് ലിങ്ക് കണ്ടെത്തിയില്ല",
|
||||
"intermediateLink": "ഇന്റർമീഡിയറ്റ് ലിങ്ക്",
|
||||
"exemptFromBackgroundUpdates": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റുകളിൽ നിന്ന് ഒഴിവാക്കുക (പ്രവർത്തനക്ഷമമാക്കിയിട്ടുണ്ടെങ്കിൽ)",
|
||||
"bgUpdatesOnWiFiOnly": "വൈഫൈയിൽ അല്ലാത്തപ്പോൾ ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റുകൾ പ്രവർത്തനരഹിതമാക്കുക",
|
||||
"bgUpdatesWhileChargingOnly": "ചാർജ് ചെയ്യാത്തപ്പോൾ ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റുകൾ പ്രവർത്തനരഹിതമാക്കുക",
|
||||
"autoSelectHighestVersionCode": "ഏറ്റവും ഉയർന്ന versionCode APK സ്വയമേവ തിരഞ്ഞെടുക്കുക",
|
||||
"versionExtractionRegEx": "പതിപ്പ് സ്ട്രിംഗ് വേർതിരിച്ചെടുക്കുന്നതിനുള്ള റെഗുലർ എക്സ്പ്രഷൻ",
|
||||
"trimVersionString": "റെഗുലർ എക്സ്പ്രഷൻ ഉപയോഗിച്ച് പതിപ്പ് സ്ട്രിംഗ് ട്രിം ചെയ്യുക",
|
||||
"matchGroupToUseForX": "\"{}\" നായി ഉപയോഗിക്കേണ്ട ഗ്രൂപ്പ് മാച്ച് ചെയ്യുക",
|
||||
"matchGroupToUse": "പതിപ്പ് സ്ട്രിംഗ് വേർതിരിച്ചെടുക്കുന്നതിനുള്ള റെഗുലർ എക്സ്പ്രഷനായി ഉപയോഗിക്കേണ്ട ഗ്രൂപ്പ് മാച്ച് ചെയ്യുക",
|
||||
"highlightTouchTargets": "കുറഞ്ഞ വ്യക്തതയുള്ള ടച്ച് ടാർഗെറ്റുകൾ ഹൈലൈറ്റ് ചെയ്യുക",
|
||||
"pickExportDir": "എക്സ്പോർട്ട് ഡയറക്ടറി തിരഞ്ഞെടുക്കുക",
|
||||
"autoExportOnChanges": "മാറ്റങ്ങൾ വരുമ്പോൾ സ്വയമേവ എക്സ്പോർട്ട് ചെയ്യുക",
|
||||
"includeSettings": "ക്രമീകരണങ്ങൾ ഉൾപ്പെടുത്തുക",
|
||||
"filterVersionsByRegEx": "റെഗുലർ എക്സ്പ്രഷൻ ഉപയോഗിച്ച് പതിപ്പുകൾ ഫിൽട്ടർ ചെയ്യുക",
|
||||
"trySelectingSuggestedVersionCode": "നിർദ്ദേശിച്ച versionCode APK തിരഞ്ഞെടുക്കാൻ ശ്രമിക്കുക",
|
||||
"dontSortReleasesList": "API-യിൽ നിന്നുള്ള റിലീസ് ഓർഡർ നിലനിർത്തുക",
|
||||
"reverseSort": "റിവേഴ്സ് സോർട്ടിംഗ്",
|
||||
"takeFirstLink": "ആദ്യ ലിങ്ക് എടുക്കുക",
|
||||
"skipSort": "സോർട്ടിംഗ് ഒഴിവാക്കുക",
|
||||
"debugMenu": "ഡീബഗ് മെനു",
|
||||
"bgTaskStarted": "ബാക്ക്ഗ്രൗണ്ട് ടാസ്ക് ആരംഭിച്ചു - ലോഗുകൾ പരിശോധിക്കുക.",
|
||||
"runBgCheckNow": "ഇപ്പോൾ ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധന പ്രവർത്തിപ്പിക്കുക",
|
||||
"versionExtractWholePage": "മുഴുവൻ പേജിലും പതിപ്പ് സ്ട്രിംഗ് വേർതിരിച്ചെടുക്കുന്ന റെഗുലർ എക്സ്പ്രഷൻ പ്രയോഗിക്കുക",
|
||||
"installing": "ഇൻസ്റ്റാൾ ചെയ്യുന്നു",
|
||||
"skipUpdateNotifications": "അപ്ഡേറ്റ് അറിയിപ്പുകൾ ഒഴിവാക്കുക",
|
||||
"updatesAvailableNotifChannel": "അപ്ഡേറ്റുകൾ ലഭ്യമാണ്",
|
||||
"appsUpdatedNotifChannel": "ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്തു",
|
||||
"appsPossiblyUpdatedNotifChannel": "ആപ്പ് അപ്ഡേറ്റുകൾ ശ്രമിച്ചു",
|
||||
"errorCheckingUpdatesNotifChannel": "അപ്ഡേറ്റുകൾ പരിശോധിക്കുന്നതിൽ പിശക്",
|
||||
"appsRemovedNotifChannel": "ആപ്പുകൾ നീക്കം ചെയ്തു",
|
||||
"downloadingXNotifChannel": "{} ഡൗൺലോഡ് ചെയ്യുന്നു",
|
||||
"completeAppInstallationNotifChannel": "ആപ്പ് ഇൻസ്റ്റാളേഷൻ പൂർത്തിയാക്കുക",
|
||||
"checkingForUpdatesNotifChannel": "അപ്ഡേറ്റുകൾ പരിശോധിക്കുന്നു",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "ഇൻസ്റ്റാൾ ചെയ്തതും ട്രാക്ക്-മാത്രം ആപ്പുകളും മാത്രം അപ്ഡേറ്റുകൾക്കായി പരിശോധിക്കുക",
|
||||
"supportFixedAPKURL": "സ്ഥിരമായ APK URL-കളെ പിന്തുണയ്ക്കുക",
|
||||
"selectX": "{} തിരഞ്ഞെടുക്കുക",
|
||||
"parallelDownloads": "പാരലൽ ഡൗൺലോഡുകൾ അനുവദിക്കുക",
|
||||
"useShizuku": "ഇൻസ്റ്റാൾ ചെയ്യാൻ ഷിസുകു അല്ലെങ്കിൽ സ്യൂ ഉപയോഗിക്കുക",
|
||||
"shizukuBinderNotFound": "ഷിസുകു സേവനം പ്രവർത്തിക്കുന്നില്ല",
|
||||
"shizukuOld": "പഴയ ഷിസുകു പതിപ്പ് (<11) - അത് അപ്ഡേറ്റ് ചെയ്യുക",
|
||||
"shizukuOldAndroidWithADB": "ആൻഡ്രോയിഡ് < 8.1 ഉള്ള ഷിസുകു ADB ഉപയോഗിച്ച് പ്രവർത്തിക്കുന്നു - ആൻഡ്രോയിഡ് അപ്ഡേറ്റ് ചെയ്യുക അല്ലെങ്കിൽ പകരം സ്യൂ ഉപയോഗിക്കുക",
|
||||
"shizukuPretendToBeGooglePlay": "Google Play ഇൻസ്റ്റാളേഷൻ ഉറവിടമായി സജ്ജമാക്കുക (ഷിസുകു ഉപയോഗിക്കുകയാണെങ്കിൽ)",
|
||||
"useSystemFont": "സിസ്റ്റം ഫോണ്ട് ഉപയോഗിക്കുക",
|
||||
"useVersionCodeAsOSVersion": "ആപ്പ് versionCode OS-ൽ കണ്ടെത്തിയ പതിപ്പായി ഉപയോഗിക്കുക",
|
||||
"requestHeader": "അഭ്യർത്ഥന തലക്കെട്ട്",
|
||||
"useLatestAssetDateAsReleaseDate": "ഏറ്റവും പുതിയ അസറ്റ് അപ്ലോഡ് റിലീസ് തീയതിയായി ഉപയോഗിക്കുക",
|
||||
"defaultPseudoVersioningMethod": "സ്ഥിരസ്ഥിതി സ്യൂഡോ-പതിപ്പ് രീതി",
|
||||
"partialAPKHash": "ഭാഗിക APK ഹാഷ്",
|
||||
"APKLinkHash": "APK ലിങ്ക് ഹാഷ്",
|
||||
"directAPKLink": "നേരിട്ടുള്ള APK ലിങ്ക്",
|
||||
"pseudoVersionInUse": "ഒരു സ്യൂഡോ-പതിപ്പ് ഉപയോഗത്തിലുണ്ട്",
|
||||
"installed": "ഇൻസ്റ്റാൾ ചെയ്തത്",
|
||||
"latest": "ഏറ്റവും പുതിയത്",
|
||||
"invertRegEx": "റെഗുലർ എക്സ്പ്രഷൻ വിപരീതമാക്കുക",
|
||||
"note": "ശ്രദ്ധിക്കുക",
|
||||
"selfHostedNote": "ഏത് ഉറവിടത്തിന്റെയും സെൽഫ്-ഹോസ്റ്റ് ചെയ്ത/കസ്റ്റം ഇൻസ്റ്റൻസുകളിലേക്ക് എത്താൻ \"{}\" ഡ്രോപ്പ്ഡൗൺ ഉപയോഗിക്കാം.",
|
||||
"badDownload": "APK പാഴ്സ് ചെയ്യാൻ കഴിഞ്ഞില്ല (അനുയോജ്യമല്ലാത്തതോ ഭാഗികമായ ഡൗൺലോഡോ)",
|
||||
"beforeNewInstallsShareToAppVerifier": "പുതിയ ആപ്പുകൾ AppVerifier-ലേക്ക് പങ്കിടുക (ലഭ്യമെങ്കിൽ)",
|
||||
"appVerifierInstructionToast": "AppVerifier-ലേക്ക് പങ്കിടുക, തുടർന്ന് തയ്യാറാകുമ്പോൾ ഇവിടെ തിരിച്ചെത്തുക.",
|
||||
"wiki": "സഹായം/വിക്കി",
|
||||
"crowdsourcedConfigsLabel": "ക്രൗഡ്സോഴ്സ് ചെയ്ത ആപ്പ് കോൺഫിഗറേഷനുകൾ (സ്വന്തം ഉത്തരവാദിത്തത്തിൽ ഉപയോഗിക്കുക)",
|
||||
"crowdsourcedConfigsShort": "ക്രൗഡ്സോഴ്സ് ചെയ്ത ആപ്പ് കോൺഫിഗറേഷനുകൾ",
|
||||
"allowInsecure": "സുരക്ഷിതമല്ലാത്ത HTTP അഭ്യർത്ഥനകൾ അനുവദിക്കുക",
|
||||
"stayOneVersionBehind": "ഏറ്റവും പുതിയ പതിപ്പിന് ഒരു പതിപ്പ് പിന്നിൽ തുടരുക",
|
||||
"useFirstApkOfVersion": "ഒന്നിലധികം APK-കളിൽ നിന്ന് ആദ്യത്തേത് സ്വയമേവ തിരഞ്ഞെടുക്കുക",
|
||||
"refreshBeforeDownload": "ഡൗൺലോഡ് ചെയ്യുന്നതിന് മുമ്പ് ആപ്പ് വിവരങ്ങൾ പുതുക്കുക",
|
||||
"tencentAppStore": "ടെൻസെന്റ് ആപ്പ് സ്റ്റോർ",
|
||||
"coolApk": "കൂൾ APK",
|
||||
"vivoAppStore": "വിവോ ആപ്പ് സ്റ്റോർ (CN)",
|
||||
"name": "പേര്",
|
||||
"smartname": "പേര് (സ്മാർട്ട്)",
|
||||
"sortMethod": "അടുക്കുന്ന രീതി",
|
||||
"welcome": "സ്വാഗതം",
|
||||
"documentationLinksNote": "താഴെ ലിങ്ക് ചെയ്തിട്ടുള്ള ഒബ്റ്റേനിയം GitHub പേജിൽ ആപ്പ് എങ്ങനെ ഉപയോഗിക്കാമെന്ന് മനസ്സിലാക്കാൻ സഹായിക്കുന്ന വീഡിയോകൾ, ലേഖനങ്ങൾ, ചർച്ചകൾ, മറ്റ് വിഭവങ്ങൾ എന്നിവയുടെ ലിങ്കുകൾ അടങ്ങിയിരിക്കുന്നു.",
|
||||
"batteryOptimizationNote": "ഒബ്റ്റേനിയത്തിനായുള്ള OS ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകൾ പ്രവർത്തനരഹിതമാക്കുകയാണെങ്കിൽ ബാക്ക്ഗ്രൗണ്ട് ഡൗൺലോഡുകൾ കൂടുതൽ വിശ്വസനീയമായി പ്രവർത്തിച്ചേക്കാം എന്നത് ശ്രദ്ധിക്കുക.",
|
||||
"fileDeletionError": "ഫയൽ ഇല്ലാതാക്കാൻ പരാജയപ്പെട്ടു (സ്വമേധയാ ഇല്ലാതാക്കാൻ ശ്രമിക്കുക, എന്നിട്ട് വീണ്ടും ശ്രമിക്കുക): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "ആപ്പ് നീക്കം ചെയ്യണോ?",
|
||||
"other": "ആപ്പുകൾ നീക്കം ചെയ്യണോ?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "നിരവധി അഭ്യർത്ഥനകൾ (നിരക്ക് പരിമിതപ്പെടുത്തി) - {} മിനിറ്റിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക",
|
||||
"other": "നിരവധി അഭ്യർത്ഥനകൾ (നിരക്ക് പരിമിതപ്പെടുത്തി) - {} മിനിറ്റുകൾക്കുള്ളിൽ വീണ്ടും ശ്രമിക്കുക"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധനയിൽ ഒരു {} നേരിട്ടു, {} മിനിറ്റിനുള്ളിൽ വീണ്ടും ശ്രമിക്കാൻ ക്രമീകരിക്കും",
|
||||
"other": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധനയിൽ ഒരു {} നേരിട്ടു, {} മിനിറ്റുകൾക്കുള്ളിൽ വീണ്ടും ശ്രമിക്കാൻ ക്രമീകരിക്കും"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധനയിൽ {} അപ്ഡേറ്റ് കണ്ടെത്തി - ആവശ്യമെങ്കിൽ ഉപയോക്താവിനെ അറിയിക്കും",
|
||||
"other": "ബാക്ക്ഗ്രൗണ്ട് അപ്ഡേറ്റ് പരിശോധനയിൽ {} അപ്ഡേറ്റുകൾ കണ്ടെത്തി - ആവശ്യമെങ്കിൽ ഉപയോക്താവിനെ അറിയിക്കും"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} ആപ്പ്",
|
||||
"other": "{} ആപ്പുകൾ"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL",
|
||||
"other": "{} URL-കൾ"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} മിനിറ്റ്",
|
||||
"other": "{} മിനിറ്റുകൾ"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} മണിക്കൂർ",
|
||||
"other": "{} മണിക്കൂർ"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} ദിവസം",
|
||||
"other": "{} ദിവസങ്ങൾ"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "{n} ലോഗ് മായ്ച്ചു (മുമ്പ് = {before}, ശേഷം = {after})",
|
||||
"other": "{n} ലോഗുകൾ മായ്ച്ചു (മുമ്പ് = {before}, ശേഷം = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} കൂടാതെ 1 ആപ്പിന് കൂടി അപ്ഡേറ്റുകൾ ലഭ്യമാണ്.",
|
||||
"other": "{} കൂടാതെ {} ആപ്പുകൾക്ക് കൂടി അപ്ഡേറ്റുകൾ ലഭ്യമാണ്."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} കൂടാതെ 1 ആപ്പ് കൂടി അപ്ഡേറ്റ് ചെയ്തു.",
|
||||
"other": "{} കൂടാതെ {} ആപ്പുകൾ കൂടി അപ്ഡേറ്റ് ചെയ്തു."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "{} കൂടാതെ 1 ആപ്പ് കൂടി അപ്ഡേറ്റ് ചെയ്യാൻ പരാജയപ്പെട്ടു.",
|
||||
"other": "{} കൂടാതെ {} ആപ്പുകൾ കൂടി അപ്ഡേറ്റ് ചെയ്യാൻ പരാജയപ്പെട്ടു."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} കൂടാതെ 1 ആപ്പ് കൂടി അപ്ഡേറ്റ് ചെയ്തിരിക്കാം.",
|
||||
"other": "{} കൂടാതെ {} ആപ്പുകൾ കൂടി അപ്ഡേറ്റ് ചെയ്തിരിക്കാം."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APK-കൾ"
|
||||
}
|
||||
}
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Standaard versieherkenning",
|
||||
"groupByCategory": "Groeperen op categorie",
|
||||
"autoApkFilterByArch": "Probeer APK's te filteren op CPU-architectuur, indien mogelijk",
|
||||
"autoLinkFilterByArch": "Probeer links zo mogelijk te filteren op CPU-architectuur",
|
||||
"overrideSource": "Bron overschrijven",
|
||||
"dontShowAgain": "Laat dit niet meer zien",
|
||||
"dontShowTrackOnlyWarnings": "Geen waarschuwingen weergeven voor 'Alleen volgen'",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "Intermediaire link niet gevonden",
|
||||
"intermediateLink": "Intermediaire link",
|
||||
"exemptFromBackgroundUpdates": "Vrijgesteld van achtergrond-updates (indien ingeschakeld)",
|
||||
"bgUpdatesOnWiFiOnly": "Achtergrond-updates uitschakelen wanneer niet verbonden metWi-Fi",
|
||||
"bgUpdatesOnWiFiOnly": "Achtergrond-updates uitschakelen wanneer niet verbonden met Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Achtergrondupdates uitschakelen als er niet wordt opgeladen",
|
||||
"autoSelectHighestVersionCode": "De APK met de hoogste versiecode automatisch selecteren",
|
||||
"versionExtractionRegEx": "Reguliere expressie voor versie-extractie",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "App-configuraties door menigte",
|
||||
"allowInsecure": "Onveilige HTTP-verzoeken toestaan",
|
||||
"stayOneVersionBehind": "Blijf een versie achter op de nieuwste",
|
||||
"useFirstApkOfVersion": "Automatisch de eerste van meerdere APK's selecteren",
|
||||
"refreshBeforeDownload": "Vernieuw app details voor download",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Naam",
|
||||
"smartname": "Naam (Slim)",
|
||||
"sortMethod": "Sorteermethode",
|
||||
"welcome": "Welkom",
|
||||
"documentationLinksNote": "De GitHub pagina van Obtainium waarnaar hieronder wordt gelinkt bevat links naar video's, artikelen, discussies en andere bronnen die je zullen helpen begrijpen hoe je de app kunt gebruiken.",
|
||||
"batteryOptimizationNote": "Merk op dat downloads op de achtergrond mogelijk betrouwbaarder werken als je de batterijoptimalisatie van het besturingssysteem voor Obtainium uitschakelt.",
|
||||
"fileDeletionError": "Bestand is niet verwijderd (probeer het handmatig te verwijderen en probeer het opnieuw): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Gebruik een voorgronddienst voor het controleren van updates (betrouwbaarder, verbruikt meer stroom)",
|
||||
"fgServiceNotice": "Deze melding is nodig voor het controleren van updates op de achtergrond (kan worden verborgen in de OS-instellingen)",
|
||||
"removeAppQuestion": {
|
||||
"one": "App verwijderen?",
|
||||
"other": "Apps verwijderen?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Standardowe wykrywanie wersji",
|
||||
"groupByCategory": "Grupuj według kategorii",
|
||||
"autoApkFilterByArch": "Spróbuj filtrować pliki APK według architektury procesora, jeśli to możliwe",
|
||||
"autoLinkFilterByArch": "Spróbuj filtrować linki według architektury procesora, jeśli to możliwe.",
|
||||
"overrideSource": "Nadpisz źródło",
|
||||
"dontShowAgain": "Nie pokazuj tego ponownie",
|
||||
"dontShowTrackOnlyWarnings": "Nie pokazuj ostrzeżeń \"Tylko obserwowana\"",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Baza konfiguracji",
|
||||
"allowInsecure": "Zezwalaj na niezabezpieczone żądania HTTP",
|
||||
"stayOneVersionBehind": "Pozostań jedną wersję w tyle za najnowszą",
|
||||
"useFirstApkOfVersion": "Automatyczny wybór pierwszego z wielu plików APK",
|
||||
"refreshBeforeDownload": "Odśwież szczegóły aplikacji przed pobraniem",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nazwa",
|
||||
"smartname": "Nazwa (Smart)",
|
||||
"sortMethod": "Metoda sortowania",
|
||||
"welcome": "Witamy",
|
||||
"documentationLinksNote": "Strona Obtainium GitHub, do której link znajduje się poniżej, zawiera linki do filmów, artykułów, dyskusji i innych zasobów, które pomogą ci zrozumieć, jak korzystać z aplikacji.",
|
||||
"batteryOptimizationNote": "Należy pamiętać, że pobieranie w tle może działać bardziej niezawodnie po wyłączeniu optymalizacji baterii systemu operacyjnego dla Obtainium.",
|
||||
"fileDeletionError": "Nie udało się usunąć pliku (spróbuj usunąć go ręcznie, a następnie spróbuj ponownie): \"{}\"",
|
||||
"foregroundService": "Usługa nowej generacji Obtainium",
|
||||
"foregroundServiceExplanation": "Używanie usługi pierwszoplanowej do sprawdzania aktualizacji (bardziej niezawodne, zużywa więcej energii)",
|
||||
"fgServiceNotice": "To powiadomienie jest wymagane do sprawdzania aktualizacji w tle (można je ukryć w ustawieniach systemu operacyjnego).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Usunąć aplikację?",
|
||||
"few": "Usunąć aplikacje?",
|
||||
|
396
assets/translations/pt-BR.json
Normal file
@ -0,0 +1,396 @@
|
||||
{
|
||||
"invalidURLForSource": "Não é uma URL de app válida de {}",
|
||||
"noReleaseFound": "Não foi possível encontrar um lançamento adequado",
|
||||
"noVersionFound": "Não foi possível determinar a versão do lançamento",
|
||||
"urlMatchesNoSource": "A URL não corresponde com nenhuma fonte conhecida",
|
||||
"cantInstallOlderVersion": "Não é possível instalar uma versão mais antiga de um app",
|
||||
"appIdMismatch": "O ID do pacote baixado não corresponde ao existente",
|
||||
"functionNotImplemented": "Essa classe não implementou esse recurso ainda",
|
||||
"placeholder": "Espaço reservado",
|
||||
"someErrors": "Ocorreram alguns erros",
|
||||
"unexpectedError": "Erro inesperado",
|
||||
"ok": "Ok",
|
||||
"and": "e",
|
||||
"githubPATLabel": "Token de acesso pessoal do GitHub (aumenta o limite de taxa)",
|
||||
"includePrereleases": "Incluir pré-lançamentos",
|
||||
"fallbackToOlderReleases": "Recorrer à lançamentos mais antigos",
|
||||
"filterReleaseTitlesByRegEx": "Filtrar títulos de lançamentos por expressão regular",
|
||||
"invalidRegEx": "Expressão regular inválida",
|
||||
"noDescription": "Sem descrição",
|
||||
"cancel": "Cancelar",
|
||||
"continue": "Continuar",
|
||||
"requiredInBrackets": "(obrigatório)",
|
||||
"dropdownNoOptsError": "ERRO: O MENU DEVE TER PELO MENOS UMA OPÇÃO",
|
||||
"colour": "Cor",
|
||||
"standard": "Padrão",
|
||||
"custom": "Personalizado",
|
||||
"useMaterialYou": "Usar Material You",
|
||||
"githubStarredRepos": "Repositórios com estrela do GitHub",
|
||||
"uname": "Nome de usuário",
|
||||
"wrongArgNum": "Número errado de argumentos fornecidos",
|
||||
"xIsTrackOnly": "{} é somente de rastreio",
|
||||
"source": "Fonte",
|
||||
"app": "Aplicativo",
|
||||
"appsFromSourceAreTrackOnly": "Apps desta fonte são somente para rastreamento.",
|
||||
"youPickedTrackOnly": "Você selecionou a opção de somente rastreamento.",
|
||||
"trackOnlyAppDescription": "As atualizações do app serão rastreadas, mas o Obtainium não baixará ou instalará elas.",
|
||||
"cancelled": "Cancelado",
|
||||
"appAlreadyAdded": "O app já foi adicionado",
|
||||
"alreadyUpToDateQuestion": "O app já está atualizado?",
|
||||
"addApp": "Adicionar app",
|
||||
"appSourceURL": "URL da fonte do app",
|
||||
"error": "Erro",
|
||||
"add": "Adicionar",
|
||||
"searchSomeSourcesLabel": "Pesquisar (somente algumas fontes)",
|
||||
"search": "Pesquisar",
|
||||
"additionalOptsFor": "Opções adicionais de {}",
|
||||
"supportedSources": "Fontes suportadas",
|
||||
"trackOnlyInBrackets": "(somente rastreamento)",
|
||||
"searchableInBrackets": "(pesquisável)",
|
||||
"appsString": "Aplicativos",
|
||||
"noApps": "Nenhum app",
|
||||
"noAppsForFilter": "Nenhum app pro filtro",
|
||||
"byX": "Por {}",
|
||||
"percentProgress": "Progresso: {}%",
|
||||
"pleaseWait": "Por favor aguarde",
|
||||
"updateAvailable": "Atualização disponível",
|
||||
"notInstalled": "Não instalado",
|
||||
"pseudoVersion": "pseudo-versão",
|
||||
"selectAll": "Selecionar tudo",
|
||||
"deselectX": "Desselecionar {}",
|
||||
"xWillBeRemovedButRemainInstalled": "{} será removido do Obtainium mas continuará instalado no dispositivo.",
|
||||
"removeSelectedAppsQuestion": "Remover os apps selecionados?",
|
||||
"removeSelectedApps": "Remover apps selecionados",
|
||||
"updateX": "Atualizar {}",
|
||||
"installX": "Instalar {}",
|
||||
"markXTrackOnlyAsUpdated": "Marcar {}\n(somente rastreamento)\ncomo atualizado",
|
||||
"changeX": "Alterar {}",
|
||||
"installUpdateApps": "Instalar/atualizar apps",
|
||||
"installUpdateSelectedApps": "Instalar/atualizar apps selecionados",
|
||||
"markXSelectedAppsAsUpdated": "Marcar os {} apps selecionados como atualizados?",
|
||||
"no": "Não",
|
||||
"yes": "Sim",
|
||||
"markSelectedAppsUpdated": "Marcar apps selecionados como atualizados",
|
||||
"pinToTop": "Fixar ao topo",
|
||||
"unpinFromTop": "Desfixar do topo",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Redefinir o estado de instalação dos apps selecionados?",
|
||||
"installStatusOfXWillBeResetExplanation": "Os estados de instalação dos apps selecionados serão redefinidos.\n\nIsso pode ajudar quando a versão exibida no Obtainium está incorreta devido a atualizações malsucedidas ou outros problemas.",
|
||||
"customLinkMessage": "Esses links funcionarão em dispositivos com o Obtainium instalado",
|
||||
"shareAppConfigLinks": "Compartilhar configuração do app como um link HTML",
|
||||
"shareSelectedAppURLs": "Compartilhar as URLs dos apps selecionados",
|
||||
"resetInstallStatus": "Redefinir estado de instalação",
|
||||
"more": "Mais",
|
||||
"removeOutdatedFilter": "Remover filtro de apps desatualizados",
|
||||
"showOutdatedOnly": "Mostrar somente apps desatualizados",
|
||||
"filter": "Filtro",
|
||||
"filterApps": "Filtrar apps",
|
||||
"appName": "Nome do app",
|
||||
"author": "Autor",
|
||||
"upToDateApps": "Apps atualizados",
|
||||
"nonInstalledApps": "Apps não instalados",
|
||||
"importExport": "Importar/Exportar",
|
||||
"settings": "Configurações",
|
||||
"exportedTo": "Exportado para {}",
|
||||
"obtainiumExport": "Exportação do Obtainium",
|
||||
"invalidInput": "Entrada inválida",
|
||||
"importedX": "{} importado(s)",
|
||||
"obtainiumImport": "Importação do Obtainium",
|
||||
"importFromURLList": "Importar da lista de URLs",
|
||||
"searchQuery": "Consulta de pesquisa",
|
||||
"appURLList": "Lista de URLs dos apps",
|
||||
"line": "Linha",
|
||||
"searchX": "Pesquisar {}",
|
||||
"noResults": "Nenhum resultado encontrado",
|
||||
"importX": "Importar {}",
|
||||
"importedAppsIdDisclaimer": "Os apps importados podem ser exibidos incorretamente como se não estivessem instalados.\nPara resolver isso, reinstale eles pelo Obtainium.\nIsso não afetará os dados dos apps.\n\nIsso somente afeta a URL e os métodos de importação de terceiros.",
|
||||
"importErrors": "Erros de importação",
|
||||
"importedXOfYApps": "{} de {} foram importados.",
|
||||
"followingURLsHadErrors": "As seguintes URLs tiveram erros:",
|
||||
"selectURL": "Selecionar URL",
|
||||
"selectURLs": "Selecionar URLs",
|
||||
"pick": "Escolher",
|
||||
"theme": "Tema",
|
||||
"dark": "Escuro",
|
||||
"light": "Claro",
|
||||
"followSystem": "Seguir o sistema",
|
||||
"followSystemThemeExplanation": "Só é possível seguir o tema do sistema ao usar aplicativos de terceiros",
|
||||
"useBlackTheme": "Usar o tema escuro de preto profundo",
|
||||
"appSortBy": "Ordenar apps por",
|
||||
"authorName": "Autor/nome",
|
||||
"nameAuthor": "Nome/autor",
|
||||
"asAdded": "Como adicionados",
|
||||
"appSortOrder": "Ordem dos apps",
|
||||
"ascending": "Crescente",
|
||||
"descending": "Decrescente",
|
||||
"bgUpdateCheckInterval": "Intervalo de busca por atualizações em segundo plano",
|
||||
"neverManualOnly": "Nunca - somente manualmente",
|
||||
"appearance": "Aparência",
|
||||
"showWebInAppView": "Mostrar a fonte da pagina web na tela de apps",
|
||||
"pinUpdates": "Fixar atualizações no topo da tela de apps",
|
||||
"updates": "Atualizações",
|
||||
"sourceSpecific": "Específico à fonte",
|
||||
"appSource": "Fonte do app",
|
||||
"noLogs": "Nenhum registro",
|
||||
"appLogs": "Registros do app",
|
||||
"close": "Fechar",
|
||||
"share": "Compartilhar",
|
||||
"appNotFound": "O app não foi encontrado",
|
||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
||||
"pickAnAPK": "Selecione um APK",
|
||||
"appHasMoreThanOnePackage": "{} tem mais de um pacote:",
|
||||
"deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.",
|
||||
"deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:",
|
||||
"warning": "Alerta",
|
||||
"sourceIsXButPackageFromYPrompt": "A fonte do app é '{}' mas o pacote de lançamento vem de '{}'. Continuar mesmo assim?",
|
||||
"updatesAvailable": "Atualizações disponíveis",
|
||||
"updatesAvailableNotifDescription": "Notifica o usuário que atualizações estão disponíveis para um ou mais apps rastreados pelo Obtainium",
|
||||
"noNewUpdates": "Nenhuma atualização disponível.",
|
||||
"xHasAnUpdate": "{} tem uma atualização.",
|
||||
"appsUpdated": "Apps atualizados",
|
||||
"appsNotUpdated": "Falhou ao atualizar os aplicativos",
|
||||
"appsUpdatedNotifDescription": "Notifica o usuário que atualizações de um ou mais apps foram aplicadas em segundo plano",
|
||||
"xWasUpdatedToY": "{} foi atualizado para a versão {}.",
|
||||
"xWasNotUpdatedToY": "Falha ao atualizar {} para a versão {}.",
|
||||
"errorCheckingUpdates": "Ocorreu um erro ao buscar atualizações",
|
||||
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a busca de atualizações em segundo plano falha",
|
||||
"appsRemoved": "Apps removidos",
|
||||
"appsRemovedNotifDescription": "Notifica o usuário que um ou mais apps foram removidos devido a erros ao carregá-los",
|
||||
"xWasRemovedDueToErrorY": "{} for removido devido ao erro: {}",
|
||||
"completeAppInstallation": "Concluir instalação do app",
|
||||
"obtainiumMustBeOpenToInstallApps": "O Obtainium precisa estar aberto para instalar apps",
|
||||
"completeAppInstallationNotifDescription": "Pede pro usuário voltar ao Obtainium para concluir a instalação de um app",
|
||||
"checkingForUpdates": "Buscando atualizações",
|
||||
"checkingForUpdatesNotifDescription": "Notificação transitória que aparece ao buscar atualizações",
|
||||
"pleaseAllowInstallPerm": "Permita que o Obtainium instale apps",
|
||||
"trackOnly": "Somente rastreamento",
|
||||
"errorWithHttpStatusCode": "Erro {}",
|
||||
"versionCorrectionDisabled": "Correção de versão desativada (o plugin parece não funcionar)",
|
||||
"unknown": "Desconhecido",
|
||||
"none": "Nenhum",
|
||||
"never": "Nunca",
|
||||
"latestVersionX": "Mais recente: {}",
|
||||
"installedVersionX": "Instalado: {}",
|
||||
"lastUpdateCheckX": "Última busca por atualizações: {}",
|
||||
"remove": "Remover",
|
||||
"yesMarkUpdated": "Sim, marcar como atualizado",
|
||||
"fdroid": "Oficial do F-Droid",
|
||||
"appIdOrName": "ID do app ou nome",
|
||||
"appId": "ID do app",
|
||||
"appWithIdOrNameNotFound": "Nenhum app foi encontrado com aquele ID ou nome",
|
||||
"reposHaveMultipleApps": "Repositórios podem conter vários apps",
|
||||
"fdroidThirdPartyRepo": "Repositório de terceiros do F-Droid",
|
||||
"install": "Instalar",
|
||||
"markInstalled": "Marcar como instalado",
|
||||
"update": "Atualizar",
|
||||
"markUpdated": "Marcar como atualizado",
|
||||
"additionalOptions": "Opções adicionais",
|
||||
"disableVersionDetection": "Desativar detecção de versão",
|
||||
"noVersionDetectionExplanation": "Essa opção só seve ser usada para apps aonde a detecção de versão não funciona corretamente.",
|
||||
"downloadingX": "Baixando {}",
|
||||
"downloadX": "Baixar {}",
|
||||
"downloadedX": "{} foi baixado",
|
||||
"releaseAsset": "Item de lançamento",
|
||||
"downloadNotifDescription": "Notifica o usuário do progresso ao baixar um app",
|
||||
"noAPKFound": "Nenhum APK encontrado",
|
||||
"noVersionDetection": "Sem detecção de versão",
|
||||
"categorize": "Categorizar",
|
||||
"categories": "Categorias",
|
||||
"category": "Categoria",
|
||||
"noCategory": "Nenhuma categoria",
|
||||
"noCategories": "Nenhuma categoria",
|
||||
"deleteCategoriesQuestion": "Excluir categorias?",
|
||||
"categoryDeleteWarning": "Todos os apps em categorias excluídas ficarão sem categoria.",
|
||||
"addCategory": "Adicionar categoria",
|
||||
"label": "Rótulo",
|
||||
"language": "Idioma",
|
||||
"copiedToClipboard": "Copiado para a área de transferência",
|
||||
"storagePermissionDenied": "Permissão de armazenamento negada",
|
||||
"selectedCategorizeWarning": "Isso substituirá a configuração de categoria existente dos apps selecionados.",
|
||||
"filterAPKsByRegEx": "Filtrar APKs por expressão regular",
|
||||
"removeFromObtainium": "Remover do Obtainium",
|
||||
"uninstallFromDevice": "Desinstalar do dispositivo",
|
||||
"onlyWorksWithNonVersionDetectApps": "Funciona somente em apps com a detecção de versão desativada.",
|
||||
"releaseDateAsVersion": "Usar data de lançamento como número da versão",
|
||||
"releaseTitleAsVersion": "Usar título do lançamento como número da versão",
|
||||
"releaseDateAsVersionExplanation": "Essa opção só deve ser usada para apps quais a detecção de versão não funciona corretamente, mas uma data de lançamento está disponível.",
|
||||
"changes": "Alterações",
|
||||
"releaseDate": "Data de lançamento",
|
||||
"importFromURLsInFile": "Importar das URLs em arquivo (como OPML)",
|
||||
"versionDetectionExplanation": "Combinar o número da versão com a versão detectada pelo sistema",
|
||||
"versionDetection": "Detecção de versão",
|
||||
"standardVersionDetection": "Detecção de versão padrão",
|
||||
"groupByCategory": "Agrupar por categoria",
|
||||
"autoApkFilterByArch": "Tentar filtrar APKs pela arquitetura da CPU quando possível",
|
||||
"autoLinkFilterByArch": "Tentar filtrar links pela arquitetura da CPU quando possível",
|
||||
"overrideSource": "Sobrescrever fonte",
|
||||
"dontShowAgain": "Não mostrar isso novamente",
|
||||
"dontShowTrackOnlyWarnings": "Não mostrar alertas de \"somente rastreamento\"",
|
||||
"dontShowAPKOriginWarnings": "Não mostrar alertas de origem dos APKs",
|
||||
"moveNonInstalledAppsToBottom": "Mover apps não instalados ao final da tela de apps",
|
||||
"gitlabPATLabel": "Token de acesso pessoal do GitLab",
|
||||
"about": "Sobre",
|
||||
"requiresCredentialsInSettings": "{} precisa de credenciais adicionais (nas Configurações)",
|
||||
"checkOnStart": "Buscar atualizações ao abrir o app",
|
||||
"tryInferAppIdFromCode": "Tentar inferir o ID do app pelo código fonte",
|
||||
"removeOnExternalUninstall": "Remover automaticamente apps desinstalados externamente",
|
||||
"pickHighestVersionCode": "Selecionar APK de versão mais alta automaticamente",
|
||||
"checkUpdateOnDetailPage": "Buscar atualizações ao abrir a tela de detalhes de um app",
|
||||
"disablePageTransitions": "Desativar animações de transição de tela",
|
||||
"reversePageTransitions": "Inverter animações de transição de tela",
|
||||
"minStarCount": "Número de estrelas mínimo",
|
||||
"addInfoBelow": "Adicione essa informação abaixo.",
|
||||
"addInfoInSettings": "Adicione essa informação nas Configurações.",
|
||||
"githubSourceNote": "O limite de taxa do GitHub pode ser evitado ao usar uma chave de API.",
|
||||
"sortByLastLinkSegment": "Ordenar somente pelo ultimo segmento do link",
|
||||
"filterReleaseNotesByRegEx": "Filtrar notas de lançamento por expressão regular",
|
||||
"customLinkFilterRegex": "Filtro de link de APK personalizado por expressão regular (padrão '.apk$')",
|
||||
"appsPossiblyUpdated": "Tentativas de atualização de apps",
|
||||
"appsPossiblyUpdatedNotifDescription": "Notifica o usuário que atualizações de um ou mais apps podem ter sido aplicadas em segundo plano",
|
||||
"xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para a versão {}.",
|
||||
"enableBackgroundUpdates": "Ativar atualizações em segundo plano",
|
||||
"backgroundUpdateReqsExplanation": "Atualizações em segundo plano podem não funcionar com todos os apps.",
|
||||
"backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo plano só pode ser determinada ao abrir o Obtainium.",
|
||||
"verifyLatestTag": "Verificar a tag 'mais recente'",
|
||||
"intermediateLinkRegex": "Filtrar por um link 'intermediário' para visitar",
|
||||
"filterByLinkText": "Filtrar links por texto do link",
|
||||
"intermediateLinkNotFound": "Link intermediário não encontrado",
|
||||
"intermediateLink": "Link intermediário",
|
||||
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (caso ativadas)",
|
||||
"bgUpdatesOnWiFiOnly": "Desativar atualizações em segundo plano fora do Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Desativar atualizações em segundo plano fora do carregador",
|
||||
"autoSelectHighestVersionCode": "Selecionar automaticamente APK com o código de versão mais alto",
|
||||
"versionExtractionRegEx": "ExReg de extração do número da versão",
|
||||
"trimVersionString": "Cortar número da versal com ExReg",
|
||||
"matchGroupToUseForX": "Corresponder grupo para o uso em \"{}\"",
|
||||
"matchGroupToUse": "Corresponder grupo para o uso para a extração do número da versão por ExReg",
|
||||
"highlightTouchTargets": "Acentuar alvos de toque menos óbvios",
|
||||
"pickExportDir": "Selecionar pasta de exportação",
|
||||
"autoExportOnChanges": "Exportar automaticamente ao ocorrer alterações",
|
||||
"includeSettings": "Incluir configurações",
|
||||
"filterVersionsByRegEx": "Filtrar versões por expressão regular",
|
||||
"trySelectingSuggestedVersionCode": "Tente selecionar o APK com o código de versão sugerido",
|
||||
"dontSortReleasesList": "Manter ordem de lançamento da API",
|
||||
"reverseSort": "Ordem inversa",
|
||||
"takeFirstLink": "Usar o primeiro link",
|
||||
"skipSort": "Pular ordenação",
|
||||
"debugMenu": "Menu de depuração",
|
||||
"bgTaskStarted": "Tarefa em segundo plano iniada - verifique os registros.",
|
||||
"runBgCheckNow": "Executar busca por atualizações em segundo plano agora",
|
||||
"versionExtractWholePage": "Aplicar ExReg de extração de número de versão à página inteira",
|
||||
"installing": "Instalando",
|
||||
"skipUpdateNotifications": "Pular notificações de atualização",
|
||||
"updatesAvailableNotifChannel": "Atualizações disponíveis",
|
||||
"appsUpdatedNotifChannel": "Apps atualizados",
|
||||
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de apps",
|
||||
"errorCheckingUpdatesNotifChannel": "Erro ao buscar atualizações",
|
||||
"appsRemovedNotifChannel": "Apps removidos",
|
||||
"downloadingXNotifChannel": "Baixando {}",
|
||||
"completeAppInstallationNotifChannel": "Concluir instalação do app",
|
||||
"checkingForUpdatesNotifChannel": "Buscando atualizações",
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Buscar atualizações somente para apps instalados e de somente rastreamento",
|
||||
"supportFixedAPKURL": "Suportar URLs de APK fixas",
|
||||
"selectX": "Selecionar {}",
|
||||
"parallelDownloads": "Permitir downloads em paralelo",
|
||||
"useShizuku": "Usar Shizuku ou Sui para instalação",
|
||||
"shizukuBinderNotFound": "Serviço Shizuku não está em execução",
|
||||
"shizukuOld": "Versão do Shizuku antiga (<11) - atualize",
|
||||
"shizukuOldAndroidWithADB": "Shizuku sendo executado no Android < 8.1 com ADB - atualize o Android ou use o Sui",
|
||||
"shizukuPretendToBeGooglePlay": "Definir Google Play como a fonte de instalação (se o Shizuku é usado)",
|
||||
"useSystemFont": "Usar a fonte do sistema",
|
||||
"useVersionCodeAsOSVersion": "Usar código de versão do app como a versão detectada pelo sistema",
|
||||
"requestHeader": "Cabeçalho da solicitação",
|
||||
"useLatestAssetDateAsReleaseDate": "Usar o envio de item mais recente como a data de lançamento",
|
||||
"defaultPseudoVersioningMethod": "Método de pseudo-versão padrão",
|
||||
"partialAPKHash": "Hash do APK parcial",
|
||||
"APKLinkHash": "Hash do link do APK",
|
||||
"directAPKLink": "Link direto ao APK",
|
||||
"pseudoVersionInUse": "Uma pseudo-versão está em uso",
|
||||
"installed": "Instalado",
|
||||
"latest": "Mais recente",
|
||||
"invertRegEx": "Inverter expressão regular",
|
||||
"note": "Observação",
|
||||
"selfHostedNote": "O menu de opções \"{}\" pode ser usado para alcançar instâncias hospedadas-por-você/personalizadas de qualquer fonte.",
|
||||
"badDownload": "O APK não pode ser interpretado (incompatível ou baixado parcialmente)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Compartilhar apps novos com o AppVerifier (se disponível)",
|
||||
"appVerifierInstructionToast": "Compartilhe com o AppVerifier, e volte aqui ao estar pronto.",
|
||||
"wiki": "Ajuda/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Configurações de app pela comunidade (use ao seu próprio risco)",
|
||||
"crowdsourcedConfigsShort": "Configurações de app da comunidade",
|
||||
"allowInsecure": "Permitir solicitações de HTTP inseguras",
|
||||
"stayOneVersionBehind": "Ficar uma versão antes da mais recente",
|
||||
"useFirstApkOfVersion": "Seleção automática do primeiro de vários APKs",
|
||||
"refreshBeforeDownload": "Atualizar detalhes do app antes de baixar",
|
||||
"tencentAppStore": "Loja de Apps da Tencent",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "Vivo App Store (CN)",
|
||||
"name": "Nome",
|
||||
"smartname": "Nome (inteligente)",
|
||||
"sortMethod": "Método de ordenação",
|
||||
"welcome": "Boas vindas",
|
||||
"documentationLinksNote": "A página do Obtainium no GitHub visível abaixo contém links de vídeos, artigos, discussões, e outros recursos que podem te ajudar ao usar o app.",
|
||||
"batteryOptimizationNote": "Observe que os downloads em segundo plano podem funcionar de forma mais confiável se você desativar as otimizações de bateria do sistema operacional para o Obtainium.",
|
||||
"fileDeletionError": "Falha ao excluir o arquivo (tente excluí-lo manualmente e tente novamente): \"{}\"",
|
||||
"foregroundService": "Serviço de primeiro plano do Obtainium",
|
||||
"foregroundServiceExplanation": "Usar um serviço em primeiro plano para verificação de atualizações (mais confiável, consome mais energia)",
|
||||
"fgServiceNotice": "Essa notificação é necessária para a verificação de atualizações em segundo plano (ela pode ser ocultada nas configurações do sistema operacional)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Remover app?",
|
||||
"other": "Remover apps?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Muitas solicitações (limitado) - tente novamente em {} minuto",
|
||||
"other": "Muitas solicitações (limitado) - tente novamente em {} minutos"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "A busca de atualizações em segundo plano encontrou um {}, será agendado uma nova tentativa em {} minuto",
|
||||
"other": "A busca de atualizações em segundo plano encontrou um {}, será agendado uma nova tentativa em {} minutos"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "Verificação da atualização do BG encontrada {} atualização - notificará o usuário se necessário",
|
||||
"other": "Verificação de atualização do BG encontrou atualizações {} - notificará o usuário se necessário"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} app",
|
||||
"other": "{} apps"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL",
|
||||
"other": "{} URLs"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} minuto",
|
||||
"other": "{} minutos"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} hora",
|
||||
"other": "{} horas"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} dia",
|
||||
"other": "{} dias"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "Limpou {n} registros (before = {before}, after = {after})",
|
||||
"other": "Limpou {n} registros (antes = {before}, depois = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "{} e mais 1 app têm atualizações.",
|
||||
"other": "{} e mais {} apps têm atualizações."
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} e mais 1 app foram atualizados.",
|
||||
"other": "{} e mais {} apps foram atualizados."
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "Falha ao atualizar {} e mais 1 app.",
|
||||
"other": "Falha ao atualizar {} e mais {} apps."
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} e mais 1 app podem ter sido atualizados.",
|
||||
"other": "{} e mais {} apps podem ter sido atualizados."
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKs"
|
||||
}
|
||||
}
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Detecção de versão padrão",
|
||||
"groupByCategory": "Agroupar por categoria",
|
||||
"autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível",
|
||||
"autoLinkFilterByArch": "Tentativa de filtrar as ligações por arquitetura de CPU, se possível",
|
||||
"overrideSource": "Substituir fonte",
|
||||
"dontShowAgain": "Não mostrar isso novamente",
|
||||
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas monitorar'",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Configurações de aplicações com base em crowdsourcing",
|
||||
"allowInsecure": "Permitir pedidos HTTP inseguros",
|
||||
"stayOneVersionBehind": "Manter-se uma versão atrás da mais recente",
|
||||
"useFirstApkOfVersion": "Seleção automática do primeiro de vários APKs",
|
||||
"refreshBeforeDownload": "Atualizar os detalhes da aplicação antes da transferência",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Nome",
|
||||
"smartname": "Nome (Smart)",
|
||||
"sortMethod": "Método de ordenação",
|
||||
"welcome": "Bem-vindo",
|
||||
"documentationLinksNote": "A página do Obtainium no GitHub com a ligação abaixo contém ligações para vídeos, artigos, discussões e outros recursos que o ajudarão a compreender como utilizar a aplicação.",
|
||||
"batteryOptimizationNote": "Note que os downloads em segundo plano podem funcionar de forma mais fiável se desativar as optimizações da bateria do SO para o Obtainium.",
|
||||
"fileDeletionError": "Falha ao eliminar o ficheiro (tente eliminá-lo manualmente e depois tente novamente): \"{}\"",
|
||||
"foregroundService": "Serviço de primeiro plano Obtainium",
|
||||
"foregroundServiceExplanation": "Utilizar um serviço em primeiro plano para verificação de actualizações (mais fiável, consome mais energia)",
|
||||
"fgServiceNotice": "Esta notificação é necessária para a verificação de actualizações em segundo plano (pode ser ocultada nas definições do SO)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Remover aplicativo?",
|
||||
"other": "Remover aplicativos?"
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"invalidURLForSource": "Неверный URL-адрес приложения: {}",
|
||||
"noReleaseFound": "Не удалось найти подходящий релиз",
|
||||
"noVersionFound": "Не удалось определить версию релиза",
|
||||
"noReleaseFound": "Не удалось найти подходящий выпуск",
|
||||
"noVersionFound": "Не удалось определить версию выпуска",
|
||||
"urlMatchesNoSource": "URL-адрес не соответствует известному источнику",
|
||||
"cantInstallOlderVersion": "Невозможно установить более старую версию приложения",
|
||||
"appIdMismatch": "ID загруженного пакета не совпадает с существующим ID приложения",
|
||||
@ -9,12 +9,12 @@
|
||||
"placeholder": "Заполнитель",
|
||||
"someErrors": "Возникли некоторые ошибки",
|
||||
"unexpectedError": "Неожиданная ошибка",
|
||||
"ok": "Ок",
|
||||
"ok": "OK",
|
||||
"and": "и",
|
||||
"githubPATLabel": "Персональный токен доступа GitHub\n(увеличивает лимит запросов)",
|
||||
"includePrereleases": "Включить предварительные релизы",
|
||||
"includePrereleases": "Включать предварительные выпуски",
|
||||
"fallbackToOlderReleases": "Откатываться к предыдущей версии",
|
||||
"filterReleaseTitlesByRegEx": "Фильтровать заголовки релизов\n(регулярное выражение)",
|
||||
"filterReleaseTitlesByRegEx": "Фильтровать заголовки выпусков\n(регулярное выражение)",
|
||||
"invalidRegEx": "Неверное регулярное выражение",
|
||||
"noDescription": "Нет описания",
|
||||
"cancel": "Отмена",
|
||||
@ -32,13 +32,13 @@
|
||||
"source": "Источник",
|
||||
"app": "Приложение",
|
||||
"appsFromSourceAreTrackOnly": "Приложения из этого источника настроены только для отслеживания",
|
||||
"youPickedTrackOnly": "Вы выбрали опцию 'Только для отслеживания'",
|
||||
"youPickedTrackOnly": "Выбрана настройка «только для отслеживания»",
|
||||
"trackOnlyAppDescription": "Приложение будет отслеживаться на предмет обновлений, но Obtainium не сможет загрузить или установить его",
|
||||
"cancelled": "Отменено",
|
||||
"appAlreadyAdded": "Приложение уже добавлено",
|
||||
"alreadyUpToDateQuestion": "Приложение уже обновлено?",
|
||||
"addApp": "Добавить",
|
||||
"appSourceURL": "URL-источник приложения",
|
||||
"appSourceURL": "URL-адрес источника приложения",
|
||||
"error": "Ошибка",
|
||||
"add": "Добавить",
|
||||
"searchSomeSourcesLabel": "Поиск (в некоторых источниках)",
|
||||
@ -63,10 +63,10 @@
|
||||
"removeSelectedApps": "Удалить выбранные приложения",
|
||||
"updateX": "Обновить {}",
|
||||
"installX": "Установить {}",
|
||||
"markXTrackOnlyAsUpdated": "Отметить {}\n(Только для отслеживания)\nкак обновленное",
|
||||
"markXTrackOnlyAsUpdated": "Отметить {}\n(только для отслеживания)\nкак обновлённое",
|
||||
"changeX": "Изменить {}",
|
||||
"installUpdateApps": "Установить/Обновить приложения",
|
||||
"installUpdateSelectedApps": "Установить/Обновить выбранные приложения",
|
||||
"installUpdateApps": "Установить/обновить приложения",
|
||||
"installUpdateSelectedApps": "Установить/обновить выбранные приложения",
|
||||
"markXSelectedAppsAsUpdated": "Выбрано приложений: {}. Отметить как обновлённые?",
|
||||
"no": "Нет",
|
||||
"yes": "Да",
|
||||
@ -74,10 +74,10 @@
|
||||
"pinToTop": "Закрепить сверху",
|
||||
"unpinFromTop": "Открепить",
|
||||
"resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?",
|
||||
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная — из-за неудачных обновлений или других проблем",
|
||||
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная (из-за неудачных обновлений или других проблем).",
|
||||
"customLinkMessage": "Эти ссылки работают на устройствах с установленным Obtainium",
|
||||
"shareAppConfigLinks": "Поделиться конфигурацией приложения в виде HTML-ссылки",
|
||||
"shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений",
|
||||
"shareSelectedAppURLs": "Поделиться URL-адресами выбранных приложений",
|
||||
"resetInstallStatus": "Сбросить статус установки",
|
||||
"more": "Ещё",
|
||||
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
|
||||
@ -86,7 +86,7 @@
|
||||
"filterApps": "Фильтровать приложения",
|
||||
"appName": "Название приложения",
|
||||
"author": "Автор",
|
||||
"upToDateApps": "Приложения со свежими обновлениями",
|
||||
"upToDateApps": "Приложения с обновлениями",
|
||||
"nonInstalledApps": "Неустановленные приложения",
|
||||
"importExport": "Данные",
|
||||
"settings": "Настройки",
|
||||
@ -97,12 +97,12 @@
|
||||
"obtainiumImport": "Импорт в Obtainium",
|
||||
"importFromURLList": "Импорт из списка URL-адресов",
|
||||
"searchQuery": "Поисковый запрос",
|
||||
"appURLList": "Список URL приложений",
|
||||
"appURLList": "Список URL-адресов приложений",
|
||||
"line": "Строка",
|
||||
"searchX": "Поиск {}",
|
||||
"noResults": "Результатов не найдено",
|
||||
"noResults": "Ничего не найдено",
|
||||
"importX": "Импорт {}",
|
||||
"importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как неустановленные.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников",
|
||||
"importedAppsIdDisclaimer": "Импортированные приложения могут неверно отображаться как неустановленные.\nДля исправления этой проблемы повторно установите их через Obtainium.\nЭто не должно повлиять на данные приложения.\n\nПроблемы возникают только при импорте из URL-адреса и сторонних источников.",
|
||||
"importErrors": "Ошибка импорта",
|
||||
"importedXOfYApps": "Импортировано приложений: {} из {}",
|
||||
"followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:",
|
||||
@ -112,12 +112,12 @@
|
||||
"theme": "Тема",
|
||||
"dark": "Тёмная",
|
||||
"light": "Светлая",
|
||||
"followSystem": "Системная",
|
||||
"followSystem": "Как в системе",
|
||||
"followSystemThemeExplanation": "Следование системной теме возможно только при использовании сторонних приложений",
|
||||
"useBlackTheme": "Использовать чёрную тему",
|
||||
"appSortBy": "Сортировка приложений",
|
||||
"authorName": "Автор/Название",
|
||||
"nameAuthor": "Название/Автор",
|
||||
"authorName": "Автор/название",
|
||||
"nameAuthor": "Название/автор",
|
||||
"asAdded": "В порядке добавления",
|
||||
"appSortOrder": "Порядок",
|
||||
"ascending": "По возрастанию",
|
||||
@ -126,12 +126,12 @@
|
||||
"neverManualOnly": "Никогда — только вручную",
|
||||
"appearance": "Внешний вид",
|
||||
"showWebInAppView": "Показывать исходную веб-страницу на странице приложения",
|
||||
"pinUpdates": "Отображать обновления приложений сверху списка",
|
||||
"pinUpdates": "Показывать обновления приложений сверху списка",
|
||||
"updates": "Обновления",
|
||||
"sourceSpecific": "Настройки источников",
|
||||
"appSource": "Источник",
|
||||
"noLogs": "Нет журналов",
|
||||
"appLogs": "Логи",
|
||||
"appLogs": "Журналы",
|
||||
"close": "Закрыть",
|
||||
"share": "Поделиться",
|
||||
"appNotFound": "Приложение не найдено",
|
||||
@ -141,21 +141,21 @@
|
||||
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}",
|
||||
"deviceSupportsFollowingArchs": "Ваше устройство поддерживает следующие архитектуры процессора:",
|
||||
"warning": "Предупреждение",
|
||||
"sourceIsXButPackageFromYPrompt": "Источник приложения — '{}', но пакет для установки получен из '{}'. Продолжить?",
|
||||
"sourceIsXButPackageFromYPrompt": "Источник приложения — «{}», но пакет для установки получен из «{}». Продолжить?",
|
||||
"updatesAvailable": "Доступны обновления",
|
||||
"updatesAvailableNotifDescription": "Уведомляет о наличии обновлений для одного или нескольких приложений в Obtainium",
|
||||
"noNewUpdates": "Нет новых обновлений",
|
||||
"xHasAnUpdate": "{} есть обновление",
|
||||
"noNewUpdates": "Нет обновлений",
|
||||
"xHasAnUpdate": "Для {} есть обновление",
|
||||
"appsUpdated": "Приложения обновлены",
|
||||
"appsNotUpdated": "Не удалось обновить приложения",
|
||||
"appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме",
|
||||
"xWasUpdatedToY": "{} была обновлена до версии {}",
|
||||
"xWasUpdatedToY": "{} обновлено до версии {}",
|
||||
"xWasNotUpdatedToY": "Не удалось обновить {} до версии {}",
|
||||
"errorCheckingUpdates": "Ошибка при проверке обновлений",
|
||||
"errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой",
|
||||
"appsRemoved": "Приложение удалено",
|
||||
"appsRemovedNotifDescription": "Уведомление об удалении одного или несколько приложений из-за ошибок при их загрузке",
|
||||
"xWasRemovedDueToErrorY": "{} был удален из-за ошибки: {}",
|
||||
"xWasRemovedDueToErrorY": "{} удалено из-за ошибки: {}",
|
||||
"completeAppInstallation": "Завершение установки приложения",
|
||||
"obtainiumMustBeOpenToInstallApps": "Obtainium должен быть открыт для установки приложений",
|
||||
"completeAppInstallationNotifDescription": "Уведомление о необходимости открыть Obtainium для завершения установки приложения",
|
||||
@ -172,7 +172,7 @@
|
||||
"installedVersionX": "Установленная версия: {}",
|
||||
"lastUpdateCheckX": "Последняя проверка: {}",
|
||||
"remove": "Удалить",
|
||||
"yesMarkUpdated": "Да, отметить как обновленное",
|
||||
"yesMarkUpdated": "Да, отметить как обновлённое",
|
||||
"fdroid": "Официальные репозитории F-Droid",
|
||||
"appIdOrName": "ID или название приложения",
|
||||
"appId": "ID приложения",
|
||||
@ -180,39 +180,39 @@
|
||||
"reposHaveMultipleApps": "В хранилище несколько приложений",
|
||||
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
|
||||
"install": "Установить",
|
||||
"markInstalled": "Пометить как установленное",
|
||||
"markInstalled": "Отметить установленным",
|
||||
"update": "Обновить",
|
||||
"markUpdated": "Отметить обновлённым",
|
||||
"additionalOptions": "Дополнительные настройки",
|
||||
"disableVersionDetection": "Отключить обнаружение версии",
|
||||
"noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где обнаружение версии не работает корректно",
|
||||
"disableVersionDetection": "Отключить определение версии",
|
||||
"noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где определение версии не работает корректно",
|
||||
"downloadingX": "Загрузка {}",
|
||||
"downloadX": "Скачать {}",
|
||||
"downloadX": "Загрузить {}",
|
||||
"downloadedX": "Загружено {}",
|
||||
"releaseAsset": "Релизный объект",
|
||||
"releaseAsset": "Объект выпуска",
|
||||
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
|
||||
"noAPKFound": "APK не найден",
|
||||
"noVersionDetection": "Обнаружение версий отключено",
|
||||
"noAPKFound": "APK-файл не найден",
|
||||
"noVersionDetection": "Определение версий отключено",
|
||||
"categorize": "Категоризировать",
|
||||
"categories": "Категории",
|
||||
"category": "Категория",
|
||||
"noCategory": "Без категории",
|
||||
"noCategories": "Без категорий",
|
||||
"deleteCategoriesQuestion": "Удалить категории?",
|
||||
"categoryDeleteWarning": "Все приложения в удаленных категориях будут помечены как без категории",
|
||||
"categoryDeleteWarning": "Все приложения в удалённых категориях будут помечены как «без категории»",
|
||||
"addCategory": "Добавить категорию",
|
||||
"label": "Метка",
|
||||
"language": "Язык",
|
||||
"copiedToClipboard": "Скопировано в буфер обмена",
|
||||
"storagePermissionDenied": "Отказано в доступе к хранилищу",
|
||||
"selectedCategorizeWarning": "Это заменит все текущие настройки категорий для выбранных приложений",
|
||||
"filterAPKsByRegEx": "Отфильтровать APK-файлы\n(регулярное выражение)",
|
||||
"filterAPKsByRegEx": "Фильтровать APK-файлы\n(регулярное выражение)",
|
||||
"removeFromObtainium": "Удалить из Obtainium",
|
||||
"uninstallFromDevice": "Удалить с устройства",
|
||||
"onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключенным определением версии",
|
||||
"releaseDateAsVersion": "Дата выпуска вместо версии",
|
||||
"releaseTitleAsVersion": "Используйте название релиза в качестве строки версии",
|
||||
"releaseDateAsVersionExplanation": "Этот параметр следует использовать только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска",
|
||||
"onlyWorksWithNonVersionDetectApps": "Работает только для приложений с отключённым определением версии",
|
||||
"releaseDateAsVersion": "Использовать дату выпуска в качестве версии",
|
||||
"releaseTitleAsVersion": "Использовать название выпуска в качестве версии",
|
||||
"releaseDateAsVersionExplanation": "Эта настройка должна использоваться только для приложений, в которых определение версии не работает правильно, но имеется дата выпуска",
|
||||
"changes": "Изменения",
|
||||
"releaseDate": "Дата выпуска",
|
||||
"importFromURLsInFile": "Импорт из файла URL-адресов (например: OPML)",
|
||||
@ -220,55 +220,56 @@
|
||||
"versionDetection": "Определение версии",
|
||||
"standardVersionDetection": "Стандартное",
|
||||
"groupByCategory": "Группировать по категориям",
|
||||
"autoApkFilterByArch": "Попытаться отфильтровать APK-файлы по архитектуре процессора",
|
||||
"autoApkFilterByArch": "Пытаться фильтровать APK по архитектуре процессора",
|
||||
"autoLinkFilterByArch": "Пытаться фильтровать ссылки по архитектуре процессора, если это возможно",
|
||||
"overrideSource": "Переопределить источник",
|
||||
"dontShowAgain": "Не показывать снова",
|
||||
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
|
||||
"dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов",
|
||||
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
|
||||
"moveNonInstalledAppsToBottom": "Показывать неустановленные приложения внизу списка",
|
||||
"gitlabPATLabel": "Персональный токен доступа GitLab",
|
||||
"about": "Описание",
|
||||
"requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)",
|
||||
"requiresCredentialsInSettings": "{}: для этого требуются дополнительные учётные данные (в настройках)",
|
||||
"checkOnStart": "Проверять наличие обновлений при запуске",
|
||||
"tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода",
|
||||
"removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения",
|
||||
"tryInferAppIdFromCode": "Пытаться определить ID приложения из исходного кода",
|
||||
"removeOnExternalUninstall": "Автоматически убирать из списка удалённые извне приложения",
|
||||
"pickHighestVersionCode": "Автовыбор актуальной версии кода APK",
|
||||
"checkUpdateOnDetailPage": "Проверять наличие обновлений при открытии страницы приложения",
|
||||
"disablePageTransitions": "Отключить анимацию перехода между страницами",
|
||||
"reversePageTransitions": "Реверс анимации перехода между страницами",
|
||||
"minStarCount": "Минимальное количество звёзд",
|
||||
"addInfoBelow": "Добавьте эту информацию ниже",
|
||||
"addInfoInSettings": "Добавьте эту информацию в Настройки",
|
||||
"githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub",
|
||||
"addInfoInSettings": "Добавьте эту информацию в «Настройки»",
|
||||
"githubSourceNote": "Используя ключ API, можно обойти ограничение запросов GitHub",
|
||||
"sortByLastLinkSegment": "Сортировать только по последнему сегменту ссылки",
|
||||
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)",
|
||||
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')",
|
||||
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: «.apk$»)",
|
||||
"appsPossiblyUpdated": "Попытки обновления приложений",
|
||||
"appsPossiblyUpdatedNotifDescription": "Уведомление о возможных обновлениях одного или нескольких приложений в фоновом режиме",
|
||||
"xWasPossiblyUpdatedToY": "{} возможно был обновлен до {}",
|
||||
"xWasPossiblyUpdatedToY": "{} возможно был обновлён до {}",
|
||||
"enableBackgroundUpdates": "Включить обновления в фоне",
|
||||
"backgroundUpdateReqsExplanation": "Фоновые обновления могут быть возможны не для всех приложений",
|
||||
"backgroundUpdateLimitsExplanation": "Успешность фоновой установки можно определить только после открытия Obtainium",
|
||||
"verifyLatestTag": "Проверять тег 'latest'",
|
||||
"intermediateLinkRegex": "Фильтр для \"промежуточной\" ссылки для посещения",
|
||||
"verifyLatestTag": "Проверять метку «latest»",
|
||||
"intermediateLinkRegex": "Фильтр для «промежуточной» ссылки для посещения",
|
||||
"filterByLinkText": "Фильтрация ссылок по тексту ссылки",
|
||||
"intermediateLinkNotFound": "Промежуточная ссылка не найдена",
|
||||
"intermediateLink": "Промежуточная ссылка",
|
||||
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
|
||||
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Отключение фоновых обновлений при отсутствии зарядки",
|
||||
"autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода",
|
||||
"versionExtractionRegEx": "Регулярное выражение для извлечения версии",
|
||||
"trimVersionString": "Обрезка строки версии с помощью RegEx",
|
||||
"matchGroupToUseForX": "Группа соответствия, которую следует использовать для \"{}\"",
|
||||
"matchGroupToUse": "Выберите группу для использования",
|
||||
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
|
||||
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет подключения к Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Отключить фоновые обновления, если не идёт зарядка",
|
||||
"autoSelectHighestVersionCode": "Автовыбор APK с актуальной версией кода",
|
||||
"versionExtractionRegEx": "Извлечение версии\n(регулярное выражение)",
|
||||
"trimVersionString": "Обрезка строки версии\n(регулярное выражение)",
|
||||
"matchGroupToUseForX": "Группа соответствия для «{}»",
|
||||
"matchGroupToUse": "Выбрать группу соответствия",
|
||||
"highlightTouchTargets": "Выделять менее очевидные элементы управления касанием",
|
||||
"pickExportDir": "Выбрать каталог для экспорта",
|
||||
"autoExportOnChanges": "Автоэкспорт при изменениях",
|
||||
"includeSettings": "Включить настройки",
|
||||
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
|
||||
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
||||
"dontSortReleasesList": "Сохранить порядок релизов от API",
|
||||
"trySelectingSuggestedVersionCode": "Пробовать выбрать предложенный код версии APK",
|
||||
"dontSortReleasesList": "Сохранить порядок выпусков от API",
|
||||
"reverseSort": "Обратная сортировка",
|
||||
"takeFirstLink": "Взять первую ссылку",
|
||||
"skipSort": "Пропустить сортировку",
|
||||
@ -289,18 +290,18 @@
|
||||
"onlyCheckInstalledOrTrackOnlyApps": "Проверять обновления только у установленных или отслеживаемых приложений",
|
||||
"supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK",
|
||||
"selectX": "Выбрать {}",
|
||||
"parallelDownloads": "Разрешить параллельные загрузки",
|
||||
"parallelDownloads": "Использовать параллельные загрузки",
|
||||
"useShizuku": "Использовать Shizuku или Sui для установки",
|
||||
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден, возможно он не запущен",
|
||||
"shizukuOld": "Устаревшая версия Shizuku (<11), обновите",
|
||||
"shizukuOldAndroidWithADB": "Shizuku работает на Android < 8.1 с ADB, обновите Android или используйте Sui",
|
||||
"shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки (если используется Shizuku)",
|
||||
"shizukuPretendToBeGooglePlay": "Указывать Google Play как источник установки (если используется Shizuku)",
|
||||
"useSystemFont": "Использовать системный шрифт",
|
||||
"useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС",
|
||||
"requestHeader": "Заголовок запроса",
|
||||
"useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска",
|
||||
"useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку объекта в качестве даты выпуска",
|
||||
"defaultPseudoVersioningMethod": "Метод псевдоверсионирования по умолчанию",
|
||||
"partialAPKHash": "Частичный хэш APK",
|
||||
"partialAPKHash": "Частичный хеш APK",
|
||||
"APKLinkHash": "Хеш ссылки APK",
|
||||
"directAPKLink": "Прямая ссылка на APK",
|
||||
"pseudoVersionInUse": "Используется псевдоверсия",
|
||||
@ -308,75 +309,100 @@
|
||||
"latest": "Последний",
|
||||
"invertRegEx": "Инвертировать регулярное выражение",
|
||||
"note": "Примечание",
|
||||
"selfHostedNote": "Выпадающий список \"{}\" можно использовать для доступа к самостоятельно размещенным/настроенным экземплярам любого источника.",
|
||||
"badDownload": "APK не удалось разобрать (несовместимая или неполная загрузка)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Поделитесь новыми приложениями с AppVerifier (если доступно)",
|
||||
"selfHostedNote": "Выпадающий список «{}» можно использовать для доступа к самостоятельно размещённым/настроенным экземплярам любого источника.",
|
||||
"badDownload": "Не удалось разобрать APK (несовместимая или неполная загрузка)",
|
||||
"beforeNewInstallsShareToAppVerifier": "Делиться новыми приложениями с AppVerifier (если доступно)",
|
||||
"appVerifierInstructionToast": "Поделитесь с AppVerifier, а затем вернитесь сюда, когда будете готовы.",
|
||||
"wiki": "Помощь/Вики",
|
||||
"crowdsourcedConfigsLabel": "Конфигурации приложений на основе краудсорсинга (используйте на свой страх и риск)",
|
||||
"crowdsourcedConfigsShort": "Конфиги приложений с помощью краудсорсинга",
|
||||
"crowdsourcedConfigsShort": "Краудсорсинг конфигураций приложений",
|
||||
"allowInsecure": "Разрешить небезопасные HTTP-запросы",
|
||||
"stayOneVersionBehind": "Не отставайте от последней версии",
|
||||
"refreshBeforeDownload": "Обновляйте информацию о приложении перед загрузкой",
|
||||
"stayOneVersionBehind": "Оставаться на одну версию ниже последней",
|
||||
"useFirstApkOfVersion": "Автовыбор первого из нескольких APK",
|
||||
"refreshBeforeDownload": "Обновлять информацию о приложении перед загрузкой",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Имя",
|
||||
"smartname": "Имя (умное)",
|
||||
"sortMethod": "Метод сортировки",
|
||||
"welcome": "Добро пожаловать",
|
||||
"documentationLinksNote": "На странице Obtainium в GitHub, ссылка на которую приведена ниже, содержатся ссылки на видео, статьи, обсуждения и другие ресурсы, которые помогут вам понять, как пользоваться приложением.",
|
||||
"batteryOptimizationNote": "Обратите внимание, что фоновая загрузка может работать более надёжно, если отключить оптимизацию батареи ОС для Obtainium.",
|
||||
"fileDeletionError": "Не удалось удалить файл (попробуйте удалить его вручную, а затем повторите попытку): «{}»",
|
||||
"foregroundService": "Передний план службы Obtainium",
|
||||
"foregroundServiceExplanation": "Используйте службу переднего плана для проверки обновлений (более надежно, потребляет больше энергии).",
|
||||
"fgServiceNotice": "Это уведомление необходимо для фоновой проверки обновлений (его можно скрыть в настройках ОС)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Удалить приложение?",
|
||||
"other": "Удалить приложения?"
|
||||
},
|
||||
"tooManyRequestsTryAgainInMinutes": {
|
||||
"one": "Слишком много запросов (ограничение скорости) — попробуйте снова через {} минуту",
|
||||
"other": "Слишком много запросов (ограничение скорости) — попробуйте снова через {} минуты"
|
||||
"few": "Слишком много запросов (ограничение скорости) — попробуйте снова через {} минуты",
|
||||
"other": "Слишком много запросов (ограничение скорости) — попробуйте снова через {} минут"
|
||||
},
|
||||
"bgUpdateGotErrorRetryInMinutes": {
|
||||
"one": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуту",
|
||||
"other": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет запланирована через {} минуты"
|
||||
"one": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет через {} минуту",
|
||||
"few": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет через {} минуты",
|
||||
"other": "При проверке обновлений в фоновом режиме возникла ошибка {}, повторная проверка будет через {} минут"
|
||||
},
|
||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||
"one": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновление — Пользователю будет отправлено уведомление, если это необходимо",
|
||||
"other": "В ходе проверки обновления в фоновом режиме было обнаружено {} обновлений — Пользователю будет отправлено уведомление, если это необходимо"
|
||||
"one": "При проверке в фоновом режиме было обнаружено {} обновления — пользователь будет уведомлен по необходимости",
|
||||
"other": "При проверке в фоновом режиме было обнаружено {} обновлений — пользователь будет уведомлен по необходимости"
|
||||
},
|
||||
"apps": {
|
||||
"one": "{} приложение",
|
||||
"few": "{} приложения",
|
||||
"other": "{} приложений"
|
||||
},
|
||||
"url": {
|
||||
"one": "{} URL-адрес",
|
||||
"other": "{} URL-адреса"
|
||||
"few": "{} URL-адреса",
|
||||
"other": "{} URL-адресов"
|
||||
},
|
||||
"minute": {
|
||||
"one": "{} минута",
|
||||
"other": "{} минуты"
|
||||
"few": "{} минуты",
|
||||
"other": "{} минут"
|
||||
},
|
||||
"hour": {
|
||||
"one": "{} час",
|
||||
"few": "{} часа",
|
||||
"other": "{} часов"
|
||||
},
|
||||
"day": {
|
||||
"one": "{} день",
|
||||
"few": "{} дня",
|
||||
"other": "{} дней"
|
||||
},
|
||||
"clearedNLogsBeforeXAfterY": {
|
||||
"one": "Очищен {n} журнал (до = {before}, после = {after})",
|
||||
"few": "Очищены {n} журнала (до = {before}, после = {after})",
|
||||
"other": "Очищено {n} журналов (до = {before}, после = {after})"
|
||||
},
|
||||
"xAndNMoreUpdatesAvailable": {
|
||||
"one": "У {} и еще 1 приложения есть обновление",
|
||||
"one": "У {} и ещё 1 приложения есть обновление",
|
||||
"other": "У {} и ещё {} приложений есть обновления"
|
||||
},
|
||||
"xAndNMoreUpdatesInstalled": {
|
||||
"one": "{} и ещё 1 приложение были обновлены",
|
||||
"few": "{} и ещё {} приложения были обновлены",
|
||||
"other": "{} и ещё {} приложений были обновлены"
|
||||
},
|
||||
"xAndNMoreUpdatesFailed": {
|
||||
"one": "Не удалось обновить {} и ещё 1 приложение",
|
||||
"few": "Не удалось обновить {} и ещё {} приложения",
|
||||
"other": "Не удалось обновить {} и ещё {} приложений"
|
||||
},
|
||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||
"one": "{} и ещё 1 приложение могли быть обновлены",
|
||||
"few": "{} и ещё {} приложения могли быть обновлены",
|
||||
"other": "{} и ещё {} приложений могли быть обновлены"
|
||||
},
|
||||
"apk": {
|
||||
"one": "{} APK",
|
||||
"other": "{} APKи"
|
||||
"one": "{} APK-файл",
|
||||
"few": "{} APK-файла",
|
||||
"other": "{} APK-файлов"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,15 @@ const neverAutoTranslate = {
|
||||
obtainiumExportHyphenatedLowercase: ['*'],
|
||||
theme: ['de'],
|
||||
appId: ['de'],
|
||||
placeholder: ['pl']
|
||||
placeholder: ['pl'],
|
||||
importExport: ['fr'],
|
||||
url: ['fr'],
|
||||
vivoAppStore: ['cs', 'ja', 'ko', 'vi'],
|
||||
coolapk: ['ja'],
|
||||
obtainiumImport: ['nl'],
|
||||
appLogs: ['nl'],
|
||||
apks: ['vi'],
|
||||
tencentAppStore: ['*']
|
||||
}
|
||||
|
||||
const translateText = async (text, targetLang, authKey) => {
|
||||
@ -76,40 +84,49 @@ const main = async () => {
|
||||
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])
|
||||
try {
|
||||
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) => {
|
||||
await new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, Math.random() * 10000); // Try to avoid rate limit
|
||||
})
|
||||
const response = await translateText(str, lang, deeplAPIKey)
|
||||
if (response.translations) {
|
||||
return response.translations[0].text
|
||||
} else {
|
||||
throw JSON.stringify(response)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (typeof e == 'string') {
|
||||
console.log(`${reportLine} :::: ${e}`)
|
||||
} else {
|
||||
throw e
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
console.log(reportLine)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(file, `${JSON.stringify(thisTranslation, null, ' ')}\n`)
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Standardversionsdetektering",
|
||||
"groupByCategory": "Gruppera via Kategori",
|
||||
"autoApkFilterByArch": "Försök att filtrera APK-filer efter CPU-arkitektur om möjligt",
|
||||
"autoLinkFilterByArch": "Försök att filtrera länkar efter CPU-arkitektur om möjligt",
|
||||
"overrideSource": "Överskrid Källa",
|
||||
"dontShowAgain": "Visa inte detta igen",
|
||||
"dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar",
|
||||
@ -255,7 +256,7 @@
|
||||
"intermediateLinkNotFound": "Mellanlänk hittades inte",
|
||||
"intermediateLink": "Mellanlänk",
|
||||
"exemptFromBackgroundUpdates": "Undta från bakgrundsuppdateringar (om aktiverad)",
|
||||
"bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utanWi-Fi",
|
||||
"bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utan Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Inaktivera bakgrundsuppdateringar när du inte laddar",
|
||||
"autoSelectHighestVersionCode": "Välj automatiskt högsta versionskod APK",
|
||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Appkonfigurationer med hjälp av crowdsourcing",
|
||||
"allowInsecure": "Tillåt osäkra HTTP-förfrågningar",
|
||||
"stayOneVersionBehind": "Håll dig en version bakom den senaste",
|
||||
"useFirstApkOfVersion": "Välj automatiskt den första av flera APK:er",
|
||||
"refreshBeforeDownload": "Uppdatera appdetaljerna före nedladdning",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Namn",
|
||||
"smartname": "Namn (Smart)",
|
||||
"sortMethod": "Sorteringsmetod",
|
||||
"welcome": "Välkommen",
|
||||
"documentationLinksNote": "Obtainium GitHub-sidan som länkas nedan innehåller länkar till videor, artiklar, diskussioner och andra resurser som hjälper dig att förstå hur du använder appen.",
|
||||
"batteryOptimizationNote": "Observera att nedladdningar i bakgrunden kan fungera mer tillförlitligt om du inaktiverar OS-batterioptimeringar för Obtainium.",
|
||||
"fileDeletionError": "Misslyckades med att radera filen (försök radera den manuellt och försök sedan igen): \"{}\"",
|
||||
"foregroundService": "Obtainium förgrundstjänst",
|
||||
"foregroundServiceExplanation": "Använd en förgrundstjänst för uppdateringskontroll (mer tillförlitlig, förbrukar mer ström)",
|
||||
"fgServiceNotice": "Detta meddelande krävs för bakgrundsuppdateringskontroll (det kan döljas i OS-inställningarna)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Ta Bort App?",
|
||||
"other": "Ta Bort Appar?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Standart sürüm tespiti",
|
||||
"groupByCategory": "Kategoriye Göre Grupla",
|
||||
"autoApkFilterByArch": "Mümkünse APK'leri CPU mimarisi ile filtreleme girişimi",
|
||||
"autoLinkFilterByArch": "Mümkünse bağlantıları CPU mimarisine göre filtrelemeye çalışın",
|
||||
"overrideSource": "Öncelenecek Kaynak",
|
||||
"dontShowAgain": "Bunu tekrar gösterme",
|
||||
"dontShowTrackOnlyWarnings": "'Yalnızca Takip Edilen' uyarılarını gösterme",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Kitle Kaynaklı Uygulama Yapılandırmaları",
|
||||
"allowInsecure": "Güvensiz HTTP isteklerine izin ver",
|
||||
"stayOneVersionBehind": "En son sürümün bir sürüm gerisinde kalın",
|
||||
"useFirstApkOfVersion": "Birden fazla APK arasından ilkini otomatik seçme",
|
||||
"refreshBeforeDownload": "İndirmeden önce uygulama ayrıntılarını yenileyin",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "İsim",
|
||||
"smartname": "İsim (Akıllı)",
|
||||
"sortMethod": "Sıralama Yöntemi",
|
||||
"welcome": "Hoş geldiniz",
|
||||
"documentationLinksNote": "Aşağıda bağlantısı verilen Obtainium GitHub sayfası, uygulamayı nasıl kullanacağınızı anlamanıza yardımcı olacak videolara, makalelere, tartışmalara ve diğer kaynaklara bağlantılar içerir.",
|
||||
"batteryOptimizationNote": "Obtainium için işletim sistemi pil optimizasyonlarını devre dışı bırakırsanız arka planda indirmelerin daha güvenilir şekilde çalışabileceğini unutmayın.",
|
||||
"fileDeletionError": "Dosya silinemedi (elle silmeyi deneyin ve sonra tekrar deneyin): \"{}\"",
|
||||
"foregroundService": "Obtainium ön plan hizmeti",
|
||||
"foregroundServiceExplanation": "Güncelleme denetimi için bir ön plan hizmeti kullanın (daha güvenilir, daha fazla güç tüketir)",
|
||||
"fgServiceNotice": "Bu bildirim arka planda güncelleme kontrolü için gereklidir (işletim sistemi ayarlarından gizlenebilir)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Uygulamayı Kaldır?",
|
||||
"other": "Uygulamaları Kaldır?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Стандартне визначення версії",
|
||||
"groupByCategory": "Групувати за категоріями",
|
||||
"autoApkFilterByArch": "Спробувати фільтрувати APK за архітектурою ЦП, якщо можливо",
|
||||
"autoLinkFilterByArch": "Спробуйте відфільтрувати посилання за архітектурою процесора, якщо це можливо",
|
||||
"overrideSource": "Перевизначити джерело",
|
||||
"dontShowAgain": "Не показувати це знову",
|
||||
"dontShowTrackOnlyWarnings": "Не показувати попередження про 'Тільки відстеження'",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "Налаштування краудсорсингових додатків",
|
||||
"allowInsecure": "Дозволити незахищені HTTP-запити",
|
||||
"stayOneVersionBehind": "Залишайтеся на одну версію актуальнішою",
|
||||
"useFirstApkOfVersion": "Автоматичний вибір першого з декількох APK",
|
||||
"refreshBeforeDownload": "Оновіть інформацію про програму перед завантаженням",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Ім'я",
|
||||
"smartname": "Ім'я (Smart)",
|
||||
"sortMethod": "Метод сортування",
|
||||
"welcome": "Ласкаво просимо.",
|
||||
"documentationLinksNote": "Сторінка Obtainium на GitHub, посилання на яку наведено нижче, містить посилання на відео, статті, дискусії та інші ресурси, які допоможуть вам зрозуміти, як користуватися додатком.",
|
||||
"batteryOptimizationNote": "Зауважте, що фонові завантаження можуть працювати надійніше, якщо ви вимкнете оптимізацію батареї ОС для Obtainium.",
|
||||
"fileDeletionError": "Не вдалося видалити файл (спробуйте видалити його вручну, а потім спробуйте ще раз): \"{}\"",
|
||||
"foregroundService": "Обслуговування переднього плану Obtainium",
|
||||
"foregroundServiceExplanation": "Використовуйте службу переднього плану для перевірки оновлень (надійніша, споживає більше енергії)",
|
||||
"fgServiceNotice": "Це сповіщення необхідне для фонової перевірки оновлень (його можна приховати в налаштуваннях ОС)",
|
||||
"removeAppQuestion": {
|
||||
"one": "Видалити застосунок?",
|
||||
"other": "Видалити застосунки?"
|
||||
|
@ -113,7 +113,7 @@
|
||||
"dark": "Tối",
|
||||
"light": "Sáng",
|
||||
"followSystem": "Theo hệ thống",
|
||||
"followSystemThemeExplanation": "Following system theme is possible only by using third-party applications",
|
||||
"followSystemThemeExplanation": "Việc áp dụng chủ đề hệ thống chỉ có thể thực hiện được thông qua việc sử dụng các ứng dụng của bên thứ ba.",
|
||||
"useBlackTheme": "Nền đen",
|
||||
"appSortBy": "Sắp xếp ứng dụng",
|
||||
"authorName": "Tác giả",
|
||||
@ -211,7 +211,7 @@
|
||||
"uninstallFromDevice": "Gỡ cài đặt khỏi thiết bị",
|
||||
"onlyWorksWithNonVersionDetectApps": "Chỉ hoạt động với Ứng dụng đã tắt tính năng phát hiện phiên bản.",
|
||||
"releaseDateAsVersion": "Sử dụng ngày phát hành làm phiên bản",
|
||||
"releaseTitleAsVersion": "Use release title as version string",
|
||||
"releaseTitleAsVersion": "Sử dụng tiêu đề bản phát hành làm chuỗi phiên bản",
|
||||
"releaseDateAsVersionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng trong đó tính năng phát hiện phiên bản không hoạt động chính xác nhưng đã có ngày phát hành.",
|
||||
"changes": "Thay đổi",
|
||||
"releaseDate": "Ngày phát hành",
|
||||
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "Phát hiện phiên bản tiêu chuẩn",
|
||||
"groupByCategory": "Nhóm theo danh mục",
|
||||
"autoApkFilterByArch": "Cố gắng lọc APK theo kiến trúc CPU nếu có thể",
|
||||
"autoLinkFilterByArch": "Cố gắng lọc các liên kết 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'",
|
||||
@ -255,8 +256,8 @@
|
||||
"intermediateLinkNotFound": "Không tìm thấy liên kết trung gian",
|
||||
"intermediateLink": "Liên kết trung gian",
|
||||
"exemptFromBackgroundUpdates": "Miễn cập nhật nền (nếu được bật)",
|
||||
"bgUpdatesOnWiFiOnly": "Tắt cập nhật nền khi không cóWi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Disable background updates when not charging",
|
||||
"bgUpdatesOnWiFiOnly": "Tắt cập nhật nền khi không có Wi-Fi",
|
||||
"bgUpdatesWhileChargingOnly": "Tắt cập nhật nền khi không sạc pin.",
|
||||
"autoSelectHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất",
|
||||
"versionExtractionRegEx": "Trích xuất phiên bản RegEx",
|
||||
"trimVersionString": "Trim Version String With RegEx",
|
||||
@ -294,7 +295,7 @@
|
||||
"shizukuBinderNotFound": "Shizuku chưa khởi động",
|
||||
"shizukuOld": "Phiên bản Shizuku lỗi thời (<11) - hãy cập nhật nó",
|
||||
"shizukuOldAndroidWithADB": "Shizuku chạy trên Android < 8.1 với ADB - hãy cập nhật Android hoặc thay bằng Sui",
|
||||
"shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
|
||||
"shizukuPretendToBeGooglePlay": "Đặt Google Play làm nguồn cài đặt (nếu sử dụng Shizuku)",
|
||||
"useSystemFont": "Sử dụng phông chữ hệ thống",
|
||||
"useVersionCodeAsOSVersion": "Sử dụng Mã phiên bản ứng dụng làm phiên bản do hệ điều hành phát hiện",
|
||||
"requestHeader": "Tiêu đề yêu cầu",
|
||||
@ -314,11 +315,24 @@
|
||||
"appVerifierInstructionToast": "Chia sẻ lên AppVerifier, sau đó quay lại đây khi sẵn sàng.",
|
||||
"wiki": "Trợ giúp/Wiki",
|
||||
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configs",
|
||||
"allowInsecure": "Allow insecure HTTP requests",
|
||||
"stayOneVersionBehind": "Stay one version behind latest",
|
||||
"refreshBeforeDownload": "Refresh app details before download",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configurations",
|
||||
"allowInsecure": "Cho phép các yêu cầu HTTP không an toàn",
|
||||
"stayOneVersionBehind": "Luôn sử dụng phiên bản cũ hơn một phiên bản so với phiên bản mới nhất.",
|
||||
"useFirstApkOfVersion": "Tự động chọn tệp APK đầu tiên trong danh sách nhiều tệp APK.",
|
||||
"refreshBeforeDownload": "Cập nhật thông tin ứng dụng trước khi tải xuống",
|
||||
"tencentAppStore": "Tencent App Store",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "Tên",
|
||||
"smartname": "Name (Smart)",
|
||||
"sortMethod": "Sort Method",
|
||||
"welcome": "Chào mừng",
|
||||
"documentationLinksNote": "Trang GitHub của Obtainium được liên kết bên dưới chứa các liên kết đến video, bài viết, thảo luận và các tài nguyên khác sẽ giúp bạn hiểu cách sử dụng ứng dụng.",
|
||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
||||
"fileDeletionError": "Không thể xóa tệp (hãy thử xóa tệp thủ công rồi thử lại): \"{}\"",
|
||||
"foregroundService": "Dịch vụ nền Obtainium",
|
||||
"foregroundServiceExplanation": "Sử dụng dịch vụ nền trước để kiểm tra cập nhật (đáng tin cậy hơn, tiêu tốn nhiều pin hơn)",
|
||||
"fgServiceNotice": "Thông báo này là bắt buộc để kiểm tra cập nhật nền (có thể ẩn trong cài đặt hệ điều hành).",
|
||||
"removeAppQuestion": {
|
||||
"one": "Gỡ ứng dụng?",
|
||||
"other": "Gỡ ứng dụng?"
|
||||
|
@ -211,7 +211,7 @@
|
||||
"uninstallFromDevice": "從裝置解除安裝",
|
||||
"onlyWorksWithNonVersionDetectApps": "僅適用於停用版本偵測的應用程式。",
|
||||
"releaseDateAsVersion": "使用發佈日期作為版本字串",
|
||||
"releaseTitleAsVersion": "Use release title as version string",
|
||||
"releaseTitleAsVersion": "使用發佈標題作為版本字串",
|
||||
"releaseDateAsVersionExplanation": "此選項僅應用於版本偵測無法正確工作但有發佈日期的應用程式。",
|
||||
"changes": "變更",
|
||||
"releaseDate": "發佈日期",
|
||||
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "標準版本偵測",
|
||||
"groupByCategory": "按類別分組",
|
||||
"autoApkFilterByArch": "如果可能,嘗試按 CPU 架構過濾 APK",
|
||||
"autoLinkFilterByArch": "若可能,自動根據 CPU 架構篩選連結",
|
||||
"overrideSource": "覆蓋來源",
|
||||
"dontShowAgain": "不要再顯示",
|
||||
"dontShowTrackOnlyWarnings": "不要顯示「僅追蹤」警告",
|
||||
@ -255,8 +256,8 @@
|
||||
"intermediateLinkNotFound": "沒有找到中間連結",
|
||||
"intermediateLink": "中間連結",
|
||||
"exemptFromBackgroundUpdates": "免除背景更新(若已啟用)",
|
||||
"bgUpdatesOnWiFiOnly": "停用非Wi-Fi 的背景更新",
|
||||
"bgUpdatesWhileChargingOnly": "Disable background updates when not charging",
|
||||
"bgUpdatesOnWiFiOnly": "停用非 Wi-Fi 的背景更新",
|
||||
"bgUpdatesWhileChargingOnly": "未充電時停用背景更新",
|
||||
"autoSelectHighestVersionCode": "自動選擇最高 versionCode 的 APK",
|
||||
"versionExtractionRegEx": "版本字串提取正則表達式",
|
||||
"trimVersionString": "用正則表達式修剪版本字串",
|
||||
@ -314,11 +315,24 @@
|
||||
"appVerifierInstructionToast": "分享至 AppVerifier,然後準備好時回到此處。",
|
||||
"wiki": "幫助/維基",
|
||||
"crowdsourcedConfigsLabel": "群眾外包的應用程式設定(使用風險自負)",
|
||||
"crowdsourcedConfigsShort": "Crowdsourced App Configs",
|
||||
"allowInsecure": "Allow insecure HTTP requests",
|
||||
"stayOneVersionBehind": "Stay one version behind latest",
|
||||
"refreshBeforeDownload": "Refresh app details before download",
|
||||
"crowdsourcedConfigsShort": "群眾外包的應用程式設定",
|
||||
"allowInsecure": "允許不安全的 HTTP 請求",
|
||||
"stayOneVersionBehind": "保持比最新版本落後一個版本",
|
||||
"useFirstApkOfVersion": "Auto-select first of multiple APKs",
|
||||
"refreshBeforeDownload": "下載前刷新應用程式詳細資訊",
|
||||
"tencentAppStore": "騰訊應用寶",
|
||||
"coolApk": "CoolApk",
|
||||
"vivoAppStore": "vivo App Store (CN)",
|
||||
"name": "名稱",
|
||||
"smartname": "名稱(智慧)",
|
||||
"sortMethod": "排序方式",
|
||||
"welcome": "歡迎",
|
||||
"documentationLinksNote": "下方連結的 Obtainium GitHub 頁面包含影片、文章、討論及其他資源,能幫助你瞭解如何使用這款應用程式。",
|
||||
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
|
||||
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
|
||||
"foregroundService": "Obtainium foreground service",
|
||||
"foregroundServiceExplanation": "Use a foreground service for update checking (more reliable, consumes more power)",
|
||||
"fgServiceNotice": "This notification is required for background update checking (it can be hidden in the OS settings)",
|
||||
"removeAppQuestion": {
|
||||
"one": "移除應用程式?",
|
||||
"other": "移除應用程式?"
|
||||
|
@ -221,6 +221,7 @@
|
||||
"standardVersionDetection": "常规版本检测",
|
||||
"groupByCategory": "按类别分组显示",
|
||||
"autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件",
|
||||
"autoLinkFilterByArch": "尽可能按 CPU 架构过滤链接",
|
||||
"overrideSource": "覆盖来源",
|
||||
"dontShowAgain": "不再显示",
|
||||
"dontShowTrackOnlyWarnings": "忽略“仅追踪”模式警告",
|
||||
@ -317,8 +318,21 @@
|
||||
"crowdsourcedConfigsShort": "众包应用程序配置",
|
||||
"allowInsecure": "允许不安全的 HTTP 请求",
|
||||
"stayOneVersionBehind": "比最新版本晚一个版本",
|
||||
"useFirstApkOfVersion": "从多个 APK 中自动选择第一个",
|
||||
"refreshBeforeDownload": "下载前刷新应用程序详细信息",
|
||||
"tencentAppStore": "腾讯应用宝",
|
||||
"coolApk": "酷安",
|
||||
"vivoAppStore": "vivo 应用商店(中国)",
|
||||
"name": "名称",
|
||||
"smartname": "姓名(智能)",
|
||||
"sortMethod": "排序方法",
|
||||
"welcome": "欢迎光临",
|
||||
"documentationLinksNote": "下面链接的 Obtainium GitHub 页面包含视频、文章、讨论和其他资源的链接,可帮助您了解如何使用该应用程序。",
|
||||
"batteryOptimizationNote": "请注意,如果为 Obtainium 禁用操作系统电池优化功能,后台下载可能会更稳定。",
|
||||
"fileDeletionError": "删除文件失败(尝试手动删除,然后再试一次):\"{}\"",
|
||||
"foregroundService": "获取前台服务",
|
||||
"foregroundServiceExplanation": "使用前台服务进行更新检查(更可靠,耗电量更大)",
|
||||
"fgServiceNotice": "后台更新检查需要此通知(可在操作系统设置中隐藏)。",
|
||||
"removeAppQuestion": {
|
||||
"one": "是否删除应用?",
|
||||
"other": "是否删除应用?"
|
||||
|
24
build.sh
@ -2,15 +2,35 @@
|
||||
# Convenience script
|
||||
|
||||
CURR_DIR="$(pwd)"
|
||||
trap "cd "$CURR_DIR"" EXIT
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
trap "cd \"$CURR_DIR\"" EXIT
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
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
|
||||
|
||||
# Update local Flutter
|
||||
git submodule update --remote
|
||||
cd .flutter
|
||||
git fetch
|
||||
git checkout "$(flutter --version | head -2 | tail -1 | awk '{print $4}')" # Ensure included Flutter submodule version equals my environment
|
||||
git checkout stable
|
||||
git pull
|
||||
FLUTTER_GIT_URL="https://github.com/flutter/flutter/" ./bin/flutter upgrade
|
||||
cd ..
|
||||
|
||||
# Keep global Flutter, if any, in sync
|
||||
if [ -f ~/flutter/bin/flutter ]; then
|
||||
cd ~/flutter
|
||||
./bin/flutter channel stable
|
||||
./bin/flutter upgrade
|
||||
cd "$SCRIPT_DIR"
|
||||
fi
|
||||
|
||||
if [ -z "$(which flutter)" ]; then
|
||||
export PATH="$PATH:$SCRIPT_DIR/.flutter/bin"
|
||||
fi
|
||||
|
||||
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
|
||||
|
86
docker/Dockerfile
Normal file
@ -0,0 +1,86 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV ANDROID_SDK_ROOT /opt/android-sdk-linux
|
||||
ENV PATH "${PATH}:/opt/flutter/bin:/root/.pub-cache/bin:${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:/opt/android-sdk-linux/platform-tools"
|
||||
|
||||
ENV HOME=/root
|
||||
|
||||
# Prereqs
|
||||
RUN \
|
||||
apt-get update &&\
|
||||
apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
curl \
|
||||
file \
|
||||
git 2.x \
|
||||
unzip \
|
||||
xz-utils \
|
||||
zip \
|
||||
libglu1-mesa \
|
||||
libxi-dev \
|
||||
libxmu-dev \
|
||||
libglu1-mesa-dev \
|
||||
git-lfs \
|
||||
openssl \
|
||||
wget
|
||||
|
||||
# Build prereqs
|
||||
RUN \
|
||||
apt-get install -y \
|
||||
cmake curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-mesa fonts-droid-fallback lib32stdc++6 python3 sed \
|
||||
cmake ninja-build build-essential libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev clang pkg-config libgtk-3-dev \
|
||||
liblzma-dev libmount-dev libblkid-dev libgcrypt20-dev libgpg-error-dev libssl-dev libpng-dev libjpeg-dev \
|
||||
libtiff-dev libgif-dev libgtk-3-dev
|
||||
|
||||
# Android SDK prequisites
|
||||
# https://developer.android.com/studio#command-tools
|
||||
RUN \
|
||||
apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
git-lfs \
|
||||
openssl \
|
||||
wget \
|
||||
unzip
|
||||
|
||||
# Android SDK
|
||||
RUN \
|
||||
wget --quiet https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip -O /tmp/tools.zip && \
|
||||
mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
|
||||
unzip -q /tmp/tools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \
|
||||
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest && \
|
||||
rm -v /tmp/tools.zip && \
|
||||
mkdir -p /root/.android/ && touch /root/.android/repositories.cfg &&\
|
||||
apt-get install -y --no-install-recommends openjdk-21-jdk openjdk-21-jre &&\
|
||||
yes | sdkmanager --licenses &&\
|
||||
sdkmanager --update
|
||||
|
||||
# Platform tools
|
||||
# Get latest with sdkmanager --list
|
||||
RUN sdkmanager --install "build-tools;33.0.1"
|
||||
RUN sdkmanager --install "ndk;26.3.11579264"
|
||||
RUN sdkmanager --install "cmake;4.0.2"
|
||||
RUN sdkmanager --install platform-tools
|
||||
RUN sdkmanager --install emulator
|
||||
RUN sdkmanager --install tools
|
||||
RUN sdkmanager --install "platforms;android-28"
|
||||
RUN sdkmanager --install "platforms;android-31"
|
||||
RUN sdkmanager --install "platforms;android-32"
|
||||
RUN sdkmanager --install "platforms;android-33"
|
||||
RUN sdkmanager --install "platforms;android-34"
|
||||
RUN sdkmanager --install "platforms;android-35"
|
||||
|
||||
# Flutter
|
||||
ARG DEV_UID=0
|
||||
RUN \
|
||||
wget --quiet https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.29.3-stable.tar.xz -O /tmp/flutter.tar.xz &&\
|
||||
mkdir -p /opt &&\
|
||||
cd /opt &&\
|
||||
tar xf /tmp/flutter.tar.xz &&\
|
||||
rm /tmp/flutter.tar.xz &&\
|
||||
git config --global --add safe.directory /opt/flutter &&\
|
||||
dart pub global activate cider &&\
|
||||
chown -R ${DEV_UID} /opt/flutter
|
||||
RUN flutter --disable-analytics
|
||||
RUN flutter upgrade
|
||||
RUN chmod a+w /opt/flutter/packages -R
|
21
docker/builder.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
cd ${SCRIPT_DIR}/..
|
||||
mkdir -p ./data/home
|
||||
docker run \
|
||||
--rm \
|
||||
-ti \
|
||||
--net host \
|
||||
-v "${PWD}/../:${PWD}/../":z \
|
||||
-w "${PWD}" \
|
||||
--name flutter-dev-obtainium \
|
||||
--user $(id -u) \
|
||||
-v ./data/home:/home/${USER}:z \
|
||||
-e USER=${USER} \
|
||||
-e HOME=/home/${USER} \
|
||||
-e ANDROID_USER_HOME=${HOME}/.android \
|
||||
-e GRADLE_USER_HOME=${HOME}/.gradle \
|
||||
-e PS1="${debian_chroot:+($debian_chroot)}${USER}@\h:\w\$ " \
|
||||
flutter-builder-obtainium
|
14
docker/mkbuilder.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
D=$(date +'%Y%m%d.%H%M%S%3N')
|
||||
|
||||
set -e
|
||||
|
||||
cd "${SCRIPT_DIR}/.."
|
||||
# Create the builder image
|
||||
docker build \
|
||||
-t flutter-builder-obtainium \
|
||||
-f ./docker/Dockerfile \
|
||||
--build-arg="DEV_UID=$(id -u)" \
|
||||
.
|
48
fastlane/metadata/android/de/full_description.txt
Normal file
@ -0,0 +1,48 @@
|
||||
<p>Obtainium erlaubt Ihnen die Installation und Aktualisierung von Apps direkt von deren Veröffentlichungsseiten aus; Sie erhalten außerdem Benachrichtigungen, wenn neue Versionen verfügbar sind.</p>
|
||||
<p>Lesen Sie das <a href="https://wiki.obtainium.imranr.dev/de/">Wiki</a></p>
|
||||
<p>
|
||||
<b>Derzeit unterstützte App-Quellen:</b>
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Open Source – Allgemein:</p>
|
||||
<ul>
|
||||
<li>GitHub</li>
|
||||
<li>GitLab</li>
|
||||
<li>Forgejo (Codeberg)</li>
|
||||
<li>F-Droid</li>
|
||||
<li>Third Party F-Droid Repos</li>
|
||||
<li>IzzyOnDroid</li>
|
||||
<li>SourceHut</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Andere – Allgemein:</p>
|
||||
<ul>
|
||||
<li>APKPure</li>
|
||||
<li>Aptoide</li>
|
||||
<li>Uptodown</li>
|
||||
<li>APKMirror (Track-Only)</li>
|
||||
<li>Huawei AppGallery</li>
|
||||
<li>Tencent App Store</li>
|
||||
<li>CoolApk</li>
|
||||
<li>vivo App Store (China)</li>
|
||||
<li>Jenkins Jobs</li>
|
||||
<li>RuStore</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Andere - App-Spezifisch:</p>
|
||||
<ul>
|
||||
<li>Telegram App</li>
|
||||
<li>Neutron Code</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><p>„HTML“ (Fallback): Jede andere URL, die eine HTML-Seite mit Links zu APK-Dateien zurückgibt.</p></li>
|
||||
</ul>
|
||||
<p>
|
||||
<b>Einschränkungen:</b>
|
||||
</p>
|
||||
<p>
|
||||
Bei einigen Quellen werden die Daten mit Hilfe von Web Scraping gesammelt und können aufgrund von Änderungen am Design der Website leicht beschädigt werden. In solchen Fällen sind zuverlässigere Methoden nicht verfügbar.
|
||||
</p>
|
1
fastlane/metadata/android/de/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
… Android-App-Updates direkt von der Quelle erhalten
|
1
fastlane/metadata/android/de/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Obtainium
|
@ -25,6 +25,8 @@
|
||||
<li>APKMirror (Track-Only)</li>
|
||||
<li>Huawei AppGallery</li>
|
||||
<li>Tencent App Store</li>
|
||||
<li>CoolApk</li>
|
||||
<li>vivo App Store (CN)</li>
|
||||
<li>Jenkins Jobs</li>
|
||||
<li>RuStore</li>
|
||||
</ul>
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 41 B |
1
fastlane/metadata/android/en-US/images/featureGraphic.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../assets/graphics/banner.png
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 41 B |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 47 B |
1
fastlane/metadata/android/en-US/images/icon.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../assets/graphics/icon-512x512.png
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 47 B |
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 47 B |
1
fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../../assets/screenshots/1.apps.png
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 47 B |
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 53 B |
1
fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../../assets/screenshots/2.dark_theme.png
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 53 B |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 55 B |
1
fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../../assets/screenshots/3.material_you.png
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 55 B |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 46 B |
1
fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../../assets/screenshots/4.app.png
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 46 B |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 51 B |
1
fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../../assets/screenshots/5.app_opts.png
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 51 B |
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 54 B |
1
fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../../assets/screenshots/6.app_webview.png
|
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 54 B |
@ -25,6 +25,8 @@
|
||||
<li>APKMirror (Track-Only)</li>
|
||||
<li>Huawei AppGallery</li>
|
||||
<li>Tencent App Store</li>
|
||||
<li>CoolApk</li>
|
||||
<li>vivo App Store (CN)</li>
|
||||
<li>Jenkins Jobs</li>
|
||||
<li>RuStore</li>
|
||||
</ul>
|
||||
|
1
fastlane/metadata/android/ru/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Obtainium
|
@ -12,8 +12,9 @@ class APKCombo extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
var match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -22,25 +23,30 @@ class APKCombo extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return Uri.parse(standardUrl).pathSegments.last;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) async {
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
return {
|
||||
"User-Agent": "curl/8.0.1",
|
||||
"Accept": "*/*",
|
||||
"Connection": "keep-alive",
|
||||
"Host": hosts[0]
|
||||
"Host": hosts[0],
|
||||
};
|
||||
}
|
||||
|
||||
Future<List<MapEntry<String, String>>> getApkUrls(
|
||||
String standardUrl, Map<String, dynamic> additionalSettings) async {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var res = await sourceRequest('$standardUrl/download/apk', {});
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
@ -65,7 +71,9 @@ class APKCombo extends AppSource {
|
||||
String verCode =
|
||||
e.querySelector('.info .header .vercode')?.text.trim() ?? '';
|
||||
return MapEntry<String, String>(
|
||||
arch != null ? '$arch-$verCode.apk' : '', url ?? '');
|
||||
arch != null ? '$arch-$verCode.apk' : '',
|
||||
url ?? '',
|
||||
);
|
||||
}).toList();
|
||||
})
|
||||
.reduce((value, element) => [...value, ...element])
|
||||
@ -74,8 +82,11 @@ class APKCombo extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||
Map<String, dynamic> additionalSettings) async {
|
||||
Future<String> apkUrlPrefetchModifier(
|
||||
String apkUrl,
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var freshURLs = await getApkUrls(standardUrl, additionalSettings);
|
||||
var path2Match = Uri.parse(apkUrl).path;
|
||||
for (var url in freshURLs) {
|
||||
@ -116,9 +127,10 @@ class APKCombo extends AppSource {
|
||||
}
|
||||
}
|
||||
return APKDetails(
|
||||
version,
|
||||
await getApkUrls(standardUrl, additionalSettings),
|
||||
AppNames(author, appName),
|
||||
releaseDate: releaseDate);
|
||||
version,
|
||||
await getApkUrls(standardUrl, additionalSettings),
|
||||
AppNames(author, appName),
|
||||
releaseDate: releaseDate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,37 +17,44 @@ class APKMirror extends AppSource {
|
||||
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormSwitch('fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
||||
GeneratedFormSwitch(
|
||||
'fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'),
|
||||
defaultValue: true,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormTextField('filterReleaseTitlesByRegEx',
|
||||
label: tr('filterReleaseTitlesByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
}
|
||||
])
|
||||
]
|
||||
GeneratedFormTextField(
|
||||
'filterReleaseTitlesByRegEx',
|
||||
label: tr('filterReleaseTitlesByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) async {
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
return {
|
||||
"User-Agent":
|
||||
"Obtainium/${(await getInstalledInfo(obtainiumId))?.versionName ?? '1.0.0'}"
|
||||
"Obtainium/${(await getInstalledInfo(obtainiumId))?.versionName ?? '1.0.0'}",
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/apk/[^/]+/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/apk/[^/]+/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -68,11 +75,14 @@ class APKMirror extends AppSource {
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
String? regexFilter =
|
||||
(additionalSettings['filterReleaseTitlesByRegEx'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterReleaseTitlesByRegEx']
|
||||
: null;
|
||||
Response res = await sourceRequest('$standardUrl/feed', additionalSettings);
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterReleaseTitlesByRegEx']
|
||||
: null;
|
||||
Response res = await sourceRequest(
|
||||
'$standardUrl/feed/',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var items = parse(res.body).querySelectorAll('item');
|
||||
dynamic targetRelease;
|
||||
@ -94,11 +104,14 @@ class APKMirror extends AppSource {
|
||||
.split(' ')
|
||||
.sublist(0, 5)
|
||||
.join(' ');
|
||||
DateTime? releaseDate =
|
||||
dateString != null ? HttpDate.parse('$dateString GMT') : null;
|
||||
DateTime? releaseDate = dateString != null
|
||||
? HttpDate.parse('$dateString GMT')
|
||||
: null;
|
||||
String? version = titleString
|
||||
?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0,
|
||||
RegExp(' by ').allMatches(titleString).last.start)
|
||||
?.substring(
|
||||
RegExp('[0-9]').firstMatch(titleString)?.start ?? 0,
|
||||
RegExp(' by ').allMatches(titleString).last.start,
|
||||
)
|
||||
.trim();
|
||||
if (version == null || version.isEmpty) {
|
||||
version = titleString;
|
||||
@ -106,8 +119,12 @@ class APKMirror extends AppSource {
|
||||
if (version == null || version.isEmpty) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
return APKDetails(version, [], getAppNames(standardUrl),
|
||||
releaseDate: releaseDate);
|
||||
return APKDetails(
|
||||
version,
|
||||
[],
|
||||
getAppNames(standardUrl),
|
||||
releaseDate: releaseDate,
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -1,24 +1,18 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:obtainium/app_sources/html.dart';
|
||||
import 'package:obtainium/components/generated_form.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
|
||||
parseDateTimeMMMddCommayyyy(String? dateString) {
|
||||
DateTime? releaseDate;
|
||||
try {
|
||||
releaseDate = dateString != null
|
||||
? DateFormat('MMM dd, yyyy').parse(dateString)
|
||||
: null;
|
||||
releaseDate = dateString != null && releaseDate == null
|
||||
? DateFormat('MMMM dd, yyyy').parse(dateString)
|
||||
: releaseDate;
|
||||
} catch (err) {
|
||||
// ignore
|
||||
extension Unique<E, Id> on List<E> {
|
||||
List<E> unique([Id Function(E element)? id, bool inplace = true]) {
|
||||
final ids = <dynamic>{};
|
||||
var list = inplace ? this : List<E>.from(this);
|
||||
list.retainWhere((x) => ids.add(id != null ? id(x) : x as Id));
|
||||
return list;
|
||||
}
|
||||
return releaseDate;
|
||||
}
|
||||
|
||||
class APKPure extends AppSource {
|
||||
@ -29,29 +23,44 @@ class APKPure extends AppSource {
|
||||
showReleaseDateAsVersionToggle = true;
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormSwitch('fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
||||
GeneratedFormSwitch(
|
||||
'fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'),
|
||||
defaultValue: true,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('stayOneVersionBehind',
|
||||
label: tr('stayOneVersionBehind'), defaultValue: false)
|
||||
]
|
||||
GeneratedFormSwitch(
|
||||
'stayOneVersionBehind',
|
||||
label: tr('stayOneVersionBehind'),
|
||||
defaultValue: false,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch(
|
||||
'useFirstApkOfVersion',
|
||||
label: tr('useFirstApkOfVersion'),
|
||||
defaultValue: true,
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegExB = RegExp(
|
||||
'^https?://m.${getSourceRegex(hosts)}(/+[^/]{2})?/+[^/]+/+[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://m.${getSourceRegex(hosts)}(/+[^/]{2})?/+[^/]+/+[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
|
||||
if (match != null) {
|
||||
var uri = Uri.parse(url);
|
||||
url = 'https://${uri.host.substring(2)}${uri.path}';
|
||||
}
|
||||
RegExp standardUrlRegExA = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}(/+[^/]{2})?/+[^/]+/+[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}(/+[^/]{2})?/+[^/]+/+[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
match = standardUrlRegExA.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -60,100 +69,88 @@ class APKPure extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return Uri.parse(standardUrl).pathSegments.last;
|
||||
}
|
||||
|
||||
getDetailsForVersionLink(
|
||||
String standardUrl,
|
||||
String appId,
|
||||
String host,
|
||||
List<String> supportedArchs,
|
||||
String link,
|
||||
Map<String, dynamic> additionalSettings) async {
|
||||
var res = await sourceRequest(link, additionalSettings);
|
||||
if (res.statusCode == 200) {
|
||||
var html = parse(res.body);
|
||||
var apksDiv =
|
||||
html.querySelector('#version-list div div.show-more-content');
|
||||
DateTime? topReleaseDate;
|
||||
var apkUrls = apksDiv
|
||||
?.querySelectorAll('div.group-title')
|
||||
.map((e) {
|
||||
String architectureString = e.text.trim();
|
||||
if (architectureString.toLowerCase() == 'unlimited' ||
|
||||
architectureString.toLowerCase() == 'universal') {
|
||||
architectureString = '';
|
||||
}
|
||||
List<String> architectures = architectureString
|
||||
.split(',')
|
||||
.map((e) => e.trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList();
|
||||
// Only take the first APK for each architecture, ignore others for now, for simplicity
|
||||
// Unclear why there can even be multiple APKs for the same version and arch
|
||||
var apkInfo = e.nextElementSibling?.querySelector('div.info');
|
||||
String? versionCode = RegExp('[0-9]+')
|
||||
.firstMatch(
|
||||
apkInfo?.querySelector('div.info-top .code')?.text ??
|
||||
'')
|
||||
?.group(0)
|
||||
?.trim();
|
||||
var types = apkInfo
|
||||
?.querySelectorAll('div.info-top span.tag')
|
||||
.map((e) => e.text.trim())
|
||||
.map((t) => t == 'APKs' ? 'APK' : t) ??
|
||||
[];
|
||||
String type = types.isEmpty ? 'APK' : types.first;
|
||||
String? dateString = apkInfo
|
||||
?.querySelector('div.info-bottom span.time')
|
||||
?.text
|
||||
.trim();
|
||||
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString);
|
||||
if (additionalSettings['autoApkFilterByArch'] == true &&
|
||||
architectures.isNotEmpty &&
|
||||
architectures
|
||||
.where((a) => supportedArchs.contains(a))
|
||||
.isEmpty) {
|
||||
return const MapEntry('', '');
|
||||
}
|
||||
topReleaseDate ??=
|
||||
releaseDate; // Just use the release date of the first APK in the list as the release date for this version
|
||||
return MapEntry(
|
||||
'$appId-$versionCode-$architectureString.${type.toLowerCase()}',
|
||||
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?versionCode=$versionCode');
|
||||
})
|
||||
.where((e) => e.key.isNotEmpty)
|
||||
.toList() ??
|
||||
[];
|
||||
if (apkUrls.isEmpty) {
|
||||
throw NoAPKError();
|
||||
}
|
||||
String version = Uri.parse(link).pathSegments.last;
|
||||
String? author;
|
||||
try {
|
||||
author = html
|
||||
.querySelector('span.info-sdk')
|
||||
?.text
|
||||
.trim()
|
||||
.substring(version.length + 4) ??
|
||||
Uri.parse(standardUrl).pathSegments.reversed.last;
|
||||
} catch (e) {
|
||||
author = html.querySelector('span.info-sdk')?.text.trim() ??
|
||||
Uri.parse(standardUrl).pathSegments.reversed.last;
|
||||
}
|
||||
String appName =
|
||||
html.querySelector('h1.info-title')?.text.trim() ?? appId;
|
||||
String? changeLog = html
|
||||
.querySelector('div.module.change-log')
|
||||
?.innerHtml
|
||||
.trim()
|
||||
.replaceAll("<br>", " \n");
|
||||
return APKDetails(version, apkUrls, AppNames(author, appName),
|
||||
releaseDate: topReleaseDate, changeLog: changeLog);
|
||||
Future<APKDetails> getDetailsForVersion(
|
||||
List<Map<String, dynamic>> versionVariants,
|
||||
List<String> supportedArchs,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var apkUrls = versionVariants
|
||||
.map((e) {
|
||||
String appId = e['package_name'];
|
||||
String versionCode = e['version_code'];
|
||||
|
||||
List<String> architectures = e['native_code']?.cast<String>();
|
||||
String architectureString = architectures.join(',');
|
||||
if (architectures.contains("universal") ||
|
||||
architectures.contains("unlimited")) {
|
||||
architectures = [];
|
||||
}
|
||||
if (additionalSettings['autoApkFilterByArch'] == true &&
|
||||
architectures.isNotEmpty &&
|
||||
architectures.where((a) => supportedArchs.contains(a)).isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String type = e['asset']['type'];
|
||||
String downloadUri = e['asset']['url'];
|
||||
|
||||
return MapEntry(
|
||||
'$appId-$versionCode-$architectureString.${type.toLowerCase()}',
|
||||
downloadUri,
|
||||
);
|
||||
})
|
||||
.nonNulls
|
||||
.toList()
|
||||
.unique((e) => e.key);
|
||||
|
||||
if (apkUrls.isEmpty) {
|
||||
throw NoAPKError();
|
||||
}
|
||||
|
||||
// get version details from first variant
|
||||
var v = versionVariants.first;
|
||||
String version = v['version_name'];
|
||||
String author = v['developer'];
|
||||
String appName = v['title'];
|
||||
DateTime releaseDate = DateTime.parse(v['update_date']);
|
||||
String? changeLog = v['whatsnew'];
|
||||
if (changeLog != null && changeLog.isEmpty) {
|
||||
changeLog = null;
|
||||
}
|
||||
|
||||
if (additionalSettings['useFirstApkOfVersion'] == true) {
|
||||
apkUrls = [apkUrls.first];
|
||||
}
|
||||
|
||||
return APKDetails(
|
||||
version,
|
||||
apkUrls,
|
||||
AppNames(author, appName),
|
||||
releaseDate: releaseDate,
|
||||
changeLog: changeLog,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
if (forAPKDownload) {
|
||||
return null;
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
return {
|
||||
"Ual-Access-Businessid": "projecta",
|
||||
"Ual-Access-ProjectA":
|
||||
'{"device_info":{"os_ver":"${((await DeviceInfoPlugin().androidInfo).version.sdkInt)}"}}',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,41 +160,56 @@ class APKPure extends AppSource {
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
String appId = (await tryInferringAppId(standardUrl))!;
|
||||
String host = Uri.parse(standardUrl).host;
|
||||
|
||||
var res0 = await sourceRequest('$standardUrl/versions', additionalSettings);
|
||||
var decodedStandardUrl = standardUrl;
|
||||
try {
|
||||
decodedStandardUrl = Uri.decodeFull(decodedStandardUrl);
|
||||
} catch (e) {
|
||||
//
|
||||
List<String> supportedArchs =
|
||||
(await DeviceInfoPlugin().androidInfo).supportedAbis;
|
||||
|
||||
// request versions from API
|
||||
var res = await sourceRequest(
|
||||
"https://tapi.pureapk.com/v3/get_app_his_version?package_name=$appId&hl=en",
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
var versionLinks = await grabLinksCommon(res0, {
|
||||
'skipSort': true,
|
||||
'customLinkFilterRegex': '$decodedStandardUrl/download/[^/]+\$'
|
||||
});
|
||||
List<Map<String, dynamic>> apks = jsonDecode(
|
||||
res.body,
|
||||
)['version_list'].cast<Map<String, dynamic>>();
|
||||
|
||||
var supportedArchs = (await DeviceInfoPlugin().androidInfo).supportedAbis;
|
||||
// group by version
|
||||
List<List<Map<String, dynamic>>> versions = apks
|
||||
.fold<Map<String, List<Map<String, dynamic>>>>({}, (
|
||||
Map<String, List<Map<String, dynamic>>> val,
|
||||
Map<String, dynamic> element,
|
||||
) {
|
||||
String v = element['version_name'];
|
||||
if (!val.containsKey(v)) {
|
||||
val[v] = [];
|
||||
}
|
||||
val[v]?.add(element);
|
||||
return val;
|
||||
})
|
||||
.values
|
||||
.toList();
|
||||
|
||||
if (additionalSettings['autoApkFilterByArch'] != true) {
|
||||
// No need to request multiple versions when we're not going to filter them (always pick the top one)
|
||||
versionLinks = versionLinks.sublist(0, 1);
|
||||
}
|
||||
if (versionLinks.isEmpty) {
|
||||
if (versions.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
|
||||
for (var i = 0; i < versionLinks.length; i++) {
|
||||
var link = versionLinks[i];
|
||||
for (var i = 0; i < versions.length; i++) {
|
||||
var v = versions[i];
|
||||
try {
|
||||
if (i == 0 && additionalSettings['stayOneVersionBehind'] == true) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
return await getDetailsForVersionLink(standardUrl, appId, host,
|
||||
supportedArchs, link.key, additionalSettings);
|
||||
return await getDetailsForVersion(
|
||||
v,
|
||||
supportedArchs,
|
||||
additionalSettings,
|
||||
);
|
||||
} catch (e) {
|
||||
if (additionalSettings['fallbackToOlderReleases'] != true ||
|
||||
i == versionLinks.length - 1) {
|
||||
i == versions.length - 1) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,9 @@ class Aptoide extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}',
|
||||
caseSensitive: false);
|
||||
'^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -26,14 +27,20 @@ class Aptoide extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return (await getAppDetailsJSON(
|
||||
standardUrl, additionalSettings))['package'];
|
||||
standardUrl,
|
||||
additionalSettings,
|
||||
))['package'];
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getAppDetailsJSON(
|
||||
String standardUrl, Map<String, dynamic> additionalSettings) async {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var res = await sourceRequest(standardUrl, additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
@ -46,7 +53,9 @@ class Aptoide extends AppSource {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
var res2 = await sourceRequest(
|
||||
'https://ws2.aptoide.com/api/7/getApp/app_id/$id', additionalSettings);
|
||||
'https://ws2.aptoide.com/api/7/getApp/app_id/$id',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res2.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
@ -76,7 +85,10 @@ class Aptoide extends AppSource {
|
||||
}
|
||||
|
||||
return APKDetails(
|
||||
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
|
||||
releaseDate: relDate);
|
||||
version,
|
||||
getApkUrlsFromUrls([apkUrl]),
|
||||
AppNames(author, appName),
|
||||
releaseDate: relDate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,9 @@ class Codeberg extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -36,8 +37,9 @@ class Codeberg extends AppSource {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
|
||||
(bool useTagUrl) async {
|
||||
return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings, (
|
||||
bool useTagUrl,
|
||||
) async {
|
||||
return 'https://${hosts[0]}/api/v1/repos${standardUrl.substring('https://${hosts[0]}'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
|
||||
}, null);
|
||||
}
|
||||
@ -49,12 +51,15 @@ class Codeberg extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<String>>> search(String query,
|
||||
{Map<String, dynamic> querySettings = const {}}) async {
|
||||
Future<Map<String, List<String>>> search(
|
||||
String query, {
|
||||
Map<String, dynamic> querySettings = const {},
|
||||
}) async {
|
||||
return gh.searchCommon(
|
||||
query,
|
||||
'https://${hosts[0]}/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100',
|
||||
'data',
|
||||
querySettings: querySettings);
|
||||
query,
|
||||
'https://${hosts[0]}/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100',
|
||||
'data',
|
||||
querySettings: querySettings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
193
lib/app_sources/coolapk.dart
Normal file
@ -0,0 +1,193 @@
|
||||
import 'dart:convert';
|
||||
import 'package:bcrypt/bcrypt.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
import 'dart:math';
|
||||
|
||||
// kanged from https://github.com/DUpdateSystem/UpgradeAll/blob/b2f92c9/core-websdk/src/main/java/net/xzos/upgradeall/core/websdk/api/client_proxy/hubs/CoolApk.kt
|
||||
class CoolApk extends AppSource {
|
||||
CoolApk() {
|
||||
name = tr('coolApk');
|
||||
hosts = ['www.coolapk.com', 'api2.coolapk.com'];
|
||||
allowSubDomains = true;
|
||||
naiveStandardVersionDetection = true;
|
||||
allowOverride = false;
|
||||
}
|
||||
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
r'^https?://(www\.)?coolapk\.com/apk/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
var match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
}
|
||||
String standardizedUrl = match.group(0)!;
|
||||
return standardizedUrl;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
String appId = Uri.parse(standardUrl).pathSegments.last;
|
||||
return appId;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<APKDetails> getLatestAPKDetails(
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
String appId = (await tryInferringAppId(standardUrl))!;
|
||||
String apiUrl = 'https://api2.coolapk.com';
|
||||
|
||||
// get latest
|
||||
var detailUrl = '$apiUrl/v6/apk/detail?id=$appId';
|
||||
var headers = await getRequestHeaders(additionalSettings);
|
||||
var res = await sourceRequest(detailUrl, additionalSettings);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
||||
var json = jsonDecode(res.body);
|
||||
if (json['status'] == -2 || json['data'] == null) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
|
||||
var detail = json['data'];
|
||||
String version = detail['apkversionname'].toString();
|
||||
String appName = detail['title'].toString();
|
||||
String author = detail['developername']?.toString() ?? 'CoolApk';
|
||||
String changelog = detail['changelog']?.toString() ?? '';
|
||||
int? releaseDate = detail['lastupdate'] != null
|
||||
? (detail['lastupdate'] is int
|
||||
? detail['lastupdate'] * 1000
|
||||
: int.parse(detail['lastupdate'].toString()) * 1000)
|
||||
: null;
|
||||
String aid = detail['id'].toString();
|
||||
|
||||
// get apk url
|
||||
String apkUrl = await _getLatestApkUrl(
|
||||
apiUrl,
|
||||
appId,
|
||||
aid,
|
||||
version,
|
||||
headers,
|
||||
);
|
||||
if (apkUrl.isEmpty) {
|
||||
throw NoAPKError();
|
||||
}
|
||||
|
||||
String apkName = '${appId}_$version.apk';
|
||||
|
||||
return APKDetails(
|
||||
version,
|
||||
[MapEntry(apkName, apkUrl)],
|
||||
AppNames(author, appName),
|
||||
releaseDate: releaseDate != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(releaseDate)
|
||||
: null,
|
||||
changeLog: changelog,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _getLatestApkUrl(
|
||||
String apiUrl,
|
||||
String appId,
|
||||
String aid,
|
||||
String version,
|
||||
Map<String, String>? headers,
|
||||
) async {
|
||||
String url = '$apiUrl/v6/apk/download?pn=$appId&aid=$aid';
|
||||
var res = await sourceRequest(url, {}, followRedirects: false);
|
||||
if (res.statusCode >= 300 && res.statusCode < 400) {
|
||||
String location = res.headers['location'] ?? '';
|
||||
return location;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
var tokenPair = _getToken();
|
||||
// CoolAPK header
|
||||
return {
|
||||
'User-Agent':
|
||||
'Dalvik/2.1.0 (Linux; U; Android 9; MI 8 SE MIUI/9.5.9) (#Build; Xiaomi; MI 8 SE; PKQ1.181121.001; 9) +CoolMarket/12.4.2-2208241-universal',
|
||||
'X-App-Id': 'com.coolapk.market',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-Sdk-Int': '30',
|
||||
'X-App-Mode': 'universal',
|
||||
'X-App-Channel': 'coolapk',
|
||||
'X-Sdk-Locale': 'zh-CN',
|
||||
'X-App-Version': '12.4.2',
|
||||
'X-Api-Supported': '2208241',
|
||||
'X-App-Code': '2208241',
|
||||
'X-Api-Version': '12',
|
||||
'X-App-Device': tokenPair['deviceCode']!,
|
||||
'X-Dark-Mode': '0',
|
||||
'X-App-Token': tokenPair['token']!,
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, String> _getToken() {
|
||||
final rand = Random();
|
||||
|
||||
String randHexString(int n) => List.generate(
|
||||
n,
|
||||
(_) => rand.nextInt(256).toRadixString(16).padLeft(2, '0'),
|
||||
).join().toUpperCase();
|
||||
|
||||
String randMacAddress() => List.generate(
|
||||
6,
|
||||
(_) => rand.nextInt(256).toRadixString(16).padLeft(2, '0'),
|
||||
).join(':');
|
||||
|
||||
// 加密算法来自 https://github.com/XiaoMengXinX/FuckCoolapkTokenV2、https://github.com/Coolapk-UWP/Coolapk-UWP
|
||||
// device
|
||||
String aid = randHexString(16);
|
||||
String mac = randMacAddress();
|
||||
const manufactor = 'Google';
|
||||
const brand = 'Google';
|
||||
const model = 'Pixel 5a';
|
||||
const buildNumber = 'SQ1D.220105.007';
|
||||
|
||||
// generate deviceCode
|
||||
String deviceCode = base64.encode(
|
||||
'$aid; ; ; $mac; $manufactor; $brand; $model; $buildNumber'.codeUnits,
|
||||
);
|
||||
|
||||
// generate timestamp
|
||||
String timeStamp = (DateTime.now().millisecondsSinceEpoch ~/ 1000)
|
||||
.toString();
|
||||
String base64TimeStamp = base64.encode(timeStamp.codeUnits);
|
||||
String md5TimeStamp = md5.convert(timeStamp.codeUnits).toString();
|
||||
String md5DeviceCode = md5.convert(deviceCode.codeUnits).toString();
|
||||
|
||||
// generate token
|
||||
String token =
|
||||
'token://com.coolapk.market/dcf01e569c1e3db93a3d0fcf191a622c?$md5TimeStamp\$$md5DeviceCode&com.coolapk.market';
|
||||
String base64Token = base64.encode(token.codeUnits);
|
||||
String md5Base64Token = md5.convert(base64Token.codeUnits).toString();
|
||||
String md5Token = md5.convert(token.codeUnits).toString();
|
||||
|
||||
// generate salt and hash
|
||||
String bcryptSalt =
|
||||
'\$2a\$10\$${base64TimeStamp.substring(0, 14)}/${md5Token.substring(0, 6)}u';
|
||||
String bcryptResult = BCrypt.hashpw(md5Base64Token, bcryptSalt);
|
||||
String reBcryptResult = bcryptResult.replaceRange(0, 3, '\$2y');
|
||||
String finalToken = 'v2${base64.encode(reBcryptResult.codeUnits)}';
|
||||
|
||||
return {'deviceCode': deviceCode, 'token': finalToken};
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:obtainium/app_sources/html.dart';
|
||||
import 'package:obtainium/components/generated_form.dart';
|
||||
import 'package:obtainium/custom_errors.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
|
||||
@ -8,19 +9,33 @@ class DirectAPKLink extends AppSource {
|
||||
|
||||
DirectAPKLink() {
|
||||
name = tr('directAPKLink');
|
||||
additionalSourceAppSpecificSettingFormItems = html
|
||||
.additionalSourceAppSpecificSettingFormItems
|
||||
.where((element) => element
|
||||
.where((element) => element.key == 'requestHeader')
|
||||
.isNotEmpty)
|
||||
.toList();
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
...html.additionalSourceAppSpecificSettingFormItems
|
||||
.where(
|
||||
(element) => element
|
||||
.where((element) => element.key == 'requestHeader')
|
||||
.isNotEmpty,
|
||||
)
|
||||
,
|
||||
[
|
||||
GeneratedFormDropdown(
|
||||
'defaultPseudoVersioningMethod',
|
||||
[
|
||||
MapEntry('partialAPKHash', tr('partialAPKHash')),
|
||||
MapEntry('ETag', 'ETag'),
|
||||
],
|
||||
label: tr('defaultPseudoVersioningMethod'),
|
||||
defaultValue: 'partialAPKHash',
|
||||
),
|
||||
],
|
||||
];
|
||||
excludeCommonSettingKeys = [
|
||||
'versionExtractionRegEx',
|
||||
'matchGroupToUse',
|
||||
'versionDetection',
|
||||
'useVersionCodeAsOSVersion',
|
||||
'apkFilterRegEx',
|
||||
'autoApkFilterByArch'
|
||||
'autoApkFilterByArch',
|
||||
];
|
||||
}
|
||||
|
||||
@ -39,10 +54,13 @@ class DirectAPKLink extends AppSource {
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) {
|
||||
return html.getRequestHeaders(additionalSettings,
|
||||
forAPKDownload: forAPKDownload);
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) {
|
||||
return html.getRequestHeaders(
|
||||
additionalSettings,
|
||||
forAPKDownload: forAPKDownload,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -50,16 +68,16 @@ class DirectAPKLink extends AppSource {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var additionalSettingsNew =
|
||||
getDefaultValuesFromFormItems(html.combinedAppSpecificSettingFormItems);
|
||||
var additionalSettingsNew = getDefaultValuesFromFormItems(
|
||||
html.combinedAppSpecificSettingFormItems,
|
||||
);
|
||||
for (var s in additionalSettings.keys) {
|
||||
if (additionalSettingsNew.containsKey(s)) {
|
||||
additionalSettingsNew[s] = additionalSettings[s];
|
||||
}
|
||||
}
|
||||
additionalSettingsNew['defaultPseudoVersioningMethod'] = 'partialAPKHash';
|
||||
additionalSettingsNew['directAPKLink'] = true;
|
||||
additionalSettings['versionDetection'] = false;
|
||||
additionalSettingsNew['versionDetection'] = false;
|
||||
return html.getLatestAPKDetails(standardUrl, additionalSettingsNew);
|
||||
}
|
||||
}
|
||||
|
@ -17,22 +17,28 @@ class FDroid extends AppSource {
|
||||
canSearch = true;
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormTextField('filterVersionsByRegEx',
|
||||
label: tr('filterVersionsByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
}
|
||||
])
|
||||
GeneratedFormTextField(
|
||||
'filterVersionsByRegEx',
|
||||
label: tr('filterVersionsByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('trySelectingSuggestedVersionCode',
|
||||
label: tr('trySelectingSuggestedVersionCode'))
|
||||
GeneratedFormSwitch(
|
||||
'trySelectingSuggestedVersionCode',
|
||||
label: tr('trySelectingSuggestedVersionCode'),
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('autoSelectHighestVersionCode',
|
||||
label: tr('autoSelectHighestVersionCode'))
|
||||
GeneratedFormSwitch(
|
||||
'autoSelectHighestVersionCode',
|
||||
label: tr('autoSelectHighestVersionCode'),
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
@ -40,16 +46,18 @@ class FDroid extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegExB = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+packages/+[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+packages/+[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
|
||||
if (match != null) {
|
||||
url =
|
||||
'https://${Uri.parse(match.group(0)!).host}/packages/${Uri.parse(url).pathSegments.where((s) => s.trim().isNotEmpty).last}';
|
||||
}
|
||||
RegExp standardUrlRegExA = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/+packages/+[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/+packages/+[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
match = standardUrlRegExA.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -58,8 +66,10 @@ class FDroid extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return Uri.parse(standardUrl).pathSegments.last;
|
||||
}
|
||||
|
||||
@ -71,22 +81,28 @@ class FDroid extends AppSource {
|
||||
String? appId = await tryInferringAppId(standardUrl);
|
||||
String host = Uri.parse(standardUrl).host;
|
||||
var details = getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||
await sourceRequest(
|
||||
'https://$host/api/v1/packages/$appId', additionalSettings),
|
||||
'https://$host/repo/$appId',
|
||||
standardUrl,
|
||||
name,
|
||||
additionalSettings: additionalSettings);
|
||||
await sourceRequest(
|
||||
'https://$host/api/v1/packages/$appId',
|
||||
additionalSettings,
|
||||
),
|
||||
'https://$host/repo/$appId',
|
||||
standardUrl,
|
||||
name,
|
||||
additionalSettings: additionalSettings,
|
||||
);
|
||||
if (!hostChanged) {
|
||||
try {
|
||||
var res = await sourceRequest(
|
||||
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml',
|
||||
additionalSettings);
|
||||
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml',
|
||||
additionalSettings,
|
||||
);
|
||||
var lines = res.body.split('\n');
|
||||
var authorLines = lines.where((l) => l.startsWith('AuthorName: '));
|
||||
if (authorLines.isNotEmpty) {
|
||||
details.names.author =
|
||||
authorLines.first.split(': ').sublist(1).join(': ');
|
||||
details.names.author = authorLines.first
|
||||
.split(': ')
|
||||
.sublist(1)
|
||||
.join(': ');
|
||||
}
|
||||
var changelogUrls = lines
|
||||
.where((l) => l.startsWith('Changelog: '))
|
||||
@ -110,9 +126,9 @@ class FDroid extends AppSource {
|
||||
if ((isGitHub || isGitLab) &&
|
||||
(details.changeLog?.indexOf('/blob/') ?? -1) >= 0) {
|
||||
details.changeLog = (await sourceRequest(
|
||||
details.changeLog!.replaceFirst('/blob/', '/raw/'),
|
||||
additionalSettings))
|
||||
.body;
|
||||
details.changeLog!.replaceFirst('/blob/', '/raw/'),
|
||||
additionalSettings,
|
||||
)).body;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -126,10 +142,14 @@ class FDroid extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<String>>> search(String query,
|
||||
{Map<String, dynamic> querySettings = const {}}) async {
|
||||
Future<Map<String, List<String>>> search(
|
||||
String query, {
|
||||
Map<String, dynamic> querySettings = const {},
|
||||
}) async {
|
||||
Response res = await sourceRequest(
|
||||
'https://search.${hosts[0]}/?q=${Uri.encodeQueryComponent(query)}', {});
|
||||
'https://search.${hosts[0]}/?q=${Uri.encodeQueryComponent(query)}',
|
||||
{},
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
Map<String, List<String>> urlsWithDescriptions = {};
|
||||
parse(res.body).querySelectorAll('.package-header').forEach((e) {
|
||||
@ -145,7 +165,7 @@ class FDroid extends AppSource {
|
||||
urlsWithDescriptions[url] = [
|
||||
e.querySelector('.package-name')?.text.trim() ?? '',
|
||||
e.querySelector('.package-summary')?.text.trim() ??
|
||||
tr('noDescription')
|
||||
tr('noDescription'),
|
||||
];
|
||||
}
|
||||
});
|
||||
@ -156,29 +176,36 @@ class FDroid extends AppSource {
|
||||
}
|
||||
|
||||
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) {
|
||||
Response res,
|
||||
String apkUrlPrefix,
|
||||
String standardUrl,
|
||||
String sourceName, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) {
|
||||
var autoSelectHighestVersionCode =
|
||||
additionalSettings['autoSelectHighestVersionCode'] == true;
|
||||
var trySelectingSuggestedVersionCode =
|
||||
additionalSettings['trySelectingSuggestedVersionCode'] == true;
|
||||
var filterVersionsByRegEx =
|
||||
(additionalSettings['filterVersionsByRegEx'] as String?)?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterVersionsByRegEx']
|
||||
: null;
|
||||
true
|
||||
? additionalSettings['filterVersionsByRegEx']
|
||||
: null;
|
||||
var apkFilterRegEx =
|
||||
(additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty == true
|
||||
? additionalSettings['apkFilterRegEx']
|
||||
: null;
|
||||
? additionalSettings['apkFilterRegEx']
|
||||
: null;
|
||||
if (res.statusCode == 200) {
|
||||
var response = jsonDecode(res.body);
|
||||
List<dynamic> releases = response['packages'] ?? [];
|
||||
if (apkFilterRegEx != null) {
|
||||
releases = releases.where((rel) {
|
||||
String apk = '${apkUrlPrefix}_${rel['versionCode']}.apk';
|
||||
return filterApks([MapEntry(apk, apk)], apkFilterRegEx, false)
|
||||
.isNotEmpty;
|
||||
return filterApks(
|
||||
[MapEntry(apk, apk)],
|
||||
apkFilterRegEx,
|
||||
false,
|
||||
).isNotEmpty;
|
||||
}).toList();
|
||||
}
|
||||
if (releases.isEmpty) {
|
||||
@ -191,8 +218,10 @@ class FDroid extends AppSource {
|
||||
if (trySelectingSuggestedVersionCode &&
|
||||
response['suggestedVersionCode'] != null &&
|
||||
filterVersionsByRegEx == null) {
|
||||
var suggestedReleases = releases.where((element) =>
|
||||
element['versionCode'] == response['suggestedVersionCode']);
|
||||
var suggestedReleases = releases.where(
|
||||
(element) =>
|
||||
element['versionCode'] == response['suggestedVersionCode'],
|
||||
);
|
||||
if (suggestedReleases.isNotEmpty) {
|
||||
releaseChoices = suggestedReleases;
|
||||
version = suggestedReleases.first['versionName'];
|
||||
@ -203,8 +232,9 @@ class FDroid extends AppSource {
|
||||
version = null;
|
||||
releaseChoices = [];
|
||||
for (var i = 0; i < releases.length; i++) {
|
||||
if (RegExp(filterVersionsByRegEx!)
|
||||
.hasMatch(releases[i]['versionName'])) {
|
||||
if (RegExp(
|
||||
filterVersionsByRegEx!,
|
||||
).hasMatch(releases[i]['versionName'])) {
|
||||
version = releases[i]['versionName'];
|
||||
}
|
||||
}
|
||||
@ -219,8 +249,9 @@ class FDroid extends AppSource {
|
||||
}
|
||||
// If a suggested release was not already picked, pick all those with the selected version
|
||||
if (releaseChoices.isEmpty) {
|
||||
releaseChoices =
|
||||
releases.where((element) => element['versionName'] == version);
|
||||
releaseChoices = releases.where(
|
||||
(element) => element['versionName'] == version,
|
||||
);
|
||||
}
|
||||
// For the remaining releases, use the toggles to auto-select one if possible
|
||||
if (releaseChoices.length > 1) {
|
||||
@ -228,8 +259,10 @@ class FDroid extends AppSource {
|
||||
releaseChoices = [releaseChoices.first];
|
||||
} else if (trySelectingSuggestedVersionCode &&
|
||||
response['suggestedVersionCode'] != null) {
|
||||
var suggestedReleases = releaseChoices.where((element) =>
|
||||
element['versionCode'] == response['suggestedVersionCode']);
|
||||
var suggestedReleases = releaseChoices.where(
|
||||
(element) =>
|
||||
element['versionCode'] == response['suggestedVersionCode'],
|
||||
);
|
||||
if (suggestedReleases.isNotEmpty) {
|
||||
releaseChoices = suggestedReleases;
|
||||
}
|
||||
@ -241,8 +274,11 @@ class FDroid extends AppSource {
|
||||
List<String> apkUrls = releaseChoices
|
||||
.map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
|
||||
.toList();
|
||||
return APKDetails(version, getApkUrlsFromUrls(apkUrls.toSet().toList()),
|
||||
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
|
||||
return APKDetails(
|
||||
version,
|
||||
getApkUrlsFromUrls(apkUrls.toSet().toList()),
|
||||
AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last),
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -15,15 +15,20 @@ class FDroidRepo extends AppSource {
|
||||
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormTextField('appIdOrName',
|
||||
label: tr('appIdOrName'),
|
||||
hint: tr('reposHaveMultipleApps'),
|
||||
required: true)
|
||||
GeneratedFormTextField(
|
||||
'appIdOrName',
|
||||
label: tr('appIdOrName'),
|
||||
hint: tr('reposHaveMultipleApps'),
|
||||
required: true,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('pickHighestVersionCode',
|
||||
label: tr('pickHighestVersionCode'), defaultValue: false)
|
||||
]
|
||||
GeneratedFormSwitch(
|
||||
'pickHighestVersionCode',
|
||||
label: tr('pickHighestVersionCode'),
|
||||
defaultValue: false,
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -54,8 +59,10 @@ class FDroidRepo extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<String>>> search(String query,
|
||||
{Map<String, dynamic> querySettings = const {}}) async {
|
||||
Future<Map<String, List<String>>> search(
|
||||
String query, {
|
||||
Map<String, dynamic> querySettings = const {},
|
||||
}) async {
|
||||
String? url = querySettings['url'];
|
||||
if (url == null) {
|
||||
throw NoReleasesError();
|
||||
@ -73,11 +80,8 @@ class FDroidRepo extends AppSource {
|
||||
appId.contains(query) ||
|
||||
appName.contains(query) ||
|
||||
appDesc.contains(query)) {
|
||||
results[
|
||||
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}?appId=$appId'] = [
|
||||
appName,
|
||||
appDesc
|
||||
];
|
||||
results['${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}?appId=$appId'] =
|
||||
[appName, appDesc];
|
||||
}
|
||||
});
|
||||
return results;
|
||||
@ -90,21 +94,21 @@ class FDroidRepo extends AppSource {
|
||||
void runOnAddAppInputChange(String userInput) {
|
||||
additionalSourceAppSpecificSettingFormItems =
|
||||
additionalSourceAppSpecificSettingFormItems.map((row) {
|
||||
row = row.map((item) {
|
||||
if (item.key == 'appIdOrName') {
|
||||
try {
|
||||
var appId = Uri.parse(userInput).queryParameters['appId'];
|
||||
if (appId != null && item is GeneratedFormTextField) {
|
||||
item.required = false;
|
||||
row = row.map((item) {
|
||||
if (item.key == 'appIdOrName') {
|
||||
try {
|
||||
var appId = Uri.parse(userInput).queryParameters['appId'];
|
||||
if (appId != null && item is GeneratedFormTextField) {
|
||||
item.required = false;
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}).toList();
|
||||
return row;
|
||||
}).toList();
|
||||
return item;
|
||||
}).toList();
|
||||
return row;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -119,8 +123,11 @@ class FDroidRepo extends AppSource {
|
||||
if (appId != null) {
|
||||
app.url = uri
|
||||
.replace(
|
||||
queryParameters: Map.fromEntries(
|
||||
[...uri.queryParameters.entries, MapEntry('appId', appId)]))
|
||||
queryParameters: Map.fromEntries([
|
||||
...uri.queryParameters.entries,
|
||||
MapEntry('appId', appId),
|
||||
]),
|
||||
)
|
||||
.toString();
|
||||
app.additionalSettings['appIdOrName'] = appId;
|
||||
app.id = appId;
|
||||
@ -133,8 +140,9 @@ class FDroidRepo extends AppSource {
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
var res = await sourceRequest(
|
||||
'$url${url.endsWith('/index.xml') ? '' : '/index.xml'}',
|
||||
additionalSettings);
|
||||
'$url${url.endsWith('/index.xml') ? '' : '/index.xml'}',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode != 200) {
|
||||
var base = url.endsWith('/index.xml')
|
||||
? url.split('/').reversed.toList().sublist(1).reversed.join('/')
|
||||
@ -142,7 +150,9 @@ class FDroidRepo extends AppSource {
|
||||
res = await sourceRequest('$base/repo/index.xml', additionalSettings);
|
||||
if (res.statusCode != 200) {
|
||||
res = await sourceRequest(
|
||||
'$base/fdroid/repo/index.xml', additionalSettings);
|
||||
'$base/fdroid/repo/index.xml',
|
||||
additionalSettings,
|
||||
);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@ -164,8 +174,10 @@ class FDroidRepo extends AppSource {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
additionalSettings['appIdOrName'] = appIdOrName;
|
||||
var res =
|
||||
await sourceRequestWithURLVariants(standardUrl, additionalSettings);
|
||||
var res = await sourceRequestWithURLVariants(
|
||||
standardUrl,
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var body = parse(res.body);
|
||||
var foundApps = body.querySelectorAll('application').where((element) {
|
||||
@ -202,24 +214,32 @@ class FDroidRepo extends AppSource {
|
||||
throw NoVersionError();
|
||||
}
|
||||
var latestVersionReleases = releases
|
||||
.where((element) =>
|
||||
element.querySelector('version')?.innerHtml == latestVersion &&
|
||||
element.querySelector('apkname') != null)
|
||||
.where(
|
||||
(element) =>
|
||||
element.querySelector('version')?.innerHtml == latestVersion &&
|
||||
element.querySelector('apkname') != null,
|
||||
)
|
||||
.toList();
|
||||
if (latestVersionReleases.length > 1 && pickHighestVersionCode) {
|
||||
latestVersionReleases.sort((e1, e2) {
|
||||
return int.parse(e2.querySelector('versioncode')!.innerHtml)
|
||||
.compareTo(int.parse(e1.querySelector('versioncode')!.innerHtml));
|
||||
return int.parse(
|
||||
e2.querySelector('versioncode')!.innerHtml,
|
||||
).compareTo(int.parse(e1.querySelector('versioncode')!.innerHtml));
|
||||
});
|
||||
latestVersionReleases = [latestVersionReleases[0]];
|
||||
}
|
||||
List<String> apkUrls = latestVersionReleases
|
||||
.map((e) =>
|
||||
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}/${e.querySelector('apkname')!.innerHtml}')
|
||||
.map(
|
||||
(e) =>
|
||||
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}/${e.querySelector('apkname')!.innerHtml}',
|
||||
)
|
||||
.toList();
|
||||
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
|
||||
AppNames(authorName, appName),
|
||||
releaseDate: releaseDate);
|
||||
return APKDetails(
|
||||
latestVersion,
|
||||
getApkUrlsFromUrls(apkUrls),
|
||||
AppNames(authorName, appName),
|
||||
releaseDate: releaseDate,
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -19,135 +19,185 @@ class GitHub extends AppSource {
|
||||
showReleaseDateAsVersionToggle = true;
|
||||
|
||||
sourceConfigSettingFormItems = [
|
||||
GeneratedFormTextField('github-creds',
|
||||
label: tr('githubPATLabel'),
|
||||
password: true,
|
||||
required: false,
|
||||
belowWidgets: [
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
GeneratedFormTextField(
|
||||
'github-creds',
|
||||
label: tr('githubPATLabel'),
|
||||
password: true,
|
||||
required: false,
|
||||
belowWidgets: [
|
||||
const SizedBox(height: 4),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString(
|
||||
'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token',
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
tr('about'),
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString(
|
||||
'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token',
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
child: Text(
|
||||
tr('about'),
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline, fontSize: 12),
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
])
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormSwitch('includePrereleases',
|
||||
label: tr('includePrereleases'), defaultValue: false)
|
||||
GeneratedFormSwitch(
|
||||
'includePrereleases',
|
||||
label: tr('includePrereleases'),
|
||||
defaultValue: false,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
||||
GeneratedFormSwitch(
|
||||
'fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'),
|
||||
defaultValue: true,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormTextField('filterReleaseTitlesByRegEx',
|
||||
label: tr('filterReleaseTitlesByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
}
|
||||
])
|
||||
GeneratedFormTextField(
|
||||
'filterReleaseTitlesByRegEx',
|
||||
label: tr('filterReleaseTitlesByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormTextField('filterReleaseNotesByRegEx',
|
||||
label: tr('filterReleaseNotesByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
}
|
||||
])
|
||||
GeneratedFormTextField(
|
||||
'filterReleaseNotesByRegEx',
|
||||
label: tr('filterReleaseNotesByRegEx'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
[GeneratedFormSwitch('verifyLatestTag', label: tr('verifyLatestTag'))],
|
||||
[
|
||||
GeneratedFormSwitch('dontSortReleasesList',
|
||||
label: tr('dontSortReleasesList'))
|
||||
GeneratedFormDropdown(
|
||||
'sortMethodChoice',
|
||||
[
|
||||
MapEntry('date', tr('releaseDate')),
|
||||
MapEntry('smartname', tr('smartname')),
|
||||
MapEntry('none', tr('none')),
|
||||
MapEntry(
|
||||
'smartname-datefallback',
|
||||
'${tr('smartname')} x ${tr('releaseDate')}',
|
||||
),
|
||||
MapEntry('name', tr('name')),
|
||||
],
|
||||
label: tr('sortMethod'),
|
||||
defaultValue: 'date',
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('useLatestAssetDateAsReleaseDate',
|
||||
label: tr('useLatestAssetDateAsReleaseDate'), defaultValue: false)
|
||||
GeneratedFormSwitch(
|
||||
'useLatestAssetDateAsReleaseDate',
|
||||
label: tr('useLatestAssetDateAsReleaseDate'),
|
||||
defaultValue: false,
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('releaseTitleAsVersion',
|
||||
label: tr('releaseTitleAsVersion'), defaultValue: false)
|
||||
]
|
||||
GeneratedFormSwitch(
|
||||
'releaseTitleAsVersion',
|
||||
label: tr('releaseTitleAsVersion'),
|
||||
defaultValue: false,
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
canSearch = true;
|
||||
searchQuerySettingFormItems = [
|
||||
GeneratedFormTextField('minStarCount',
|
||||
label: tr('minStarCount'),
|
||||
defaultValue: '0',
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
try {
|
||||
int.parse(value ?? '0');
|
||||
} catch (e) {
|
||||
return tr('invalidInput');
|
||||
}
|
||||
return null;
|
||||
GeneratedFormTextField(
|
||||
'minStarCount',
|
||||
label: tr('minStarCount'),
|
||||
defaultValue: '0',
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
try {
|
||||
int.parse(value ?? '0');
|
||||
} catch (e) {
|
||||
return tr('invalidInput');
|
||||
}
|
||||
])
|
||||
return null;
|
||||
},
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
const possibleBuildGradleLocations = [
|
||||
'/app/build.gradle',
|
||||
'android/app/build.gradle',
|
||||
'src/app/build.gradle'
|
||||
'src/app/build.gradle',
|
||||
];
|
||||
for (var path in possibleBuildGradleLocations) {
|
||||
try {
|
||||
var res = await sourceRequest(
|
||||
'${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/contents/$path',
|
||||
additionalSettings);
|
||||
'${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/contents/$path',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
try {
|
||||
var body = jsonDecode(res.body);
|
||||
var trimmedLines = utf8
|
||||
.decode(base64
|
||||
.decode(body['content'].toString().split('\n').join('')))
|
||||
.decode(
|
||||
base64.decode(
|
||||
body['content'].toString().split('\n').join(''),
|
||||
),
|
||||
)
|
||||
.split('\n')
|
||||
.map((e) => e.trim());
|
||||
var appIds = trimmedLines.where((l) =>
|
||||
l.startsWith('applicationId "') ||
|
||||
l.startsWith('applicationId \''));
|
||||
appIds = appIds.map((appId) => appId
|
||||
.split(appId.startsWith('applicationId "') ? '"' : '\'')[1]);
|
||||
appIds = appIds.map((appId) {
|
||||
if (appId.startsWith('\${') && appId.endsWith('}')) {
|
||||
appId = trimmedLines
|
||||
.where((l) => l.startsWith(
|
||||
'def ${appId.substring(2, appId.length - 1)}'))
|
||||
.first;
|
||||
appId = appId.split(appId.contains('"') ? '"' : '\'')[1];
|
||||
}
|
||||
return appId;
|
||||
}).where((appId) => appId.isNotEmpty);
|
||||
var appIds = trimmedLines.where(
|
||||
(l) =>
|
||||
l.startsWith('applicationId "') ||
|
||||
l.startsWith('applicationId \''),
|
||||
);
|
||||
appIds = appIds.map(
|
||||
(appId) => appId.split(
|
||||
appId.startsWith('applicationId "') ? '"' : '\'',
|
||||
)[1],
|
||||
);
|
||||
appIds = appIds
|
||||
.map((appId) {
|
||||
if (appId.startsWith('\${') && appId.endsWith('}')) {
|
||||
appId = trimmedLines
|
||||
.where(
|
||||
(l) => l.startsWith(
|
||||
'def ${appId.substring(2, appId.length - 1)}',
|
||||
),
|
||||
)
|
||||
.first;
|
||||
appId = appId.split(appId.contains('"') ? '"' : '\'')[1];
|
||||
}
|
||||
return appId;
|
||||
})
|
||||
.where((appId) => appId.isNotEmpty);
|
||||
if (appIds.length == 1) {
|
||||
return appIds.first;
|
||||
}
|
||||
} catch (err) {
|
||||
LogsProvider().add(
|
||||
'Error parsing build.gradle from ${res.request!.url.toString()}: ${err.toString()}');
|
||||
'Error parsing build.gradle from ${res.request!.url.toString()}: ${err.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@ -160,8 +210,9 @@ class GitHub extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -171,8 +222,9 @@ class GitHub extends AppSource {
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) async {
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
var token = await getTokenIfAny(additionalSettings);
|
||||
var headers = <String, String>{};
|
||||
if (token != null && token.isNotEmpty) {
|
||||
@ -191,14 +243,17 @@ class GitHub extends AppSource {
|
||||
Future<String?> getTokenIfAny(Map<String, dynamic> additionalSettings) async {
|
||||
SettingsProvider settingsProvider = SettingsProvider();
|
||||
await settingsProvider.initializeSettings();
|
||||
var sourceConfig =
|
||||
await getSourceConfigValues(additionalSettings, settingsProvider);
|
||||
var sourceConfig = await getSourceConfigValues(
|
||||
additionalSettings,
|
||||
settingsProvider,
|
||||
);
|
||||
String? creds = sourceConfig['github-creds'];
|
||||
if (creds != null) {
|
||||
var userNameEndIndex = creds.indexOf(':');
|
||||
if (userNameEndIndex > 0) {
|
||||
creds = creds.substring(
|
||||
userNameEndIndex + 1); // For old username-included token inputs
|
||||
userNameEndIndex + 1,
|
||||
); // For old username-included token inputs
|
||||
}
|
||||
return creds;
|
||||
} else {
|
||||
@ -218,42 +273,48 @@ class GitHub extends AppSource {
|
||||
'https://api.${hosts[0]}';
|
||||
|
||||
Future<String> convertStandardUrlToAPIUrl(
|
||||
String standardUrl, Map<String, dynamic> additionalSettings) async =>
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async =>
|
||||
'${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://${hosts[0]}'.length)}';
|
||||
|
||||
@override
|
||||
String? changeLogPageFromStandardUrl(String standardUrl) =>
|
||||
'$standardUrl/releases';
|
||||
|
||||
Future<APKDetails> getLatestAPKDetailsCommon(String requestUrl,
|
||||
String standardUrl, Map<String, dynamic> additionalSettings,
|
||||
{Function(Response)? onHttpErrorCode}) async {
|
||||
Future<APKDetails> getLatestAPKDetailsCommon(
|
||||
String requestUrl,
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
Function(Response)? onHttpErrorCode,
|
||||
}) async {
|
||||
bool includePrereleases = additionalSettings['includePrereleases'] == true;
|
||||
bool fallbackToOlderReleases =
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
String? regexFilter =
|
||||
(additionalSettings['filterReleaseTitlesByRegEx'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterReleaseTitlesByRegEx']
|
||||
: null;
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterReleaseTitlesByRegEx']
|
||||
: null;
|
||||
String? regexNotesFilter =
|
||||
(additionalSettings['filterReleaseNotesByRegEx'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterReleaseNotesByRegEx']
|
||||
: null;
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? additionalSettings['filterReleaseNotesByRegEx']
|
||||
: null;
|
||||
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
|
||||
bool dontSortReleasesList =
|
||||
additionalSettings['dontSortReleasesList'] == true;
|
||||
bool useLatestAssetDateAsReleaseDate =
|
||||
additionalSettings['useLatestAssetDateAsReleaseDate'] == true;
|
||||
String sortMethod =
|
||||
additionalSettings['sortMethodChoice'] ?? 'smartname-datefallback';
|
||||
dynamic latestRelease;
|
||||
if (verifyLatestTag) {
|
||||
var temp = requestUrl.split('?');
|
||||
Response res = await sourceRequest(
|
||||
'${temp[0]}/latest${temp.length > 1 ? '?${temp.sublist(1).join('?')}' : ''}',
|
||||
additionalSettings);
|
||||
'${temp[0]}/latest${temp.length > 1 ? '?${temp.sublist(1).join('?')}' : ''}',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode != 200) {
|
||||
if (onHttpErrorCode != null) {
|
||||
onHttpErrorCode(res);
|
||||
@ -268,8 +329,10 @@ class GitHub extends AppSource {
|
||||
if (latestRelease != null) {
|
||||
var latestTag = latestRelease['tag_name'] ?? latestRelease['name'];
|
||||
if (releases
|
||||
.where((element) =>
|
||||
(element['tag_name'] ?? element['name']) == latestTag)
|
||||
.where(
|
||||
(element) =>
|
||||
(element['tag_name'] ?? element['name']) == latestTag,
|
||||
)
|
||||
.isEmpty) {
|
||||
releases = [latestRelease, ...releases];
|
||||
}
|
||||
@ -289,10 +352,10 @@ class GitHub extends AppSource {
|
||||
|
||||
DateTime? getPublishDateFromRelease(dynamic rel) =>
|
||||
rel?['published_at'] != null
|
||||
? DateTime.parse(rel['published_at'])
|
||||
: rel?['commit']?['created'] != null
|
||||
? DateTime.parse(rel['commit']['created'])
|
||||
: null;
|
||||
? DateTime.parse(rel['published_at'])
|
||||
: rel?['commit']?['created'] != null
|
||||
? DateTime.parse(rel['commit']['created'])
|
||||
: null;
|
||||
DateTime? getNewestAssetDateFromRelease(dynamic rel) {
|
||||
var allAssets = rel['assets'] as List<dynamic>?;
|
||||
var filteredAssets = rel['filteredAssets'] as List<dynamic>?;
|
||||
@ -313,10 +376,10 @@ class GitHub extends AppSource {
|
||||
|
||||
DateTime? getReleaseDateFromRelease(dynamic rel, bool useAssetDate) =>
|
||||
!useAssetDate
|
||||
? getPublishDateFromRelease(rel)
|
||||
: getNewestAssetDateFromRelease(rel);
|
||||
? getPublishDateFromRelease(rel)
|
||||
: getNewestAssetDateFromRelease(rel);
|
||||
|
||||
if (dontSortReleasesList) {
|
||||
if (sortMethod == 'none') {
|
||||
releases = releases.reversed.toList();
|
||||
} else {
|
||||
releases.sort((a, b) {
|
||||
@ -330,22 +393,41 @@ class GitHub extends AppSource {
|
||||
} else {
|
||||
var nameA = a['tag_name'] ?? a['name'];
|
||||
var nameB = b['tag_name'] ?? b['name'];
|
||||
var stdFormats = findStandardFormatsForVersion(nameA, true)
|
||||
.intersection(findStandardFormatsForVersion(nameB, true));
|
||||
if (stdFormats.isNotEmpty) {
|
||||
var reg = RegExp(stdFormats.first);
|
||||
var matchA = reg.firstMatch(nameA);
|
||||
var matchB = reg.firstMatch(nameB);
|
||||
return compareAlphaNumeric(
|
||||
(nameA as String).substring(matchA!.start, matchA.end),
|
||||
(nameB as String).substring(matchB!.start, matchB.end));
|
||||
} else {
|
||||
var stdFormats = findStandardFormatsForVersion(
|
||||
nameA,
|
||||
false,
|
||||
).intersection(findStandardFormatsForVersion(nameB, false));
|
||||
if (sortMethod == 'date' ||
|
||||
(sortMethod == 'smartname-datefallback' &&
|
||||
stdFormats.isEmpty)) {
|
||||
return (getReleaseDateFromRelease(
|
||||
a, useLatestAssetDateAsReleaseDate) ??
|
||||
a,
|
||||
useLatestAssetDateAsReleaseDate,
|
||||
) ??
|
||||
DateTime(1))
|
||||
.compareTo(getReleaseDateFromRelease(
|
||||
b, useLatestAssetDateAsReleaseDate) ??
|
||||
DateTime(0));
|
||||
.compareTo(
|
||||
getReleaseDateFromRelease(
|
||||
b,
|
||||
useLatestAssetDateAsReleaseDate,
|
||||
) ??
|
||||
DateTime(0),
|
||||
);
|
||||
} else {
|
||||
if (sortMethod != 'name' && stdFormats.isNotEmpty) {
|
||||
var reg = RegExp(stdFormats.last);
|
||||
var matchA = reg.firstMatch(nameA);
|
||||
var matchB = reg.firstMatch(nameB);
|
||||
return compareAlphaNumeric(
|
||||
(nameA as String).substring(matchA!.start, matchA.end),
|
||||
(nameB as String).substring(matchB!.start, matchB.end),
|
||||
);
|
||||
} else {
|
||||
// 'name'
|
||||
return compareAlphaNumeric(
|
||||
(nameA as String),
|
||||
(nameB as String),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -356,9 +438,11 @@ class GitHub extends AppSource {
|
||||
latestRelease !=
|
||||
(releases[releases.length - 1]['tag_name'] ??
|
||||
releases[0]['name'])) {
|
||||
var ind = releases.indexWhere((element) =>
|
||||
(latestRelease['tag_name'] ?? latestRelease['name']) ==
|
||||
(element['tag_name'] ?? element['name']));
|
||||
var ind = releases.indexWhere(
|
||||
(element) =>
|
||||
(latestRelease['tag_name'] ?? latestRelease['name']) ==
|
||||
(element['tag_name'] ?? element['name']),
|
||||
);
|
||||
if (ind >= 0) {
|
||||
releases.add(releases.removeAt(ind));
|
||||
}
|
||||
@ -386,8 +470,9 @@ class GitHub extends AppSource {
|
||||
continue;
|
||||
}
|
||||
if (regexNotesFilter != null &&
|
||||
!RegExp(regexNotesFilter)
|
||||
.hasMatch(((releases[i]['body'] as String?) ?? '').trim())) {
|
||||
!RegExp(
|
||||
regexNotesFilter,
|
||||
).hasMatch(((releases[i]['body'] as String?) ?? '').trim())) {
|
||||
continue;
|
||||
}
|
||||
var allAssetsWithUrls = findReleaseAssetUrls(releases[i]);
|
||||
@ -395,24 +480,31 @@ class GitHub extends AppSource {
|
||||
.map((e) => e['final_url'] as MapEntry<String, String>)
|
||||
.toList();
|
||||
var apkAssetsWithUrls = allAssetsWithUrls
|
||||
.where((element) =>
|
||||
(element['final_url'] as MapEntry<String, String>)
|
||||
.key
|
||||
.toLowerCase()
|
||||
.endsWith('.apk'))
|
||||
.where(
|
||||
(element) => (element['final_url'] as MapEntry<String, String>)
|
||||
.key
|
||||
.toLowerCase()
|
||||
.endsWith('.apk'),
|
||||
)
|
||||
.toList();
|
||||
|
||||
var filteredApkUrls = filterApks(
|
||||
apkAssetsWithUrls
|
||||
.map((e) => e['final_url'] as MapEntry<String, String>)
|
||||
.toList(),
|
||||
additionalSettings['apkFilterRegEx'],
|
||||
additionalSettings['invertAPKFilter']);
|
||||
apkAssetsWithUrls
|
||||
.map((e) => e['final_url'] as MapEntry<String, String>)
|
||||
.toList(),
|
||||
additionalSettings['apkFilterRegEx'],
|
||||
additionalSettings['invertAPKFilter'],
|
||||
);
|
||||
var filteredApks = apkAssetsWithUrls
|
||||
.where((e) => filteredApkUrls
|
||||
.where((e2) =>
|
||||
e2.key == (e['final_url'] as MapEntry<String, String>).key)
|
||||
.isNotEmpty)
|
||||
.where(
|
||||
(e) => filteredApkUrls
|
||||
.where(
|
||||
(e2) =>
|
||||
e2.key ==
|
||||
(e['final_url'] as MapEntry<String, String>).key,
|
||||
)
|
||||
.isNotEmpty,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (filteredApks.isEmpty && additionalSettings['trackOnly'] != true) {
|
||||
@ -423,17 +515,23 @@ class GitHub extends AppSource {
|
||||
targetRelease['filteredAssets'] = filteredApks;
|
||||
targetRelease['version'] =
|
||||
additionalSettings['releaseTitleAsVersion'] == true
|
||||
? nameToFilter
|
||||
: targetRelease['tag_name'] ?? targetRelease['name'];
|
||||
? nameToFilter
|
||||
: targetRelease['tag_name'] ?? targetRelease['name'];
|
||||
if (targetRelease['tarball_url'] != null) {
|
||||
allAssetUrls.add(MapEntry(
|
||||
allAssetUrls.add(
|
||||
MapEntry(
|
||||
(targetRelease['version'] ?? 'source') + '.tar.gz',
|
||||
targetRelease['tarball_url']));
|
||||
targetRelease['tarball_url'],
|
||||
),
|
||||
);
|
||||
}
|
||||
if (targetRelease['zipball_url'] != null) {
|
||||
allAssetUrls.add(MapEntry(
|
||||
allAssetUrls.add(
|
||||
MapEntry(
|
||||
(targetRelease['version'] ?? 'source') + '.zip',
|
||||
targetRelease['zipball_url']));
|
||||
targetRelease['zipball_url'],
|
||||
),
|
||||
);
|
||||
}
|
||||
targetRelease['allAssetUrls'] = allAssetUrls;
|
||||
break;
|
||||
@ -444,19 +542,22 @@ class GitHub extends AppSource {
|
||||
String? version = targetRelease['version'];
|
||||
|
||||
DateTime? releaseDate = getReleaseDateFromRelease(
|
||||
targetRelease, useLatestAssetDateAsReleaseDate);
|
||||
targetRelease,
|
||||
useLatestAssetDateAsReleaseDate,
|
||||
);
|
||||
if (version == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
var changeLog = (targetRelease['body'] ?? '').toString();
|
||||
return APKDetails(
|
||||
version,
|
||||
targetRelease['apkUrls'] as List<MapEntry<String, String>>,
|
||||
getAppNames(standardUrl),
|
||||
releaseDate: releaseDate,
|
||||
changeLog: changeLog.isEmpty ? null : changeLog,
|
||||
allAssetUrls:
|
||||
targetRelease['allAssetUrls'] as List<MapEntry<String, String>>);
|
||||
version,
|
||||
targetRelease['apkUrls'] as List<MapEntry<String, String>>,
|
||||
getAppNames(standardUrl),
|
||||
releaseDate: releaseDate,
|
||||
changeLog: changeLog.isEmpty ? null : changeLog,
|
||||
allAssetUrls:
|
||||
targetRelease['allAssetUrls'] as List<MapEntry<String, String>>,
|
||||
);
|
||||
} else {
|
||||
if (onHttpErrorCode != null) {
|
||||
onHttpErrorCode(res);
|
||||
@ -465,20 +566,27 @@ class GitHub extends AppSource {
|
||||
}
|
||||
}
|
||||
|
||||
getLatestAPKDetailsCommon2(
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
Future<String> Function(bool) reqUrlGenerator,
|
||||
dynamic Function(Response)? onHttpErrorCode) async {
|
||||
Future<APKDetails> getLatestAPKDetailsCommon2(
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
Future<String> Function(bool) reqUrlGenerator,
|
||||
dynamic Function(Response)? onHttpErrorCode,
|
||||
) async {
|
||||
try {
|
||||
return await getLatestAPKDetailsCommon(
|
||||
await reqUrlGenerator(false), standardUrl, additionalSettings,
|
||||
onHttpErrorCode: onHttpErrorCode);
|
||||
await reqUrlGenerator(false),
|
||||
standardUrl,
|
||||
additionalSettings,
|
||||
onHttpErrorCode: onHttpErrorCode,
|
||||
);
|
||||
} catch (err) {
|
||||
if (err is NoReleasesError && additionalSettings['trackOnly'] == true) {
|
||||
return await getLatestAPKDetailsCommon(
|
||||
await reqUrlGenerator(true), standardUrl, additionalSettings,
|
||||
onHttpErrorCode: onHttpErrorCode);
|
||||
await reqUrlGenerator(true),
|
||||
standardUrl,
|
||||
additionalSettings,
|
||||
onHttpErrorCode: onHttpErrorCode,
|
||||
);
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
@ -490,12 +598,16 @@ class GitHub extends AppSource {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
return await getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
|
||||
(bool useTagUrl) async {
|
||||
return '${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
|
||||
}, (Response res) {
|
||||
rateLimitErrorCheck(res);
|
||||
});
|
||||
return await getLatestAPKDetailsCommon2(
|
||||
standardUrl,
|
||||
additionalSettings,
|
||||
(bool useTagUrl) async {
|
||||
return '${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
|
||||
},
|
||||
(Response res) {
|
||||
rateLimitErrorCheck(res);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
AppNames getAppNames(String standardUrl) {
|
||||
@ -505,9 +617,12 @@ class GitHub extends AppSource {
|
||||
}
|
||||
|
||||
Future<Map<String, List<String>>> searchCommon(
|
||||
String query, String requestUrl, String rootProp,
|
||||
{Function(Response)? onHttpErrorCode,
|
||||
Map<String, dynamic> querySettings = const {}}) async {
|
||||
String query,
|
||||
String requestUrl,
|
||||
String rootProp, {
|
||||
Function(Response)? onHttpErrorCode,
|
||||
Map<String, dynamic> querySettings = const {},
|
||||
}) async {
|
||||
Response res = await sourceRequest(requestUrl, {});
|
||||
if (res.statusCode == 200) {
|
||||
int minStarCount = querySettings['minStarCount'] != null
|
||||
@ -522,8 +637,8 @@ class GitHub extends AppSource {
|
||||
((e['archived'] == true ? '[ARCHIVED] ' : '') +
|
||||
(e['description'] != null
|
||||
? e['description'] as String
|
||||
: tr('noDescription')))
|
||||
]
|
||||
: tr('noDescription'))),
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -537,22 +652,27 @@ class GitHub extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<String>>> search(String query,
|
||||
{Map<String, dynamic> querySettings = const {}}) async {
|
||||
Future<Map<String, List<String>>> search(
|
||||
String query, {
|
||||
Map<String, dynamic> querySettings = const {},
|
||||
}) async {
|
||||
return searchCommon(
|
||||
query,
|
||||
'${await getAPIHost({})}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100',
|
||||
'items', onHttpErrorCode: (Response res) {
|
||||
rateLimitErrorCheck(res);
|
||||
}, querySettings: querySettings);
|
||||
query,
|
||||
'${await getAPIHost({})}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100',
|
||||
'items',
|
||||
onHttpErrorCode: (Response res) {
|
||||
rateLimitErrorCheck(res);
|
||||
},
|
||||
querySettings: querySettings,
|
||||
);
|
||||
}
|
||||
|
||||
rateLimitErrorCheck(Response res) {
|
||||
void rateLimitErrorCheck(Response res) {
|
||||
if (res.headers['x-ratelimit-remaining'] == '0') {
|
||||
throw RateLimitError(
|
||||
(int.parse(res.headers['x-ratelimit-reset'] ?? '1800000000') /
|
||||
60000000)
|
||||
.round());
|
||||
(int.parse(res.headers['x-ratelimit-reset'] ?? '1800000000') / 60000000)
|
||||
.round(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,36 +18,41 @@ class GitLab extends AppSource {
|
||||
showReleaseDateAsVersionToggle = true;
|
||||
|
||||
sourceConfigSettingFormItems = [
|
||||
GeneratedFormTextField('gitlab-creds',
|
||||
label: tr('gitlabPATLabel'),
|
||||
password: true,
|
||||
required: false,
|
||||
belowWidgets: [
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
GeneratedFormTextField(
|
||||
'gitlab-creds',
|
||||
label: tr('gitlabPATLabel'),
|
||||
password: true,
|
||||
required: false,
|
||||
belowWidgets: [
|
||||
const SizedBox(height: 4),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString(
|
||||
'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token',
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
tr('about'),
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString(
|
||||
'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token',
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
child: Text(
|
||||
tr('about'),
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline, fontSize: 12),
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
)
|
||||
])
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormSwitch('fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
||||
]
|
||||
GeneratedFormSwitch(
|
||||
'fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'),
|
||||
defaultValue: true,
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -55,11 +60,13 @@ class GitLab extends AppSource {
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
var urlSegments = url.split('/');
|
||||
var cutOffIndex = urlSegments.indexWhere((s) => s == '-');
|
||||
url =
|
||||
urlSegments.sublist(0, cutOffIndex <= 0 ? null : cutOffIndex).join('/');
|
||||
url = urlSegments
|
||||
.sublist(0, cutOffIndex <= 0 ? null : cutOffIndex)
|
||||
.join('/');
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+(/[^/-]+){1,20}',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+(/[^((\b/\b)|(\b/-/\b))]+){1,20}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -70,15 +77,19 @@ class GitLab extends AppSource {
|
||||
Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async {
|
||||
SettingsProvider settingsProvider = SettingsProvider();
|
||||
await settingsProvider.initializeSettings();
|
||||
var sourceConfig =
|
||||
await getSourceConfigValues(additionalSettings, settingsProvider);
|
||||
var sourceConfig = await getSourceConfigValues(
|
||||
additionalSettings,
|
||||
settingsProvider,
|
||||
);
|
||||
String? creds = sourceConfig['gitlab-creds'];
|
||||
return creds != null && creds.isNotEmpty ? creds : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, List<String>>> search(String query,
|
||||
{Map<String, dynamic> querySettings = const {}}) async {
|
||||
Future<Map<String, List<String>>> search(
|
||||
String query, {
|
||||
Map<String, dynamic> querySettings = const {},
|
||||
}) async {
|
||||
var url =
|
||||
'https://${hosts[0]}/api/v4/projects?search=${Uri.encodeQueryComponent(query)}';
|
||||
var res = await sourceRequest(url, {});
|
||||
@ -90,7 +101,7 @@ class GitLab extends AppSource {
|
||||
for (var element in json) {
|
||||
results['https://${hosts[0]}/${element['path_with_namespace']}'] = [
|
||||
element['name_with_namespace'],
|
||||
element['description'] ?? tr('noDescription')
|
||||
element['description'] ?? tr('noDescription'),
|
||||
];
|
||||
}
|
||||
return results;
|
||||
@ -102,8 +113,9 @@ class GitLab extends AppSource {
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) async {
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
// Change headers to pacify, e.g. cloudflare protection
|
||||
// Related to: (#1397, #1389, #1384, #1382, #1381, #1380, #1359, #854, #785, #697)
|
||||
var headers = <String, String>{};
|
||||
@ -116,11 +128,14 @@ class GitLab extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
|
||||
Map<String, dynamic> additionalSettings) async {
|
||||
Future<String> apkUrlPrefetchModifier(
|
||||
String apkUrl,
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
|
||||
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
|
||||
return '$apkUrl?$optionalAuth';
|
||||
return '$apkUrl${(Uri.parse(apkUrl).query.isEmpty ? '?' : '&')}$optionalAuth';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -139,8 +154,9 @@ class GitLab extends AppSource {
|
||||
|
||||
// Get project ID
|
||||
Response res0 = await sourceRequest(
|
||||
'https://${hosts[0]}/api/v4/projects/$projectUriComponent?$optionalAuth',
|
||||
additionalSettings);
|
||||
'https://${hosts[0]}/api/v4/projects/$projectUriComponent?$optionalAuth',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res0.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res0);
|
||||
}
|
||||
@ -151,8 +167,9 @@ class GitLab extends AppSource {
|
||||
|
||||
// Request data from REST API
|
||||
Response res = await sourceRequest(
|
||||
'https://${hosts[0]}/api/v4/projects/$projectUriComponent/${trackOnly ? 'repository/tags' : 'releases'}?$optionalAuth',
|
||||
additionalSettings);
|
||||
'https://${hosts[0]}/api/v4/projects/$projectUriComponent/${trackOnly ? 'repository/tags' : 'releases'}?$optionalAuth',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
@ -166,11 +183,13 @@ class GitLab extends AppSource {
|
||||
var url = (e['direct_asset_url'] ?? e['url'] ?? '') as String;
|
||||
var parsedUrl = url.isNotEmpty ? Uri.parse(url) : null;
|
||||
return MapEntry(
|
||||
(e['name'] ??
|
||||
(parsedUrl != null && parsedUrl.pathSegments.isNotEmpty
|
||||
? parsedUrl.pathSegments.last
|
||||
: 'unknown')) as String,
|
||||
(e['direct_asset_url'] ?? e['url'] ?? '') as String);
|
||||
(e['name'] ??
|
||||
(parsedUrl != null && parsedUrl.pathSegments.isNotEmpty
|
||||
? parsedUrl.pathSegments.last
|
||||
: 'unknown'))
|
||||
as String,
|
||||
(e['direct_asset_url'] ?? e['url'] ?? '') as String,
|
||||
);
|
||||
})
|
||||
.where((s) => s.key.isNotEmpty)
|
||||
.toList();
|
||||
@ -193,11 +212,15 @@ class GitLab extends AppSource {
|
||||
}
|
||||
var releaseDateString =
|
||||
e['released_at'] ?? e['created_at'] ?? e['commit']?['created_at'];
|
||||
DateTime? releaseDate =
|
||||
releaseDateString != null ? DateTime.parse(releaseDateString) : null;
|
||||
return APKDetails(e['tag_name'] ?? e['name'], apkUrls.entries.toList(),
|
||||
AppNames(names.author, names.name.split('/').last),
|
||||
releaseDate: releaseDate);
|
||||
DateTime? releaseDate = releaseDateString != null
|
||||
? DateTime.parse(releaseDateString)
|
||||
: null;
|
||||
return APKDetails(
|
||||
e['tag_name'] ?? e['name'],
|
||||
apkUrls.entries.toList(),
|
||||
AppNames(names.author, names.name.split('/').last),
|
||||
releaseDate: releaseDate,
|
||||
);
|
||||
});
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
@ -208,8 +231,9 @@ class GitLab extends AppSource {
|
||||
bool fallbackToOlderReleases =
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
if (finalResult.apkUrls.isEmpty && fallbackToOlderReleases && !trackOnly) {
|
||||
apkDetailsList =
|
||||
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
||||
apkDetailsList = apkDetailsList
|
||||
.where((e) => e.apkUrls.isNotEmpty)
|
||||
.toList();
|
||||
finalResult = apkDetailsList.first;
|
||||
}
|
||||
|
||||
@ -218,10 +242,13 @@ class GitLab extends AppSource {
|
||||
}
|
||||
|
||||
finalResult.apkUrls = finalResult.apkUrls.map((apkUrl) {
|
||||
if (RegExp('^$standardUrl/-/jobs/[0-9]+/artifacts/file/[^/]+')
|
||||
.hasMatch(apkUrl.value)) {
|
||||
if (RegExp(
|
||||
'^$standardUrl/-/jobs/[0-9]+/artifacts/file/[^/]+',
|
||||
).hasMatch(apkUrl.value)) {
|
||||
return MapEntry(
|
||||
apkUrl.key, apkUrl.value.replaceFirst('/file/', '/raw/'));
|
||||
apkUrl.key,
|
||||
apkUrl.value.replaceFirst('/file/', '/raw/'),
|
||||
);
|
||||
} else {
|
||||
return apkUrl;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:http/http.dart';
|
||||
@ -7,32 +9,14 @@ import 'package:obtainium/providers/apps_provider.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
|
||||
String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
|
||||
if (ambiguousUrl.startsWith('//')) {
|
||||
ambiguousUrl = '${referenceAbsoluteUrl.scheme}:$ambiguousUrl';
|
||||
}
|
||||
try {
|
||||
Uri.parse(ambiguousUrl).origin;
|
||||
return ambiguousUrl;
|
||||
} catch (err) {
|
||||
// is relative
|
||||
if (Uri.parse(ambiguousUrl).isAbsolute) {
|
||||
return ambiguousUrl; // #2315
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
var currPathSegments = referenceAbsoluteUrl.path
|
||||
.split('/')
|
||||
.where((element) => element.trim().isNotEmpty)
|
||||
.toList();
|
||||
String absoluteUrl;
|
||||
if (ambiguousUrl.startsWith('/')) {
|
||||
absoluteUrl = '${referenceAbsoluteUrl.origin}$ambiguousUrl';
|
||||
} else if (currPathSegments.isEmpty) {
|
||||
absoluteUrl = '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
|
||||
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
|
||||
absoluteUrl =
|
||||
'${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
|
||||
} else {
|
||||
absoluteUrl =
|
||||
'${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl';
|
||||
}
|
||||
return Uri.parse(absoluteUrl).toString();
|
||||
return referenceAbsoluteUrl.resolve(ambiguousUrl).toString();
|
||||
}
|
||||
|
||||
int compareAlphaNumeric(String a, String b) {
|
||||
@ -67,6 +51,27 @@ int compareAlphaNumeric(String a, String b) {
|
||||
return aParts.length.compareTo(bParts.length);
|
||||
}
|
||||
|
||||
List<String> collectAllStringsFromJSONObject(dynamic obj) {
|
||||
List<String> extractor(dynamic obj) {
|
||||
final results = <String>[];
|
||||
if (obj is String) {
|
||||
results.add(obj);
|
||||
} else if (obj is List) {
|
||||
for (final item in obj) {
|
||||
results.addAll(extractor(item));
|
||||
}
|
||||
} else if (obj is Map<String, dynamic>) {
|
||||
for (final value in obj.values) {
|
||||
results.addAll(extractor(value));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
return extractor(obj);
|
||||
}
|
||||
|
||||
List<String> _splitAlphaNumeric(String s) {
|
||||
List<String> parts = [];
|
||||
StringBuffer sb = StringBuffer();
|
||||
@ -95,31 +100,60 @@ bool _isNumeric(String s) {
|
||||
return s.codeUnitAt(0) >= 48 && s.codeUnitAt(0) <= 57;
|
||||
}
|
||||
|
||||
List<MapEntry<String, String>> getLinksInLines(String lines) =>
|
||||
RegExp(
|
||||
r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?',
|
||||
)
|
||||
.allMatches(lines)
|
||||
.map(
|
||||
(match) =>
|
||||
MapEntry(match.group(0)!, match.group(0)?.split('/').last ?? ''),
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Given an HTTP response, grab some links according to the common additional settings
|
||||
// (those that apply to intermediate and final steps)
|
||||
Future<List<MapEntry<String, String>>> grabLinksCommon(
|
||||
Response res, Map<String, dynamic> additionalSettings) async {
|
||||
Response res,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
if (res.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
var html = parse(res.body);
|
||||
List<MapEntry<String, String>> allLinks = html
|
||||
.querySelectorAll('a')
|
||||
.map((element) => MapEntry(
|
||||
.map(
|
||||
(element) => MapEntry(
|
||||
element.attributes['href'] ?? '',
|
||||
element.text.isNotEmpty
|
||||
? element.text
|
||||
: (element.attributes['href'] ?? '').split('/').last))
|
||||
: (element.attributes['href'] ?? '').split('/').last,
|
||||
),
|
||||
)
|
||||
.where((element) => element.key.isNotEmpty)
|
||||
.map((e) => MapEntry(ensureAbsoluteUrl(e.key, res.request!.url), e.value))
|
||||
.toList();
|
||||
if (allLinks.isEmpty) {
|
||||
allLinks = RegExp(
|
||||
r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?')
|
||||
.allMatches(res.body)
|
||||
.map((match) =>
|
||||
MapEntry(match.group(0)!, match.group(0)?.split('/').last ?? ''))
|
||||
.toList();
|
||||
allLinks = getLinksInLines(res.body);
|
||||
}
|
||||
if (allLinks.isEmpty) {
|
||||
// Getting desperate
|
||||
try {
|
||||
var jsonStrings = collectAllStringsFromJSONObject(jsonDecode(res.body));
|
||||
allLinks = getLinksInLines(jsonStrings.join('\n'));
|
||||
if (allLinks.isEmpty) {
|
||||
allLinks = getLinksInLines(
|
||||
jsonStrings
|
||||
.map((l) {
|
||||
return ensureAbsoluteUrl(l, res.request!.url);
|
||||
})
|
||||
.join('\n'),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
List<MapEntry<String, String>> links = [];
|
||||
bool skipSort = additionalSettings['skipSort'] == true;
|
||||
@ -144,17 +178,20 @@ Future<List<MapEntry<String, String>>> grabLinksCommon(
|
||||
} catch (e) {
|
||||
// Some links may not have valid encoding
|
||||
}
|
||||
return Uri.parse(filterLinkByText ? element.value : link)
|
||||
.path
|
||||
.toLowerCase()
|
||||
.endsWith('.apk');
|
||||
return Uri.parse(
|
||||
filterLinkByText ? element.value : link,
|
||||
).path.toLowerCase().endsWith('.apk');
|
||||
}).toList();
|
||||
}
|
||||
if (!skipSort) {
|
||||
links.sort((a, b) => additionalSettings['sortByLastLinkSegment'] == true
|
||||
? compareAlphaNumeric(a.key.split('/').where((e) => e.isNotEmpty).last,
|
||||
b.key.split('/').where((e) => e.isNotEmpty).last)
|
||||
: compareAlphaNumeric(a.key, b.key));
|
||||
links.sort(
|
||||
(a, b) => additionalSettings['sortByLastLinkSegment'] == true
|
||||
? compareAlphaNumeric(
|
||||
a.key.split('/').where((e) => e.isNotEmpty).last,
|
||||
b.key.split('/').where((e) => e.isNotEmpty).last,
|
||||
)
|
||||
: compareAlphaNumeric(a.key, b.key),
|
||||
);
|
||||
}
|
||||
if (additionalSettings['reverseSort'] == true) {
|
||||
links = links.reversed.toList();
|
||||
@ -180,97 +217,119 @@ class HTML extends AppSource {
|
||||
|
||||
var finalStepFormitems = [
|
||||
[
|
||||
GeneratedFormTextField('customLinkFilterRegex',
|
||||
label: tr('customLinkFilterRegex'),
|
||||
hint: 'download/(.*/)?(android|apk|mobile)',
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
}
|
||||
])
|
||||
GeneratedFormTextField(
|
||||
'customLinkFilterRegex',
|
||||
label: tr('customLinkFilterRegex'),
|
||||
hint: 'download/(.*/)?(android|apk|mobile)',
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
return regExValidator(value);
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch('versionExtractWholePage',
|
||||
label: tr('versionExtractWholePage'))
|
||||
]
|
||||
GeneratedFormSwitch(
|
||||
'versionExtractWholePage',
|
||||
label: tr('versionExtractWholePage'),
|
||||
),
|
||||
],
|
||||
];
|
||||
var commonFormItems = [
|
||||
[GeneratedFormSwitch('filterByLinkText', label: tr('filterByLinkText'))],
|
||||
[GeneratedFormSwitch('skipSort', label: tr('skipSort'))],
|
||||
[GeneratedFormSwitch('reverseSort', label: tr('takeFirstLink'))],
|
||||
[
|
||||
GeneratedFormSwitch('sortByLastLinkSegment',
|
||||
label: tr('sortByLastLinkSegment'))
|
||||
GeneratedFormSwitch(
|
||||
'sortByLastLinkSegment',
|
||||
label: tr('sortByLastLinkSegment'),
|
||||
),
|
||||
],
|
||||
];
|
||||
var intermediateFormItems = [
|
||||
[
|
||||
GeneratedFormTextField('customLinkFilterRegex',
|
||||
label: tr('intermediateLinkRegex'),
|
||||
hint: '([0-9]+.)*[0-9]+/\$',
|
||||
required: true,
|
||||
additionalValidators: [(value) => regExValidator(value)])
|
||||
GeneratedFormTextField(
|
||||
'customLinkFilterRegex',
|
||||
label: tr('intermediateLinkRegex'),
|
||||
hint: '([0-9]+.)*[0-9]+/\$',
|
||||
required: true,
|
||||
additionalValidators: [(value) => regExValidator(value)],
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormSwitch(
|
||||
'autoLinkFilterByArch',
|
||||
label: tr('autoLinkFilterByArch'),
|
||||
defaultValue: false,
|
||||
),
|
||||
],
|
||||
];
|
||||
HTML() {
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormSubForm(
|
||||
'intermediateLink', [...intermediateFormItems, ...commonFormItems],
|
||||
label: tr('intermediateLink'))
|
||||
GeneratedFormSubForm('intermediateLink', [
|
||||
...intermediateFormItems,
|
||||
...commonFormItems,
|
||||
], label: tr('intermediateLink')),
|
||||
],
|
||||
finalStepFormitems[0],
|
||||
...commonFormItems,
|
||||
...finalStepFormitems.sublist(1),
|
||||
[
|
||||
GeneratedFormSubForm(
|
||||
'requestHeader',
|
||||
'requestHeader',
|
||||
[
|
||||
[
|
||||
[
|
||||
GeneratedFormTextField('requestHeader',
|
||||
label: tr('requestHeader'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
if ((value ?? 'empty:valid')
|
||||
.split(':')
|
||||
.map((e) => e.trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.length <
|
||||
2) {
|
||||
return tr('invalidInput');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
])
|
||||
]
|
||||
GeneratedFormTextField(
|
||||
'requestHeader',
|
||||
label: tr('requestHeader'),
|
||||
required: false,
|
||||
additionalValidators: [
|
||||
(value) {
|
||||
if ((value ?? 'empty:valid')
|
||||
.split(':')
|
||||
.map((e) => e.trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.length <
|
||||
2) {
|
||||
return tr('invalidInput');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
label: tr('requestHeader'),
|
||||
defaultValue: [
|
||||
{
|
||||
'requestHeader':
|
||||
'User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
|
||||
}
|
||||
])
|
||||
],
|
||||
label: tr('requestHeader'),
|
||||
defaultValue: [
|
||||
{
|
||||
'requestHeader':
|
||||
'User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36',
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
[
|
||||
GeneratedFormDropdown(
|
||||
'defaultPseudoVersioningMethod',
|
||||
[
|
||||
MapEntry('partialAPKHash', tr('partialAPKHash')),
|
||||
MapEntry('APKLinkHash', tr('APKLinkHash'))
|
||||
],
|
||||
label: tr('defaultPseudoVersioningMethod'),
|
||||
defaultValue: 'partialAPKHash')
|
||||
]
|
||||
'defaultPseudoVersioningMethod',
|
||||
[
|
||||
MapEntry('partialAPKHash', tr('partialAPKHash')),
|
||||
MapEntry('APKLinkHash', tr('APKLinkHash')),
|
||||
MapEntry('ETag', 'ETag'),
|
||||
],
|
||||
label: tr('defaultPseudoVersioningMethod'),
|
||||
defaultValue: 'partialAPKHash',
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>?> getRequestHeaders(
|
||||
Map<String, dynamic> additionalSettings,
|
||||
{bool forAPKDownload = false}) async {
|
||||
Map<String, dynamic> additionalSettings, {
|
||||
bool forAPKDownload = false,
|
||||
}) async {
|
||||
if (additionalSettings.isNotEmpty) {
|
||||
if (additionalSettings['requestHeader']?.isNotEmpty != true) {
|
||||
additionalSettings['requestHeader'] = [];
|
||||
@ -310,11 +369,16 @@ class HTML extends AppSource {
|
||||
.toList();
|
||||
for (int i = 0; i < (additionalSettings['intermediateLink'].length); i++) {
|
||||
var intLinks = await grabLinksCommon(
|
||||
await sourceRequest(currentUrl, additionalSettings),
|
||||
additionalSettings['intermediateLink'][i]);
|
||||
await sourceRequest(currentUrl, additionalSettings),
|
||||
additionalSettings['intermediateLink'][i],
|
||||
);
|
||||
if (intLinks.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
throw NoReleasesError(note: currentUrl);
|
||||
} else {
|
||||
if (additionalSettings['intermediateLink'][i]['autoLinkFilterByArch'] ==
|
||||
true) {
|
||||
intLinks = await filterApksByArch(intLinks);
|
||||
}
|
||||
currentUrl = intLinks.last.key;
|
||||
}
|
||||
}
|
||||
@ -323,13 +387,19 @@ class HTML extends AppSource {
|
||||
String versionExtractionWholePageString = currentUrl;
|
||||
if (additionalSettings['directAPKLink'] != true) {
|
||||
Response res = await sourceRequest(currentUrl, additionalSettings);
|
||||
versionExtractionWholePageString =
|
||||
res.body.split('\r\n').join('\n').split('\n').join('\\n');
|
||||
versionExtractionWholePageString = res.body
|
||||
.split('\r\n')
|
||||
.join('\n')
|
||||
.split('\n')
|
||||
.join('\\n');
|
||||
links = await grabLinksCommon(res, additionalSettings);
|
||||
links = filterApks(links, additionalSettings['apkFilterRegEx'],
|
||||
additionalSettings['invertAPKFilter']);
|
||||
links = filterApks(
|
||||
links,
|
||||
additionalSettings['apkFilterRegEx'],
|
||||
additionalSettings['invertAPKFilter'],
|
||||
);
|
||||
if (links.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
throw NoReleasesError(note: currentUrl);
|
||||
}
|
||||
} else {
|
||||
links = [MapEntry(currentUrl, currentUrl)];
|
||||
@ -343,27 +413,45 @@ class HTML extends AppSource {
|
||||
}
|
||||
String? version;
|
||||
version = extractVersion(
|
||||
additionalSettings['versionExtractionRegEx'] as String?,
|
||||
additionalSettings['matchGroupToUse'] as String?,
|
||||
additionalSettings['versionExtractWholePage'] == true
|
||||
? versionExtractionWholePageString
|
||||
: relDecoded);
|
||||
version ??= additionalSettings['defaultPseudoVersioningMethod'] ==
|
||||
'APKLinkHash'
|
||||
additionalSettings['versionExtractionRegEx'] as String?,
|
||||
additionalSettings['matchGroupToUse'] as String?,
|
||||
additionalSettings['versionExtractWholePage'] == true
|
||||
? versionExtractionWholePageString
|
||||
: relDecoded,
|
||||
);
|
||||
var apkReqHeaders = await getRequestHeaders(
|
||||
additionalSettings,
|
||||
forAPKDownload: true,
|
||||
);
|
||||
if (version == null &&
|
||||
additionalSettings['defaultPseudoVersioningMethod'] == 'ETag') {
|
||||
version = await checkETagHeader(
|
||||
rel,
|
||||
headers: apkReqHeaders,
|
||||
allowInsecure: additionalSettings['allowInsecure'] == true,
|
||||
);
|
||||
if (version == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
}
|
||||
version ??=
|
||||
additionalSettings['defaultPseudoVersioningMethod'] == 'APKLinkHash'
|
||||
? rel.hashCode.toString()
|
||||
: (await checkPartialDownloadHashDynamic(rel,
|
||||
headers: await getRequestHeaders(additionalSettings,
|
||||
forAPKDownload: true),
|
||||
allowInsecure: additionalSettings['allowInsecure'] == true))
|
||||
.toString();
|
||||
: (await checkPartialDownloadHashDynamic(
|
||||
rel,
|
||||
headers: apkReqHeaders,
|
||||
allowInsecure: additionalSettings['allowInsecure'] == true,
|
||||
)).toString();
|
||||
return APKDetails(
|
||||
version,
|
||||
[rel].map((e) {
|
||||
var uri = Uri.parse(e);
|
||||
var fileName =
|
||||
uri.pathSegments.isNotEmpty ? uri.pathSegments.last : uri.origin;
|
||||
return MapEntry('${e.hashCode}-$fileName', e);
|
||||
}).toList(),
|
||||
AppNames(uri.host, tr('app')));
|
||||
version,
|
||||
[rel].map((e) {
|
||||
var uri = Uri.parse(e);
|
||||
var fileName = uri.pathSegments.isNotEmpty
|
||||
? uri.pathSegments.last
|
||||
: uri.origin;
|
||||
return MapEntry('${e.hashCode}-$fileName', e);
|
||||
}).toList(),
|
||||
AppNames(uri.host, tr('app')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,9 @@ class HuaweiAppGallery extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}(/#)?/(app|appdl)/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}(/#)?/(app|appdl)/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -23,13 +24,18 @@ class HuaweiAppGallery extends AppSource {
|
||||
return match.group(0)!;
|
||||
}
|
||||
|
||||
getDlUrl(String standardUrl) =>
|
||||
String getDlUrl(String standardUrl) =>
|
||||
'https://${hosts[0].replaceAll('appgallery.huawei', 'appgallery.cloud.huawei')}/appdl/${standardUrl.split('/').last}';
|
||||
|
||||
requestAppdlRedirect(
|
||||
String dlUrl, Map<String, dynamic> additionalSettings) async {
|
||||
Response res =
|
||||
await sourceRequest(dlUrl, additionalSettings, followRedirects: false);
|
||||
Future<Response> requestAppdlRedirect(
|
||||
String dlUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
Response res = await sourceRequest(
|
||||
dlUrl,
|
||||
additionalSettings,
|
||||
followRedirects: false,
|
||||
);
|
||||
if (res.statusCode == 200 ||
|
||||
res.statusCode == 302 ||
|
||||
res.statusCode == 304) {
|
||||
@ -39,7 +45,7 @@ class HuaweiAppGallery extends AppSource {
|
||||
}
|
||||
}
|
||||
|
||||
appIdFromRedirectDlUrl(String redirectDlUrl) {
|
||||
String appIdFromRedirectDlUrl(String redirectDlUrl) {
|
||||
var parts = redirectDlUrl
|
||||
.split('?')[0]
|
||||
.split('/')
|
||||
@ -53,8 +59,10 @@ class HuaweiAppGallery extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
String dlUrl = getDlUrl(standardUrl);
|
||||
Response res = await requestAppdlRedirect(dlUrl, additionalSettings);
|
||||
return res.headers['location'] != null
|
||||
@ -76,8 +84,11 @@ class HuaweiAppGallery extends AppSource {
|
||||
if (appId.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
var relDateStr =
|
||||
res.headers['location']?.split('?')[0].split('.').reversed.toList()[1];
|
||||
var relDateStr = res.headers['location']
|
||||
?.split('?')[0]
|
||||
.split('.')
|
||||
.reversed
|
||||
.toList()[1];
|
||||
if (relDateStr == null || relDateStr.length != 10) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
@ -88,10 +99,15 @@ class HuaweiAppGallery extends AppSource {
|
||||
relDateStrAdj.insert((i + i ~/ 2 - 1), '-');
|
||||
i += 2;
|
||||
}
|
||||
var relDate =
|
||||
DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
|
||||
var relDate = DateFormat(
|
||||
'yy-MM-dd-HH-mm',
|
||||
'en_US',
|
||||
).parse(relDateStrAdj.join(''));
|
||||
return APKDetails(
|
||||
relDateStr, [MapEntry('$appId.apk', dlUrl)], AppNames(name, appId),
|
||||
releaseDate: relDate);
|
||||
relDateStr,
|
||||
[MapEntry('$appId.apk', dlUrl)],
|
||||
AppNames(name, appId),
|
||||
releaseDate: relDate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,13 +16,15 @@ class IzzyOnDroid extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegExA = RegExp(
|
||||
'^https?://android.${getSourceRegex(hosts)}/repo/apk/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://android.${getSourceRegex(hosts)}/repo/apk/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegExA.firstMatch(url);
|
||||
if (match == null) {
|
||||
RegExp standardUrlRegExB = RegExp(
|
||||
'^https?://apt.${getSourceRegex(hosts)}/fdroid/index/apk/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://apt.${getSourceRegex(hosts)}/fdroid/index/apk/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
match = standardUrlRegExB.firstMatch(url);
|
||||
}
|
||||
if (match == null) {
|
||||
@ -32,8 +34,10 @@ class IzzyOnDroid extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return fd.tryInferringAppId(standardUrl);
|
||||
}
|
||||
|
||||
@ -44,12 +48,14 @@ class IzzyOnDroid extends AppSource {
|
||||
) async {
|
||||
String? appId = await tryInferringAppId(standardUrl);
|
||||
return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||
await sourceRequest(
|
||||
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId',
|
||||
additionalSettings),
|
||||
'https://android.izzysoft.de/frepo/$appId',
|
||||
standardUrl,
|
||||
name,
|
||||
additionalSettings: additionalSettings);
|
||||
await sourceRequest(
|
||||
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId',
|
||||
additionalSettings,
|
||||
),
|
||||
'https://android.izzysoft.de/frepo/$appId',
|
||||
standardUrl,
|
||||
name,
|
||||
additionalSettings: additionalSettings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,17 @@ class Jenkins extends AppSource {
|
||||
) async {
|
||||
standardUrl = trimJobUrl(standardUrl);
|
||||
Response res = await sourceRequest(
|
||||
'$standardUrl/lastSuccessfulBuild/api/json', additionalSettings);
|
||||
'$standardUrl/lastSuccessfulBuild/api/json',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var json = jsonDecode(res.body);
|
||||
var releaseDate = json['timestamp'] == null
|
||||
? null
|
||||
: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int);
|
||||
var version =
|
||||
json['number'] == null ? null : (json['number'] as int).toString();
|
||||
var version = json['number'] == null
|
||||
? null
|
||||
: (json['number'] as int).toString();
|
||||
if (version == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
@ -51,16 +54,21 @@ class Jenkins extends AppSource {
|
||||
return path == null
|
||||
? const MapEntry<String, String>('', '')
|
||||
: MapEntry<String, String>(
|
||||
(e['fileName'] ?? e['relativePath']) as String, path);
|
||||
(e['fileName'] ?? e['relativePath']) as String,
|
||||
path,
|
||||
);
|
||||
})
|
||||
.where((url) =>
|
||||
url.value.isNotEmpty && url.key.toLowerCase().endsWith('.apk'))
|
||||
.where(
|
||||
(url) =>
|
||||
url.value.isNotEmpty && url.key.toLowerCase().endsWith('.apk'),
|
||||
)
|
||||
.toList();
|
||||
return APKDetails(
|
||||
version,
|
||||
apkUrls,
|
||||
releaseDate: releaseDate,
|
||||
AppNames(Uri.parse(standardUrl).host, standardUrl.split('/').last));
|
||||
version,
|
||||
apkUrls,
|
||||
releaseDate: releaseDate,
|
||||
AppNames(Uri.parse(standardUrl).host, standardUrl.split('/').last),
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ class Mullvad extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -31,7 +32,9 @@ class Mullvad extends AppSource {
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
Response res = await sourceRequest(
|
||||
'$standardUrl/en/download/android', additionalSettings);
|
||||
'$standardUrl/en/download/android',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var versions = parse(res.body)
|
||||
.querySelectorAll('p')
|
||||
@ -53,17 +56,18 @@ class Mullvad extends AppSource {
|
||||
String? changeLog;
|
||||
try {
|
||||
changeLog = (await GitHub().getLatestAPKDetails(
|
||||
'https://github.com/mullvad/mullvadvpn-app',
|
||||
{'fallbackToOlderReleases': true}))
|
||||
.changeLog;
|
||||
'https://github.com/mullvad/mullvadvpn-app',
|
||||
{'fallbackToOlderReleases': true},
|
||||
)).changeLog;
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
return APKDetails(
|
||||
versions[0],
|
||||
getApkUrlsFromUrls(['https://mullvad.net/download/app/apk/latest']),
|
||||
AppNames(name, 'Mullvad-VPN'),
|
||||
changeLog: changeLog);
|
||||
versions[0],
|
||||
getApkUrlsFromUrls(['https://mullvad.net/download/app/apk/latest']),
|
||||
AppNames(name, 'Mullvad-VPN'),
|
||||
changeLog: changeLog,
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ class NeutronCode extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/downloads/file/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/downloads/file/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -55,7 +56,7 @@ class NeutronCode extends AppSource {
|
||||
}
|
||||
}
|
||||
|
||||
customDateParse(String dateString) {
|
||||
String? customDateParse(String dateString) {
|
||||
List<String> parts = dateString.split(' ');
|
||||
if (parts.length != 3) {
|
||||
return null;
|
||||
@ -89,24 +90,31 @@ class NeutronCode extends AppSource {
|
||||
if (filename == null) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
var version =
|
||||
http.querySelector('.pd-version-txt')?.nextElementSibling?.innerHtml;
|
||||
var version = http
|
||||
.querySelector('.pd-version-txt')
|
||||
?.nextElementSibling
|
||||
?.innerHtml;
|
||||
if (version == null) {
|
||||
throw NoVersionError();
|
||||
}
|
||||
String? apkUrl = 'https://${hosts[0]}/download/$filename';
|
||||
var dateStringOriginal =
|
||||
http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml;
|
||||
var dateStringOriginal = http
|
||||
.querySelector('.pd-date-txt')
|
||||
?.nextElementSibling
|
||||
?.innerHtml;
|
||||
var dateString = dateStringOriginal != null
|
||||
? (customDateParse(dateStringOriginal))
|
||||
: null;
|
||||
var changeLogElements = http.querySelectorAll('.pd-fdesc p');
|
||||
return APKDetails(version, getApkUrlsFromUrls([apkUrl]),
|
||||
AppNames(runtimeType.toString(), name ?? standardUrl.split('/').last),
|
||||
releaseDate: dateString != null ? DateTime.parse(dateString) : null,
|
||||
changeLog: changeLogElements.isNotEmpty
|
||||
? changeLogElements.last.innerHtml
|
||||
: null);
|
||||
return APKDetails(
|
||||
version,
|
||||
getApkUrlsFromUrls([apkUrl]),
|
||||
AppNames(runtimeType.toString(), name ?? standardUrl.split('/').last),
|
||||
releaseDate: dateString != null ? DateTime.parse(dateString) : null,
|
||||
changeLog: changeLogElements.isNotEmpty
|
||||
? changeLogElements.last.innerHtml
|
||||
: null,
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -18,8 +18,9 @@ class RuStore extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/catalog/app/+[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/catalog/app/+[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -28,11 +29,23 @@ class RuStore extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return Uri.parse(standardUrl).pathSegments.last;
|
||||
}
|
||||
|
||||
Future<String> decodeString(String str) async {
|
||||
try {
|
||||
return (await CharsetDetector.autoDecode(
|
||||
Uint8List.fromList(str.codeUnits),
|
||||
)).string;
|
||||
} catch (e) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<APKDetails> getLatestAPKDetails(
|
||||
String standardUrl,
|
||||
@ -40,8 +53,9 @@ class RuStore extends AppSource {
|
||||
) async {
|
||||
String? appId = await tryInferringAppId(standardUrl);
|
||||
Response res0 = await sourceRequest(
|
||||
'https://backapi.rustore.ru/applicationData/overallInfo/$appId',
|
||||
additionalSettings);
|
||||
'https://backapi.rustore.ru/applicationData/overallInfo/$appId',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res0.statusCode != 200) {
|
||||
throw getObtainiumHttpError(res0);
|
||||
}
|
||||
@ -64,35 +78,31 @@ class RuStore extends AppSource {
|
||||
}
|
||||
|
||||
Response res1 = await sourceRequest(
|
||||
'https://backapi.rustore.ru/applicationData/download-link',
|
||||
additionalSettings,
|
||||
followRedirects: false,
|
||||
postBody: {"appId": appDetails['appId'], "firstInstall": true});
|
||||
'https://backapi.rustore.ru/applicationData/download-link',
|
||||
additionalSettings,
|
||||
followRedirects: false,
|
||||
postBody: {"appId": appDetails['appId'], "firstInstall": true},
|
||||
);
|
||||
var downloadDetails = jsonDecode(res1.body)['body'];
|
||||
if (res1.statusCode != 200 || downloadDetails['apkUrl'] == null) {
|
||||
throw NoAPKError();
|
||||
}
|
||||
|
||||
appName = (await CharsetDetector.autoDecode(
|
||||
Uint8List.fromList(appName.codeUnits)))
|
||||
.string;
|
||||
author =
|
||||
(await CharsetDetector.autoDecode(Uint8List.fromList(author.codeUnits)))
|
||||
.string;
|
||||
changeLog = changeLog != null
|
||||
? (await CharsetDetector.autoDecode(
|
||||
Uint8List.fromList(changeLog.codeUnits)))
|
||||
.string
|
||||
: null;
|
||||
appName = await decodeString(appName);
|
||||
author = await decodeString(author);
|
||||
changeLog = changeLog != null ? await decodeString(changeLog) : null;
|
||||
|
||||
return APKDetails(
|
||||
version,
|
||||
getApkUrlsFromUrls([
|
||||
(downloadDetails['apkUrl'] as String)
|
||||
.replaceAll(RegExp('\\.zip\$'), '.apk')
|
||||
]),
|
||||
AppNames(author, appName),
|
||||
releaseDate: relDate,
|
||||
changeLog: changeLog);
|
||||
version,
|
||||
getApkUrlsFromUrls([
|
||||
(downloadDetails['apkUrl'] as String).replaceAll(
|
||||
RegExp('\\.zip\$'),
|
||||
'.apk',
|
||||
),
|
||||
]),
|
||||
AppNames(author, appName),
|
||||
releaseDate: relDate,
|
||||
changeLog: changeLog,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,23 +11,27 @@ class SourceForge extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
var sourceRegex = getSourceRegex(hosts);
|
||||
RegExp standardUrlRegExC =
|
||||
RegExp('^https?://(www\\.)?$sourceRegex/p/.+', caseSensitive: false);
|
||||
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);
|
||||
'^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\\.)?$sourceRegex/projects/[^/]+/files(/.+)?',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?$sourceRegex/projects/[^/]+/files(/.+)?',
|
||||
caseSensitive: false,
|
||||
);
|
||||
match = standardUrlRegExA.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -46,8 +50,9 @@ class SourceForge extends AppSource {
|
||||
standardUri = Uri.parse(standardUrl);
|
||||
}
|
||||
Response res = await sourceRequest(
|
||||
'${standardUri.origin}/${standardUri.pathSegments.sublist(0, 2).join('/')}/rss?path=/',
|
||||
additionalSettings);
|
||||
'${standardUri.origin}/${standardUri.pathSegments.sublist(0, 2).join('/')}/rss?path=/',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var parsedHtml = parse(res.body);
|
||||
var allDownloadLinks = parsedHtml
|
||||
@ -74,9 +79,10 @@ class SourceForge extends AppSource {
|
||||
if (version != null) {
|
||||
try {
|
||||
var extractedVersion = extractVersion(
|
||||
additionalSettings['versionExtractionRegEx'] as String?,
|
||||
additionalSettings['matchGroupToUse'] as String?,
|
||||
version);
|
||||
additionalSettings['versionExtractionRegEx'] as String?,
|
||||
additionalSettings['matchGroupToUse'] as String?,
|
||||
version,
|
||||
);
|
||||
if (extractedVersion != null) {
|
||||
version = extractedVersion;
|
||||
}
|
||||
@ -111,8 +117,11 @@ class SourceForge extends AppSource {
|
||||
.where((element) => getVersion(element) == version)
|
||||
.toList();
|
||||
var segments = standardUrl.split('/');
|
||||
return APKDetails(version, getApkUrlsFromUrls(apkUrlList),
|
||||
AppNames(name, segments[segments.indexOf('files') - 1]));
|
||||
return APKDetails(
|
||||
version,
|
||||
getApkUrlsFromUrls(apkUrlList),
|
||||
AppNames(name, segments[segments.indexOf('files') - 1]),
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -13,17 +13,21 @@ class SourceHut extends AppSource {
|
||||
|
||||
additionalSourceAppSpecificSettingFormItems = [
|
||||
[
|
||||
GeneratedFormSwitch('fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'), defaultValue: true)
|
||||
]
|
||||
GeneratedFormSwitch(
|
||||
'fallbackToOlderReleases',
|
||||
label: tr('fallbackToOlderReleases'),
|
||||
defaultValue: true,
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -52,8 +56,10 @@ class SourceHut extends AppSource {
|
||||
String appName = standardUri.pathSegments.last;
|
||||
bool fallbackToOlderReleases =
|
||||
additionalSettings['fallbackToOlderReleases'] == true;
|
||||
Response res =
|
||||
await sourceRequest('$standardUrl/refs/rss.xml', additionalSettings);
|
||||
Response res = await sourceRequest(
|
||||
'$standardUrl/refs/rss.xml',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var parsedHtml = parse(res.body);
|
||||
List<APKDetails> apkDetailsList = [];
|
||||
@ -63,10 +69,10 @@ class SourceHut extends AppSource {
|
||||
ind++;
|
||||
String releasePage = // querySelector('link') fails for some reason
|
||||
entry
|
||||
.querySelector('guid') // Luckily guid is identical
|
||||
?.innerHtml
|
||||
.trim() ??
|
||||
'';
|
||||
.querySelector('guid') // Luckily guid is identical
|
||||
?.innerHtml
|
||||
.trim() ??
|
||||
'';
|
||||
if (!releasePage.startsWith('$standardUrl/refs')) {
|
||||
continue;
|
||||
}
|
||||
@ -84,8 +90,9 @@ class SourceHut extends AppSource {
|
||||
? DateFormat('E, dd MMM yyyy HH:mm:ss Z').parse(releaseDateString)
|
||||
: null;
|
||||
releaseDate = releaseDateString != null
|
||||
? DateFormat('EEE, dd MMM yyyy HH:mm:ss Z')
|
||||
.parse(releaseDateString)
|
||||
? DateFormat(
|
||||
'EEE, dd MMM yyyy HH:mm:ss Z',
|
||||
).parse(releaseDateString)
|
||||
: null;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
@ -93,27 +100,35 @@ class SourceHut extends AppSource {
|
||||
var res2 = await sourceRequest(releasePage, additionalSettings);
|
||||
List<MapEntry<String, String>> apkUrls = [];
|
||||
if (res2.statusCode == 200) {
|
||||
apkUrls = getApkUrlsFromUrls(parse(res2.body)
|
||||
.querySelectorAll('a')
|
||||
.map((e) => e.attributes['href'] ?? '')
|
||||
.where((e) => e.toLowerCase().endsWith('.apk'))
|
||||
.map((e) => ensureAbsoluteUrl(e, standardUri))
|
||||
.toList());
|
||||
apkUrls = getApkUrlsFromUrls(
|
||||
parse(res2.body)
|
||||
.querySelectorAll('a')
|
||||
.map((e) => e.attributes['href'] ?? '')
|
||||
.where((e) => e.toLowerCase().endsWith('.apk'))
|
||||
.map((e) => ensureAbsoluteUrl(e, standardUri))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
apkDetailsList.add(APKDetails(
|
||||
apkDetailsList.add(
|
||||
APKDetails(
|
||||
version,
|
||||
apkUrls,
|
||||
AppNames(entry.querySelector('author')?.innerHtml.trim() ?? appName,
|
||||
appName),
|
||||
releaseDate: releaseDate));
|
||||
AppNames(
|
||||
entry.querySelector('author')?.innerHtml.trim() ?? appName,
|
||||
appName,
|
||||
),
|
||||
releaseDate: releaseDate,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
}
|
||||
if (fallbackToOlderReleases) {
|
||||
if (additionalSettings['trackOnly'] != true) {
|
||||
apkDetailsList =
|
||||
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
|
||||
apkDetailsList = apkDetailsList
|
||||
.where((e) => e.apkUrls.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
if (apkDetailsList.isEmpty) {
|
||||
throw NoReleasesError();
|
||||
|
@ -20,12 +20,15 @@ class TelegramApp extends AppSource {
|
||||
String standardUrl,
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
Response res =
|
||||
await sourceRequest('https://t.me/s/TAndroidAPK', additionalSettings);
|
||||
Response res = await sourceRequest(
|
||||
'https://t.me/s/TAndroidAPK',
|
||||
additionalSettings,
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
var http = parse(res.body);
|
||||
var messages =
|
||||
http.querySelectorAll('.tgme_widget_message_text.js-message_text');
|
||||
var messages = http.querySelectorAll(
|
||||
'.tgme_widget_message_text.js-message_text',
|
||||
);
|
||||
var version = messages.isNotEmpty
|
||||
? messages.last.innerHtml.split('\n').first.trim().split(' ').first
|
||||
: null;
|
||||
@ -33,10 +36,9 @@ class TelegramApp extends AppSource {
|
||||
throw NoVersionError();
|
||||
}
|
||||
String? apkUrl = 'https://telegram.org/dl/android/apk';
|
||||
return APKDetails(
|
||||
version,
|
||||
[MapEntry<String, String>('telegram-$version.apk', apkUrl)],
|
||||
AppNames('Telegram', 'Telegram'));
|
||||
return APKDetails(version, [
|
||||
MapEntry<String, String>('telegram-$version.apk', apkUrl),
|
||||
], AppNames('Telegram', 'Telegram'));
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|
@ -15,8 +15,9 @@ class Tencent extends AppSource {
|
||||
@override
|
||||
String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
|
||||
RegExp standardUrlRegEx = RegExp(
|
||||
'^https?://${getSourceRegex(hosts)}/appdetail/[^/]+',
|
||||
caseSensitive: false);
|
||||
'^https?://${getSourceRegex(hosts)}/appdetail/[^/]+',
|
||||
caseSensitive: false,
|
||||
);
|
||||
var match = standardUrlRegEx.firstMatch(url);
|
||||
if (match == null) {
|
||||
throw InvalidURLError(name);
|
||||
@ -25,8 +26,10 @@ class Tencent extends AppSource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> tryInferringAppId(String standardUrl,
|
||||
{Map<String, dynamic> additionalSettings = const {}}) async {
|
||||
Future<String?> tryInferringAppId(
|
||||
String standardUrl, {
|
||||
Map<String, dynamic> additionalSettings = const {},
|
||||
}) async {
|
||||
return Uri.parse(standardUrl).pathSegments.last;
|
||||
}
|
||||
|
||||
@ -36,18 +39,16 @@ class Tencent extends AppSource {
|
||||
Map<String, dynamic> additionalSettings,
|
||||
) async {
|
||||
String appId = (await tryInferringAppId(standardUrl))!;
|
||||
String baseHost = Uri.parse(standardUrl)
|
||||
.host
|
||||
.split('.')
|
||||
.reversed
|
||||
.toList()
|
||||
.sublist(0, 2)
|
||||
.reversed
|
||||
.join('.');
|
||||
String baseHost = Uri.parse(
|
||||
standardUrl,
|
||||
).host.split('.').reversed.toList().sublist(0, 2).reversed.join('.');
|
||||
|
||||
var res = await sourceRequest(
|
||||
'https://upage.html5.$baseHost/wechat-apkinfo', additionalSettings,
|
||||
followRedirects: false, postBody: {"packagename": appId});
|
||||
'https://upage.html5.$baseHost/wechat-apkinfo',
|
||||
additionalSettings,
|
||||
followRedirects: false,
|
||||
postBody: {"packagename": appId},
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
var json = jsonDecode(res.body);
|
||||
@ -64,14 +65,18 @@ class Tencent extends AppSource {
|
||||
var author = json['app_detail_records'][appId]['app_info']['author'];
|
||||
var releaseDate =
|
||||
json['app_detail_records'][appId]['app_info']['update_time'];
|
||||
var apkName = Uri.parse(apkUrl).queryParameters['fsname'] ??
|
||||
'${appId}_${version}.apk';
|
||||
var apkName =
|
||||
Uri.parse(apkUrl).queryParameters['fsname'] ??
|
||||
'${appId}_$version.apk';
|
||||
|
||||
return APKDetails(
|
||||
version, [MapEntry(apkName, apkUrl)], AppNames(author, appName),
|
||||
releaseDate: releaseDate != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(releaseDate * 1000)
|
||||
: null);
|
||||
version,
|
||||
[MapEntry(apkName, apkUrl)],
|
||||
AppNames(author, appName),
|
||||
releaseDate: releaseDate != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(releaseDate * 1000)
|
||||
: null,
|
||||
);
|
||||
} else {
|
||||
throw getObtainiumHttpError(res);
|
||||
}
|
||||
|