Compare commits

...

339 Commits

Author SHA1 Message Date
d33ca0948f Merge pull request #1567 from ImranR98/dev
- Share apps.obtainium.imranr.dev config links instead of HTML raw (#1553)
- Remove foreground requirement for Shizuku (#1561)
- Fix "illegal encoding" error (#1563)
2024-04-23 14:48:53 -04:00
f76637a2e1 Update packages, increment version 2024-04-23 14:47:13 -04:00
b7de627c7b Share apps.obtainium.imranr.dev config links instead of HTML raw (#1553) 2024-04-23 14:43:37 -04:00
27fc60d437 Fix "illegal encoding" error (#1563), Remove foreground requirement for Shizuku (#1561) 2024-04-23 14:35:28 -04:00
ec240f946e Merge pull request #1555 from DwainZwerg/patch-15
Update de.json
2024-04-23 14:15:56 -04:00
ecd80fc371 Merge pull request #1556 from re7gog/re7gog
Fixes
2024-04-23 14:15:47 -04:00
68fa660e6d Merge pull request #1559 from CertainBot/patch-2
Spanish update
2024-04-23 14:15:37 -04:00
70f9e33d17 Merge pull request #1560 from jmoerschbach/conflicting_provider
Use applicationId for provider authorities
2024-04-23 14:15:09 -04:00
2e2dffd8e2 Merge pull request #1562 from ArcticFoxPro/main
Fix some questions of 简体中文(Simplified Chinese) and English.
2024-04-23 14:15:00 -04:00
0b1d5bf514 Fix some questions of 简体中文(zh-CN) and English.
Google's "Material You" is a specific noun in China, and it can be used directly in English, because Google China is also directly used in English, and translated into Chinese "您的材料" will look strange to Chinese users.
2024-04-22 16:01:21 +08:00
2570c8e289 Use applicationId for provider authorities
Fixes #1378

Instead of hardcoding the authority string, use the ${applicationId} to
always make the provider unique.
2024-04-21 21:49:33 +02:00
43a8ba4de1 Spanish update
- English:
Minor changes.

- Spanish:
Modificaciones menores.
2024-04-21 21:20:55 +02:00
3f2fb1c1ed Update de.json 2024-04-21 15:45:26 +02:00
40d303fb57 Partially rollback system theme (#1552) 2024-04-21 16:31:55 +03:00
2c1687c33d Hide some settings if they are not available 2024-04-21 12:53:57 +03:00
b688e7f160 Fix #968 2024-04-21 12:21:52 +03:00
0c80d88583 Merge pull request #1551 from ImranR98/dev
Minor Adjustments
2024-04-20 19:45:46 -04:00
b293b1e9ff Update Flutter, packages, increment version 2024-04-20 19:44:32 -04:00
61038a8969 Updated translations 2024-04-20 19:38:59 -04:00
b92d1541bf Restore Flutter branch 2024-04-20 19:36:02 -04:00
13f0ccb10d Merge pull request #1548 from re7gog/re7gog
Native and UI improvements
2024-04-20 19:35:37 -04:00
ed732cfad2 Look at all these colors!🌈 2024-04-20 18:24:08 +03:00
fb206aee84 Add system theme api level check 2024-04-20 11:57:12 +03:00
285530784d little 2024-04-20 11:30:34 +03:00
0ddb5b5e81 Merge branch 'main' into re7gog
# Conflicts:
#	pubspec.lock
2024-04-19 18:07:51 +03:00
c16cda1962 Make pretending to be a Google Play setting per app based⚙️ 2024-04-19 17:57:22 +03:00
49022726d3 Shizuku logic 3: Prettify 2024-04-19 16:41:34 +03:00
a311894b9f Shizuku logic 2: Determination of installation 2024-04-18 20:01:40 +03:00
29e13efd66 Change background shizuku logic 2024-04-17 17:05:08 +03:00
8c2a97e092 Merge pull request #1544 from ImranR98/dev
- Don't trust content-length header from sites w/o accept-ranges support (#1542) + bugfixes
- Allow for spaces in HTML link and version filters (#1537)
2024-04-15 19:16:10 -04:00
34d571f586 Merge remote-tracking branch 'origin/main' into dev 2024-04-15 19:14:47 -04:00
a20b87889b Update packages, increment version 2024-04-15 19:14:39 -04:00
c7319cd958 Merge pull request #1540 from o101010/main
Corrects french translations
2024-04-15 19:09:50 -04:00
d61fd800ef Merge pull request #1543 from yurtpage/main
fastlane: i18n ru for app description
2024-04-15 19:09:26 -04:00
12dda8bfa9 Allow for spaces in HTML link and version filters (#1537) 2024-04-15 19:06:24 -04:00
0657f832e1 Don't trust content-length header from sites w/o accept-ranges support (#1542) + bugfixes 2024-04-15 18:39:21 -04:00
6431357b15 ru.json: translate apk.other 2024-04-16 01:16:08 +03:00
dcc42bdfe5 fastlane: i18n ru for app description 2024-04-16 01:09:18 +03:00
85718dc3a3 Update README.md 2024-04-15 17:53:55 -04:00
9dff352796 Update README.md 2024-04-15 17:52:35 -04:00
c5477767a0 Update README.md 2024-04-15 17:52:08 -04:00
3131ef8c4e Neat slider 2024-04-15 12:55:34 +03:00
4d9f05aa87 Corrects french translations
Corrects french translations in fr.json.
2024-04-14 20:52:51 +02:00
7b882d9bd8 Move to plugins🐱🎉 2024-04-14 18:40:32 +03:00
d1a2831922 Merge pull request #1535 from ImranR98/dev
Update packages, increment version
2024-04-13 03:09:10 -04:00
b042050ea3 Update packages, increment version 2024-04-13 03:05:44 -04:00
2d43dfe0a7 Merge pull request #1525 from gidano/main
Update hu.json
2024-04-13 03:04:13 -04:00
b1a740223c Merge pull request #1529 from DwainZwerg/patch-14
Update de.json
2024-04-13 03:04:02 -04:00
41c98d97b1 Merge pull request #1532 from gepbird/fix-generated-form-update
Fix out of bounds array access in GeneratedForm update
2024-04-13 03:03:47 -04:00
fb06babb96 Merge branch 'main' into re7gog 2024-04-12 13:21:52 +03:00
ccc0e7696b Fix out of bounds array access in GeneratedForm update 2024-04-08 21:45:19 +02:00
6d41ed8011 Update de.json
Corrected translation
2024-04-08 08:44:28 +02:00
c1476a7d58 Update hu.json 2024-04-07 09:37:28 +02:00
b1ee39be3b Merge pull request #1524 from ImranR98/dev
Add release asset download button (#1493) + Minor UI change
2024-04-07 02:25:27 -04:00
c2ccdd3d05 Merge remote-tracking branch 'origin/main' into dev 2024-04-07 02:21:45 -04:00
a621f1dfb4 Update packages, increment version 2024-04-07 02:21:38 -04:00
8527ad0007 Bugfix in release asset download 2024-04-07 02:19:40 -04:00
377ecef1b4 More resilient release asset import (if Downloads inaccessible, use export dir) 2024-04-07 02:18:26 -04:00
1f9921e6ff UI improvements 2024-04-07 02:13:36 -04:00
00988ed04d Improve release asset download UI 2024-04-07 01:41:35 -04:00
3d1113c057 Add release asset download button (#1493) 2024-04-07 01:28:45 -04:00
1c81f0c1e1 Merge pull request #1516 from ImranR98/dev
* Add support for GitLab CI artifact links in releases (#1506)
* Improve load speed on return to foreground
2024-04-03 18:15:44 -04:00
b26043ef5e Merge remote-tracking branch 'origin/main' into dev 2024-04-03 18:14:04 -04:00
e3ad144c06 Merge pull request #1503 from ngocanhtve/main
Update vi.json
2024-04-03 18:13:53 -04:00
fdb45c48ce Upgrade packages, increment version 2024-04-03 18:12:42 -04:00
8619cfa819 Add support for GitLab CI artifact links in releases (#1506) 2024-04-02 14:07:32 -04:00
1fe4cdd648 Improve load speed on return to foreground 2024-04-02 13:45:32 -04:00
2289e58dda Move to fonts plugin 2024-03-31 14:18:21 +03:00
049bcfbaf5 Ooops 2024-03-30 16:11:55 +03:00
5b5f922b54 Synced 2024-03-30 15:58:28 +03:00
6545498c21 Return random request code 2024-03-30 15:39:48 +03:00
ce22197ec9 Update vi.json
Complete.
2024-03-29 12:07:13 +07:00
f3d2dfe386 Update release.yml 2024-03-29 00:23:06 -04:00
677790af9c Update release.yml 2024-03-29 00:22:43 -04:00
85941349d8 Update release.yml 2024-03-29 00:17:35 -04:00
db755e0f90 Update release.yml 2024-03-29 00:14:26 -04:00
1019a01249 Update release.yml 2024-03-29 00:02:11 -04:00
35da44bc64 Update release.yml 2024-03-29 00:00:53 -04:00
b7ce07f965 Merge pull request #1502 from ImranR98/dev
- Added 'Share new Apps with AppVerifier' (#255)
- **Removed SourceForge (#1487)**
- Delete downloaded APK on install fail (#1495)
- Fix GitLab's broken track-only mode (#1496)
- Auto-delete download if likely invalid (#1498)
2024-03-28 23:43:25 -04:00
f2c15c5c8e Update packages and Flutter, increment version 2024-03-28 23:40:36 -04:00
8f0a6b7635 Merge remote-tracking branch 'origin/main' into dev 2024-03-28 23:40:11 -04:00
491c42d68b Removed SourceForge (#1487) 2024-03-28 23:27:22 -04:00
a883857230 Auto-delete download if likely invalid (#1498) 2024-03-28 23:13:45 -04:00
6468d0edcc Fix GitLab's broken track-only mode (#1496) 2024-03-28 23:05:16 -04:00
48eeeb4f00 Merge remote-tracking branch 'origin/main' into dev 2024-03-28 22:29:52 -04:00
0b25f25669 Merge pull request #1486 from fitojb/patch-1
Fixes to Spanish translation
2024-03-28 22:29:45 -04:00
d76b7375cb Added 'Share new Apps with AppVerifier' (#255) 2024-03-28 22:28:55 -04:00
f76aa51b54 Delete downloaded APK on install fail (#1495) 2024-03-23 19:26:39 -04:00
9e19a293d4 Fixes to Spanish translation
Includes assorted typography and treatment fixes.
2024-03-18 17:52:08 -06:00
381758550c Merge pull request #1478 from ImranR98/dev
Resume failed downloads when possible instead of starting again (#634), Typo: 'Installed' not 'Updated' (#1469) + Bugfix: Don't incorrectly show message when user cancel, Add changelog button to app page (#1474)
2024-03-17 05:15:46 -04:00
da496f7c53 GitHub actions: Don't add '-beta' to beta tags (pre-release mark is enough) 2024-03-17 05:14:08 -04:00
f905aa7dfd Made required changes according to https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply 2024-03-17 05:12:31 -04:00
0db27c773b Upgrade packages, increment version 2024-03-17 04:52:17 -04:00
4e968826ba Add changelog button to app page (#1474) 2024-03-17 04:15:43 -04:00
e95f575530 Typo: 'Installed' not 'Updated' (#1469) + Bugfix: Don't incorrectly show message when user cancels 2024-03-17 04:01:09 -04:00
3943caeedb Resume failed downloads when possible (#634) 2024-03-17 03:04:05 -04:00
4495051813 Merge pull request #1464 from dik08razz/main
Update uk.json
2024-03-10 14:40:28 -04:00
558d23b8c0 Update uk.json 2024-03-09 23:28:12 +02:00
2e90d1fb18 Update uk.json 2024-03-09 22:37:27 +02:00
9dae24ace6 Merge pull request #1462 from ImranR98/dev
Removed outdated translations related to #1459
2024-03-09 14:25:50 -05:00
b6e6568500 Increment version, upgrade Flutter + packages 2024-03-09 14:24:13 -05:00
a8eae7f04b Merge remote-tracking branch 'origin/main' into dev 2024-03-09 14:18:36 -05:00
4902e0ef06 Removed outdated translations related to #1459 2024-03-09 14:17:18 -05:00
e6926a714f Merge pull request #1442 from gidano/main
Update hu.json
2024-03-09 14:08:15 -05:00
c9eee4331d Merge pull request #1444 from rollsicecream/main
Fix 'F-Droid' translation' in fr.json
2024-03-09 14:08:09 -05:00
9a8cc2e5c3 Merge pull request #1445 from LilligantMatsuri/main
Update Chinese translation
2024-03-09 14:07:54 -05:00
a7c9cd0f27 Merge pull request #1448 from dik08razz/dev
add ukrainian
2024-03-09 14:07:49 -05:00
efc6846c1c Merge pull request #1459 from akramer-zibra/improve-gitlab-apk-retrieval
Improve gitlab .apk retrieval (#1450, #1381, #1380)
2024-03-09 14:07:36 -05:00
89edddd38c Merge pull request #1461 from ImranR98/dev
Fix bug in GitHub's 'verify latest' option (#1449)
2024-03-09 14:06:24 -05:00
e7c2112f41 Bugfix from prev. commit 2024-03-09 13:50:22 -05:00
d8cd3b6c92 Remove unused package import 2024-03-09 10:17:27 +01:00
bb1dd4ecfd Inject Private Access Token as optional request param
It turned out: If the parameter `private_token` is given but empty, the Gitlab REST API does expect a valid access token otherwise responds with an authorization error
2024-03-09 10:16:54 +01:00
3824b386d7 Regroup functions by their intention
This may prepare for later refactorings and code simplification
2024-03-09 10:11:54 +01:00
a9159fc8a0 Remove .apk retrieval from tags-feed
So this code does focus on the official Gitlab REST API. With this the retrieval logic changes.
2024-03-09 09:23:48 +01:00
7f4cf6e681 Fix bug in GitHub's 'verify latest' option (#1449) 2024-03-05 10:37:36 -05:00
215f05fbc2 add ukrainian 2024-03-04 18:14:37 +02:00
6d6afe9e69 Fix 'F-Droid' translation' in fr.json 2024-03-03 18:30:48 +01:00
66122f1608 Update zh.json
- Translate new strings
- Correct inaccurate translations

Signed-off-by: Matsuri <matsuri@vmoe.info>
2024-03-03 23:58:18 +08:00
0ad9bbdd8e Update hu.json 2024-03-03 09:32:21 +01:00
59cc08f28a Merge pull request #1439 from ImranR98/dev
- Allow any source to be overridden (#1420)
- Bugfix for installing apps with ID change step (#1424)
- Bugfix: 'null' changelog for some track-only apps (#1425)
- Show latest APK name/count on app page (#1426)
- More BG update interval options (#1431)
2024-03-01 20:37:02 -05:00
afc0c3a2fa Upgrade packages, increment version 2024-03-01 20:34:15 -05:00
a827046acc More BG update interval options (#1431) 2024-03-01 20:21:46 -05:00
fd8f967036 Show latest APK name/count on app page (#1426) 2024-03-01 20:18:07 -05:00
26be524c6d Bugfix: 'null' changelog for some track-only apps (#1425) 2024-03-01 19:50:04 -05:00
8ec3360575 Merge remote-tracking branch 'origin/main' into dev 2024-03-01 19:46:38 -05:00
f66753498b Bugfix for installing apps with ID change step (#1424) 2024-03-01 19:46:30 -05:00
90e6e5a3a3 Update README.md 2024-02-29 19:17:53 -05:00
2c4713ff25 Merge pull request #1411 from akramer-zibra/fix-gitlab-request-headers
Solve issues with gitlab .apk files and cloudflare protection
2024-02-26 10:56:11 -05:00
ef3b01ac56 Use gitlab host defined in class member variable 2024-02-26 13:42:56 +01:00
1cfb258dcc Allow any source to be overridden (#1420) 2024-02-25 15:41:12 -05:00
d13464a392 Merge pull request #1415 from ngocanhtve/main
Update vi.json
2024-02-25 14:20:38 -05:00
f1dd50faee Merge pull request #1413 from DwainZwerg/patch-13
Update README.md
2024-02-25 14:20:27 -05:00
6f5315db27 Update vi.json 2024-02-20 21:00:39 +07:00
02922c3c8e Merge remote-tracking branch 'upstream/main' 2024-02-20 20:35:07 +07:00
03ef649c0f Update README.md
Add IzzyOnDroid-Badge, since it isn't out of day
2024-02-20 09:38:57 +00:00
9083e28637 Merge branch 'main' into fix-gitlab-request-headers 2024-02-19 16:16:42 +01:00
cfa4c680cf Set referer header for gitlab app source context
There occur issues if a gitlab project refers in its release data to external hosted .apk file. In some cases (e.g. Aurora Store) download is not possible because cloudflare protection gives "forbidden" error. The referer header seems to pacify this cloudflare protection. Tested with Android 14 in an AVD emulator.

Related to: #1397, #1389, #1384, #1382, #1381, #1380, #1359, #854, #785, #697
2024-02-19 16:02:49 +01:00
a5b3b9d1d0 Merge pull request #1408 from ImranR98/dev
- Improved URL handling for F-Droid third party repos (#1399)
- Fixed F-Droid repo search (#1400) + general search bugfixes
- Improved APK filter for F-Droid (#1386)
- Don't remove storage permission (#1391)
- Jump in versionCode to fix updates in x86 versions (#1370)
2024-02-18 18:20:54 -05:00
13cd7e76c1 Update Flutter + packages, increment version 2024-02-18 18:19:29 -05:00
6e7ccfba37 Merge remote-tracking branch 'origin/main' into dev 2024-02-18 18:18:40 -05:00
34fd673e25 Jump in versionCode to fix updates in x86 versions (#1370) 2024-02-18 18:16:33 -05:00
db484f7b28 Don't remove storage permission (#1391) 2024-02-18 18:13:17 -05:00
b4cf0f5d29 Improved APK filter for F-Droid (#1386) 2024-02-18 18:00:03 -05:00
4db205dd9a Fixed F-Droid repo search (#1400) + general search bugfixes 2024-02-18 17:43:24 -05:00
64f8e2a57b Improved URL handling for F-Droid third party repos (#1399) 2024-02-18 17:00:56 -05:00
eddc245ff4 Update issue templates 2024-02-15 17:13:02 -05:00
9717db0ca4 Probably fixed 2024-02-05 18:26:17 +03:00
ed2f0f2c0c Merge pull request #1368 from gidano/main
Update hu.json
2024-02-03 15:47:45 -05:00
7d5a422855 Merge pull request #1371 from inson1/patch-1
Add F-droid badge to README.md
2024-02-03 15:47:28 -05:00
e420862546 Add F-droid badge to README.md 2024-02-03 17:45:32 +01:00
2cc59948a7 Update hu.json 2024-02-03 10:59:10 +01:00
188d33199e Merge pull request #1365 from heuwerk/patch-1
Update de.json
2024-02-03 04:29:36 -05:00
24928261bb Merge pull request #1367 from ImranR98/dev
Updated a package
2024-02-03 04:29:26 -05:00
4c7bda8343 Updated a package 2024-02-03 04:24:59 -05:00
d88709c999 Update de.json
Added forgotten translation
2024-02-02 11:30:20 +01:00
29e1481a3b Update de.json
Changed "Update" in translated text to "Aktualisierung", since it is used everywhere else.
2024-02-02 11:24:47 +01:00
9d76359543 Merge pull request #1364 from ImranR98/dev
Add DeepL translations as placeholders
2024-02-01 20:34:46 -05:00
f50e791221 Add DeepL translations as placeholders 2024-02-01 20:34:32 -05:00
7d08e5225c Merge pull request #1363 from ImranR98/dev
Correctly report and auto-delete bad APKs (#1251)
2024-02-01 19:32:15 -05:00
3842c1e2df Update packages, increment version 2024-02-01 19:31:32 -05:00
21b1990991 Merge remote-tracking branch 'origin/main' into dev 2024-02-01 19:31:19 -05:00
e278c9fb5a Correctly report and auto-delete bad APKs (#1251) 2024-02-01 19:26:36 -05:00
bfa29bb7c2 Merge pull request #1361 from DwainZwerg/patch-12
Translation of the Note of the sources window
2024-02-01 19:00:41 -05:00
efa55a9696 Update de.json
Okay → OK because it’s shorter and more common
2024-01-31 14:55:23 +00:00
8888cd6264 Update de.json
Translation of the note of the Sources Menu
2024-01-31 14:51:08 +00:00
34e2c014e3 Merge pull request #1357 from jont4/main
Update pt translation
2024-01-29 18:44:36 -05:00
5d92a6d013 Update pt translation 2024-01-28 22:58:17 -03:00
fb03b2e95c Merge pull request #1356 from ImranR98/dev
Better support for SourceForge (#1352), Remove READ_EXTERNAL_STORAGE again (#1309) + fix typo
2024-01-28 17:00:10 -05:00
5a1e09564c Merge pull request #1350 from gidano/main
Update hu.json
2024-01-28 16:58:53 -05:00
3783eba401 Merge pull request #1351 from unbranched/patch-5
Update it.json
2024-01-28 16:58:39 -05:00
a3530ce6bb Remove READ_EXTERNAL_STORAGE again (#1309) + fix typo 2024-01-28 16:58:09 -05:00
27d8655d58 Better support for SourceForge (#1352) 2024-01-28 16:32:58 -05:00
fb845ce601 Update it.json 2024-01-28 10:59:15 +00:00
dbd433df9d Update hu.json 2024-01-28 09:15:28 +01:00
badf32ff11 Merge pull request #1349 from ImranR98/dev
- Allow existing '/refs' in SourceHut URLs (#1347)
- Revert to showing App URL separately again (#1336)
- F-Droid: Don't pull changelog text if it isn't a raw file from GitHub/GitLab (#1340)
- Add a note on self-hosted instances of sources (#1342)
- versionCode changes
2024-01-27 22:00:54 -05:00
5e40f3264e Merge pull request #1337 from jont4/main
Translation: pt update
2024-01-27 21:58:41 -05:00
71bb6d9410 Merge pull request #1338 from teaminh/main
Update vi.json
2024-01-27 21:58:14 -05:00
731b682fc9 Merge pull request #1339 from DwainZwerg/patch-11
Update de.json
2024-01-27 21:57:03 -05:00
c9751227a5 Flutter upgrade + versionCode changes + increment version 2024-01-27 21:56:26 -05:00
6ef2a26e94 Add a note on self-hosted instances of sources (#1342) 2024-01-27 21:26:32 -05:00
5a8efa2388 F-Droid: Don't pull changelog text if it isn't a raw file from GitHub/GitLab (#1340) 2024-01-27 21:01:44 -05:00
d25895fa28 Revert to showing App URL separately again (#1336) 2024-01-27 20:13:01 -05:00
de09f3ece2 Allow existing '/refs' in SourceHut URLs (#1347) 2024-01-27 20:08:05 -05:00
1135ffb30f Update de.json
Better Translation of "latest"
2024-01-25 13:49:57 +00:00
5379cb31e8 Update vi.json 2024-01-25 18:31:46 +07:00
7d41ab44b7 Minor update 2024-01-24 23:21:42 -03:00
b239f9bd05 Merge pull request #1335 from ImranR98/dev
Don't include prereleases in default app entry
2024-01-24 20:15:35 -05:00
1acc923ec2 Don't include prereleases in default app entry 2024-01-24 20:15:13 -05:00
8d6edfb91f Update release.yml 2024-01-24 20:12:56 -05:00
491b62bb0d Update release.yml 2024-01-24 20:10:02 -05:00
d1413fc478 Update release.yml 2024-01-24 20:05:47 -05:00
761f7039e9 Merge pull request #1334 from ImranR98/dev
- De-emphasize 'reset install status' button to avoid confusion
- Minor bugfix (#1331)
2024-01-24 19:54:16 -05:00
59783a341d Upgrade packages, increment version (1.0.0) 2024-01-24 19:53:31 -05:00
0100fa4236 Merge remote-tracking branch 'origin/main' into dev 2024-01-24 19:52:42 -05:00
f6f9ed68dc Merge pull request #1332 from palopezv/patch-1
Update es.json
2024-01-24 19:50:46 -05:00
8adb5e84d1 Reordered a button for consistency 2024-01-24 19:50:11 -05:00
3940b65156 De-emphasize 'reset install status' button to avoid confusion 2024-01-24 19:49:07 -05:00
98e7f27eab Merge remote-tracking branch 'origin/main' into dev 2024-01-24 19:33:45 -05:00
7cf5cafd75 Minor bugfix (#1331) 2024-01-24 19:28:40 -05:00
d750e35273 Update release.yml 2024-01-24 19:22:56 -05:00
1f12e12130 Update es.json
I'm submitting a revision (call it heartless copy editing :😉:) of the Spanish translation file. This revision corrects several grammatical errors, most are syntactical and semantic, some stylistic in order to correct literal (and intellegible) translations and typos.
2024-01-24 16:11:54 -05:00
4a4fb4716e Merge pull request #1328 from ImranR98/dev
Updated modules
2024-01-23 11:37:29 -05:00
efe6ec0d43 Updated modules 2024-01-23 11:37:08 -05:00
00d605e195 Merge pull request #1325 from ImranR98/dev
Update Flutter submodule and make GitHub actions error out if it doesn't match latest/stable
2024-01-22 12:22:15 -05:00
f06de8d19f Update Flutter submodule and make GitHub actions error out if it doesn't match latest/stable 2024-01-22 12:21:38 -05:00
fac335c849 Merge pull request #1324 from p1gp1g/reproducible-build
Add flutter as a submodule (again)
2024-01-22 12:07:43 -05:00
5bef546ae4 Merge pull request #1323 from DwainZwerg/patch-10
Update de.json
2024-01-21 14:13:07 -05:00
b690cd6a67 Update de.json
Edit german translation
2024-01-21 18:59:22 +00:00
dbee4a2657 Merge pull request #1316 from bluefly000/japanese-translation
Update ja.json
2024-01-21 12:35:30 -05:00
67420f293b Merge pull request #1319 from teaminh/main
Update vi.json
2024-01-21 12:35:23 -05:00
d1d6b0792b Merge pull request #1322 from ygxbnet/patch-1
Update zh.json
2024-01-21 12:35:17 -05:00
2ec039ba0f Update zh.json 2024-01-21 23:38:20 +08:00
4b9b9a10ad Update vi.json 2024-01-21 10:00:00 +07:00
4193d8ada1 Update ja.json 2024-01-21 10:19:35 +09:00
4701b090b0 Update ja.json 2024-01-21 10:18:45 +09:00
8b0c85827c Merge pull request #1315 from ClockGen/fastlane
Added fastlane structure for F-Droid
2024-01-20 19:26:31 -05:00
57e02d1c80 Added initial fastlane structure 2024-01-21 01:51:57 +03:00
4446929b10 Merge pull request #1314 from ImranR98/dev
Remove unnecessary padding from badge graphic
2024-01-20 16:42:25 -05:00
c31f65b0e4 Remove unnecessary padding from badge graphic 2024-01-20 16:42:01 -05:00
f6c3b8dceb Merge pull request #1313 from ImranR98/dev
Increment version
2024-01-20 16:31:49 -05:00
10bd825972 Increment version 2024-01-20 16:31:30 -05:00
303bee7c57 Merge pull request #1312 from ImranR98/dev
Bugfix: don't show 'Apps Updated' toast when nothing was updated (#1307), Disable app page buttons while app info is being updated
2024-01-20 16:29:47 -05:00
369ae43062 Disable app page buttons while app info is being updated 2024-01-20 16:28:54 -05:00
62dc7fd88b Bugfix: don't show 'Apps Updated' toast when nothing was updated (#1307) 2024-01-20 16:18:19 -05:00
4aace63cd7 Merge pull request #1311 from ImranR98/dev
Removed unnecessary READ_EXTERNAL_STORAGE permission (#1309)
2024-01-20 16:05:33 -05:00
5d588a05a7 Removed unnecessary READ_EXTERNAL_STORAGE permission (#1309) 2024-01-20 16:04:17 -05:00
cab6f9d026 Merge pull request #1310 from ImranR98/dev
"Standardize" translation files + use machine-translations as placeholders for missing translations
2024-01-20 15:42:24 -05:00
fc94d553eb Merge remote-tracking branch 'origin/main' into dev 2024-01-20 15:41:44 -05:00
2952345585 Use machine-translations as placeholders for missing translations 2024-01-20 15:39:10 -05:00
becc3828b7 Remove some unused translations 2024-01-20 14:58:37 -05:00
2a62248991 "Standardize" translation files 2024-01-20 14:45:28 -05:00
eb2b31219c Merge pull request #1306 from gidano/main
Update hu.json
2024-01-20 14:31:51 -05:00
55053f9627 Merge pull request #1304 from bluefly000/japanese-translation
Update ja.json
2024-01-20 14:31:19 -05:00
5ff6c64938 Update hu.json 2024-01-20 10:35:18 +01:00
c1c517cf38 Update ja.json 2024-01-20 18:21:00 +09:00
b3de2e5d6b Merge pull request #1303 from ImranR98/dev
- Better partial APK hash as pseudo-version (related to #1101)
- Support Direct APK Links (#1292)
- Easier to understand version detection settings
- Bugfix in automatic source selection for URL input
- Various UI improvements and wording changes
- Add invert option to apk filter regex 
- Filter out fdroid APKs from default Obtainium entry (for later)
2024-01-19 23:41:27 -05:00
0183070b2b Merge remote-tracking branch 'origin/main' into dev 2024-01-19 23:38:23 -05:00
089bbc3ee9 Merge pull request #1291 from Bardesss/patch-1
Update nl.json
2024-01-19 23:29:49 -05:00
b085801c2c Merge pull request #1298 from jont4/main
Translation: Some fixes and updates in pt.json
2024-01-19 23:29:40 -05:00
ad4cae4288 Add F-Droid build to GitHub action 2024-01-19 23:29:22 -05:00
95c285e1f7 Filter out fdroid APKs from default Obtainium entry 2024-01-19 23:25:00 -05:00
c31a1912a5 Add invert option to apk filter regex 2024-01-19 23:22:00 -05:00
99da1f8481 Upgrade packages, increment version 2024-01-19 20:41:07 -05:00
bf6b3a7da0 UI bug 2024-01-19 20:36:40 -05:00
4ab4f58f65 Cleaner 'add app' page 2024-01-19 20:35:44 -05:00
29a76ac8e6 Removed unused divider and commented code on settings page 2024-01-19 19:59:30 -05:00
e2761ce284 UI improvements on app page 2024-01-19 19:57:28 -05:00
dc45334cb9 Support Direct APK Links (#1292) 2024-01-19 18:54:01 -05:00
56052bfd79 Bugfix in version detection toggles 2024-01-19 00:43:02 -05:00
f5067d636a More UI tweaks 2024-01-19 00:21:57 -05:00
b173b1300a Clearer wording 2024-01-19 00:12:43 -05:00
15a34e53bf Bugfix in automatic source selection for URL input 2024-01-19 00:00:47 -05:00
183fb26988 Clearer version detection settings 2024-01-18 23:46:23 -05:00
2da9a8cd59 Remove unnecessary print() 2024-01-18 20:45:34 -05:00
bb07000280 Better partial APK hash as pseudo-version (related to #1101) 2024-01-18 20:45:06 -05:00
3a46ad2957 Some fixes and updates 2024-01-16 15:48:59 -03:00
6b631d119c Merge branch 'ImranR98:main' into main 2024-01-16 15:24:02 -03:00
051762fcc1 Update nl.json
Added new strings.
2024-01-15 11:38:00 +00:00
595d5dc283 Merge pull request #1289 from ImranR98/dev
Added badge graphic (#1287)
2024-01-14 15:14:41 -05:00
1d328f07e2 Merge remote-tracking branch 'origin/main' into dev 2024-01-14 15:14:01 -05:00
b1c8ac6f2a Added badge graphic (#1287) 2024-01-14 15:09:16 -05:00
da619d37f7 Merge pull request #1288 from iDazai/main
Update de.json
2024-01-14 14:56:54 -05:00
236c4722e5 Update de.json
added 2 missing strings, translated new strings
2024-01-14 15:36:08 +01:00
92f59116a0 Merge remote-tracking branch 'origin/main' into dev 2024-01-14 01:37:17 -05:00
c28040f0cb Merge remote-tracking branch 'origin/main' into dev 2024-01-14 01:36:43 -05:00
e8b9654320 Update release.yml 2024-01-14 01:36:35 -05:00
be1a793a37 Update release.yml 2024-01-14 01:34:32 -05:00
440720afb6 Update release.yml 2024-01-14 01:31:42 -05:00
900d3e734e Update release.yml 2024-01-14 01:27:13 -05:00
0bf096abb5 Temporary GitHub action change 2024-01-14 01:26:17 -05:00
632efb9e22 Merge remote-tracking branch 'origin/main' into dev 2024-01-14 01:24:50 -05:00
bfc506e450 Merge pull request #1285 from ImranR98/dev
- Improve loading time (parallelize version detection)
- Newest asset upload date as release date for GitHub (#1284)
- Enable 'start on boot' for BG task (#1234)
- Remove the need to hardcode Obtainium's version number
2024-01-14 01:24:25 -05:00
ffe612708c Remove the need to hardcode Obtainium's version number 2024-01-14 01:22:35 -05:00
5d161160aa Increment version 2024-01-14 00:56:25 -05:00
eadf3e5a29 Enable 'start on boot' for BG task (#1234) 2024-01-14 00:22:25 -05:00
6b6b4084a0 Newest asset upload date as release date for GitHub (#1284) 2024-01-14 00:14:44 -05:00
30a4633f72 Improve loading time (parallelize version detection) 2024-01-13 23:26:57 -05:00
d44139dd72 Merge pull request #1281 from ImranR98/dev
Allow entire app config to be shared as link (see https://github.com/ImranR98/apps.obtainium.imranr.dev/issues/5)
2024-01-12 23:47:31 -05:00
60869a0490 Allow entire app config to be shared as link (see https://github.com/ImranR98/apps.obtainium.imranr.dev/issues/5) 2024-01-12 23:46:50 -05:00
102be5fa2e Merge pull request #1280 from ImranR98/dev
- Fix form input issues (related to #1272)
- HTML Source bugfix (related to #1259)
2024-01-12 22:32:49 -05:00
a25c04b390 Update packages, increment version, dart fix 2024-01-12 22:32:33 -05:00
d46f0a1c33 HTML Source bugfix (related to #1259) 2024-01-12 22:16:06 -05:00
5c7f5a99e1 Attempt to fix form input issues for recursive forms (NOT FULLY TESTED) 2024-01-12 21:51:48 -05:00
d949a80f19 Merge pull request #1279 from gidano/main
Update hu.json
2024-01-12 20:29:33 -05:00
6b5d4e2027 Merge pull request #1278 from mehdeej/main
Update fa.json
2024-01-12 20:29:25 -05:00
199d071ee8 Update hu.json 2024-01-12 15:45:26 +01:00
5b78de5d61 Merge branch 'ImranR98:main' into main 2024-01-12 05:46:33 -03:00
db4f13e2e1 Update fa.json 2024-01-12 07:32:52 +03:30
5e869b7e03 Update release.yml 2024-01-11 21:58:48 -05:00
34fee36132 Merge pull request #1277 from ImranR98/dev
- Fix APKPure downloads (#1250)
- F-Droid bugfix (#1264)
- Allow versionCode to be used as the version for comparison (#1269)
- Add custom header support to HTML (#709, #1272)
- Disabled Mullvad source (#1276)
- Bugfixes: Don't make URLs lowercase, never auto-select Jenkins
2024-01-11 21:44:36 -05:00
ea09bc36a5 Merge branch 'main' into dev 2024-01-11 21:44:24 -05:00
f7e0678cb2 Remove unused variable 2024-01-11 21:39:41 -05:00
223ae378a9 Update packages, increment version 2024-01-11 21:39:26 -05:00
6f52a48991 Merge remote-tracking branch 'origin/main' into dev 2024-01-11 21:36:15 -05:00
0280935955 Merge pull request #1268 from bluefly000/japanese-translation
Update ja.json
2024-01-11 21:36:03 -05:00
eebc7d9ab3 Merge pull request #1271 from pmtpro/main
update vi.json
2024-01-11 21:35:53 -05:00
aa7b5652ff Fix APKPure downloads (#1250) 2024-01-11 21:34:46 -05:00
ec6683a198 Bugfixes: Don't make URLs lowercase, never auto-select Jenkins 2024-01-11 21:28:15 -05:00
05372924a0 F-Droid bugfix (#1264) 2024-01-11 21:16:25 -05:00
e63c1399dd Allow versionCode to be used as the version for comparison (#1269) 2024-01-11 20:10:20 -05:00
4fcad92e1d Disabled Mullvad source (#1276) 2024-01-11 19:50:51 -05:00
e18e7298bc GeneratedFormSubForm UI improvements 2024-01-11 19:43:17 -05:00
76a6a509cd Header usage bugfix 2024-01-11 19:23:59 -05:00
7a03561ff6 Add custom header support to HTML (#709, #1272) 2024-01-11 19:01:17 -05:00
70e54ce14a F-Droid small fix 2024-01-10 18:51:22 -05:00
b5f86f0e79 update vi.json 2024-01-10 10:20:59 +07:00
4ebca49ef7 Update ja.json 2024-01-09 22:26:11 +09:00
49cfa95463 Merge pull request #1265 from ImranR98/dev
- Multi-host support + add '.net' host to APKPure source (#1250)
- HTML link parsing bugfix (#1259)
- APKMirror version extraction bugfix (#1264)
2024-01-08 19:28:31 -05:00
4600ab0593 Increment version 2024-01-08 19:20:08 -05:00
7f2ca98bde Multi-host support + add '.net' host to APKPure source (#1250) 2024-01-08 19:17:50 -05:00
6511485bcf APKMirror version extraction bugfix (#1264) 2024-01-08 18:33:24 -05:00
fd22113e44 HTML link parsing bugfix (#1259) 2024-01-08 18:18:57 -05:00
f2ae078723 Merge branch 'ImranR98:main' into main 2024-01-08 18:20:27 -03:00
c3dacf2b9b Translation: tiny bug 2024-01-08 18:18:57 -03:00
e8580dc1d5 Merge pull request #1257 from ImranR98/dev
Add F-Droid changelogs (#1255), Fix corrupt pt.json (#1256)
2024-01-07 21:59:17 -05:00
daffff7eb0 Add F-Droid changelogs (#1255) 2024-01-07 21:57:23 -05:00
751fda5e37 Increment version 2024-01-07 21:45:41 -05:00
1e38abc500 Fix corrupt pt.json (#1256) 2024-01-07 21:40:56 -05:00
8e7137815b Merge pull request #1254 from ImranR98/dev
URL parsing bugfix for HTML source (#1253)
2024-01-07 12:37:51 -05:00
4226ee453f Merge remote-tracking branch 'origin/main' into dev 2024-01-07 12:37:14 -05:00
568a110443 Merge pull request #1249 from LilligantMatsuri/main
Update Chinese translation
2024-01-07 12:37:00 -05:00
7fd7a6ca8d Merge pull request #1252 from jont4/main
Translation: many fixes in PT translation
2024-01-07 12:36:47 -05:00
3092c854ff Increment version 2024-01-07 12:36:20 -05:00
a9566f4b23 Remove redundant function call 2024-01-07 12:34:09 -05:00
7a5aa3c11d URL parsing bugfix for HTML source (#1253) 2024-01-07 12:33:06 -05:00
f2454bb028 Translation: many fixes in PT translation 2024-01-07 12:28:24 -03:00
2c1023a6fd Update zh.json
- Add & translate new strings
- Correct inaccurate translations

Signed-off-by: Matsuri <matsuri@vmoe.info>
2024-01-07 16:55:00 +08:00
9187ac6684 Merge pull request #1248 from ImranR98/dev
Removed Steam Source (existing apps auto-switched to HTML) (#1244) + Bugfixes
2024-01-06 23:39:11 -05:00
9640e43bc5 Upgrade packages, increment version 2024-01-06 23:38:36 -05:00
09b24b36f2 Removed Steam Source (existing apps auto-switched to HTML) (#1244) + bugfixes 2024-01-06 23:37:27 -05:00
06a761c0d0 Merge pull request #1247 from CertainBot/main
Update Spanish
2024-01-06 22:46:06 -05:00
6ff7d7fcd7 Merge pull request #1246 from bluefly000/japanese-translation
Update ja.json
2024-01-06 22:45:47 -05:00
0f27c79303 Merge pull request #1243 from pmtpro/patch-1
Update vi.json
2024-01-06 22:45:25 -05:00
9961ad081f Update Spanish
- English: new strings, typos, minor changes
- Español: strings nuevos, correcciones, algunas mejoras
2024-01-07 02:55:18 +01:00
02056b4f78 Update ja.json 2024-01-06 01:05:52 +09:00
0492588c25 Update vi.json 2024-01-04 09:26:37 +07:00
8dd8f471a2 Update vi.json 2023-11-20 10:09:45 +07:00
657d1cd042 Update vi.json 2023-11-20 10:00:08 +07:00
sim
7987083555 Add flutter as a submodule 2023-02-02 21:56:32 +01:00
93 changed files with 5323 additions and 3424 deletions

1
.flutter Submodule

Submodule .flutter added at 54e66469a9

View File

@ -10,6 +10,7 @@ assignees: ''
**Prerequisites** **Prerequisites**
<!-- Please ensure your request is not part of an existing issue. --> <!-- Please ensure your request is not part of an existing issue. -->
<!-- Please ensure you have checked the Obtainium Wiki. --> <!-- Please ensure you have checked the Obtainium Wiki. -->
<!-- Please ensure your request is an actual bug and not intended behaviour (this is frequently the case for issues involving version strings and the HTML source. -->
**Describe the bug** **Describe the bug**
<!-- A clear and concise description of what the bug is. --> <!-- A clear and concise description of what the bug is. -->

View File

@ -14,11 +14,14 @@ assignees: ''
**Describe the feature** **Describe the feature**
<!-- A clear and concise description of what you want to happen. <!-- A clear and concise description of what you want to happen.
For new Sources, it's preferable (not required) if you suggest how the following details can be extracted from the Source in a reliable way (like an API or through web scraping): For new Sources, please ensure:
1. It is not already possible to use the HTML Source for your purposes.
2. It must be possible to extract the following details from the Source in a reliable way:
- The App version (or any release-specific identifier - a "pseudo-version") for the latest release - The App version (or any release-specific identifier - a "pseudo-version") for the latest release
- One or more APK URL(s) for the latest release - One or more APK URL(s) for the latest release
- Above details for previous releases (optional) - Above details for previous releases (optional)
If you're not sure about 1 or 2, open a discussion item instead.
Note that the Web scraper cannot deal with JavaScript-enabled content. --> Note that the Web scraper cannot deal with JavaScript-enabled content. -->
**Describe alternatives you've considered (if applicable)** **Describe alternatives you've considered (if applicable)**

View File

@ -2,6 +2,10 @@ name: Build and Release
on: on:
workflow_dispatch: workflow_dispatch:
inputs:
beta:
type: boolean
description: Is beta?
jobs: jobs:
build: build:
@ -11,7 +15,16 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: subosito/flutter-action@v2 - uses: subosito/flutter-action@v2
- uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Flutter Doctor
id: flutter_doctor
run: |
flutter doctor -v
- name: Import GPG key - name: Import GPG key
id: import_pgp_key id: import_pgp_key
uses: crazy-max/ghaction-import-gpg@v6 uses: crazy-max/ghaction-import-gpg@v6
@ -19,11 +32,33 @@ jobs:
gpg_private_key: ${{ secrets.PGP_KEY_BASE64 }} gpg_private_key: ${{ secrets.PGP_KEY_BASE64 }}
passphrase: ${{ secrets.PGP_PASSPHRASE }} passphrase: ${{ secrets.PGP_PASSPHRASE }}
- name: Check submodule
id: check_submodule
run: |
SUBMODULE_COMMIT_LONG="$(git submodule status | head -1 | tail -c +2 | awk '{print $1}')"
FLUTTER_COMMIT_SHORT="$(flutter --version | head -2 | tail -1 | awk '{print $4}')"
echo "SUBMODULE_COMMIT_LONG=$SUBMODULE_COMMIT_LONG, FLUTTER_COMMIT_SHORT=$FLUTTER_COMMIT_SHORT"
if ! [[ "$SUBMODULE_COMMIT_LONG" =~ ^$FLUTTER_COMMIT_SHORT ]]; then
echo "Your submodule has not been updated!"
exit 1
fi
- name: Extract Version
id: extract_version
run: |
VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10)
echo "version=$VERSION" >> $GITHUB_OUTPUT
if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi
echo "beta=$BETA" >> $GITHUB_OUTPUT
TAG="v$VERSION"
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Build APKs - name: Build APKs
run: | run: |
sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle
flutter build apk --flavor normal && flutter build apk --split-per-abi --flavor normal 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 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
rm ./build/app/outputs/flutter-apk/*.sha1 rm ./build/app/outputs/flutter-apk/*.sha1
ls -l ./build/app/outputs/flutter-apk/ ls -l ./build/app/outputs/flutter-apk/
@ -37,22 +72,12 @@ jobs:
for apk in ./build/app/outputs/flutter-apk/*-release*.apk; do for apk in ./build/app/outputs/flutter-apk/*-release*.apk; do
unsignedFn=${apk/-release/-unsigned} unsignedFn=${apk/-release/-unsigned}
mv "$apk" "$unsignedFn" mv "$apk" "$unsignedFn"
${ANDROID_HOME}/build-tools/30.0.2/apksigner sign --ks apksign.keystore --ks-pass pass:"${KEYSTORE_PASSWORD}" --out "${apk}" "${unsignedFn}" ${ANDROID_HOME}/build-tools/$(ls ${ANDROID_HOME}/build-tools/ | tail -1)/apksigner sign --ks apksign.keystore --ks-pass pass:"${KEYSTORE_PASSWORD}" --out "${apk}" "${unsignedFn}"
sha256sum ${apk} | cut -d " " -f 1 > "$apk".sha256 sha256sum ${apk} | cut -d " " -f 1 > "$apk".sha256
gpg --batch --pinentry-mode loopback --passphrase "${PGP_PASSPHRASE}" --sign --detach-sig "$apk".sha256 gpg --batch --pinentry-mode loopback --passphrase "${PGP_PASSPHRASE}" --sign --detach-sig "$apk".sha256
done done
rm apksign.keystore rm apksign.keystore
PGP_KEY_FINGERPRINT="${{ steps.import_pgp_key.outputs.fingerprint }}" PGP_KEY_FINGERPRINT="${{ steps.import_pgp_key.outputs.fingerprint }}"
- name: Extract Version
id: extract_version
run: |
VERSION=$(grep -oP "currentVersion = '\K[^']+" lib/main.dart)
echo "version=$VERSION" >> $GITHUB_OUTPUT
TAG=$(grep -oP "'.*\\\$currentVersion.*'" lib/main.dart | head -c -2 | tail -c +2 | sed "s/\$currentVersion/$VERSION/g")
echo "tag=$TAG" >> $GITHUB_OUTPUT
if [ -n "$(echo $TAG | grep -oP '\-beta$')" ]; then BETA=true; else BETA=false; fi
echo "beta=$BETA" >> $GITHUB_OUTPUT
- name: Create Tag - name: Create Tag
uses: mathieudutour/github-tag-action@v6.1 uses: mathieudutour/github-tag-action@v6.1

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule ".flutter"]
path = .flutter
url = https://github.com/flutter/flutter/

View File

@ -4,11 +4,13 @@
Get Android App Updates Directly From the Source. Get Android App Updates Directly From the Source.
Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available. Obtainium allows you to install and update apps directly from their releases pages, and receive notifications when new releases are made available.
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0) More info:
- [Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki)
Read the Wiki: [https://github.com/ImranR98/Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki) - [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
- [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
Currently supported App sources: Currently supported App sources:
- Open Source - General: - Open Source - General:
@ -18,10 +20,9 @@ Currently supported App sources:
- [F-Droid](https://f-droid.org/) - [F-Droid](https://f-droid.org/)
- Third Party F-Droid Repos - Third Party F-Droid Repos
- [IzzyOnDroid](https://android.izzysoft.de/) - [IzzyOnDroid](https://android.izzysoft.de/)
- [SourceForge](https://sourceforge.net/)
- [SourceHut](https://git.sr.ht/) - [SourceHut](https://git.sr.ht/)
- Other - General: - Other - General:
- [APKPure](https://apkpure.com/) - [APKPure](https://apkpure.net/)
- [Aptoide](https://aptoide.com/) - [Aptoide](https://aptoide.com/)
- [Uptodown](https://uptodown.com/) - [Uptodown](https://uptodown.com/)
- [APKMirror](https://apkmirror.com/) (Track-Only) - [APKMirror](https://apkmirror.com/) (Track-Only)
@ -34,7 +35,6 @@ Currently supported App sources:
- Other - App-Specific: - Other - App-Specific:
- [WhatsApp](https://whatsapp.com) - [WhatsApp](https://whatsapp.com)
- [Telegram App](https://telegram.org) - [Telegram App](https://telegram.org)
- [Steam Mobile Apps](https://store.steampowered.com/mobile)
- [Neutron Code](https://neutroncode.com) - [Neutron Code](https://neutroncode.com)
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files - "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files
@ -43,7 +43,13 @@ Currently supported App sources:
[<img src="https://github.com/machiav3lli/oandbackupx/blob/034b226cea5c1b30eb4f6a6f313e4dadcbb0ece4/badge_github.png" [<img src="https://github.com/machiav3lli/oandbackupx/blob/034b226cea5c1b30eb4f6a6f313e4dadcbb0ece4/badge_github.png"
alt="Get it on GitHub" alt="Get it on GitHub"
height="80">](https://github.com/ImranR98/Obtainium/releases) height="80">](https://github.com/ImranR98/Obtainium/releases)
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
alt="Get it on IzzyOnDroid"
height="80">](https://apt.izzysoft.de/fdroid/index/apk/dev.imranr.obtainium)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/dev.imranr.obtainium.fdroid/)
[PGP Public Key](https://keyserver.ubuntu.com/pks/lookup?search=contact%40imranr.dev&fingerprint=on&op=index) [PGP Public Key](https://keyserver.ubuntu.com/pks/lookup?search=contact%40imranr.dev&fingerprint=on&op=index)
## Limitations ## Limitations

View File

@ -1,3 +1,9 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties() def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties') def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) { if (localPropertiesFile.exists()) {
@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
} }
} }
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = '1' flutterVersionCode = '1'
@ -21,11 +22,6 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0' flutterVersionName = '1.0'
} }
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'dev.rikka.tools.refine'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties') def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) { if (keystorePropertiesFile.exists()) {
@ -33,7 +29,8 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion namespace "dev.imranr.obtainium"
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
@ -54,7 +51,7 @@ android {
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 24 minSdkVersion 24
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
} }
@ -95,16 +92,15 @@ repositories {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
dependencies { ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
def shizuku_version = '13.1.5' import com.android.build.OutputFile
implementation "dev.rikka.shizuku:api:$shizuku_version" android.applicationVariants.all { variant ->
implementation "dev.rikka.shizuku:provider:$shizuku_version" variant.outputs.each { output ->
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
def hidden_api_version = '4.3.1' if (abiVersionCode != null) {
implementation "dev.rikka.tools.refine:runtime:$hidden_api_version" output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
implementation "dev.rikka.hidden:compat:$hidden_api_version" } else {
compileOnly "dev.rikka.hidden:stub:$hidden_api_version" output.versionCodeOverride = variant.versionCode * 10
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3" }
}
implementation "com.github.topjohnwu.libsu:core:5.2.2" }
}

View File

@ -47,7 +47,7 @@
android:value="2" /> android:value="2" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="dev.imranr.obtainium" android:authorities="${applicationId}"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
@ -64,13 +64,12 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" /> <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" /> android:maxSdkVersion="29" />\
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
</manifest> </manifest>

View File

@ -1,44 +0,0 @@
package dev.imranr.obtainium
import android.util.Xml
import org.xmlpull.v1.XmlPullParser
import java.io.File
import java.io.FileInputStream
class DefaultSystemFont {
fun get(): String {
return try {
val file = File("/system/etc/fonts.xml")
val fileStream = FileInputStream(file)
parseFontsFileStream(fileStream)
} catch (e: Exception) {
e.message ?: "Unknown fonts.xml parsing exception"
}
}
private fun parseFontsFileStream(fileStream: FileInputStream): String {
fileStream.use { stream ->
val parser = Xml.newPullParser()
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
parser.setInput(stream, null)
parser.nextTag()
return parseFonts(parser)
}
}
private fun parseFonts(parser: XmlPullParser): String {
while (!((parser.next() == XmlPullParser.END_TAG) && (parser.name == "family"))) {
if ((parser.eventType == XmlPullParser.START_TAG) && (parser.name == "font")
&& (parser.getAttributeValue(null, "style") == "normal")
&& (parser.getAttributeValue(null, "weight") == "400")) {
break
}
}
parser.next()
val fontFile = parser.text.trim()
if (fontFile == "") {
throw NoSuchFieldException("The font filename couldn't be found in fonts.xml")
}
return "/system/fonts/$fontFile"
}
}

View File

@ -1,179 +1,5 @@
package dev.imranr.obtainium package dev.imranr.obtainium
import android.content.Intent
import android.content.IntentSender
import android.content.pm.IPackageInstaller
import android.content.pm.IPackageInstallerSession
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Process
import androidx.annotation.NonNull
import com.topjohnwu.superuser.Shell
import dev.imranr.obtainium.util.IIntentSenderAdaptor
import dev.imranr.obtainium.util.IntentSenderUtils
import dev.imranr.obtainium.util.PackageInstallerUtils
import dev.imranr.obtainium.util.ShizukuSystemServerApi
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.Result
import java.io.IOException
import java.util.concurrent.CountDownLatch
import org.lsposed.hiddenapibypass.HiddenApiBypass
import rikka.shizuku.Shizuku
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
import rikka.shizuku.ShizukuBinderWrapper
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity()
private var nativeChannel: MethodChannel? = null
private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random()
private fun shizukuCheckPermission(result: Result) {
try {
if (Shizuku.isPreV11()) { // Unsupported
result.success(-1)
} else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
result.success(1)
} else if (Shizuku.shouldShowRequestPermissionRationale()) { // Deny and don't ask again
result.success(0)
} else {
Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE)
result.success(-2)
}
} catch (_: Exception) { // If shizuku not running
result.success(-1)
}
}
private val shizukuRequestPermissionResultListener = OnRequestPermissionResultListener {
requestCode: Int, grantResult: Int ->
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
nativeChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
}
}
private fun shizukuInstallApk(apkFileUri: String, result: Result) {
val uri = Uri.parse(apkFileUri)
var res = false
var session: PackageInstaller.Session? = null
try {
val iPackageInstaller: IPackageInstaller =
ShizukuSystemServerApi.PackageManager_getPackageInstaller()
val isRoot = Shizuku.getUid() == 0
// The reason for use "com.android.shell" as installer package under adb
// is that getMySessions will check installer package's owner
val installerPackageName = if (isRoot) packageName else "com.android.shell"
var installerAttributionTag: String? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
installerAttributionTag = attributionTag
}
val userId = if (isRoot) Process.myUserHandle().hashCode() else 0
val packageInstaller = PackageInstallerUtils.createPackageInstaller(
iPackageInstaller, installerPackageName, installerAttributionTag, userId)
val params =
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
installFlags = installFlags or (0x00000002/*PackageManager.INSTALL_REPLACE_EXISTING*/
or 0x00000004 /*PackageManager.INSTALL_ALLOW_TEST*/)
PackageInstallerUtils.setInstallFlags(params, installFlags)
val sessionId = packageInstaller.createSession(params)
val iSession = IPackageInstallerSession.Stub.asInterface(
ShizukuBinderWrapper(iPackageInstaller.openSession(sessionId).asBinder()))
session = PackageInstallerUtils.createSession(iSession)
val inputStream = contentResolver.openInputStream(uri)
val openedSession = session.openWrite("apk.apk", 0, -1)
val buffer = ByteArray(8192)
var length: Int
try {
while (inputStream!!.read(buffer).also { length = it } > 0) {
openedSession.write(buffer, 0, length)
openedSession.flush()
session.fsync(openedSession)
}
} finally {
try {
inputStream!!.close()
openedSession.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
val results = arrayOf<Intent?>(null)
val countDownLatch = CountDownLatch(1)
val intentSender: IntentSender =
IntentSenderUtils.newInstance(object : IIntentSenderAdaptor() {
override fun send(intent: Intent?) {
results[0] = intent
countDownLatch.countDown()
}
})
session.commit(intentSender)
countDownLatch.await()
res = results[0]!!.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE) == 0
} catch (_: Exception) {
res = false
} finally {
if (session != null) {
try {
session.close()
} catch (_: Exception) {
res = false
}
}
}
result.success(res)
}
private fun rootCheckPermission(result: Result) {
Shell.getShell(Shell.GetShellCallback(
fun(shell: Shell) {
result.success(shell.isRoot)
}
))
}
private fun rootInstallApk(apkFilePath: String, result: Result) {
Shell.sh("pm install -r -t " + apkFilePath).submit { out ->
val builder = StringBuilder()
for (data in out.getOut()) { builder.append(data) }
result.success(builder.toString().endsWith("Success"))
}
}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
HiddenApiBypass.addHiddenApiExemptions("")
}
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
nativeChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger, "native")
nativeChannel!!.setMethodCallHandler {
call, result ->
if (call.method == "getSystemFont") {
val res = DefaultSystemFont().get()
result.success(res)
} else if (call.method == "checkPermissionShizuku") {
shizukuCheckPermission(result)
} else if (call.method == "checkPermissionRoot") {
rootCheckPermission(result)
} else if (call.method == "installWithShizuku") {
val apkFileUri: String? = call.argument("apkFileUri")
shizukuInstallApk(apkFileUri!!, result)
} else if (call.method == "installWithRoot") {
val apkFilePath: String? = call.argument("apkFilePath")
rootInstallApk(apkFilePath!!, result)
}
}
}
override fun onDestroy() {
super.onDestroy()
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
}
}

View File

@ -1,37 +0,0 @@
package dev.imranr.obtainium.util;
import android.annotation.SuppressLint;
import android.app.Application;
import android.os.Build;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ApplicationUtils {
private static Application application;
public static Application getApplication() {
return application;
}
public static void setApplication(Application application) {
ApplicationUtils.application = application;
}
public static String getProcessName() {
if (Build.VERSION.SDK_INT >= 28)
return Application.getProcessName();
else {
try {
@SuppressLint("PrivateApi")
Class<?> activityThread = Class.forName("android.app.ActivityThread");
@SuppressLint("DiscouragedPrivateApi")
Method method = activityThread.getDeclaredMethod("currentProcessName");
return (String) method.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -1,23 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
public abstract class IIntentSenderAdaptor extends IIntentSender.Stub {
public abstract void send(Intent intent);
@Override
public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
send(intent);
return 0;
}
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
send(intent);
}
}

View File

@ -1,14 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.IIntentSender;
import android.content.IntentSender;
import java.lang.reflect.InvocationTargetException;
public class IntentSenderUtils {
public static IntentSender newInstance(IIntentSender binder) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//noinspection JavaReflectionMemberAccess
return IntentSender.class.getConstructor(IIntentSender.class).newInstance(binder);
}
}

View File

@ -1,41 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.Context;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Build;
import java.lang.reflect.InvocationTargetException;
@SuppressWarnings({"JavaReflectionMemberAccess"})
public class PackageInstallerUtils {
public static PackageInstaller createPackageInstaller(IPackageInstaller installer, String installerPackageName, String installerAttributionTag, int userId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, String.class, int.class)
.newInstance(installer, installerPackageName, installerAttributionTag, userId);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class)
.newInstance(installer, installerPackageName, userId);
} else {
return PackageInstaller.class.getConstructor(Context.class, PackageManager.class, IPackageInstaller.class, String.class, int.class)
.newInstance(ApplicationUtils.getApplication(), ApplicationUtils.getApplication().getPackageManager(), installer, installerPackageName, userId);
}
}
public static PackageInstaller.Session createSession(IPackageInstallerSession session) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return PackageInstaller.Session.class.getConstructor(IPackageInstallerSession.class)
.newInstance(session);
}
public static int getInstallFlags(PackageInstaller.SessionParams params) throws NoSuchFieldException, IllegalAccessException {
return (int) PackageInstaller.SessionParams.class.getDeclaredField("installFlags").get(params);
}
public static void setInstallFlags(PackageInstaller.SessionParams params, int newValue) throws NoSuchFieldException, IllegalAccessException {
PackageInstaller.SessionParams.class.getDeclaredField("installFlags").set(params, newValue);
}
}

View File

@ -1,68 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.Context;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.os.Build;
import android.os.IUserManager;
import android.os.RemoteException;
import java.util.List;
import rikka.shizuku.ShizukuBinderWrapper;
import rikka.shizuku.SystemServiceHelper;
public class ShizukuSystemServerApi {
private static final Singleton<IPackageManager> PACKAGE_MANAGER = new Singleton<IPackageManager>() {
@Override
protected IPackageManager create() {
return IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
}
};
private static final Singleton<IUserManager> USER_MANAGER = new Singleton<IUserManager>() {
@Override
protected IUserManager create() {
return IUserManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService(Context.USER_SERVICE)));
}
};
public static IPackageInstaller PackageManager_getPackageInstaller() throws RemoteException {
IPackageInstaller packageInstaller = PACKAGE_MANAGER.get().getPackageInstaller();
return IPackageInstaller.Stub.asInterface(new ShizukuBinderWrapper(packageInstaller.asBinder()));
}
public static List<UserInfo> UserManager_getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated) throws RemoteException {
if (Build.VERSION.SDK_INT >= 30) {
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
} else {
try {
return USER_MANAGER.get().getUsers(excludeDying);
} catch (NoSuchFieldError e) {
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
}
}
}
// method 2: use transactRemote directly
/*public static List<UserInfo> UserManager_getUsers(boolean excludeDying) {
Parcel data = SystemServiceHelper.obtainParcel(Context.USER_SERVICE, "android.os.IUserManager", "getUsers");
Parcel reply = Parcel.obtain();
data.writeInt(excludeDying ? 1 : 0);
List<UserInfo> res = null;
try {
ShizukuService.transactRemote(data, reply, 0);
reply.readException();
res = reply.createTypedArrayList(UserInfo.CREATOR);
} catch (RemoteException e) {
Log.e("ShizukuSample", "UserManager#getUsers", e);
} finally {
data.recycle();
reply.recycle();
}
return res;
}*/
}

View File

@ -1,17 +0,0 @@
package dev.imranr.obtainium.util;
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}

View File

@ -1,22 +1,3 @@
buildscript {
ext.kotlin_version = '1.8.10'
ext {
compileSdkVersion = 34 // or latest
targetSdkVersion = 34 // or latest
appCompatVersion = "1.4.2" // or latest
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.4.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "dev.rikka.tools.refine:gradle-plugin:4.3.1"
}
}
allprojects { allprojects {
repositories { repositories {
google() google()

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx2048M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@ -1,11 +1,25 @@
include ':app' 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
}()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties") includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
def properties = new Properties()
assert localPropertiesFile.exists() repositories {
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk") plugins {
assert flutterSdkPath != null, "flutter.sdk not set in local.properties" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" id "com.android.application" version "7.4.2" apply false
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
}
include ":app"

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(obavezno)", "requiredInBrackets": "(obavezno)",
"dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU", "dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU",
"colour": "Boja", "colour": "Boja",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Use Material You",
"githubStarredRepos": "GitHub repo-i sa zvjezdicom", "githubStarredRepos": "GitHub repo-i sa zvjezdicom",
"uname": "Korisničko ime", "uname": "Korisničko ime",
"wrongArgNum": "Naveden je pogrešan broj argumenata", "wrongArgNum": "Naveden je pogrešan broj argumenata",
@ -51,9 +54,8 @@
"percentProgress": "Napredak: {}%", "percentProgress": "Napredak: {}%",
"pleaseWait": "Molimo sačekajte", "pleaseWait": "Molimo sačekajte",
"updateAvailable": "Ažuriranje dostupno", "updateAvailable": "Ažuriranje dostupno",
"estimateInBracketsShort": "(Procjena)",
"notInstalled": "Nije instalirano", "notInstalled": "Nije instalirano",
"estimateInBrackets": "(Procjena)", "pseudoVersion": "pseudo-verzija",
"selectAll": "Označi sve", "selectAll": "Označi sve",
"deselectX": "Poništi odabir {}", "deselectX": "Poništi odabir {}",
"xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.", "xWillBeRemovedButRemainInstalled": "{} će biti uklonjen iz Obtainiuma, ali će ostati instaliran na uređaju.",
@ -73,13 +75,14 @@
"unpinFromTop": "Otkvači sa vrha", "unpinFromTop": "Otkvači sa vrha",
"resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?", "resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?",
"installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.", "installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.",
"customLinkMessage": "Ove veze rade na uređajima s instaliranim Obtainiumom",
"shareAppConfigLinks": "Podijelite konfiguraciju aplikacije kao HTML vezu",
"shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija", "shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija",
"resetInstallStatus": "Resetujte status instalacije", "resetInstallStatus": "Resetujte status instalacije",
"more": "Više", "more": "Više",
"removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija", "removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija",
"showOutdatedOnly": "Prikaži samo zastarjele aplikacije", "showOutdatedOnly": "Prikaži samo zastarjele aplikacije",
"filter": "Filtriranje", "filter": "Filtriranje",
"filterActive": "Filtriranje",
"filterApps": "Filtriraj aplikacije", "filterApps": "Filtriraj aplikacije",
"appName": "Naziv aplikacije", "appName": "Naziv aplikacije",
"author": "Autor", "author": "Autor",
@ -110,8 +113,6 @@
"dark": "Tamna", "dark": "Tamna",
"light": "Svijetla", "light": "Svijetla",
"followSystem": "Pratite sistem", "followSystem": "Pratite sistem",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Koristite čisto crnu tamnu temu", "useBlackTheme": "Koristite čisto crnu tamnu temu",
"appSortBy": "Aplikacije sortirane po", "appSortBy": "Aplikacije sortirane po",
"authorName": "Autor/Ime", "authorName": "Autor/Ime",
@ -145,8 +146,10 @@
"noNewUpdates": "Nema novih ažuriranja.", "noNewUpdates": "Nema novih ažuriranja.",
"xHasAnUpdate": "{} ima ažuriranje.", "xHasAnUpdate": "{} ima ažuriranje.",
"appsUpdated": "Aplikacije su ažurirane", "appsUpdated": "Aplikacije su ažurirane",
"appsNotUpdated": "Failed to update applications",
"appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija", "appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija",
"xWasUpdatedToY": "{} je ažuriran na {}.", "xWasUpdatedToY": "{} je ažuriran na {}.",
"xWasNotUpdatedToY": "Failed to update {} to {}.",
"errorCheckingUpdates": "Greška pri provjeri ažuriranja", "errorCheckingUpdates": "Greška pri provjeri ažuriranja",
"errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije", "errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije",
"appsRemoved": "Aplikacije su uklonjene", "appsRemoved": "Aplikacije su uklonjene",
@ -169,13 +172,12 @@
"lastUpdateCheckX": "Posljednja provjera ažuriranja: {}", "lastUpdateCheckX": "Posljednja provjera ažuriranja: {}",
"remove": "Izbriši", "remove": "Izbriši",
"yesMarkUpdated": "Da, označi kao ažurirano", "yesMarkUpdated": "Da, označi kao ažurirano",
"fdroid": "F-Droid Official", "fdroid": "Službeni F-Droid",
"appIdOrName": "ID ili ime aplikacije", "appIdOrName": "ID ili ime aplikacije",
"appId": "Apl ID", "appId": "Apl ID",
"appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom", "appWithIdOrNameNotFound": "Nije pronađena aplikacija s tim ID-om ili imenom",
"reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija", "reposHaveMultipleApps": "Repo-i mogu sadržavati više aplikacija",
"fdroidThirdPartyRepo": "F-Droid Repo treće strane", "fdroidThirdPartyRepo": "F-Droid Repo treće strane",
"steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Razgovor na Steamu (chat)", "steamChat": "Razgovor na Steamu (chat)",
"install": "Instaliraj", "install": "Instaliraj",
@ -186,6 +188,9 @@
"disableVersionDetection": "Onemogući detekciju verzije", "disableVersionDetection": "Onemogući detekciju verzije",
"noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.", "noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.",
"downloadingX": "Preuzimanje {}", "downloadingX": "Preuzimanje {}",
"downloadX": "Download {}",
"downloadedX": "Downloaded {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije", "downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije",
"noAPKFound": "APK nije pronađen", "noAPKFound": "APK nije pronađen",
"noVersionDetection": "Nema detekcije verzije", "noVersionDetection": "Nema detekcije verzije",
@ -211,6 +216,7 @@
"changes": "Promjene", "changes": "Promjene",
"releaseDate": "Datum izdavanja", "releaseDate": "Datum izdavanja",
"importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)", "importFromURLsInFile": "Uvoz iz URL-ova u datoteci (kao što je OPML)",
"versionDetectionExplanation": "Pomiri niz verzije sa verzijom otkrivenom iz OS-a",
"versionDetection": "Otkrivanje verzije", "versionDetection": "Otkrivanje verzije",
"standardVersionDetection": "Detekcija standardne verzije", "standardVersionDetection": "Detekcija standardne verzije",
"groupByCategory": "Grupiši po kategoriji", "groupByCategory": "Grupiši po kategoriji",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”", "dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”",
"dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a", "dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a",
"moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija", "moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija",
"gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)", "gitlabPATLabel": "GitLab token za lični pristup",
"about": "O nama", "about": "O nama",
"requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)", "requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)",
"checkOnStart": "Provjerite ima li novosti pri pokretanju", "checkOnStart": "Provjerite ima li novosti pri pokretanju",
@ -234,8 +240,7 @@
"addInfoBelow": "Dodajte ove informacije ispod.", "addInfoBelow": "Dodajte ove informacije ispod.",
"addInfoInSettings": "Dodajte ove informacije u Postavkama.", "addInfoInSettings": "Dodajte ove informacije u Postavkama.",
"githubSourceNote": "GitHub ograničavanje se može izbjeći korišćenjem tokena za lični pristup.", "githubSourceNote": "GitHub ograničavanje se može izbjeći korišćenjem tokena za lični pristup.",
"gitlabSourceNote": "GitLab APK preuzimanje možda neće raditi bez tokena za lični pristup.", "sortByLastLinkSegment": "Sortiraj samo po zadnjem segmentu veze",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu", "filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu",
"customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')", "customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')",
"appsPossiblyUpdated": "Pokušano ažuriranje aplikacija", "appsPossiblyUpdated": "Pokušano ažuriranje aplikacija",
@ -245,10 +250,10 @@
"backgroundUpdateReqsExplanation": "Ažuriranja u pozadini možda neće raditi za sve aplikacije.", "backgroundUpdateReqsExplanation": "Ažuriranja u pozadini možda neće raditi za sve aplikacije.",
"backgroundUpdateLimitsExplanation": "Uspjeh ažuriranja u pozadini se može provjeriti tek kada otvorite Obtainium.", "backgroundUpdateLimitsExplanation": "Uspjeh ažuriranja u pozadini se može provjeriti tek kada otvorite Obtainium.",
"verifyLatestTag": "Provjerite 'posljednu' ('latest') oznaku", "verifyLatestTag": "Provjerite 'posljednu' ('latest') oznaku",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Filter za 'srednju' vezu za posjetu",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filtriraj linkove prema tekstu linka",
"intermediateLinkNotFound": "Intermediate link nije nađen", "intermediateLinkNotFound": "Intermediate link nije nađen",
"intermediateLink": "Intermediate link", "intermediateLink": "srednja karika",
"exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)", "exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)",
"bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na WiFi-ju", "bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na WiFi-ju",
"autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a", "autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a",
@ -257,13 +262,13 @@
"highlightTouchTargets": "Istaknite manje vidljive touch mete", "highlightTouchTargets": "Istaknite manje vidljive touch mete",
"pickExportDir": "Izaberite datoteku za izvoz", "pickExportDir": "Izaberite datoteku za izvoz",
"autoExportOnChanges": "Automatski izvezite pri promjenama", "autoExportOnChanges": "Automatski izvezite pri promjenama",
"includeSettings": "Include settings", "includeSettings": "Uključi postavke",
"filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu", "filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu",
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a", "trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a", "dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
"reverseSort": "Obrni redosled", "reverseSort": "Obrni redosled",
"takeFirstLink": "Take first link", "takeFirstLink": "Uzmi prvi link",
"skipSort": "Skip sorting", "skipSort": "Preskoči sortiranje",
"debugMenu": "Meni za otkrivanje grešaka", "debugMenu": "Meni za otkrivanje grešaka",
"bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.", "bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.",
"runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad", "runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad",
@ -281,12 +286,29 @@
"onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'", "onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'",
"supportFixedAPKURL": "Podržite fiksne APK URL-ove", "supportFixedAPKURL": "Podržite fiksne APK URL-ove",
"selectX": "Izaberite {}", "selectX": "Izaberite {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Dozvoli paralelna preuzimanja",
"installMethod": "Installation method", "useShizuku": "Use Shizuku or Sui to install",
"normal": "Normal",
"shizuku": "Shizuku",
"root": "Root",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizuku is not running",
"shizukuOld": "Old Shizuku version (<11) - update it",
"shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB - update Android or use Sui instead",
"shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
"useSystemFont": "Koristite sistemski font",
"useVersionCodeAsOSVersion": "Koristite kod verzije aplikacije kao verziju koju je otkrio OS",
"requestHeader": "Zaglavlje zahtjeva",
"useLatestAssetDateAsReleaseDate": "Koristite najnovije otpremanje materijala kao datum izdavanja",
"defaultPseudoVersioningMethod": "Zadana metoda pseudo-verzija",
"partialAPKHash": "Djelomični APK Hash",
"APKLinkHash": "APK Link Hash",
"directAPKLink": "Direktna APK veza",
"pseudoVersionInUse": "Pseudo-verzija je u upotrebi",
"installed": "Instalirano",
"latest": "Najnoviji",
"invertRegEx": "Obrni regularni izraz",
"note": "Note",
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
"badDownload": "The APK could not be parsed (incompatible or partial download)",
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Želite li ukloniti aplikaciju?", "one": "Želite li ukloniti aplikaciju?",
"other": "Želite li ukloniti aplikacije?" "other": "Želite li ukloniti aplikacije?"
@ -335,8 +357,16 @@
"one": "{} i još 1 aplikacija je ažurirana.", "one": "{} i još 1 aplikacija je ažurirana.",
"other": "{} i još {} aplikacija je ažurirano." "other": "{} i još {} aplikacija je ažurirano."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.",
"other": "Failed to update {} and {} more apps."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} i još jedna aplikacija je vjerovatno ažurirana.", "one": "{} i još jedna aplikacija je vjerovatno ažurirana.",
"other": "{} i još {} aplikacija su vjerovatno ažurirane." "other": "{} i još {} aplikacija su vjerovatno ažurirane."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -19,15 +19,18 @@
"noDescription": "Žádný popis", "noDescription": "Žádný popis",
"cancel": "Zrušit", "cancel": "Zrušit",
"continue": "Pokračovat", "continue": "Pokračovat",
"requiredInBracets": "(Required)", "requiredInBrackets": "(Požadované)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST", "dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
"colour": "Barva", "colour": "Barva",
"githubStarredRepos": "GitHub Starred Repos", "standard": "Standardní",
"custom": "Vlastní",
"useMaterialYou": "Použijte materiál, který jste",
"githubStarredRepos": "GitHub označená hvězdičkou",
"uname": "Uživatelské jméno", "uname": "Uživatelské jméno",
"wrongArgNum": "Nesprávný počet zadaných argumentů", "wrongArgNum": "Nesprávný počet zadaných argumentů",
"xIsTrackOnly":"{} je určeno pouze pro sledování", "xIsTrackOnly": "{} je určeno pouze pro sledování",
"source": "Zdroj", "source": "Zdroj",
"app": "App", "app": "Aplikace",
"appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou Jen sledovány.", "appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou Jen sledovány.",
"youPickedTrackOnly": "Vybrali jste možnost Jen sledovat.", "youPickedTrackOnly": "Vybrali jste možnost Jen sledovat.",
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.", "trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
@ -51,9 +54,8 @@
"percentProgress": "Pokrok: {}%", "percentProgress": "Pokrok: {}%",
"pleaseWait": "Počkejte prosím", "pleaseWait": "Počkejte prosím",
"updateAvailable": "Aktualizace je k dispozici", "updateAvailable": "Aktualizace je k dispozici",
"estimateInBracketsShort": "(approx.)",
"notInstalled": "Není nainstalováno", "notInstalled": "Není nainstalováno",
"estimateInBrackets": "(přibližně)", "pseudoVersion": "pseudoverze",
"selectAll": "Vybrat vše", "selectAll": "Vybrat vše",
"deselectX": "{} deselected", "deselectX": "{} deselected",
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.", "xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
@ -73,30 +75,31 @@
"unpinFromTop": "Odepnout shora", "unpinFromTop": "Odepnout shora",
"resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?", "resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?",
"installStatusOfXWillBeResetExplanation": "Stav instalace vybraných aplikací bude resetován. To může být užitečné, pokud je verze aplikace zobrazená v Obtainium nesprávná z důvodu neúspěšných aktualizací nebo jiných problémů.", "installStatusOfXWillBeResetExplanation": "Stav instalace vybraných aplikací bude resetován. To může být užitečné, pokud je verze aplikace zobrazená v Obtainium nesprávná z důvodu neúspěšných aktualizací nebo jiných problémů.",
"customLinkMessage": "Tyto odkazy fungují na zařízeních s nainstalovaným Obtainium",
"shareAppConfigLinks": "Sdílet konfiguraci aplikace jako odkaz HTML",
"shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací", "shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací",
"resetInstallStatus": "Obnovit stav instalace", "resetInstallStatus": "Obnovit stav instalace",
"more": "Více", "more": "Více",
"removeOutdatedFilter": "Odstranit filtr Neaktuální", "removeOutdatedFilter": "Odstranit filtr Neaktuální",
"showOutdatedOnly": "Zobrazovat pouze zastaralé aplikace", "showOutdatedOnly": "Zobrazovat pouze zastaralé aplikace",
"filter": "Filtr", "filter": "Filtr",
"filterActive": "Filtr *",
"filterApps": "Filtrovat aplikace", "filterApps": "Filtrovat aplikace",
"appName": "Název aplikace", "appName": "Název aplikace",
"author": "Autor", "author": "Autor",
"upToDateApps": "Aktuální apky", "upToDateApps": "Aktuální apky",
"nonInstalledApps": "Neinstalované apky", "nonInstalledApps": "Neinstalované apky",
"importExport": "Import/Export", "importExport": "Import Export",
"settings": "Nastavení", "settings": "Nastavení",
"exportedTo": "Exportováno do {}", "exportedTo": "Exportováno do {}",
"obtainiumExport": "Obtainium Export", "obtainiumExport": "Export obtainia",
"invalidInput": "Neplatný vstup", "invalidInput": "Neplatný vstup",
"importedX": "Importováno {}", "importedX": "Importováno {}",
"obtainiumImport": "Obtainium Import", "obtainiumImport": "Dovoz obtainia",
"importFromURLList": "Import ze seznamu URL", "importFromURLList": "Import ze seznamu URL",
"searchQuery": "Vyhledávací dotaz", "searchQuery": "Vyhledávací dotaz",
"appURLList": "Seznam adres aplikací", "appURLList": "Seznam adres aplikací",
"line": "Linka", "line": "Linka",
"searchX": "Search {}", "searchX": "Vyhledávání {}",
"noResults": "Nebyly nalezeny žádné výsledky", "noResults": "Nebyly nalezeny žádné výsledky",
"importX": "Import {}", "importX": "Import {}",
"importedAppsIdDisclaimer": "Importované aplikace mohou být nesprávně zobrazeny jako \"Neinstalovány\". Chcete-li to opravit, nainstalujte je znovu prostřednictvím Obtainium. To nemá vliv na data aplikací. Ovlivňuje pouze metody importu URL a třetích stran.", "importedAppsIdDisclaimer": "Importované aplikace mohou být nesprávně zobrazeny jako \"Neinstalovány\". Chcete-li to opravit, nainstalujte je znovu prostřednictvím Obtainium. To nemá vliv na data aplikací. Ovlivňuje pouze metody importu URL a třetích stran.",
@ -110,8 +113,6 @@
"dark": "Tmavé", "dark": "Tmavé",
"light": "Světlé", "light": "Světlé",
"followSystem": "Jako systém", "followSystem": "Jako systém",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Použít čistě černé tmavé téma", "useBlackTheme": "Použít čistě černé tmavé téma",
"appSortBy": "Seřadit podle", "appSortBy": "Seřadit podle",
"authorName": "Autor/Jméno", "authorName": "Autor/Jméno",
@ -125,7 +126,7 @@
"appearance": "Vzhled", "appearance": "Vzhled",
"showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace", "showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace",
"pinUpdates": "Připnout aplikace s aktualizacemi nahoru", "pinUpdates": "Připnout aplikace s aktualizacemi nahoru",
"updates": "Updates", "updates": "Aktualizace",
"sourceSpecific": "Specifické pro zdroj", "sourceSpecific": "Specifické pro zdroj",
"appSource": "Zdroj aplikace", "appSource": "Zdroj aplikace",
"noLogs": "Žádné protokoly", "noLogs": "Žádné protokoly",
@ -145,8 +146,10 @@
"noNewUpdates": "Žádné nové aktualizace.", "noNewUpdates": "Žádné nové aktualizace.",
"xHasAnUpdate": "{} má aktualizaci.", "xHasAnUpdate": "{} má aktualizaci.",
"appsUpdated": "Aplikace aktualizovány", "appsUpdated": "Aplikace aktualizovány",
"appsNotUpdated": "Nepodařilo se aktualizovat aplikace",
"appsUpdatedNotifDescription": "Upozornit, že byly provedeny aktualizace jedné nebo více aplikací na pozadí", "appsUpdatedNotifDescription": "Upozornit, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
"xWasUpdatedToY": "{} byla aktualizována na {}", "xWasUpdatedToY": "{} byla aktualizována na {}",
"xWasNotUpdatedToY": "Nepodařilo se aktualizovat {} na {}.",
"errorCheckingUpdates": "Chyba kontroly aktualizací", "errorCheckingUpdates": "Chyba kontroly aktualizací",
"errorCheckingUpdatesNotifDescription": "Zobrazit oznámení při neúspěšné kontrole aktualizací na pozadí", "errorCheckingUpdatesNotifDescription": "Zobrazit oznámení při neúspěšné kontrole aktualizací na pozadí",
"appsRemoved": "Odstraněné aplikace", "appsRemoved": "Odstraněné aplikace",
@ -162,7 +165,7 @@
"errorWithHttpStatusCode": "Chyba {}", "errorWithHttpStatusCode": "Chyba {}",
"versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)", "versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)",
"unknown": "Neznám", "unknown": "Neznám",
"none": "None", "none": "Žádný",
"never": "Nikdy", "never": "Nikdy",
"latestVersionX": "Nejnovější verze: {}", "latestVersionX": "Nejnovější verze: {}",
"installedVersionX": "Nainstalovaná verze: {}", "installedVersionX": "Nainstalovaná verze: {}",
@ -171,14 +174,13 @@
"yesMarkUpdated": "Ano, označit jako aktualizované", "yesMarkUpdated": "Ano, označit jako aktualizované",
"fdroid": "Oficiální repozitář F-Droid", "fdroid": "Oficiální repozitář F-Droid",
"appIdOrName": "ID nebo název apky", "appIdOrName": "ID nebo název apky",
"appId": "App ID", "appId": "ID aplikace",
"appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena", "appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena",
"reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací", "reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací",
"fdroidThirdPartyRepo": "F-Droid repozitář třetí strany", "fdroidThirdPartyRepo": "F-Droid repozitář třetí strany",
"steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam Chat",
"install": "Install", "install": "Nainstalujte",
"markInstalled": "Označit jako nainstalovaný", "markInstalled": "Označit jako nainstalovaný",
"update": "Aktualizovat", "update": "Aktualizovat",
"markUpdated": "Označit jako aktuální", "markUpdated": "Označit jako aktuální",
@ -186,6 +188,9 @@
"disableVersionDetection": "Deaktivovat detekci verze", "disableVersionDetection": "Deaktivovat detekci verze",
"noVersionDetectionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně.", "noVersionDetectionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně.",
"downloadingX": "Stáhnout {}", "downloadingX": "Stáhnout {}",
"downloadX": "Stáhnout {}",
"downloadedX": "Staženo {}",
"releaseAsset": "Vydání aktiva",
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace", "downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
"noAPKFound": "Žádná APK nebyla nalezena", "noAPKFound": "Žádná APK nebyla nalezena",
"noVersionDetection": "Žádná detekce verze", "noVersionDetection": "Žádná detekce verze",
@ -211,6 +216,7 @@
"changes": "Změny", "changes": "Změny",
"releaseDate": "Datum vydání", "releaseDate": "Datum vydání",
"importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)", "importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)",
"versionDetectionExplanation": "Sloučit řetězec verze s verzí zjištěnou z OS",
"versionDetection": "Detekce verze", "versionDetection": "Detekce verze",
"standardVersionDetection": "Standardní detekce verze", "standardVersionDetection": "Standardní detekce verze",
"groupByCategory": "Seskupit podle kategorie", "groupByCategory": "Seskupit podle kategorie",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'", "dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'",
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK", "dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace", "moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)", "gitlabPATLabel": "Osobní přístupový token GitLab",
"about": "O", "about": "O",
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)", "requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
"checkOnStart": "Zkontrolovat jednou při spuštění", "checkOnStart": "Zkontrolovat jednou při spuštění",
@ -234,13 +240,12 @@
"addInfoBelow": "Přidat tuto informaci na konec stránky.", "addInfoBelow": "Přidat tuto informaci na konec stránky.",
"addInfoInSettings": "Přidat tuto informaci do nastavení.", "addInfoInSettings": "Přidat tuto informaci do nastavení.",
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.", "githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
"sortByLastLinkSegment": "Seřadit pouze podle poslední části odkazu", "sortByLastLinkSegment": "Seřadit pouze podle poslední části odkazu",
"filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu", "filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu",
"customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')", "customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')",
"appsPossiblyUpdated": "Byly provedeny pokusy o aktualizaci aplikací", "appsPossiblyUpdated": "Byly provedeny pokusy o aktualizaci aplikací",
"appsPossiblyUpdatedNotifDescription": "Upozorňuje uživatele, že na pozadí mohly být provedeny aktualizace jedné nebo více aplikací", "appsPossiblyUpdatedNotifDescription": "Upozorňuje uživatele, že na pozadí mohly být provedeny aktualizace jedné nebo více aplikací",
"xWasPossiblyUpdatedToY":"{} mohlo být aktualizováno na {}.", "xWasPossiblyUpdatedToY": "{} mohlo být aktualizováno na {}.",
"enableBackgroundUpdates": "Povolit aktualizace na pozadí", "enableBackgroundUpdates": "Povolit aktualizace na pozadí",
"backgroundUpdateReqsExplanation": "Aktualizace na pozadí nemusí být možná pro všechny aplikace.", "backgroundUpdateReqsExplanation": "Aktualizace na pozadí nemusí být možná pro všechny aplikace.",
"backgroundUpdateLimitsExplanation": "Úspěšnost instalace na pozadí lze určit pouze v případě, že je otevřeno Obtainium.", "backgroundUpdateLimitsExplanation": "Úspěšnost instalace na pozadí lze určit pouze v případě, že je otevřeno Obtainium.",
@ -282,11 +287,28 @@
"supportFixedAPKURL": "Odhadnout novější verzi na základě prvních třiceti číslic kontrolního součtu adresy URL APK, pokud není podporována jinak", "supportFixedAPKURL": "Odhadnout novější verzi na základě prvních třiceti číslic kontrolního součtu adresy URL APK, pokud není podporována jinak",
"selectX": "Vybrat {}", "selectX": "Vybrat {}",
"parallelDownloads": "Povolit souběžné stahování", "parallelDownloads": "Povolit souběžné stahování",
"installMethod": "Metoda instalace", "useShizuku": "K instalaci použijte Shizuku nebo Sui",
"normal": "Normální",
"shizuku": "Shizuku",
"root": "Správce",
"shizukuBinderNotFound": "Shizuku neběží", "shizukuBinderNotFound": "Shizuku neběží",
"shizukuOld": "Stará verze Shizuku (<11) - aktualizujte ji",
"shizukuOldAndroidWithADB": "Shizuku běží na Androidu < 8.1 s ADB - aktualizujte Android nebo místo toho použijte Sui",
"shizukuPretendToBeGooglePlay": "Nastavení Google Play jako zdroje instalace (pokud se používá Shizuku)",
"useSystemFont": "Použít systémové písmo",
"useVersionCodeAsOSVersion": "Použít kód verze aplikace jako verzi zjištěnou OS",
"requestHeader": "Hlavička požadavku",
"useLatestAssetDateAsReleaseDate": "Použít poslední nahrané dílo jako datum vydání",
"defaultPseudoVersioningMethod": "Výchozí metoda pseudoverze",
"partialAPKHash": "Částečný hash APK",
"APKLinkHash": "Odkaz APK Hash",
"directAPKLink": "Přímý odkaz APK",
"pseudoVersionInUse": "Pseudoverze se používá",
"installed": "Instalováno",
"latest": "Nejnovější",
"invertRegEx": "Invertovat regulární výraz",
"note": "Poznámka",
"selfHostedNote": "Rozbalovací seznam \"{}\" lze použít k dosažení vlastních/obvyklých instancí libovolného zdroje.",
"badDownload": "APK nelze analyzovat (nekompatibilní nebo částečné stažení)",
"beforeNewInstallsShareToAppVerifier": "Sdílení nových aplikací s aplikací AppVerifier (pokud je k dispozici)",
"appVerifierInstructionToast": "Sdílejte do aplikace AppVerifier a po dokončení se sem vraťte.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Odstranit Apku?", "one": "Odstranit Apku?",
"other": "Odstranit Apky?" "other": "Odstranit Apky?"
@ -335,8 +357,16 @@
"one": "{} a 1 další aplikace mají aktualizace.", "one": "{} a 1 další aplikace mají aktualizace.",
"other": "{} a {} další aplikace byly aktualizovány." "other": "{} a {} další aplikace byly aktualizovány."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Nepodařilo se aktualizovat {} a 1 další aplikaci.",
"other": "Nepodařilo se aktualizovat {} a {} další aplikace."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} a 1 další aplikace možno aktualizovat", "one": "{} a 1 další aplikace možno aktualizovat",
"other": "{} a {} další aplikace mohou být aktualizovány." "other": "{} a {} další aplikace mohou být aktualizovány."
},
"apk": {
"one": "{} APK",
"other": "{} APK"
} }
} }

View File

@ -9,7 +9,7 @@
"placeholder": "Platzhalter", "placeholder": "Platzhalter",
"someErrors": "Es traten einige Fehler auf", "someErrors": "Es traten einige Fehler auf",
"unexpectedError": "Unerwarteter Fehler", "unexpectedError": "Unerwarteter Fehler",
"ok": "Okay", "ok": "OK",
"and": "und", "and": "und",
"githubPATLabel": "GitHub Personal Access Token (Erhöht das Ratenlimit)", "githubPATLabel": "GitHub Personal Access Token (Erhöht das Ratenlimit)",
"includePrereleases": "Vorabversionen einbeziehen", "includePrereleases": "Vorabversionen einbeziehen",
@ -19,9 +19,12 @@
"noDescription": "Keine Beschreibung", "noDescription": "Keine Beschreibung",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"continue": "Weiter", "continue": "Weiter",
"requiredInBrackets": "(Benötigt)", "requiredInBrackets": "(wird benötigt)",
"dropdownNoOptsError": "FEHLER: DROPDOWN MUSS MINDESTENS EINE OPTION HABEN", "dropdownNoOptsError": "FEHLER: DROPDOWN MUSS MINDESTENS EINE OPTION HABEN",
"colour": "Farbe", "colour": "Farbe",
"standard": "Standard",
"custom": "Benutzerdefiniert",
"useMaterialYou": "Verwende Material You",
"githubStarredRepos": "GitHub Starred Repos", "githubStarredRepos": "GitHub Starred Repos",
"uname": "Benutzername", "uname": "Benutzername",
"wrongArgNum": "Falsche Anzahl von Argumenten (Parametern) übermittelt", "wrongArgNum": "Falsche Anzahl von Argumenten (Parametern) übermittelt",
@ -30,7 +33,7 @@
"app": "App", "app": "App",
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zum Nachverfolgen.", "appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zum Nachverfolgen.",
"youPickedTrackOnly": "Sie haben die Option „Nur Nachverfolgen“ gewählt.", "youPickedTrackOnly": "Sie haben die Option „Nur Nachverfolgen“ gewählt.",
"trackOnlyAppDescription": "Die App wird auf Updates überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.", "trackOnlyAppDescription": "Die App wird auf Aktualisierungen überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
"cancelled": "Abgebrochen", "cancelled": "Abgebrochen",
"appAlreadyAdded": "App bereits hinzugefügt", "appAlreadyAdded": "App bereits hinzugefügt",
"alreadyUpToDateQuestion": "App bereits auf dem neuesten Stand?", "alreadyUpToDateQuestion": "App bereits auf dem neuesten Stand?",
@ -51,9 +54,8 @@
"percentProgress": "Fortschritt: {}%", "percentProgress": "Fortschritt: {}%",
"pleaseWait": "Bitte warten", "pleaseWait": "Bitte warten",
"updateAvailable": "Aktualisierung verfügbar", "updateAvailable": "Aktualisierung verfügbar",
"estimateInBracketsShort": "(ca.)",
"notInstalled": "Nicht installiert", "notInstalled": "Nicht installiert",
"estimateInBrackets": "(Ungefähr)", "pseudoVersion": "Pseudoversion",
"selectAll": "Alle auswählen", "selectAll": "Alle auswählen",
"deselectX": "{} abgewählt", "deselectX": "{} abgewählt",
"xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.", "xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.",
@ -73,13 +75,14 @@
"unpinFromTop": "„Oben anheften“ aufheben", "unpinFromTop": "„Oben anheften“ aufheben",
"resetInstallStatusForSelectedAppsQuestion": "Installationsstatus für ausgewählte Apps zurücksetzen?", "resetInstallStatusForSelectedAppsQuestion": "Installationsstatus für ausgewählte Apps zurücksetzen?",
"installStatusOfXWillBeResetExplanation": "Der Installationsstatus der ausgewählten Apps wird zurückgesetzt. Dies kann hilfreich sein, wenn die in Obtainium angezeigte App-Version aufgrund fehlgeschlagener Aktualisierungen oder anderer Probleme falsch ist.", "installStatusOfXWillBeResetExplanation": "Der Installationsstatus der ausgewählten Apps wird zurückgesetzt. Dies kann hilfreich sein, wenn die in Obtainium angezeigte App-Version aufgrund fehlgeschlagener Aktualisierungen oder anderer Probleme falsch ist.",
"customLinkMessage": "Diese Links funktionieren auf Geräten, wo Obtainium installiert ist",
"shareAppConfigLinks": "Teile die Appkonfiguration als HTML-Link",
"shareSelectedAppURLs": "Ausgewählte App-URLs teilen", "shareSelectedAppURLs": "Ausgewählte App-URLs teilen",
"resetInstallStatus": "Installationsstatus zurücksetzen", "resetInstallStatus": "Installationsstatus zurücksetzen",
"more": "Mehr", "more": "Mehr",
"removeOutdatedFilter": "App-Filter Nicht aktuell entfernen", "removeOutdatedFilter": "App-Filter Nicht aktuell entfernen",
"showOutdatedOnly": "Nur nicht aktuelle Apps anzeigen", "showOutdatedOnly": "Nur nicht aktuelle Apps anzeigen",
"filter": "Filter", "filter": "Filter",
"filterActive": "Filter *",
"filterApps": "Apps filtern", "filterApps": "Apps filtern",
"appName": "App Name", "appName": "App Name",
"author": "Autor", "author": "Autor",
@ -88,17 +91,17 @@
"importExport": "Import/Export", "importExport": "Import/Export",
"settings": "Einstellungen", "settings": "Einstellungen",
"exportedTo": "Exportiert zu {}", "exportedTo": "Exportiert zu {}",
"obtainiumExport": "Obtainium Export", "obtainiumExport": "Obtainium-Export",
"invalidInput": "Ungültige Eingabe", "invalidInput": "Ungültige Eingabe",
"importedX": "Importiert {}", "importedX": "Importiert {}",
"obtainiumImport": "Obtainium Import", "obtainiumImport": "Obtainium-Import",
"importFromURLList": "Importieren aus URL-Liste", "importFromURLList": "Importieren aus URL-Liste",
"searchQuery": "Suchanfrage", "searchQuery": "Suchanfrage",
"appURLList": "App URL-Liste", "appURLList": "App URL-Liste",
"line": "Linie", "line": "Linie",
"searchX": "Suche {}", "searchX": "Suche {}",
"noResults": "Keine Ergebnisse gefunden", "noResults": "Keine Ergebnisse gefunden",
"importX": "Import {}", "importX": "Importieren {}",
"importedAppsIdDisclaimer": "Importierte Apps werden möglicherweise 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.", "importedAppsIdDisclaimer": "Importierte Apps werden möglicherweise 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", "importErrors": "Importfehler",
"importedXOfYApps": "{} von {} Apps importiert.", "importedXOfYApps": "{} von {} Apps importiert.",
@ -110,8 +113,6 @@
"dark": "Dunkel", "dark": "Dunkel",
"light": "Hell", "light": "Hell",
"followSystem": "System folgen", "followSystem": "System folgen",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Verwende Pure Black Dark Theme", "useBlackTheme": "Verwende Pure Black Dark Theme",
"appSortBy": "App sortieren nach", "appSortBy": "App sortieren nach",
"authorName": "Autor/Name", "authorName": "Autor/Name",
@ -133,7 +134,7 @@
"close": "Schließen", "close": "Schließen",
"share": "Teilen", "share": "Teilen",
"appNotFound": "App nicht gefunden", "appNotFound": "App nicht gefunden",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "Obtainium-Export",
"pickAnAPK": "APK auswählen", "pickAnAPK": "APK auswählen",
"appHasMoreThanOnePackage": "{} verfügt über mehr als ein Paket:", "appHasMoreThanOnePackage": "{} verfügt über mehr als ein Paket:",
"deviceSupportsXArch": "Ihr Gerät unterstützt die CPU-Architektur {}.", "deviceSupportsXArch": "Ihr Gerät unterstützt die CPU-Architektur {}.",
@ -145,8 +146,10 @@
"noNewUpdates": "Keine neuen Aktualisierungen.", "noNewUpdates": "Keine neuen Aktualisierungen.",
"xHasAnUpdate": "{} hat eine Aktualisierung.", "xHasAnUpdate": "{} hat eine Aktualisierung.",
"appsUpdated": "Apps aktualisiert", "appsUpdated": "Apps aktualisiert",
"appsNotUpdated": "Aktualisierung der Anwendungen fehlgeschlagen",
"appsUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps im Hintergrund durchgeführt wurden", "appsUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps im Hintergrund durchgeführt wurden",
"xWasUpdatedToY": "{} wurde auf {} aktualisiert.", "xWasUpdatedToY": "{} wurde auf {} aktualisiert.",
"xWasNotUpdatedToY": "Die Aktualisierung von {} auf {} ist fehlgeschlagen.",
"errorCheckingUpdates": "Fehler beim Prüfen auf Aktualisierungen", "errorCheckingUpdates": "Fehler beim Prüfen auf Aktualisierungen",
"errorCheckingUpdatesNotifDescription": "Eine Benachrichtigung, die angezeigt wird, wenn die Prüfung der Hintergrundaktualisierung fehlschlägt", "errorCheckingUpdatesNotifDescription": "Eine Benachrichtigung, die angezeigt wird, wenn die Prüfung der Hintergrundaktualisierung fehlschlägt",
"appsRemoved": "Apps entfernt", "appsRemoved": "Apps entfernt",
@ -175,9 +178,8 @@
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden", "appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten", "reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
"fdroidThirdPartyRepo": "F-Droid Drittparteienrepo", "fdroidThirdPartyRepo": "F-Droid Drittparteienrepo",
"steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam-Chat",
"install": "Installieren", "install": "Installieren",
"markInstalled": "Als Installiert markieren", "markInstalled": "Als Installiert markieren",
"update": "Aktualisieren", "update": "Aktualisieren",
@ -186,6 +188,9 @@
"disableVersionDetection": "Versionsermittlung deaktivieren", "disableVersionDetection": "Versionsermittlung deaktivieren",
"noVersionDetectionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert.", "noVersionDetectionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert.",
"downloadingX": "Lade {} herunter", "downloadingX": "Lade {} herunter",
"downloadX": "{} herunterladen",
"downloadedX": "{} heruntergeladen",
"releaseAsset": "release Asset",
"downloadNotifDescription": "Benachrichtigt den Nutzer über den Fortschritt beim Herunterladen einer App", "downloadNotifDescription": "Benachrichtigt den Nutzer über den Fortschritt beim Herunterladen einer App",
"noAPKFound": "Keine APK gefunden", "noAPKFound": "Keine APK gefunden",
"noVersionDetection": "Keine Versionserkennung", "noVersionDetection": "Keine Versionserkennung",
@ -211,6 +216,7 @@
"changes": "Änderungen", "changes": "Änderungen",
"releaseDate": "Veröffentlichungsdatum", "releaseDate": "Veröffentlichungsdatum",
"importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)", "importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)",
"versionDetectionExplanation": "Abgleich der Versionsnummer mit der vom Betriebssystem erkannten Version",
"versionDetection": "Versionserkennung", "versionDetection": "Versionserkennung",
"standardVersionDetection": "Standardversionserkennung", "standardVersionDetection": "Standardversionserkennung",
"groupByCategory": "Nach Kategorie gruppieren", "groupByCategory": "Nach Kategorie gruppieren",
@ -220,26 +226,25 @@
"dontShowTrackOnlyWarnings": "Warnung für 'Nur Nachverfolgen' nicht anzeigen", "dontShowTrackOnlyWarnings": "Warnung für 'Nur Nachverfolgen' nicht anzeigen",
"dontShowAPKOriginWarnings": "Warnung für APK-Herkunft nicht anzeigen", "dontShowAPKOriginWarnings": "Warnung für APK-Herkunft nicht anzeigen",
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben", "moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)", "gitlabPATLabel": "GitLab Personal Access Token",
"about": "Über", "about": "Über",
"requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)", "requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
"checkOnStart": "Überprüfe einmalig beim Start", "checkOnStart": "Überprüfe einmalig beim Start",
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln", "tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps", "removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
"pickHighestVersionCode": "Automatische Auswahl des APK mit höchstem Versionscode", "pickHighestVersionCode": "Automatische Auswahl des APK mit höchstem Versionscode",
"checkUpdateOnDetailPage": "Nach Updates suchen, wenn eine App-Detailseite geöffnet wird", "checkUpdateOnDetailPage": "Nach Aktualisierungen suchen, wenn eine App-Detailseite geöffnet wird",
"disablePageTransitions": "Animationen für Seitenübergänge deaktivieren", "disablePageTransitions": "Animationen für Seitenübergänge deaktivieren",
"reversePageTransitions": "Umgekehrte Animationen für Seitenübergänge", "reversePageTransitions": "Umgekehrte Animationen für Seitenübergänge",
"minStarCount": "Minimale Anzahl von Sternen", "minStarCount": "Minimale Anzahl von Sternen",
"addInfoBelow": "Fügen Sie diese Informationen unten hinzu.", "addInfoBelow": "Fügen Sie diese Informationen unten hinzu.",
"addInfoInSettings": "Fügen Sie diese Info in den Einstellungen hinzu.", "addInfoInSettings": "Fügen Sie diese Info in den Einstellungen hinzu.",
"githubSourceNote": "Die GitHub-Ratenbegrenzung kann mit einem API-Schlüssel umgangen werden.", "githubSourceNote": "Die GitHub-Ratenbegrenzung kann mit einem API-Schlüssel umgangen werden.",
"gitlabSourceNote": "GitLab APK-Extraktion funktioniert möglicherweise nicht ohne API-Schlüssel",
"sortByLastLinkSegment": "Sortiere nur nach dem letzten Teil des Links", "sortByLastLinkSegment": "Sortiere nur nach dem letzten Teil des Links",
"filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern", "filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern",
"customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')", "customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')",
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht", "appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden", "appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.", "xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen", "enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.", "backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
@ -269,7 +274,7 @@
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen", "runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
"versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden", "versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden",
"installing": "Installiere", "installing": "Installiere",
"skipUpdateNotifications": "Keine Benachrichtigung zu App-Updates geben", "skipUpdateNotifications": "Keine Benachrichtigung zu App-Aktualisierungen geben",
"updatesAvailableNotifChannel": "Aktualisierungen verfügbar", "updatesAvailableNotifChannel": "Aktualisierungen verfügbar",
"appsUpdatedNotifChannel": "Apps aktualisiert", "appsUpdatedNotifChannel": "Apps aktualisiert",
"appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht", "appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht",
@ -282,18 +287,35 @@
"supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt", "supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
"selectX": "Wähle {}", "selectX": "Wähle {}",
"parallelDownloads": "Erlaube parallele Downloads", "parallelDownloads": "Erlaube parallele Downloads",
"installMethod": "Installationsmethode", "useShizuku": "Verwenden Sie Shizuku oder Sui zur Installation",
"normal": "Normal", "shizukuBinderNotFound": "Kompatibler Shizukudienst wurde nicht gefunden",
"shizuku": "Shizuku", "shizukuOld": "Alte Shizuku-Version (<11) - aktualisieren Sie sie",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku läuft auf Android < 8.1 mit ADB - aktualisieren Sie Android oder verwenden Sie stattdessen Sui",
"shizukuBinderNotFound": "Shizuku läuft nicht", "shizukuPretendToBeGooglePlay": "Google Play als Installationsquelle festlegen (wenn Shizuku verwendet wird)",
"useSystemFont": "Verwende die Systemschriftart",
"useVersionCodeAsOSVersion": "Verwende die Appversion als erkannte Version vom Betriebssystem",
"requestHeader": "Request Header",
"useLatestAssetDateAsReleaseDate": "Den letzten Asset-Upload als Veröffentlichungsdatum verwenden",
"defaultPseudoVersioningMethod": "Standardmäßiges Verfahren zur Pseudo-Versionierung",
"partialAPKHash": "partieller APK-Hash",
"APKLinkHash": "APK-Link-Hash",
"directAPKLink": "Direkter APK-Link",
"pseudoVersionInUse": "Es werden Pseudoversionen verwendet",
"installed": "Installiert",
"latest": "Neueste Version",
"invertRegEx": "Regulären Ausdruck invertieren",
"note": "Hinweis",
"selfHostedNote": "Das „{}“-Dropdown-Menü kann verwendet werden, um selbst gehostete/angepasste Instanzen einer beliebigen Quelle zu erreichen.",
"badDownload": "Die APK konnte nicht geparst werden (inkompatibler oder teilweiser Download)",
"beforeNewInstallsShareToAppVerifier": "Neue Apps mit AppVerifier teilen (falls verfügbar)",
"appVerifierInstructionToast": "Geben Sie die Daten an AppVerifier weiter und kehren Sie dann hierher zurück, wenn Sie fertig sind.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "App entfernen?", "one": "App entfernen?",
"other": "Apps entfernen?" "other": "Apps entfernen?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Zu viele Anfragen (Rate begrenzt) versuchen Sie es in {} Minute erneut", "one": "Zu viele Anfragen (Rate begrenzt) versuche es in {} Minute erneut",
"other": "Zu viele Anfragen (Rate begrenzt) versuchen Sie es in {} Minuten erneut" "other": "Zu viele Anfragen (Rate begrenzt) versuche es in {} Minuten erneut"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant", "one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
@ -304,12 +326,12 @@
"other": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierungen benachrichtigt den Benutzer, falls erforderlich" "other": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierungen benachrichtigt den Benutzer, falls erforderlich"
}, },
"apps": { "apps": {
"one": "{} App", "eine": "{} App",
"other": "{} Apps" "andere": "{} Apps"
}, },
"url": { "url": {
"one": "{} URL", "eine": "{} URL",
"other": "{} URLs" "andere": "{} URLs"
}, },
"minute": { "minute": {
"one": "{} Minute", "one": "{} Minute",
@ -335,8 +357,16 @@
"one": "{} und 1 weitere Anwendung wurden aktualisiert.", "one": "{} und 1 weitere Anwendung wurden aktualisiert.",
"other": "{} und {} weitere Anwendungen wurden aktualisiert." "other": "{} und {} weitere Anwendungen wurden aktualisiert."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Aktualisierung fehlgeschlagen {} und 1 weitere Anwendung.",
"other": "Die Aktualisierung von {} und {} weiteren Anwendungen ist fehlgeschlagen."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.", "one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert." "other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(Required)", "requiredInBrackets": "(Required)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT", "dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT",
"colour": "Colour", "colour": "Colour",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Use Material You",
"githubStarredRepos": "GitHub Starred Repos", "githubStarredRepos": "GitHub Starred Repos",
"uname": "Username", "uname": "Username",
"wrongArgNum": "Wrong number of arguments provided", "wrongArgNum": "Wrong number of arguments provided",
@ -51,9 +54,8 @@
"percentProgress": "Progress: {}%", "percentProgress": "Progress: {}%",
"pleaseWait": "Please Wait", "pleaseWait": "Please Wait",
"updateAvailable": "Update Available", "updateAvailable": "Update Available",
"estimateInBracketsShort": "(Est.)",
"notInstalled": "Not Installed", "notInstalled": "Not Installed",
"estimateInBrackets": "(Estimate)", "pseudoVersion": "pseudo-version",
"selectAll": "Select All", "selectAll": "Select All",
"deselectX": "Deselect {}", "deselectX": "Deselect {}",
"xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.", "xWillBeRemovedButRemainInstalled": "{} will be removed from Obtainium but remain installed on device.",
@ -73,13 +75,14 @@
"unpinFromTop": "Unpin from top", "unpinFromTop": "Unpin from top",
"resetInstallStatusForSelectedAppsQuestion": "Reset Install Status for Selected Apps?", "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.", "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", "shareSelectedAppURLs": "Share Selected App URLs",
"resetInstallStatus": "Reset Install Status", "resetInstallStatus": "Reset Install Status",
"more": "More", "more": "More",
"removeOutdatedFilter": "Remove Out-of-Date App Filter", "removeOutdatedFilter": "Remove Out-of-Date App Filter",
"showOutdatedOnly": "Show Out-of-Date Apps Only", "showOutdatedOnly": "Show Out-of-Date Apps Only",
"filter": "Filter", "filter": "Filter",
"filterActive": "Filter *",
"filterApps": "Filter Apps", "filterApps": "Filter Apps",
"appName": "App Name", "appName": "App Name",
"author": "Author", "author": "Author",
@ -110,8 +113,7 @@
"dark": "Dark", "dark": "Dark",
"light": "Light", "light": "Light",
"followSystem": "Follow System", "followSystem": "Follow System",
"obtainium": "Obtainium", "followSystemThemeExplanation": "Following system theme is possible only by using third-party applications",
"materialYou": "Material You",
"useBlackTheme": "Use pure black dark theme", "useBlackTheme": "Use pure black dark theme",
"appSortBy": "App Sort By", "appSortBy": "App Sort By",
"authorName": "Author/Name", "authorName": "Author/Name",
@ -145,8 +147,10 @@
"noNewUpdates": "No new updates.", "noNewUpdates": "No new updates.",
"xHasAnUpdate": "{} has an update.", "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 {}.", "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", "errorCheckingUpdatesNotifDescription": "A notification that shows when background update checking fails",
"appsRemoved": "Apps Removed", "appsRemoved": "Apps Removed",
@ -164,8 +168,8 @@
"unknown": "Unknown", "unknown": "Unknown",
"none": "None", "none": "None",
"never": "Never", "never": "Never",
"latestVersionX": "Latest Version: {}", "latestVersionX": "Latest: {}",
"installedVersionX": "Installed Version: {}", "installedVersionX": "Installed: {}",
"lastUpdateCheckX": "Last Update Check: {}", "lastUpdateCheckX": "Last Update Check: {}",
"remove": "Remove", "remove": "Remove",
"yesMarkUpdated": "Yes, Mark as Updated", "yesMarkUpdated": "Yes, Mark as Updated",
@ -175,7 +179,6 @@
"appWithIdOrNameNotFound": "No App was found with that ID or Name", "appWithIdOrNameNotFound": "No App was found with that ID or Name",
"reposHaveMultipleApps": "Repos may contain multiple Apps", "reposHaveMultipleApps": "Repos may contain multiple Apps",
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo", "fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
"steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam Chat",
"install": "Install", "install": "Install",
@ -186,6 +189,9 @@
"disableVersionDetection": "Disable Version Detection", "disableVersionDetection": "Disable Version Detection",
"noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.",
"downloadingX": "Downloading {}", "downloadingX": "Downloading {}",
"downloadX": "Download {}",
"downloadedX": "Downloaded {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "Notifies the user of the progress in downloading an App", "downloadNotifDescription": "Notifies the user of the progress in downloading an App",
"noAPKFound": "No APK found", "noAPKFound": "No APK found",
"noVersionDetection": "No version detection", "noVersionDetection": "No version detection",
@ -206,11 +212,12 @@
"removeFromObtainium": "Remove from Obtainium", "removeFromObtainium": "Remove from Obtainium",
"uninstallFromDevice": "Uninstall from Device", "uninstallFromDevice": "Uninstall from Device",
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
"releaseDateAsVersion": "Use Release Date as Version", "releaseDateAsVersion": "Use release date 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", "changes": "Changes",
"releaseDate": "Release Date", "releaseDate": "Release Date",
"importFromURLsInFile": "Import from URLs in File (like OPML)", "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", "standardVersionDetection": "Standard version detection",
"groupByCategory": "Group by Category", "groupByCategory": "Group by Category",
@ -220,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Don't show 'Track-Only' warnings", "dontShowTrackOnlyWarnings": "Don't show 'Track-Only' warnings",
"dontShowAPKOriginWarnings": "Don't show APK origin warnings", "dontShowAPKOriginWarnings": "Don't show APK origin warnings",
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "gitlabPATLabel": "GitLab Personal Access Token",
"about": "About", "about": "About",
"requiresCredentialsInSettings": "{} needs additional credentials (in Settings)", "requiresCredentialsInSettings": "{} needs additional credentials (in Settings)",
"checkOnStart": "Check for updates on startup", "checkOnStart": "Check for updates on startup",
@ -234,7 +241,6 @@
"addInfoBelow": "Add this info below.", "addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.", "addInfoInSettings": "Add this info in the Settings.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.", "githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression", "filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')", "customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
@ -252,8 +258,8 @@
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)", "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version Extraction RegEx", "versionExtractionRegEx": "Version String Extraction RegEx",
"matchGroupToUse": "Match Group to Use for Version Extraction Regex", "matchGroupToUse": "Match Group to Use for Version String Extraction RegEx",
"highlightTouchTargets": "Highlight less obvious touch targets", "highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory", "pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes", "autoExportOnChanges": "Auto-export on changes",
@ -267,7 +273,7 @@
"debugMenu": "Debug Menu", "debugMenu": "Debug Menu",
"bgTaskStarted": "Background task started - check logs.", "bgTaskStarted": "Background task started - check logs.",
"runBgCheckNow": "Run Background Update Check Now", "runBgCheckNow": "Run Background Update Check Now",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page", "versionExtractWholePage": "Apply version string extraction Regex to entire page",
"installing": "Installing", "installing": "Installing",
"skipUpdateNotifications": "Skip update notifications", "skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Updates Available", "updatesAvailableNotifChannel": "Updates Available",
@ -282,13 +288,28 @@
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}", "selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Allow parallel downloads",
"installMethod": "Installation method", "useShizuku": "Use Shizuku or Sui to install",
"normal": "Normal", "shizukuBinderNotFound": "Shizuku service not running",
"shizuku": "Shizuku", "shizukuOld": "Old Shizuku version (<11) - update it",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB - update Android or use Sui instead",
"shizukuBinderNotFound": "Сompatible Shizuku service wasn't found", "shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
"useSystemFont": "Use the system font", "useSystemFont": "Use the system font",
"systemFontError": "Error loading the system font: {}", "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",
"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)",
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remove App?", "one": "Remove App?",
"other": "Remove Apps?" "other": "Remove Apps?"
@ -337,8 +358,16 @@
"one": "{} and 1 more app was updated.", "one": "{} and 1 more app was updated.",
"other": "{} and {} more apps were updated." "other": "{} and {} more apps were updated."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.",
"other": "Failed to update {} and {} more apps."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.", "one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated." "other": "{} and {} more apps may have been updated."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -1,20 +1,20 @@
{ {
"invalidURLForSource": "URL de la aplicación {} no válida", "invalidURLForSource": "El URL de la aplicación {} no es válido",
"noReleaseFound": "No se ha podido encontrar una versión válida", "noReleaseFound": "No se ha encontrado una versión válida",
"noVersionFound": "No se ha podido determinar la versión de la publicación", "noVersionFound": "No se ha podido determinar la versión",
"urlMatchesNoSource": "La URL no coincide con ninguna fuente conocida", "urlMatchesNoSource": "El URL no coincide con ninguna fuente conocida",
"cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación", "cantInstallOlderVersion": "No se puede instalar una versión previa de la aplicación",
"appIdMismatch": "La ID del paquete descargado no coincide con la ID de la aplicación instalada", "appIdMismatch": "El ID del paquete descargado no coincide con el ID de la aplicación instalada",
"functionNotImplemented": "Esta clase no ha implementado esta función", "functionNotImplemented": "Esta clase no ha implementado esta función",
"placeholder": "Espacio reservado", "placeholder": "Espacio reservado",
"someErrors": "Han ocurrido algunos errores", "someErrors": "Han ocurrido algunos errores",
"unexpectedError": "Error Inesperado", "unexpectedError": "Error inesperado",
"ok": "OK", "ok": "Aceptar",
"and": "y", "and": "y",
"githubPATLabel": "Token Github de Acceso Personal\n(Reduce tiempos de espera)", "githubPATLabel": "Token de acceso personal a GitHub\n(reduce tiempos de espera)",
"includePrereleases": "Incluir versiones preliminares", "includePrereleases": "Incluir versiones preliminares",
"fallbackToOlderReleases": "Retroceder a versiones previas", "fallbackToOlderReleases": "Retroceder a versiones previas",
"filterReleaseTitlesByRegEx": "Filtrar por título de version", "filterReleaseTitlesByRegEx": "Filtrar por título de versión",
"invalidRegEx": "Expresión inválida", "invalidRegEx": "Expresión inválida",
"noDescription": "Sin descripción", "noDescription": "Sin descripción",
"cancel": "Cancelar", "cancel": "Cancelar",
@ -22,321 +22,351 @@
"requiredInBrackets": "(Requerido)", "requiredInBrackets": "(Requerido)",
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN", "dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN",
"colour": "Color", "colour": "Color",
"githubStarredRepos": "Repositorios favoritos GitHub", "standard": "Estándar",
"custom": "A medida",
"useMaterialYou": "Utilice el material que",
"githubStarredRepos": "Repositorios favoritos en GitHub",
"uname": "Nombre de usuario", "uname": "Nombre de usuario",
"wrongArgNum": "Número de argumentos provistos inválido", "wrongArgNum": "Número de argumentos provistos inválido",
"xIsTrackOnly": "{} es de 'Solo Seguimiento'", "xIsTrackOnly": "{} es de 'lo seguimiento'",
"source": "Origen", "source": "Origen",
"app": "Aplicación", "app": "Aplicación",
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.", "appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'solo seguimiento'.",
"youPickedTrackOnly": "Debe seleccionar la opción de 'Solo Seguimiento'.", "youPickedTrackOnly": "Debe seleccionar la opción de 'solo seguimiento'.",
"trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargarla o actalizarla.", "trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargar o actualizarla.",
"cancelled": "Cancelado", "cancelled": "Cancelado",
"appAlreadyAdded": "Aplicación ya añadida", "appAlreadyAdded": "Aplicación añadida anteriormente",
"alreadyUpToDateQuestion": "¿Aplicación ya actualizada?", "alreadyUpToDateQuestion": "¿Aplicación actualizada previamente?",
"addApp": "Añadir Aplicación", "addApp": "Añadir aplicación",
"appSourceURL": "URL de Origen de la Aplicación", "appSourceURL": "URL de origen de la aplicación",
"error": "Error", "error": "Error",
"add": "Añadir", "add": "Añadir",
"searchSomeSourcesLabel": "Buscar (solo algunas fuentes)", "searchSomeSourcesLabel": "Buscar (solo algunas fuentes)",
"search": "Buscar", "search": "Buscar",
"additionalOptsFor": "Opciones Adicionales para {}", "additionalOptsFor": "Opciones adicionales para {}",
"supportedSources": "Fuentes Soportadas", "supportedSources": "Fuentes admitidas",
"trackOnlyInBrackets": "(Solo Seguimiento)", "trackOnlyInBrackets": "(Solo seguimiento)",
"searchableInBrackets": "(soporta búsqueda)", "searchableInBrackets": "(permite búsqueda)",
"appsString": "Aplicaciones", "appsString": "Aplicaciones",
"noApps": "Sin Aplicaciones", "noApps": "Sin Aplicaciones",
"noAppsForFilter": "Sin aplicaciones para filtrar", "noAppsForFilter": "Sin aplicaciones para filtrar",
"byX": "por: {}", "byX": "por: {}",
"percentProgress": "Progreso: {}%", "percentProgress": "Progreso: {} %",
"pleaseWait": "Por favor, espere", "pleaseWait": "Espere...",
"updateAvailable": "Actualización Disponible", "updateAvailable": "Actualización disponible",
"estimateInBracketsShort": "(Aprox.)", "notInstalled": "No instalado",
"notInstalled": "No Instalado", "pseudoVersion": "pseudoversión",
"estimateInBrackets": "(Aproximado)", "selectAll": "Seleccionar todo",
"selectAll": "Seleccionar Todo",
"deselectX": "Deseleccionar {}", "deselectX": "Deseleccionar {}",
"xWillBeRemovedButRemainInstalled": "{} se eliminada de Obtainium pero continuará instalada en el dispositivo.", "xWillBeRemovedButRemainInstalled": "{} se elimina de Obtainium pero continuará instalada en el dispositivo.",
"removeSelectedAppsQuestion": "¿Eliminar aplicaciones seleccionadas?", "removeSelectedAppsQuestion": "¿Eliminar aplicaciones seleccionadas?",
"removeSelectedApps": "Eliminar Aplicaciones Seleccionadas", "removeSelectedApps": "Eliminar aplicaciones seleccionadas",
"updateX": "Actualizar {}", "updateX": "Actualizar {}",
"installX": "Instalar {}", "installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimiento)\ncomo Actualizada", "markXTrackOnlyAsUpdated": "Marcar {}\n(Solo seguimiento)\ncomo actualizada",
"changeX": "Cambiar {}", "changeX": "Cambiar {}",
"installUpdateApps": "Instalar/Actualizar Aplicaciones", "installUpdateApps": "Instalar/actualizar aplicaciones",
"installUpdateSelectedApps": "Instalar/Actualizar Aplicaciones Seleccionadas", "installUpdateSelectedApps": "Instalar/actualizar aplicaciones seleccionadas",
"markXSelectedAppsAsUpdated": "¿Marcar {} Aplicaciones Seleccionadas como Actualizadas?", "markXSelectedAppsAsUpdated": "¿Marcar {} aplicaciones seleccionadas como actualizadas?",
"no": "No", "no": "No",
"yes": "Sí", "yes": "Sí",
"markSelectedAppsUpdated": "Marcar Aplicaciones Seleccionadas como Actualizadas", "markSelectedAppsUpdated": "Marcar aplicaciones seleccionadas como actualizadas",
"pinToTop": "Fijar arriba", "pinToTop": "Anclar al principio",
"unpinFromTop": "Desfijar de arriba", "unpinFromTop": "Desanclar del principio",
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar Estado de Instalación para las Aplicaciones Seleccionadas?", "resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?",
"installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.", "installStatusOfXWillBeResetExplanation": "Se restaurará el estado de instalación de las aplicaciones seleccionadas.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
"shareSelectedAppURLs": "Compartir URLs de las Aplicaciones Seleccionadas", "customLinkMessage": "Estos enlaces funcionan en dispositivos con Obtainium instalado",
"resetInstallStatus": "Restaurar Estado de Instalación", "shareAppConfigLinks": "Compartir la configuración de la aplicación como enlace HTML",
"shareSelectedAppURLs": "Compartir URL de las aplicaciones seleccionadas",
"resetInstallStatus": "Restaurar estado de instalación",
"more": "Más", "more": "Más",
"removeOutdatedFilter": "Elimiar Filtro de Aplicaciones Desactualizado", "removeOutdatedFilter": "Eliminar filtro de aplicaciones desactualizado",
"showOutdatedOnly": "Mostrar solo Aplicaciones Desactualizadas", "showOutdatedOnly": "Mostrar solo aplicaciones desactualizadas",
"filter": "Filtrar", "filter": "Filtrar",
"filterActive": "Filtrar *", "filterApps": "Filtrar actualizaciones",
"filterApps": "Filtrar Actualizaciones", "appName": "Nombre de la aplicación",
"appName": "Nombre de la Aplicación",
"author": "Autor", "author": "Autor",
"upToDateApps": "Aplicaciones Actualizadas", "upToDateApps": "Aplicaciones actualizadas",
"nonInstalledApps": "Aplicaciones No Instaladas", "nonInstalledApps": "Aplicaciones no instaladas",
"importExport": "Importar/Exportar", "importExport": "Importar/exportar",
"settings": "Ajustes", "settings": "Ajustes",
"exportedTo": "Exportado a {}", "exportedTo": "Exportado a {}",
"obtainiumExport": "Exportar Obtainium", "obtainiumExport": "Exportar Obtainium",
"invalidInput": "Input incorrecto", "invalidInput": "Entrada no válida",
"importedX": "Importado {}", "importedX": "Importado {}",
"obtainiumImport": "Importar Obtainium", "obtainiumImport": "Importar Obtainium",
"importFromURLList": "Importar desde lista de URLs", "importFromURLList": "Importar desde lista de URL",
"searchQuery": "Consulta de Búsqueda", "searchQuery": "Término de búsqueda",
"appURLList": "Lista de URLs de Aplicaciones", "appURLList": "Lista de URL de aplicaciones",
"line": "Línea", "line": "Línea",
"searchX": "Buscar {}", "searchX": "Buscar {}",
"noResults": "Resultados no encontrados", "noResults": "No se ha encontrado ningún resultado",
"importX": "Importar desde {}", "importX": "Importar desde {}",
"importedAppsIdDisclaimer": "Las aplicaciones importadas podrían mostrarse incorrectamente como \"No Instalada\".\nPara solucionarlo, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.", "importedAppsIdDisclaimer": "Las aplicaciones importadas podrían mostrarse incorrectamente como «No instalada».\nPara solucionarlo, reinstálelas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a los URL y a los métodos de importación mediante terceros.",
"importErrors": "Errores de Importación", "importErrors": "Errores de Importación",
"importedXOfYApps": "{} de {} Aplicaciones importadas.", "importedXOfYApps": "{} de {} aplicaciones importadas.",
"followingURLsHadErrors": "Las siguientes URLs han tenido problemas:", "followingURLsHadErrors": "Los URL siguientes han tenido problemas:",
"selectURL": "Seleccionar URL", "selectURL": "Seleccionar URL",
"selectURLs": "Seleccionar URLs", "selectURLs": "Seleccionar URLs",
"pick": "Escoger", "pick": "Escoger",
"theme": "Tema", "theme": "Tema",
"dark": "Oscuro", "dark": "Oscuro",
"light": "Claro", "light": "Claro",
"followSystem": "Seguir al Sistema", "followSystem": "Seguir al sistema",
"obtainium": "Obtainium", "useBlackTheme": "Negro puro en tema oscuro",
"materialYou": "Material You", "appSortBy": "Ordenar aplicaciones por",
"useBlackTheme": "Negro puro en tema Oscuro", "authorName": "Autor/nombre",
"appSortBy": "Ordenar Apps Por",
"authorName": "Autor/Nombre",
"nameAuthor": "Nombre/Autor", "nameAuthor": "Nombre/Autor",
"asAdded": "Según se Añadieron", "asAdded": "Según se Añadieron",
"appSortOrder": "Orden de Clasificación", "appSortOrder": "Orden de Clasificación",
"ascending": "Ascendente", "ascending": "Ascendente",
"descending": "Descendente", "descending": "Descendente",
"bgUpdateCheckInterval": "Intervalo de Comprobación de Actualizaciones en Segundo Plano", "bgUpdateCheckInterval": "Comprobación actualizaciones en segundo plano",
"neverManualOnly": "Nunca - Solo Manual", "neverManualOnly": "Nunca, solo manual",
"appearance": "Apariencia", "appearance": "Apariencia",
"showWebInAppView": "Mostrar Vista de la Web de Origen", "showWebInAppView": "Mostrar vista de la web de origen",
"pinUpdates": "Fijar Actualizaciones en la Parte Superior de la Vista de Aplicaciones", "pinUpdates": "Anclar actualizaciones al principio",
"updates": "Actualizaciones", "updates": "Actualizaciones",
"sourceSpecific": "Fuente Específica", "sourceSpecific": "Fuente específica",
"appSource": "Fuente de la Aplicación", "appSource": "Obtainium en GitHub",
"noLogs": "Sin Logs", "noLogs": "Ningún registro",
"appLogs": "Logs de la Aplicación", "appLogs": "Registros",
"close": "Cerrar", "close": "Cerrar",
"share": "Compartir", "share": "Compartir",
"appNotFound": "Aplicación no encontrada", "appNotFound": "Aplicación no encontrada",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "obtainium-exportación",
"pickAnAPK": "Seleccione una APK", "pickAnAPK": "Seleccione una APK",
"appHasMoreThanOnePackage": "{} tiene más de un paquete:", "appHasMoreThanOnePackage": "{} tiene más de un paquete:",
"deviceSupportsXArch": "Su dispositivo soporta las siguientes arquitecturas de procesador: {}.", "deviceSupportsXArch": "Su dispositivo admite las siguientes arquitecturas de procesador: {}.",
"deviceSupportsFollowingArchs": "Su dispositivo soporta las siguientes arquitecturas de procesador:", "deviceSupportsFollowingArchs": "Su dispositivo admite las siguientes arquitecturas de procesador:",
"warning": "Aviso", "warning": "Aviso",
"sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es '{}' pero el paquete de la actualización viene de '{}'. ¿Desea continuar?", "sourceIsXButPackageFromYPrompt": "La fuente de la aplicación es «{}» pero el paquete de la actualización viene de «{}». ¿Desea continuar?",
"updatesAvailable": "Actualizaciones Disponibles", "updatesAvailable": "Actualizaciones Disponibles",
"updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitorizadas por Obtainium", "updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitoreadas por Obtainium",
"noNewUpdates": "No hay nuevas actualizaciones.", "noNewUpdates": "No hay nuevas actualizaciones.",
"xHasAnUpdate": "{} tiene una actualización.", "xHasAnUpdate": "{} tiene una actualización.",
"appsUpdated": "Aplicaciones Actualizadas", "appsUpdated": "Aplicaciones actualizadas",
"appsNotUpdated": "Error al actualizar las aplicaciones",
"appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano", "appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano",
"xWasUpdatedToY": "{} ha sido actualizada a {}.", "xWasUpdatedToY": "{} ha sido actualizada a {}.",
"errorCheckingUpdates": "Error Buscando Actualizaciones", "xWasNotUpdatedToY": "Error al actualizar {} a {}.",
"errorCheckingUpdates": "Error al buscar actualizaciones",
"errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla", "errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla",
"appsRemoved": "Aplicaciones Eliminadas", "appsRemoved": "Aplicaciones eliminadas",
"appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas", "appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
"xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}", "xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
"completeAppInstallation": "Instalación Completa de la Aplicación", "completeAppInstallation": "Instalación completa de la aplicación",
"obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones", "obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones",
"completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para terminar de instalar una aplicación", "completeAppInstallationNotifDescription": "Le pide volver a Obtainium para terminar de instalar una aplicación",
"checkingForUpdates": "Buscando Actualizaciones", "checkingForUpdates": "Buscando actualizaciones...",
"checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones", "checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
"pleaseAllowInstallPerm": "Por favor, permita que Obtainium instale aplicaciones", "pleaseAllowInstallPerm": "Permita que Obtainium instale aplicaciones",
"trackOnly": "Solo Seguimiento", "trackOnly": "Solo para seguimiento",
"errorWithHttpStatusCode": "Error {}", "errorWithHttpStatusCode": "Error {}",
"versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)", "versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)",
"unknown": "Desconocido", "unknown": "Desconocido",
"none": "Ninguno", "none": "Ninguno",
"never": "Nunca", "never": "Nunca",
"latestVersionX": "Última Versión: {}", "latestVersionX": "Última versión: {}",
"installedVersionX": "Versión Instalada: {}", "installedVersionX": "Versión instalada: {}",
"lastUpdateCheckX": "Última Comprobación: {}", "lastUpdateCheckX": "Última comprobación: {}",
"remove": "Eliminar", "remove": "Eliminar",
"yesMarkUpdated": "Sí, Marcar como Actualizada", "yesMarkUpdated": "Sí, marcar como actualizada",
"fdroid": "Repositorio oficial F-Droid", "fdroid": "Repositorio oficial F-Droid",
"appIdOrName": "ID o Nombre de la Aplicación", "appIdOrName": "ID o Nombre de la Aplicación",
"appId": "ID de la Aplicación", "appId": "ID de la Aplicación",
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre", "appWithIdOrNameNotFound": "No se han encontrado aplicaciones con ese ID o nombre",
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones", "reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
"fdroidThirdPartyRepo": "Rpositorios de terceros F-Droid", "fdroidThirdPartyRepo": "Repositorio de tercera parte F-Droid",
"steam": "Steam", "steamMobile": "Steam para móviles",
"steamMobile": "Steam Mobile", "steamChat": "Chat de Steam",
"steamChat": "Steam Chat",
"install": "Instalar", "install": "Instalar",
"markInstalled": "Marcar como Instalada", "markInstalled": "Marcar como instalada",
"update": "Actualizar", "update": "Actualizar",
"markUpdated": "Marcar como Actualizada", "markUpdated": "Marcar como actualizada",
"additionalOptions": "Opciones Adicionales", "additionalOptions": "Opciones adicionales",
"disableVersionDetection": "Descativar Detección de Versiones", "disableVersionDetection": "Desactivar la detección de versiones",
"noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda no funcionar correctamente.", "noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda que no funcionar correctamente.",
"downloadingX": "Descargando {}", "downloadingX": "Descargando {}",
"downloadX": "Descargar {}",
"downloadedX": "Descargado {}",
"releaseAsset": "Liberar activos",
"downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación", "downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación",
"noAPKFound": "APK no encontrada", "noAPKFound": "No se ha encontrado el paquete de instalación APK",
"noVersionDetection": "Sin detección de versiones", "noVersionDetection": "Sin detección de versiones",
"categorize": "Catogorizar", "categorize": "Catogorizar",
"categories": "Categorías", "categories": "Categorías",
"category": "Categoría", "category": "Categoría",
"noCategory": "Sin Categoría", "noCategory": "Sin categoría",
"noCategories": "Sin Categorías", "noCategories": "Sin categorías",
"deleteCategoriesQuestion": "¿Eliminar Categorías?", "deleteCategoriesQuestion": "¿Eliminar categorías?",
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin Categoría'.", "categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas se marcarán como «Sin categoría».",
"addCategory": "Añadir Categoría", "addCategory": "Añadir categoría",
"label": "Nombre", "label": "Nombre",
"language": "Idioma", "language": "Idioma",
"copiedToClipboard": "Copiado al Portapapeles", "copiedToClipboard": "Copiado en el portapapeles",
"storagePermissionDenied": "Permiso de Almacenamiento rechazado", "storagePermissionDenied": "Permiso de almacenamiento rechazado",
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.", "selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.",
"filterAPKsByRegEx": "Filtrar por APKs", "filterAPKsByRegEx": "Filtrar por APK",
"removeFromObtainium": "Eliminar de Obtainium", "removeFromObtainium": "Eliminar de Obtainium",
"uninstallFromDevice": "Desinstalar del Dispositivo", "uninstallFromDevice": "Desinstalar del dispositivo",
"onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.", "onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
"releaseDateAsVersion": "Por fecha de publicación", "releaseDateAsVersion": "Por fecha de publicación",
"releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.", "releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
"changes": "Cambios", "changes": "Cambios",
"releaseDate": "Fecha de Publicación", "releaseDate": "Fecha de publicación",
"importFromURLsInFile": "Importar URLs desde archivo (como OPML)", "importFromURLsInFile": "Importar URLs desde archivo (como OPML)",
"versionDetection": "Detección de Versiones", "versionDetectionExplanation": "Conciliar la cadena de versión con la versión detectada desde el sistema operativo",
"versionDetection": "Detección de versiones",
"standardVersionDetection": "Por versión", "standardVersionDetection": "Por versión",
"groupByCategory": "Agrupar por Categoría", "groupByCategory": "Agrupar por categoría",
"autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador, si es posible", "autoApkFilterByArch": "Filtrar APK por arquitectura del procesador (si es posible)",
"overrideSource": "Sobrescribir Fuente", "overrideSource": "Forzar desde la fuente",
"dontShowAgain": "No mostrar de nuevo", "dontShowAgain": "No mostrar de nuevo",
"dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'", "dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps en 'solo seguimiento'",
"dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APKs", "dontShowAPKOriginWarnings": "No mostrar avisos sobre las fuentes de las APKs",
"moveNonInstalledAppsToBottom": "Mover Apps no instaladas en la Parte Inferior de la Vista de Aplicaciones", "moveNonInstalledAppsToBottom": "Mover Apps no instaladas al final",
"gitlabPATLabel": "Token GitLab de Acceso Personal\n(Habilita la Búsqueda y Mejor Detección de APKs)", "gitlabPATLabel": "Token de acceso personal a GitLab",
"about": "Acerca", "about": "Acerca",
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en Ajustes)", "requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en ajustes)",
"checkOnStart": "Comprobar actualizaciones durante el inicio", "checkOnStart": "Comprobar actualizaciones al inicio",
"tryInferAppIdFromCode": "Intentar deducir la ID de la APP por el código fuente", "tryInferAppIdFromCode": "Intentar deducir el ID de la app por el código fuente",
"removeOnExternalUninstall": "Auto eliminar Apps desinstaladas externamente", "removeOnExternalUninstall": "Auto eliminar apps desinstaladas externamente",
"pickHighestVersionCode": "Auto selección versión superior del código APK", "pickHighestVersionCode": "Auto selección de versión superior del paquete APK",
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la App", "checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la app",
"disablePageTransitions": "Deshabilitar animaciones de transición de la página", "disablePageTransitions": "Deshabilitar animaciones de transición",
"reversePageTransitions": "Invertir animaciones de transición de la página", "reversePageTransitions": "Invertir animaciones de transición",
"minStarCount": "Número Mínimo de Estrellas", "minStarCount": "Número Mínimo de Estrellas",
"addInfoBelow": "Añadir esta información debajo.", "addInfoBelow": "Añadir esta información debajo.",
"addInfoInSettings": "Puede añadir esta información en Ajustes.", "addInfoInSettings": "Puede añadir esta información en Ajustes.",
"githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con una clave API.", "githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con un 'token de acceso personal'.",
"gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin una clave API.", "sortByLastLinkSegment": "Ordenar sólo por el último segmento del enlace",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "filterReleaseNotesByRegEx": "Filtrar por notas de versión (release notes)",
"filterReleaseNotesByRegEx": "Filtrar por notas de nersión (release notes)", "customLinkFilterRegex": "Filtro personalizado de enlace APK (por defecto '.apk$')",
"customLinkFilterRegex": "Filtro personalizado de Enlace APK (por defecto '.apk$')", "appsPossiblyUpdated": "Actualización de apps intentada",
"appsPossiblyUpdated": "Actualización de Apps intentada",
"appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones", "appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones",
"xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.", "xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.",
"enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano", "enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
"backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.", "backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
"backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede comprobar con Obtainium abierto.", "backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede comprobar con Obtainium abierto.",
"verifyLatestTag": "Comprueba la etiqueta 'Latest'", "verifyLatestTag": "Comprobar la etiqueta 'Latest'",
"intermediateLinkRegex": "Filtrar por enlace 'intermedio' para visitar primero", "intermediateLinkRegex": "Filtrar por enlace 'intermedio' para visitar primero",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filtrar enlaces por texto del enlace",
"intermediateLinkNotFound": "Enlace intermedio no encontrado", "intermediateLinkNotFound": "Enlace intermedio no encontrado",
"intermediateLink": "Intermediate link", "intermediateLink": "Enlace intermedio",
"exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)", "exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)",
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi", "bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
"autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior", "autoSelectHighestVersionCode": "Auto selección del paquete APK con versión más reciente",
"versionExtractionRegEx": "Versión de Extracción de RegEx", "versionExtractionRegEx": "Versión de extracción regex",
"matchGroupToUse": "Coincidir en Grupo a Usar", "matchGroupToUse": "Grupo a usar para versión de extracción regex",
"highlightTouchTargets": "Resaltar objetivos menos obvios", "highlightTouchTargets": "Resaltar objetivos menos obvios",
"pickExportDir": "Selecciona el Directorio para Exportar", "pickExportDir": "Directorio para exportar",
"autoExportOnChanges": "Auto Exportar cuando haya cambios", "autoExportOnChanges": "Auto exportar cuando haya cambios",
"includeSettings": "Incluir ajustes", "includeSettings": "Incluir ajustes",
"filterVersionsByRegEx": "Filtrar por Versiones", "filterVersionsByRegEx": "Filtrar por versiones",
"trySelectingSuggestedVersionCode": "Pruebe seleccionando la versionCode APK sugerida", "trySelectingSuggestedVersionCode": "Pruebe seleccionando la versión del paquete APK sugerida",
"dontSortReleasesList": "Mantener el order de publicación desde API", "dontSortReleasesList": "Mantener el order de publicación de la fuente original",
"reverseSort": "Orden inverso", "reverseSort": "Orden inverso",
"takeFirstLink": "Usar primer enlace", "takeFirstLink": "Usar primer enlace",
"skipSort": "Omitir orden", "skipSort": "Omitir orden",
"debugMenu": "Menu Depurar", "debugMenu": "Menu Depurar",
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.", "bgTaskStarted": "Iniciada tarea en segundo plano; revise los registros.",
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano", "runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
"versionExtractWholePage": "Aplicar la Versión de Extracción Regex a la Página Entera", "versionExtractWholePage": "Aplicar la versión de extracción regex a la página entera",
"installing": "Instalando", "installing": "Instalando",
"skipUpdateNotifications": "Omitir de notificaciones sobre actualizaciones", "skipUpdateNotifications": "No notificar sobre actualizaciones",
"updatesAvailableNotifChannel": "Actualizaciones Disponibles", "updatesAvailableNotifChannel": "Actualizaciones disponibles",
"appsUpdatedNotifChannel": "Aplicaciones Actualizadas", "appsUpdatedNotifChannel": "Aplicaciones actualizadas",
"appsPossiblyUpdatedNotifChannel": "Se ha Intentado Actualizar la Aplicación", "appsPossiblyUpdatedNotifChannel": "Se ha intentado actualizar la aplicación",
"errorCheckingUpdatesNotifChannel": "Error Buscando Actualizaciones", "errorCheckingUpdatesNotifChannel": "Error buscando actualizaciones",
"appsRemovedNotifChannel": "Aplicaciones Eliminadas", "appsRemovedNotifChannel": "Aplicaciones eliminadas",
"downloadingXNotifChannel": "Descargando {}", "downloadingXNotifChannel": "Descargando {}",
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación", "completeAppInstallationNotifChannel": "Instalación completada",
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones", "checkingForUpdatesNotifChannel": "Buscando actualizaciones",
"onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas y en seguimiento", "onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas o en seguimiento",
"supportFixedAPKURL": "Soporte para URLs fijas de APK", "supportFixedAPKURL": "Soporte para URLs fijas de APK",
"selectX": "Selecciona {}", "selectX": "Selecciona {}",
"parallelDownloads": "Permitir descargas paralelas", "parallelDownloads": "Permitir descargas paralelas",
"installMethod": "Installation method", "useShizuku": "Utilice Shizuku o Sui para instalar",
"normal": "Normal", "shizukuBinderNotFound": "Shizuku no funciona",
"shizuku": "Shizuku", "shizukuOld": "Versión antigua de Shizuku (<11) - actualícela",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku corriendo en Android < 8.1 con ADB - actualiza Android o usa Sui en su lugar",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuPretendToBeGooglePlay": "Establecer Google Play como fuente de instalación (si se utiliza Shizuku)",
"useSystemFont": "Usar fuente del sistema",
"useVersionCodeAsOSVersion": "Usar la versión de la aplicación como versión detectada por el sistema operativo",
"requestHeader": "Encabezado de solicitud",
"useLatestAssetDateAsReleaseDate": "Usar la última carga de recursos como fecha de lanzamiento",
"defaultPseudoVersioningMethod": "Método de pseudoversionado predeterminado",
"partialAPKHash": "Hash de APK parcial",
"APKLinkHash": "Hash de enlace APK",
"directAPKLink": "Enlace APK directo",
"pseudoVersionInUse": "Se está utilizando una pseudoversión",
"installed": "Instalado",
"latest": "Versión más reciente",
"invertRegEx": "Invertir expresión regular",
"note": "Nota",
"selfHostedNote": "El desplegable «{}» puede utilizarse para acceder a instancias autoalojadas/personalizadas de cualquier fuente.",
"badDownload": "No se ha podido analizar el APK (incompatible o descarga parcial)",
"beforeNewInstallsShareToAppVerifier": "Compartir nuevas aplicaciones con AppVerifier (si está disponible)",
"appVerifierInstructionToast": "Comparta con AppVerifier y vuelva aquí cuando esté listo.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "¿Eliminar Aplicación?", "one": "¿Eliminar aplicación?",
"other": "¿Eliminar Aplicaciones?" "other": "¿Eliminar aplicaciones?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Muchas peticiones (limitado) - prueba de nuevo en {} minuto", "one": "Muchas peticiones (limitado); pruebe de nuevo en {} minuto",
"other": "Muchas peticiones (limitado) - prueba de nuevo en {} minutos" "other": "Muchas peticiones (limitado); pruebe de nuevo en {} minutos"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}, se volverá a probar en {} minuto", "one": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}; se volverá a probar en {} minuto",
"other": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}, se volverá a probar en {} minutos" "other": "La comprobación de actualizaciones en segundo plano se ha encontrado un {}; se volverá a probar en {} minutos"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualización - se notificará al usuario si es necesario", "one": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualización; se le notificará si es necesario",
"other": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualizaciones - se notificará al usuario si es necesario" "other": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualizaciones; se le notificará si es necesario"
}, },
"apps": { "apps": {
"one": "{} Aplicación", "one": "{} Aplicación",
"other": "{} Aplicaciones" "other": "{} Aplicaciones"
}, },
"url": { "url": {
"one": "{} URL", "uno": "{} URL",
"other": "{} URLs" "otro": "{} URL"
}, },
"minute": { "minute": {
"one": "{} Minuto", "one": "{} minuto",
"other": "{} Minutos" "other": "{} minutos"
}, },
"hour": { "hour": {
"one": "{} Hora", "one": "{} hora",
"other": "{} Horas" "other": "{} horas"
}, },
"day": { "day": {
"one": "{} Día", "one": "{} día",
"other": "{} Días" "other": "{} días"
}, },
"clearedNLogsBeforeXAfterY": { "clearedNLogsBeforeXAfterY": {
"one": "Eliminado {n} log (previo a = {before}, posterior a = {after})", "one": "Eliminado {n} registro (previo a = {before}, posterior a = {after})",
"other": "Eliminados {n} logs (previos a = {before}, posteriores a = {after})" "other": "Eliminados {n} registros (previos a = {before}, posteriores a = {after})"
}, },
"xAndNMoreUpdatesAvailable": { "xAndNMoreUpdatesAvailable": {
"one": "{} y 1 aplicación más tiene actualizaciones.", "one": "{} y 1 aplicación más tiene actualizaciones.",
"other": "{} y {} aplicaciones más tienen actualizaciones." "other": "{} y {} aplicaciones más tienen actualizaciones."
}, },
"xAndNMoreUpdatesInstalled": { "xAndNMoreUpdatesInstalled": {
"one": "{} y 1 aplicación más han sido actualizadas.", "one": "{} y 1 aplicación más se han actualizado.",
"other": "{} y {} aplicaciones más han sido actualizadas." "other": "{} y {} aplicaciones más se han actualizado."
},
"xAndNMoreUpdatesFailed": {
"one": "Error al actualizar {} y 1 aplicación más.",
"other": "No se han podido actualizar {} y {} aplicaciones más."
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} y 1 aplicación más podría haber sido actualizada.", "one": "{} y 1 aplicación más podría haber sido actualizada.",
"other": "{} y {} aplicaciones más podrían haber sido actualizadas." "other": "{} y {} aplicaciones más podrían haber sido actualizadas."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(ضروری)", "requiredInBrackets": "(ضروری)",
"dropdownNoOptsError": "خطا: کشویی باید حداقل یک گزینه داشته باشد", "dropdownNoOptsError": "خطا: کشویی باید حداقل یک گزینه داشته باشد",
"colour": "رنگ", "colour": "رنگ",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Use Material You",
"githubStarredRepos": "مخازن ستاره دار گیتهاب", "githubStarredRepos": "مخازن ستاره دار گیتهاب",
"uname": "نام کاربری", "uname": "نام کاربری",
"wrongArgNum": "تعداد آرگومان های ارائه شده اشتباه است", "wrongArgNum": "تعداد آرگومان های ارائه شده اشتباه است",
@ -51,9 +54,8 @@
"percentProgress": "پیش رفتن: {}%", "percentProgress": "پیش رفتن: {}%",
"pleaseWait": "لطفا صبر کنید", "pleaseWait": "لطفا صبر کنید",
"updateAvailable": "بروزرسانی در دسترس", "updateAvailable": "بروزرسانی در دسترس",
"estimateInBracketsShort": "(تخمین)",
"notInstalled": "نصب نشده", "notInstalled": "نصب نشده",
"estimateInBrackets": "(تخمین زدن)", "pseudoVersion": "نسخه شبه",
"selectAll": "انتخاب همه", "selectAll": "انتخاب همه",
"deselectX": "لغو انتخاب {}", "deselectX": "لغو انتخاب {}",
"xWillBeRemovedButRemainInstalled": "{} از Obtainium حذف می‌شود اما روی دستگاه نصب می‌ماند.", "xWillBeRemovedButRemainInstalled": "{} از Obtainium حذف می‌شود اما روی دستگاه نصب می‌ماند.",
@ -73,34 +75,35 @@
"unpinFromTop": "برداشتن پین از بالا", "unpinFromTop": "برداشتن پین از بالا",
"resetInstallStatusForSelectedAppsQuestion": "وضعیت نصب برنامه‌های انتخابی بازنشانی شود؟", "resetInstallStatusForSelectedAppsQuestion": "وضعیت نصب برنامه‌های انتخابی بازنشانی شود؟",
"installStatusOfXWillBeResetExplanation": "وضعیت نصب برنامه‌های انتخاب‌شده بازنشانی می‌شود.\n\nاگر نسخه برنامه نشان‌داده‌شده در Obtainium به دلیل به‌روزرسانی‌های ناموفق یا مشکلات دیگر نادرست باشد، می‌تواند کمک کند.", "installStatusOfXWillBeResetExplanation": "وضعیت نصب برنامه‌های انتخاب‌شده بازنشانی می‌شود.\n\nاگر نسخه برنامه نشان‌داده‌شده در Obtainium به دلیل به‌روزرسانی‌های ناموفق یا مشکلات دیگر نادرست باشد، می‌تواند کمک کند.",
"customLinkMessage": "این پیوندها بر روی دستگاه هایی با Obtainium نصب شده کار می کنند",
"shareAppConfigLinks": "اشتراک گذاری پیکربندی برنامه به عنوان پیوند HTML",
"shareSelectedAppURLs": "اشتراک گذاری آدرس اینترنتی برنامه های انتخاب شده", "shareSelectedAppURLs": "اشتراک گذاری آدرس اینترنتی برنامه های انتخاب شده",
"resetInstallStatus": "بازنشانی وضعیت نصب", "resetInstallStatus": "بازنشانی وضعیت نصب",
"more": "بیشتر", "more": "بیشتر",
"removeOutdatedFilter": "فیلتر برنامه قدیمی را حذف کنید", "removeOutdatedFilter": "فیلتر برنامه قدیمی را حذف کنید",
"showOutdatedOnly": "فقط برنامه های قدیمی را نشان دهید", "showOutdatedOnly": "فقط برنامه های قدیمی را نشان دهید",
"filter": "فیلتر", "filter": "فیلتر",
"filterActive": "فیلتر *",
"filterApps": "فیلتر کردن برنامه ها", "filterApps": "فیلتر کردن برنامه ها",
"appName": "نام برنامه", "appName": "نام برنامه",
"author": "سازنده", "author": "سازنده",
"upToDateApps": "برنامه های به روز", "upToDateApps": "برنامه های به روز",
"nonInstalledApps": "برنامه های نصب نشده", "nonInstalledApps": "برنامه های نصب نشده",
"importExport": "وادر کردن/صادر کردن", "importExport": "درون ریزی/برون ریزی",
"settings": "تنظیمات", "settings": "تنظیمات",
"exportedTo": "صادر کردن به{}", "exportedTo": "برون ریزی به{}",
"obtainiumExport": "صادرکردن Obtainium", "obtainiumExport": "صادرکردن Obtainium",
"invalidInput": "ورودی نامعتبر", "invalidInput": "ورودی نامعتبر",
"importedX": "وارد شده {}", "importedX": "وارد شده {}",
"obtainiumImport": "واردکردن Obtainium", "obtainiumImport": "واردکردن Obtainium",
"importFromURLList": "وارد کردن از فهرست آدرس اینترنتی", "importFromURLList": "درون ریزی از فهرست آدرس اینترنتی",
"searchQuery": "جستجوی سوال", "searchQuery": "جستجوی سوال",
"appURLList": "فهرست آدرس اینترنتی برنامه", "appURLList": "فهرست آدرس اینترنتی برنامه",
"line": "خط", "line": "خط",
"searchX": "جستجو {}", "searchX": "جستجو {}",
"noResults": "نتیجه ای پیدا نشد", "noResults": "نتیجه ای پیدا نشد",
"importX": "وارد کردن {}", "importX": "درون ریزی {}",
"importedAppsIdDisclaimer": "ممکن است برنامه‌های وارد شده به اشتباه به‌عنوان \"نصب نشده\" نشان داده شوند.\nبرای رفع این مشکل، آنها را دوباره از طریق Obtainium نصب کنید.\nاین نباید روی داده‌های برنامه تأثیر بگذارد.\n\nفقط بر روی آدرس اینترنتی و روش‌های وارد کردن شخص ثالث تأثیر می‌گذارد.", "importedAppsIdDisclaimer": "ممکن است برنامه‌های وارد شده به اشتباه به‌عنوان \"نصب نشده\" نشان داده شوند.\nبرای رفع این مشکل، آنها را دوباره از طریق Obtainium نصب کنید.\nاین نباید روی داده‌های برنامه تأثیر بگذارد.\n\nفقط بر روی آدرس اینترنتی و روش‌های درون ریزی شخص ثالث تأثیر می‌گذارد.",
"importErrors": "خطاهای وارد کردن", "importErrors": "خطاهای درون ریزی",
"importedXOfYApps": "{} از {} برنامه وارد شد.", "importedXOfYApps": "{} از {} برنامه وارد شد.",
"followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:", "followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:",
"selectURL": "آدرس اینترنتی انتخاب شده", "selectURL": "آدرس اینترنتی انتخاب شده",
@ -110,8 +113,6 @@
"dark": "تاریک", "dark": "تاریک",
"light": "روشن", "light": "روشن",
"followSystem": "هماهنگ با سیستم", "followSystem": "هماهنگ با سیستم",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "استفاده از تم تیره سیاه خالص", "useBlackTheme": "استفاده از تم تیره سیاه خالص",
"appSortBy": "مرتب سازی برنامه بر اساس", "appSortBy": "مرتب سازی برنامه بر اساس",
"authorName": "سازنده/اسم", "authorName": "سازنده/اسم",
@ -133,7 +134,7 @@
"close": "بستن", "close": "بستن",
"share": "اشتراک گذاری", "share": "اشتراک گذاری",
"appNotFound": "برنامه پیدا نشد", "appNotFound": "برنامه پیدا نشد",
"obtainiumExportHyphenatedLowercase": "صادر کردن-obtainium", "obtainiumExportHyphenatedLowercase": "برون ریزی-obtainium",
"pickAnAPK": "یک APK انتخاب کنید", "pickAnAPK": "یک APK انتخاب کنید",
"appHasMoreThanOnePackage": "{} بیش از یک بسته دارد:", "appHasMoreThanOnePackage": "{} بیش از یک بسته دارد:",
"deviceSupportsXArch": "دستگاه شما از معماری پردازنده {} پشتیبانی میکند", "deviceSupportsXArch": "دستگاه شما از معماری پردازنده {} پشتیبانی میکند",
@ -145,8 +146,10 @@
"noNewUpdates": "به روز رسانی جدیدی وجود ندارد.", "noNewUpdates": "به روز رسانی جدیدی وجود ندارد.",
"xHasAnUpdate": "{} یک به روز رسانی دارد.", "xHasAnUpdate": "{} یک به روز رسانی دارد.",
"appsUpdated": "برنامه ها به روز شدند", "appsUpdated": "برنامه ها به روز شدند",
"appsNotUpdated": "Failed to update applications",
"appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است", "appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است",
"xWasUpdatedToY": "{} به {} به روز شد.", "xWasUpdatedToY": "{} به {} به روز شد.",
"xWasNotUpdatedToY": "Failed to update {} to {}.",
"errorCheckingUpdates": "خطا در بررسی به‌روزرسانی‌ها", "errorCheckingUpdates": "خطا در بررسی به‌روزرسانی‌ها",
"errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی به‌روزرسانی پس‌زمینه ناموفق است نشان می‌دهد", "errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی به‌روزرسانی پس‌زمینه ناموفق است نشان می‌دهد",
"appsRemoved": "برنامه ها حذف شدند", "appsRemoved": "برنامه ها حذف شدند",
@ -171,13 +174,12 @@
"yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده", "yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده",
"fdroid": "F-Droid Official", "fdroid": "F-Droid Official",
"appIdOrName": "شناسه یا نام برنامه", "appIdOrName": "شناسه یا نام برنامه",
"appId": "App ID", "appId": "شناسه برنامه",
"appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد", "appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد",
"reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد", "reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد",
"fdroidThirdPartyRepo": "مخازن شخص ثالث F-Droid", "fdroidThirdPartyRepo": "مخازن شخص ثالث F-Droid",
"steam": "Steam", "steamMobile": "استیم موبایل",
"steamMobile": "Steam Mobile", "steamChat": "چت استیم",
"steamChat": "Steam Chat",
"install": "نصب", "install": "نصب",
"markInstalled": "علامت گذاری به عنوان نصب شده", "markInstalled": "علامت گذاری به عنوان نصب شده",
"update": "به روز رسانی", "update": "به روز رسانی",
@ -186,6 +188,9 @@
"disableVersionDetection": "غیرفعال کردن تشخیص نسخه", "disableVersionDetection": "غیرفعال کردن تشخیص نسخه",
"noVersionDetectionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند.", "noVersionDetectionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند.",
"downloadingX": "در حال دانلود {}", "downloadingX": "در حال دانلود {}",
"downloadX": "Download {}",
"downloadedX": "Downloaded {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "کاربر را از پیشرفت دانلود یک برنامه مطلع می کند", "downloadNotifDescription": "کاربر را از پیشرفت دانلود یک برنامه مطلع می کند",
"noAPKFound": "APK پیدا نشد فایل", "noAPKFound": "APK پیدا نشد فایل",
"noVersionDetection": "بدون تشخیص نسخه", "noVersionDetection": "بدون تشخیص نسخه",
@ -210,7 +215,8 @@
"releaseDateAsVersionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند، اما تاریخ انتشار در دسترس است.", "releaseDateAsVersionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند، اما تاریخ انتشار در دسترس است.",
"changes": "تغییرات", "changes": "تغییرات",
"releaseDate": "تاریخ انتشار", "releaseDate": "تاریخ انتشار",
"importFromURLsInFile": "وارد کردن از آدرس های اینترنتی موجود در فایل (مانند OPML)", "importFromURLsInFile": "درون ریزی از آدرس های اینترنتی موجود در فایل (مانند OPML)",
"versionDetectionExplanation": "تطبیق رشته نسخه با نسخه شناسایی شده از سیستم عامل",
"versionDetection": "تشخیص نسخه", "versionDetection": "تشخیص نسخه",
"standardVersionDetection": "تشخیص نسخه استاندارد", "standardVersionDetection": "تشخیص نسخه استاندارد",
"groupByCategory": "گروه بر اساس دسته", "groupByCategory": "گروه بر اساس دسته",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "هشدار 'فقط ردیابی' را نشان ندهید", "dontShowTrackOnlyWarnings": "هشدار 'فقط ردیابی' را نشان ندهید",
"dontShowAPKOriginWarnings": "هشدارهای منبع APK را نشان ندهید", "dontShowAPKOriginWarnings": "هشدارهای منبع APK را نشان ندهید",
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید", "moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)", "gitlabPATLabel": "رمز دسترسی شخصی GitLab",
"about": "درباره", "about": "درباره",
"requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)", "requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
"checkOnStart": "بررسی در شروع", "checkOnStart": "بررسی در شروع",
@ -234,8 +240,7 @@
"addInfoBelow": "این اطلاعات را در زیر اضافه کنید", "addInfoBelow": "این اطلاعات را در زیر اضافه کنید",
"addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.", "addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.",
"githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.", "githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.",
"gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.", "sortByLastLinkSegment": "فقط بر اساس آخرین بخش پیوند مرتب کنید",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید", "filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیش‌فرض '.apk$')", "customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیش‌فرض '.apk$')",
"appsPossiblyUpdated": "به‌روزرسانی برنامه انجام شد", "appsPossiblyUpdated": "به‌روزرسانی برنامه انجام شد",
@ -245,25 +250,25 @@
"backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.", "backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.",
"backgroundUpdateLimitsExplanation": "موفقیت نصب پس‌زمینه تنها زمانی مشخص می‌شود که Obtainium باز شود.", "backgroundUpdateLimitsExplanation": "موفقیت نصب پس‌زمینه تنها زمانی مشخص می‌شود که Obtainium باز شود.",
"verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید", "verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "برای بازدید از پیوند «میانگین» فیلتر کنید",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "لینک ها را بر اساس متن پیوند فیلتر کنید",
"intermediateLinkNotFound": "لینک میانی پیدا نشد", "intermediateLinkNotFound": "لینک میانی پیدا نشد",
"intermediateLink": "Intermediate link", "intermediateLink": "پیوند میانی",
"exemptFromBackgroundUpdates": "معاف از به‌روزرسانی‌های پس‌زمینه (در صورت فعال بودن)", "exemptFromBackgroundUpdates": "معاف از به‌روزرسانی‌های پس‌زمینه (در صورت فعال بودن)",
"bgUpdatesOnWiFiOnly": "به‌روزرسانی‌های پس‌زمینه را در صورت عدم اتصال به WiFi غیرفعال کنید", "bgUpdatesOnWiFiOnly": "به‌روزرسانی‌های پس‌زمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
"autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK", "autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK",
"versionExtractionRegEx": "نسخه استخراج RegEx", "versionExtractionRegEx": "نسخه استخراج RegEx",
"matchGroupToUse": "گروه مورد استفاده را مطابقت دهید", "matchGroupToUse": "گروه مورد استفاده را مطابقت دهید",
"highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید", "highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
"pickExportDir": "فهرست صادرات را انتخاب کنید", "pickExportDir": "فهرست برون ریزی را انتخاب کنید",
"autoExportOnChanges": "صادرات خودکار تغییرات", "autoExportOnChanges": "برون ریزی خودکار تغییرات",
"includeSettings": "Include settings", "includeSettings": "شامل تنظیمات",
"filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx", "filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx",
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید", "trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
"dontSortReleasesList": "حفظ سفارش انتشار از API", "dontSortReleasesList": "حفظ سفارش انتشار از API",
"reverseSort": "مرتب سازی معکوس", "reverseSort": "مرتب سازی معکوس",
"takeFirstLink": "Take first link", "takeFirstLink": "لینک اول را بگیرید",
"skipSort": "Skip sorting", "skipSort": "از مرتب سازی صرف نظر کنید",
"debugMenu": "منوی اشکال زدایی", "debugMenu": "منوی اشکال زدایی",
"bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.", "bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.",
"runBgCheckNow": "اکنون به‌روزرسانی پس‌زمینه را بررسی کنید", "runBgCheckNow": "اکنون به‌روزرسانی پس‌زمینه را بررسی کنید",
@ -281,12 +286,29 @@
"onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید", "onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید",
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت", "supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
"selectX": "انتخاب کنید {}", "selectX": "انتخاب کنید {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "اجازه دانلود موازی",
"installMethod": "Installation method", "useShizuku": "Use Shizuku or Sui to install",
"normal": "Normal", "shizukuBinderNotFound": "Shizuku در حال اجرا نیست",
"shizuku": "Shizuku", "shizukuOld": "Old Shizuku version (<11) - update it",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB - update Android or use Sui instead",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
"useSystemFont": "استفاده از فونت سیستم",
"useVersionCodeAsOSVersion": "استفاده کد نسخه برنامه به جای نسخه شناسایی شده توسط سیستم عامل استفاده کنید",
"requestHeader": "درخواست سطر بالایی",
"useLatestAssetDateAsReleaseDate": "استفاده از آخرین بارگذاری دارایی به عنوان تاریخ انتشار",
"defaultPseudoVersioningMethod": "روش پیش فرض شبه نسخه",
"partialAPKHash": "هش جزئی APK",
"APKLinkHash": "هش پیوند APK",
"directAPKLink": "لینک مستقیم APK",
"pseudoVersionInUse": "یک نسخه شبه در حال استفاده است",
"installed": "نصب شده است",
"latest": "آخرین",
"invertRegEx": "معکوس کردن عبارت منظم",
"note": "Note",
"selfHostedNote": "The \"{}\" dropdown can be used to reach self-hosted/custom instances of any source.",
"badDownload": "The APK could not be parsed (incompatible or partial download)",
"beforeNewInstallsShareToAppVerifier": "Share new Apps with AppVerifier (if available)",
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "برنامه حذف شود؟", "one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟" "other": "برنامه ها حذف شوند؟"
@ -335,8 +357,16 @@
"one": "{} و 1 برنامه دیگر به روز شدند.", "one": "{} و 1 برنامه دیگر به روز شدند.",
"other": "{} و {} برنامه دیگر به روز شدند." "other": "{} و {} برنامه دیگر به روز شدند."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.",
"other": "Failed to update {} and {} more apps."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.", "one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
"other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند." "other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"invalidURLForSource": "URL d'application {} invalide", "invalidURLForSource": "URL d'application {} invalide",
"noReleaseFound": "Impossible de trouver une version appropriée", "noReleaseFound": "Impossible de trouver une version adaptée",
"noVersionFound": "Impossible de déterminer la version de la version", "noVersionFound": "Impossible de déterminer la variante de la version",
"urlMatchesNoSource": "L'URL ne correspond pas à une source connue", "urlMatchesNoSource": "L'URL ne correspond pas à une source connue",
"cantInstallOlderVersion": "Impossible d'installer une ancienne version d'une application", "cantInstallOlderVersion": "Impossible d'installer une ancienne version d'une application",
"appIdMismatch": "L'ID de paquet téléchargé ne correspond pas à l'ID de l'application existante", "appIdMismatch": "L'ID de paquet téléchargé ne correspond pas à l'ID de l'application existante",
@ -9,7 +9,7 @@
"placeholder": "Espace réservé", "placeholder": "Espace réservé",
"someErrors": "Des erreurs se sont produites", "someErrors": "Des erreurs se sont produites",
"unexpectedError": "Erreur inattendue", "unexpectedError": "Erreur inattendue",
"ok": "Okay", "ok": "D'accord",
"and": "et", "and": "et",
"githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)", "githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)",
"includePrereleases": "Inclure les avant-premières", "includePrereleases": "Inclure les avant-premières",
@ -22,6 +22,9 @@
"requiredInBrackets": "(Requis)", "requiredInBrackets": "(Requis)",
"dropdownNoOptsError": "ERREUR : LE DÉROULEMENT DOIT AVOIR AU MOINS UNE OPT", "dropdownNoOptsError": "ERREUR : LE DÉROULEMENT DOIT AVOIR AU MOINS UNE OPT",
"colour": "Couleur", "colour": "Couleur",
"standard": "Standard",
"custom": "Sur mesure",
"useMaterialYou": "Utiliser le matériel que vous",
"githubStarredRepos": "Dépôts étoilés GitHub", "githubStarredRepos": "Dépôts étoilés GitHub",
"uname": "Nom d'utilisateur", "uname": "Nom d'utilisateur",
"wrongArgNum": "Mauvais nombre d'arguments fournis", "wrongArgNum": "Mauvais nombre d'arguments fournis",
@ -43,7 +46,7 @@
"additionalOptsFor": "Options supplémentaires pour {}", "additionalOptsFor": "Options supplémentaires pour {}",
"supportedSources": "Sources prises en charge ", "supportedSources": "Sources prises en charge ",
"trackOnlyInBrackets": "(Suivi uniquement)", "trackOnlyInBrackets": "(Suivi uniquement)",
"searchableInBrackets": "(Recherchable)", "searchableInBrackets": "(Intérrogeable)",
"appsString": "Applications", "appsString": "Applications",
"noApps": "Aucune application", "noApps": "Aucune application",
"noAppsForFilter": "Aucune application pour le filtre", "noAppsForFilter": "Aucune application pour le filtre",
@ -51,9 +54,8 @@
"percentProgress": "Progrès: {}%", "percentProgress": "Progrès: {}%",
"pleaseWait": "Veuillez patienter", "pleaseWait": "Veuillez patienter",
"updateAvailable": "Mise à jour disponible", "updateAvailable": "Mise à jour disponible",
"estimateInBracketsShort": "(Est.)", "notInstalled": "Non installé",
"notInstalled": "Pas installé", "pseudoVersion": "pseudo-version",
"estimateInBrackets": "(Estimation)",
"selectAll": "Tout sélectionner", "selectAll": "Tout sélectionner",
"deselectX": "Déselectionner {}", "deselectX": "Déselectionner {}",
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.", "xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
@ -61,25 +63,26 @@
"removeSelectedApps": "Supprimer les applications sélectionnées", "removeSelectedApps": "Supprimer les applications sélectionnées",
"updateX": "Mise à jour {}", "updateX": "Mise à jour {}",
"installX": "Installer {}", "installX": "Installer {}",
"markXTrackOnlyAsUpdated": "Marquer {}\n(Suivi uniquement)\nas mis à jour", "markXTrackOnlyAsUpdated": "Marquer {}\n(Suivi uniquement)\n comme mis à jour",
"changeX": "Changer {}", "changeX": "Changer {}",
"installUpdateApps": "Installer/Mettre à jour les applications", "installUpdateApps": "Installer/Mettre à jour les applications",
"installUpdateSelectedApps": "Installer/Mettre à jour les applications sélectionnées", "installUpdateSelectedApps": "Installer/Mettre à jour les applications sélectionnées",
"markXSelectedAppsAsUpdated": "Marquer {} les applications sélectionnées comme mises à jour ?", "markXSelectedAppsAsUpdated": "Marquer {} les applications sélectionnées comme étant à jour ?",
"no": "Non", "no": "Non",
"yes": "Oui", "yes": "Oui",
"markSelectedAppsUpdated": "Marquer les applications sélectionnées comme mises à jour", "markSelectedAppsUpdated": "Marquer les applications sélectionnées comme étant à jour",
"pinToTop": "Épingler en haut", "pinToTop": "Épingler en haut",
"unpinFromTop": "Détacher du haut", "unpinFromTop": "Désépingler du haut",
"resetInstallStatusForSelectedAppsQuestion": "Réinitialiser ltat d'installation des applications sélectionnées ?", "resetInstallStatusForSelectedAppsQuestion": "Réinitialiser le statu d'installation des applications sélectionnées ?",
"installStatusOfXWillBeResetExplanation": "Ltat d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut aider lorsque la version de l'application affichée dans Obtainium est incorrecte en raison d'échecs de mises à jour ou d'autres problèmes.", "installStatusOfXWillBeResetExplanation": "Le statu d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut aider lorsque la version de l'application affichée dans Obtainium est incorrecte en raison d'échecs de mises à jour ou d'autres problèmes.",
"customLinkMessage": "Ces liens fonctionnent sur les appareils sur lesquels Obtainium est installé",
"shareAppConfigLinks": "Partager la configuration de l'application sous forme de lien HTML",
"shareSelectedAppURLs": "Partager les URL d'application sélectionnées", "shareSelectedAppURLs": "Partager les URL d'application sélectionnées",
"resetInstallStatus": "Réinitialiser le statut d'installation", "resetInstallStatus": "Réinitialiser le statu d'installation",
"more": "Plus", "more": "Plus",
"removeOutdatedFilter": "Supprimer le filtre d'application obsolète", "removeOutdatedFilter": "Supprimer le filtre d'application obsolète",
"showOutdatedOnly": "Afficher uniquement les applications obsolètes", "showOutdatedOnly": "Afficher uniquement les applications obsolètes",
"filter": "Filtre", "filter": "Filtre",
"filterActive": "Filtre *",
"filterApps": "Filtrer les applications", "filterApps": "Filtrer les applications",
"appName": "Nom de l'application", "appName": "Nom de l'application",
"author": "Auteur", "author": "Auteur",
@ -88,12 +91,12 @@
"importExport": "Importer/Exporter", "importExport": "Importer/Exporter",
"settings": "Paramètres", "settings": "Paramètres",
"exportedTo": "Exporté vers {}", "exportedTo": "Exporté vers {}",
"obtainiumExport": "Exportation d'Obtainium", "obtainiumExport": "Exporter d'Obtainium",
"invalidInput": "Entrée invalide", "invalidInput": "Entrée invalide",
"importedX": "Importé {}", "importedX": "Importé {}",
"obtainiumImport": "Importation d'Obtainium", "obtainiumImport": "Importer d'Obtainium",
"importFromURLList": "Importer à partir de la liste d'URL", "importFromURLList": "Importer à partir de la liste d'URL",
"searchQuery": "Requête de recherche", "searchQuery": "Requête",
"appURLList": "Liste d'URL d'application", "appURLList": "Liste d'URL d'application",
"line": "Queue", "line": "Queue",
"searchX": "Rechercher {}", "searchX": "Rechercher {}",
@ -110,16 +113,14 @@
"dark": "Sombre", "dark": "Sombre",
"light": "Clair", "light": "Clair",
"followSystem": "Suivre le système", "followSystem": "Suivre le système",
"obtainium": "Obtainium", "useBlackTheme": "Utilisez le thème noir pur",
"materialYou": "Material You",
"useBlackTheme": "Use pure black dark theme",
"appSortBy": "Applications triées par", "appSortBy": "Applications triées par",
"authorName": "Auteur/Nom", "authorName": "Auteur/Nom",
"nameAuthor": "Nom/Auteur", "nameAuthor": "Nom/Auteur",
"asAdded": "Comme ajouté", "asAdded": "Comme ajouté",
"appSortOrder": "Ordre de tri des applications", "appSortOrder": "Ordre de tri des applications",
"ascending": "Ascendant", "ascending": "Ascendant",
"descending": "Descendanr", "descending": "Descendant",
"bgUpdateCheckInterval": "Intervalle de vérification des mises à jour en arrière-plan", "bgUpdateCheckInterval": "Intervalle de vérification des mises à jour en arrière-plan",
"neverManualOnly": "Jamais - Manuel uniquement", "neverManualOnly": "Jamais - Manuel uniquement",
"appearance": "Apparence", "appearance": "Apparence",
@ -133,20 +134,22 @@
"close": "Fermer", "close": "Fermer",
"share": "Partager", "share": "Partager",
"appNotFound": "Application introuvable", "appNotFound": "Application introuvable",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "exportation d'Obtainium",
"pickAnAPK": "Choisissez un APK", "pickAnAPK": "Choisissez un APK",
"appHasMoreThanOnePackage": "{} a plus d'un paquet :", "appHasMoreThanOnePackage": "{} a plus d'un paquet :",
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture de processeur {}.", "deviceSupportsXArch": "Votre appareil prend en charge l'architecture CPU {}.",
"deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivantes :", "deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivantes :",
"warning": "Avertissement", "warning": "Avertissement",
"sourceIsXButPackageFromYPrompt": "La source de l'application est '{}' mais le paquet de version provient de '{}'. Continuer?", "sourceIsXButPackageFromYPrompt": "La source de l'application est '{}' mais la version du paquet provient de '{}'. Continuer?",
"updatesAvailable": "Mises à jour disponibles", "updatesAvailable": "Mises à jour disponibles",
"updatesAvailableNotifDescription": "Avertit l'utilisateur que des mises à jour sont disponibles pour une ou plusieurs applications suivies par Obtainium", "updatesAvailableNotifDescription": "Avertit l'utilisateur que des mises à jour sont disponibles pour une ou plusieurs applications suivies par Obtainium",
"noNewUpdates": "Aucune nouvelle mise à jour.", "noNewUpdates": "Aucune nouvelle mise à jour.",
"xHasAnUpdate": "{} a une mise à jour.", "xHasAnUpdate": "{} a une mise à jour.",
"appsUpdated": "Applications mises à jour", "appsUpdated": "Applications mises à jour",
"appsNotUpdated": "Échec de la mise à jour des applications",
"appsUpdatedNotifDescription": "Avertit l'utilisateur que les mises à jour d'une ou plusieurs applications ont été appliquées en arrière-plan", "appsUpdatedNotifDescription": "Avertit l'utilisateur que les mises à jour d'une ou plusieurs applications ont été appliquées en arrière-plan",
"xWasUpdatedToY": "{} a été mis à jour pour {}.", "xWasUpdatedToY": "{} a été mis à jour pour {}.",
"xWasNotUpdatedToY": "Échec de la mise à jour de {} vers {}.",
"errorCheckingUpdates": "Erreur lors de la vérification des mises à jour", "errorCheckingUpdates": "Erreur lors de la vérification des mises à jour",
"errorCheckingUpdatesNotifDescription": "Une notification qui s'affiche lorsque la vérification de la mise à jour en arrière-plan échoue", "errorCheckingUpdatesNotifDescription": "Une notification qui s'affiche lorsque la vérification de la mise à jour en arrière-plan échoue",
"appsRemoved": "Applications supprimées", "appsRemoved": "Applications supprimées",
@ -169,124 +172,143 @@
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}", "lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
"remove": "Retirer", "remove": "Retirer",
"yesMarkUpdated": "Oui, marquer comme mis à jour", "yesMarkUpdated": "Oui, marquer comme mis à jour",
"fdroid": "F-Droid Official", "fdroid": "F-Droid Officiel",
"appIdOrName": "ID ou nom de l'application", "appIdOrName": "ID ou nom de l'application",
"appId": "ID de l'application", "appId": "ID de l'application",
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom", "appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications", "reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
"fdroidThirdPartyRepo": "Dépôt tiers F-Droid", "fdroidThirdPartyRepo": "Dépôt tiers F-Droid",
"steam": "Steam", "steamMobile": "Vapeur Mobile",
"steamMobile": "Steam Mobile", "steamChat": "Chat sur Steam",
"steamChat": "Steam Chat",
"install": "Installer", "install": "Installer",
"markInstalled": "Marquer installée", "markInstalled": "Marquer installée",
"update": "Mettre à jour", "update": "Mettre à jour",
"markUpdated": "Marquer à jour", "markUpdated": "Marquer à jour",
"additionalOptions": "Options additionelles", "additionalOptions": "Options additionnelles",
"disableVersionDetection": "Désactiver la détection de version", "disableVersionDetection": "Désactiver la détection de version",
"noVersionDetectionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de version ne fonctionne pas correctement.", "noVersionDetectionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de version ne fonctionne pas correctement.",
"downloadingX": "Téléchargement {}", "downloadingX": "Téléchargement {}",
"downloadX": "Télécharger {}",
"downloadedX": "Téléchargé {}",
"releaseAsset": "Actif libéré",
"downloadNotifDescription": "Avertit l'utilisateur de la progression du téléchargement d'une application", "downloadNotifDescription": "Avertit l'utilisateur de la progression du téléchargement d'une application",
"noAPKFound": "Aucun APK trouvé", "noAPKFound": "Aucun APK trouvé",
"noVersionDetection": "Pas de détection de version", "noVersionDetection": "Aucune de détection de version",
"categorize": "Catégoriser", "categorize": "Catégoriser",
"categories": "Catégories", "categories": "Catégories",
"category": "Catégorie", "category": "Catégorie",
"noCategory": "No Category", "noCategory": "Aucune catégorie",
"noCategories": "Aucune catégorie", "noCategories": "Aucune catégorie",
"deleteCategoriesQuestion": "Supprimer les catégories ?", "deleteCategoriesQuestion": "Supprimer les catégories ?",
"categoryDeleteWarning": "Toutes les applications dans les catégories supprimées seront définies sur non catégorisées.", "categoryDeleteWarning": "Toutes les applications dans les catégories supprimées seront définies sur non catégorisées.",
"addCategory": "Ajouter une catégorie", "addCategory": "Ajouter une catégorie",
"label": "Étiquette", "label": "Étiquette",
"language": "Langue", "language": "Langue",
"copiedToClipboard": "Copied to Clipboard", "copiedToClipboard": "Copié dans le presse-papier",
"storagePermissionDenied": "Autorisation de stockage refusée", "storagePermissionDenied": "Autorisation de stockage refusée",
"selectedCategorizeWarning": "Cela remplacera tous les paramètres de catégorie existants pour les applications sélectionnées.", "selectedCategorizeWarning": "Cela remplacera toutes les catégorie définies pour les applications sélectionnées.",
"filterAPKsByRegEx": "Filtrer les APK par expression régulière", "filterAPKsByRegEx": "Filtrer les APK par expression régulière",
"removeFromObtainium": "Supprimer d'Obtainium", "removeFromObtainium": "Supprimer d'Obtainium",
"uninstallFromDevice": "Désinstaller de l'appareil", "uninstallFromDevice": "Désinstaller de l'appareil",
"onlyWorksWithNonVersionDetectApps": "Fonctionne uniquement pour les applications avec la détection de version désactivée.", "onlyWorksWithNonVersionDetectApps": "Fonctionne uniquement pour les applications avec la détection de version désactivée.",
"releaseDateAsVersion": "Utiliser la date de sortie comme version", "releaseDateAsVersion": "Utiliser la date de sortie comme version",
"releaseDateAsVersionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de version ne fonctionne pas correctement, mais une date de sortie est disponible.", "releaseDateAsVersionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de version ne fonctionne pas correctement, mais dont une date de sortie est disponible.",
"changes": "Changements", "changes": "Changements",
"releaseDate": "Date de sortie", "releaseDate": "Date de sortie",
"importFromURLsInFile": "Importer à partir d'URL dans un fichier (comme OPML)", "importFromURLsInFile": "Importer à partir d'URL dans un fichier (comme OPML)",
"versionDetectionExplanation": "Réconcilier la chaîne de version avec la version détectée à partir du système d'exploitation",
"versionDetection": "Détection des versions", "versionDetection": "Détection des versions",
"standardVersionDetection": "Détection de version standard", "standardVersionDetection": "Détection de version standard",
"groupByCategory": "Group by Category", "groupByCategory": "Regrouper par catégorie",
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible", "autoApkFilterByArch": "Si possible, essayez de filtrer les APK par architecture CPU",
"overrideSource": "Override Source", "overrideSource": "Remplacer la source",
"dontShowAgain": "Don't show this again", "dontShowAgain": "Ne plus montrer",
"dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning", "dontShowTrackOnlyWarnings": "Ne pas afficher l'avertissement 'Track-Only'",
"dontShowAPKOriginWarnings": "Don't show APK origin warnings", "dontShowAPKOriginWarnings": "Ne pas afficher les avertissements sur l'origine de l'APK",
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "moveNonInstalledAppsToBottom": "Déplacer les applications non installées vers le bas de la vue Applications",
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "gitlabPATLabel": "Jeton d'accès personnel GitLab",
"about": "About", "about": "À propos de",
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)", "requiresCredentialsInSettings": "{}: Cela nécessite des identifiants supplémentaires (dans Paramètres)",
"checkOnStart": "Check for updates on startup", "checkOnStart": "Vérifier les mises à jour au démarrage",
"tryInferAppIdFromCode": "Try inferring App ID from source code", "tryInferAppIdFromCode": "Essayez de déduire l'ID de l'application à partir du code source",
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", "removeOnExternalUninstall": "Supprimer automatiquement les applications désinstallées en externe",
"pickHighestVersionCode": "Auto-select highest version code APK", "pickHighestVersionCode": "Sélectionner automatiquement le code de version de l'APK la plus élevée",
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page", "checkUpdateOnDetailPage": "Vérifier les mises à jour lors de l'ouverture de la page détaillée d'une application",
"disablePageTransitions": "Disable page transition animations", "disablePageTransitions": "Désactiver les animations de transition de page",
"reversePageTransitions": "Reverse page transition animations", "reversePageTransitions": "Inverser les animations de transition de page",
"minStarCount": "Minimum Star Count", "minStarCount": "Nombre minimum d'étoiles",
"addInfoBelow": "Add this info below.", "addInfoBelow": "Ajoutez ces informations ci-dessous.",
"addInfoInSettings": "Add this info in the Settings.", "addInfoInSettings": "Ajoutez ces informations dans les paramètres.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.", "githubSourceNote": "La limite de débit GitHub peut être évitée à l'aide d'une clé API.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.", "sortByLastLinkSegment": "Trier uniquement sur le dernier segment du lien",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "filterReleaseNotesByRegEx": "Filtrer les notes de version par expression régulière",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression", "customLinkFilterRegex": "Filtre du lien APK personnalisé par expression régulière (par défaut '.apk$')",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')", "appsPossiblyUpdated": "Tentative de mise à jour de l'application",
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdatedNotifDescription": "Avertit l'utilisateur que des mises à jour d'une ou plusieurs applications ont été potentiellement appliquées en arrière-plan",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "xWasPossiblyUpdatedToY": "{} a peut-être été mis à jour vers {}.",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.", "enableBackgroundUpdates": "Activer les mises à jour en arrière-plan",
"enableBackgroundUpdates": "Enable background updates", "backgroundUpdateReqsExplanation": "Les mises à jour en arrière-plan peuvent ne pas être possibles pour toutes les applications.",
"backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.", "backgroundUpdateLimitsExplanation": "Le succès d'une installation en arrière-plan ne peut être déterminé qu'à l'ouverture d'Obtainium.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.", "verifyLatestTag": "Vérifiez la balise 'Latest'",
"verifyLatestTag": "Verify the 'latest' tag", "intermediateLinkRegex": " Filtrer un lien \" intermédiaire \" à visiter ",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "filterByLinkText": "Filtrer les liens par le texte du lien",
"filterByLinkText": "Filter links by link text", "intermediateLinkNotFound": "Lien intermédiaire introuvable",
"intermediateLinkNotFound": "Intermediate link not found", "intermediateLink": "Lien intermédiaire",
"intermediateLink": "Intermediate link", "exemptFromBackgroundUpdates": "Exempté des mises à jour en arrière-plan (si activé)",
"exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)", "bgUpdatesOnWiFiOnly": "Désactiver les mises à jour en arrière-plan lorsque vous n'êtes pas connecté au WiFi",
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi", "autoSelectHighestVersionCode": "Sélection automatique du code de version de l'APK la plus élevée",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "versionExtractionRegEx": "Expression régulière d'extraction de version",
"versionExtractionRegEx": "Version Extraction RegEx", "matchGroupToUse": "Groupe de correspondance pour l'expression régulière d'extraction de version",
"matchGroupToUse": "Match Group to Use", "highlightTouchTargets": "Mettre en évidence les cibles tactiles moins évidentes",
"highlightTouchTargets": "Highlight less obvious touch targets", "pickExportDir": "Choisir le répertoire d'exportation",
"pickExportDir": "Pick Export Directory", "autoExportOnChanges": "Exporter automatiquement après modification",
"autoExportOnChanges": "Auto-export on changes", "includeSettings": "Inclure les paramètres",
"includeSettings": "Include settings", "filterVersionsByRegEx": "Filtrer les versions par expression régulière",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "trySelectingSuggestedVersionCode": "Essayez de sélectionner le code de la version APK suggérée",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "dontSortReleasesList": "Conserver l'ordre des version de l'API",
"dontSortReleasesList": "Retain release order from API", "reverseSort": "Tri inversé",
"reverseSort": "Reverse sorting", "takeFirstLink": "Prendre le premier lien",
"takeFirstLink": "Take first link", "skipSort": "Sauter le tri",
"skipSort": "Skip sorting", "debugMenu": "Menu de débogage",
"debugMenu": "Debug Menu", "bgTaskStarted": "Tâche en arrière-plan démarrée - vérifier les journaux.",
"bgTaskStarted": "Background task started - check logs.", "runBgCheckNow": "Exécuter maintenant la vérification de la mise à jour en arrière-plan",
"runBgCheckNow": "Run Background Update Check Now", "versionExtractWholePage": "Appliquer l'expression régulière d'extraction de version sur l'ensemble de la page",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page", "installing": "Installation",
"installing": "Installing", "skipUpdateNotifications": "Ignorer les notifications de mise à jour",
"skipUpdateNotifications": "Skip update notifications",
"updatesAvailableNotifChannel": "Mises à jour disponibles", "updatesAvailableNotifChannel": "Mises à jour disponibles",
"appsUpdatedNotifChannel": "Applications mises à jour", "appsUpdatedNotifChannel": "Applications mises à jour",
"appsPossiblyUpdatedNotifChannel": "App Updates Attempted", "appsPossiblyUpdatedNotifChannel": "Tentative de mise à jour de l'application",
"errorCheckingUpdatesNotifChannel": "Erreur lors de la vérification des mises à jour", "errorCheckingUpdatesNotifChannel": "Erreur lors de la vérification des mises à jour",
"appsRemovedNotifChannel": "Applications supprimées", "appsRemovedNotifChannel": "Applications supprimées",
"downloadingXNotifChannel": "Téléchargement {}", "downloadingXNotifChannel": "Téléchargement {}",
"completeAppInstallationNotifChannel": "Installation complète de l'application", "completeAppInstallationNotifChannel": "Installation complète de l'application",
"checkingForUpdatesNotifChannel": "Vérification des mises à jour", "checkingForUpdatesNotifChannel": "Vérification des mises à jour",
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates", "onlyCheckInstalledOrTrackOnlyApps": "Vérifiez uniquement les mises à jour des applications installées et 'Track-Only'",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "Prise en charge des URL APK fixes",
"selectX": "Select {}", "selectX": "Sélectionner {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Autoriser les téléchargements parallèles",
"installMethod": "Installation method", "useShizuku": "Utiliser Shizuku ou Sui pour l'installation",
"normal": "Normal", "shizukuBinderNotFound": "Service Shizuku compatible non trouvé",
"shizuku": "Shizuku", "shizukuOld": "Ancienne version de Shizuku (<11) - la mettre à jour",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku fonctionne sur Android < 8.1 avec ADB - mettre à jour Android ou utiliser Sui à la place",
"shizukuBinderNotFound": "Shizuku is not running", "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 comme version détectée par le système d'exploitation",
"requestHeader": "En-tête de demande",
"useLatestAssetDateAsReleaseDate": "Utiliser le dernier élément téléversé comme date de sortie",
"defaultPseudoVersioningMethod": "Méthode de pseudo-version par défaut",
"partialAPKHash": "Hash APK partiel",
"APKLinkHash": "Hash de lien APK",
"directAPKLink": "Lien APK direct",
"pseudoVersionInUse": "Une pseudo-version est utilisée",
"installed": "Installée",
"latest": "Dernier",
"invertRegEx": "Inverser l'expression régulière",
"note": "Note",
"selfHostedNote": "La liste déroulante \"{}\" peut être utilisée pour accéder aux instances auto-hébergées/personnalisées de n'importe quelle source.",
"badDownload": "L'APK n'a pas pu être analysé (téléchargement incompatible ou partiel)",
"beforeNewInstallsShareToAppVerifier": "Partager les nouvelles applications avec AppVerifier (si disponible)",
"appVerifierInstructionToast": "Partagez avec AppVerifier, puis revenez ici lorsque vous êtes prêt.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Supprimer l'application ?", "one": "Supprimer l'application ?",
"other": "Supprimer les applications ?" "other": "Supprimer les applications ?"
@ -296,12 +318,12 @@
"other": "Trop de demandes (taux limité) - réessayez dans {} minutes" "other": "Trop de demandes (taux limité) - réessayez dans {} minutes"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "La vérification de la mise à jour en arrière-plan a rencontré un {}, planifiera une nouvelle tentative de vérification dans {} minute", "one": "La vérification de la mise à jour en arrière-plan a rencontré un {}, une nouvelle tentative de vérification sera planifié dans {} minute",
"other": "La vérification de la mise à jour en arrière-plan a rencontré un {}, planifiera une nouvelle tentative de vérification dans {} minutes" "other": "La vérification de la mise à jour en arrière-plan a rencontré un {}, une nouvelle tentative de vérification sera planifié dans {} minute"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "La vérification des mises à jour en arrière-plan trouvée {} mise à jour - avertira l'utilisateur si nécessaire", "one": "La vérification des mises à jour en arrière-plan a trouvée {} mise à jour - l'utilisateur sera notifié si nécessaire",
"other": "La vérification des mises à jour en arrière-plan a trouvé {} mises à jour - avertira l'utilisateur si nécessaire" "other": "La vérification des mises à jour en arrière-plan a trouvé {} mises à jour - l'utilisateur sera notifié si nécessaire"
}, },
"apps": { "apps": {
"one": "{} Application", "one": "{} Application",
@ -309,10 +331,10 @@
}, },
"url": { "url": {
"one": "{} URL", "one": "{} URL",
"other": "{} URLs" "other": "{} URL"
}, },
"minute": { "minute": {
"one": "{} Minute", "one": "{} Minutes",
"other": "{} Minutes" "other": "{} Minutes"
}, },
"hour": { "hour": {
@ -335,8 +357,16 @@
"one": "{} et 1 autre application ont été mises à jour.", "one": "{} et 1 autre application ont été mises à jour.",
"other": "{} et {} autres applications ont été mises à jour." "other": "{} et {} autres applications ont été mises à jour."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Échec de la mise à jour de {} et d'une autre application.",
"other": "Échec de la mise à jour de {} et {} autres applications."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.", "une": "{} et 1 application supplémentaire ont peut-être été mises à jour.",
"other": "{} and {} more apps may have been updated." "other": "{} et {} autres applications peuvent avoir été mises à jour."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -22,12 +22,15 @@
"requiredInBrackets": "(Kötelező)", "requiredInBrackets": "(Kötelező)",
"dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI", "dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI",
"colour": "Szín", "colour": "Szín",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Használja az Ön által használt anyagot",
"githubStarredRepos": "GitHub Csillagos Repo-k", "githubStarredRepos": "GitHub Csillagos Repo-k",
"uname": "Felh.név", "uname": "Felh.név",
"wrongArgNum": "Rossz számú argumentumot adott meg", "wrongArgNum": "Rossz számú argumentumot adott meg",
"xIsTrackOnly": "A(z) {} csak nyomonkövethető", "xIsTrackOnly": "A(z) {} csak nyomonkövethető",
"source": "Forrás", "source": "Forrás",
"app": "App", "app": "Alkalmazás",
"appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások 'Csak nyomon követhetőek'.", "appsFromSourceAreTrackOnly": "Az ebből a forrásból származó alkalmazások 'Csak nyomon követhetőek'.",
"youPickedTrackOnly": "A 'Csak követés' opciót választotta.", "youPickedTrackOnly": "A 'Csak 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.", "trackOnlyAppDescription": "Az alkalmazás frissítéseit nyomon követi, de az Obtainium nem tudja letölteni vagy telepíteni.",
@ -51,9 +54,8 @@
"percentProgress": "Folyamat: {}%", "percentProgress": "Folyamat: {}%",
"pleaseWait": "Kis türelmet", "pleaseWait": "Kis türelmet",
"updateAvailable": "Frissítés érhető el", "updateAvailable": "Frissítés érhető el",
"estimateInBracketsShort": "(Becsült)",
"notInstalled": "Nem telepített", "notInstalled": "Nem telepített",
"estimateInBrackets": "(Becslés)", "pseudoVersion": "ál-verzió",
"selectAll": "Mindet kiválaszt", "selectAll": "Mindet kiválaszt",
"deselectX": "Törölje {} kijelölését", "deselectX": "Törölje {} kijelölését",
"xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.", "xWillBeRemovedButRemainInstalled": "A(z) {} el lesz távolítva az Obtainiumból, de továbbra is telepítve marad az eszközön.",
@ -73,19 +75,20 @@
"unpinFromTop": "Eltávolít felülről", "unpinFromTop": "Eltávolít felülről",
"resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?", "resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?",
"installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.", "installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.",
"customLinkMessage": "Ezek a hivatkozások a telepített Obtainium-mal rendelkező eszközökön működnek",
"shareAppConfigLinks": "App konfiguráció megosztása HTML linkként",
"shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit", "shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit",
"resetInstallStatus": "Telepítési állapot visszaállítása", "resetInstallStatus": "Telepítési állapot visszaállítása",
"more": "További", "more": "További",
"removeOutdatedFilter": "Távolítsa el az elavult app szűrőt", "removeOutdatedFilter": "Távolítsa el az elavult app szűrőt",
"showOutdatedOnly": "Csak az elavult appok megjelenítése", "showOutdatedOnly": "Csak az elavult appok megjelenítése",
"filter": "Szűrő", "filter": "Szűrő",
"filterActive": "Szűrő *",
"filterApps": "Appok szűrése", "filterApps": "Appok szűrése",
"appName": "App név", "appName": "App név",
"author": "Szerző", "author": "Szerző",
"upToDateApps": "Naprakész appok", "upToDateApps": "Naprakész appok",
"nonInstalledApps": "Nem telepített appok", "nonInstalledApps": "Nem telepített appok",
"importExport": "Import/Export", "importExport": "Import Export",
"settings": "Beállítások", "settings": "Beállítások",
"exportedTo": "Exportálva ide {}", "exportedTo": "Exportálva ide {}",
"obtainiumExport": "Obtainium Adat Exportálás", "obtainiumExport": "Obtainium Adat Exportálás",
@ -98,7 +101,7 @@
"line": "Sor", "line": "Sor",
"searchX": "Keresés {}", "searchX": "Keresés {}",
"noResults": "Nincs találat", "noResults": "Nincs találat",
"importX": "Import {}", "importX": "Importálás: {}",
"importedAppsIdDisclaimer": "Előfordulhat, hogy az importált appok helytelenül \"Nincs telepítve\" jelzéssel jelennek meg.\nA probléma megoldásához telepítse újra őket az Obtainiumon keresztül.\nEz nem érinti az alkalmazásadatokat.\n\nCsak az URL-ekre és a harmadik féltől származó importálási módszerekre vonatkozik..", "importedAppsIdDisclaimer": "Előfordulhat, hogy az importált appok helytelenül \"Nincs telepítve\" jelzéssel jelennek meg.\nA probléma megoldásához telepítse újra őket az Obtainiumon keresztül.\nEz nem érinti az alkalmazásadatokat.\n\nCsak az URL-ekre és a harmadik féltől származó importálási módszerekre vonatkozik..",
"importErrors": "Importálási hibák", "importErrors": "Importálási hibák",
"importedXOfYApps": "{}/{} app importálva.", "importedXOfYApps": "{}/{} app importálva.",
@ -110,8 +113,6 @@
"dark": "Sötét", "dark": "Sötét",
"light": "Világos", "light": "Világos",
"followSystem": "Rendszer szerint", "followSystem": "Rendszer szerint",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Használjon teljesen fekete sötét témát", "useBlackTheme": "Használjon teljesen fekete sötét témát",
"appSortBy": "App rendezés...", "appSortBy": "App rendezés...",
"authorName": "Szerző/Név", "authorName": "Szerző/Név",
@ -145,8 +146,10 @@
"noNewUpdates": "Nincsenek új frissítések.", "noNewUpdates": "Nincsenek új frissítések.",
"xHasAnUpdate": "A(z) {} frissítést kapott.", "xHasAnUpdate": "A(z) {} frissítést kapott.",
"appsUpdated": "Alkalmazások frissítve", "appsUpdated": "Alkalmazások frissítve",
"appsNotUpdated": "Nem sikerült frissíteni az alkalmazásokat",
"appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben", "appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben",
"xWasUpdatedToY": "{} frissítve a következőre: {}.", "xWasUpdatedToY": "{} frissítve a következőre: {}.",
"xWasNotUpdatedToY": "A {} frissítése a {}-ra nem sikerült.",
"errorCheckingUpdates": "Hiba a frissítések keresésekor", "errorCheckingUpdates": "Hiba a frissítések keresésekor",
"errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen", "errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen",
"appsRemoved": "Alkalmazások eltávolítva", "appsRemoved": "Alkalmazások eltávolítva",
@ -169,14 +172,13 @@
"lastUpdateCheckX": "Frissítés ellenőrizve: {}", "lastUpdateCheckX": "Frissítés ellenőrizve: {}",
"remove": "Eltávolítás", "remove": "Eltávolítás",
"yesMarkUpdated": "Igen, megjelölés frissítettként", "yesMarkUpdated": "Igen, megjelölés frissítettként",
"fdroid": "F-Droid Official", "fdroid": "F-Droid hivatalos",
"appIdOrName": "App ID vagy név", "appIdOrName": "App ID vagy név",
"appId": "App ID", "appId": "Alkalmazásazonosító",
"appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel", "appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel",
"reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak", "reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak",
"fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo", "fdroidThirdPartyRepo": "F-Droid Harmadik-fél Repo",
"steam": "Steam", "steamMobile": "Steam mobil",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam Chat",
"install": "Telepít", "install": "Telepít",
"markInstalled": "Telepítettnek jelöl", "markInstalled": "Telepítettnek jelöl",
@ -186,6 +188,9 @@
"disableVersionDetection": "Verzió érzékelés letiltása", "disableVersionDetection": "Verzió érzékelés letiltása",
"noVersionDetectionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzióérzékelés nem működik megfelelően.", "noVersionDetectionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzióérzékelés nem működik megfelelően.",
"downloadingX": "{} letöltés", "downloadingX": "{} letöltés",
"downloadX": "Letöltés {}",
"downloadedX": "Letöltés {}",
"releaseAsset": "Kiadási tartalom",
"downloadNotifDescription": "Értesíti a felhasználót az app letöltésének előrehaladásáról", "downloadNotifDescription": "Értesíti a felhasználót az app letöltésének előrehaladásáról",
"noAPKFound": "Nem található APK", "noAPKFound": "Nem található APK",
"noVersionDetection": "Nincs verzió érzékelés", "noVersionDetection": "Nincs verzió érzékelés",
@ -194,7 +199,7 @@
"category": "Kategória", "category": "Kategória",
"noCategory": "Nincs kategória", "noCategory": "Nincs kategória",
"noCategories": "Nincsenek kategóriák", "noCategories": "Nincsenek kategóriák",
"deleteCategoryQuestion": "Törli a kategóriát?", "deleteCategoriesQuestion": "Törli a kategóriákat?",
"categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.", "categoryDeleteWarning": "A(z) {} összes app kategorizálatlan állapotba kerül.",
"addCategory": "Új kategória", "addCategory": "Új kategória",
"label": "Címke", "label": "Címke",
@ -211,6 +216,7 @@
"changes": "Változtatások", "changes": "Változtatások",
"releaseDate": "Kiadás dátuma", "releaseDate": "Kiadás dátuma",
"importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)", "importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)",
"versionDetectionExplanation": "A verzió karakterlánc egyeztetése az OS által észlelt verzióval",
"versionDetection": "Verzió érzékelés", "versionDetection": "Verzió érzékelés",
"standardVersionDetection": "Alapért. verzió érzékelés", "standardVersionDetection": "Alapért. verzió érzékelés",
"groupByCategory": "Csoportosítás Kategória alapján", "groupByCategory": "Csoportosítás Kategória alapján",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést", "dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
"dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket", "dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket",
"moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára", "moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára",
"gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)", "gitlabPATLabel": "GitLab személyes hozzáférési token",
"about": "Rólunk", "about": "Rólunk",
"requiresCredentialsInSettings": "{}: Ehhez további hitelesítő adatokra van szükség (a Beállításokban)", "requiresCredentialsInSettings": "{}: Ehhez további hitelesítő adatokra van szükség (a Beállításokban)",
"checkOnStart": "Egyszer az alkalmazás indításakor is", "checkOnStart": "Egyszer az alkalmazás indításakor is",
@ -234,20 +240,20 @@
"addInfoBelow": "Adja hozzá ezt az infót alább.", "addInfoBelow": "Adja hozzá ezt az infót alább.",
"addInfoInSettings": "Adja hozzá ezt az infót a Beállításokban.", "addInfoInSettings": "Adja hozzá ezt az infót a Beállításokban.",
"githubSourceNote": "A GitHub sebességkorlátozás elkerülhető API-kulcs használatával.", "githubSourceNote": "A GitHub sebességkorlátozás elkerülhető API-kulcs használatával.",
"gitlabSourceNote": "Előfordulhat, hogy a GitLab APK kibontása nem működik API-kulcs nélkül.",
"sortByLastLinkSegment": "Rendezés csak a link utolsó szegmense szerint", "sortByLastLinkSegment": "Rendezés csak a link utolsó szegmense szerint",
"filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel", "filterReleaseNotesByRegEx": "Kiadási megjegyzések szűrése reguláris kifejezéssel",
"customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')", "customLinkFilterRegex": "Egyéni APK hivatkozásszűrő reguláris kifejezéssel (Alapérték '.apk$')",
"appsPossiblyUpdated": "App frissítési kísérlet", "appsPossiblyUpdated": "App frissítési kísérlet",
"appsPossiblyUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás frissítése lehetséges a háttérben", "appsPossiblyUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy vagy több alkalmazás frissítése lehetséges a háttérben",
"xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.", "xWasPossiblyUpdatedToY": "{} frissítve lehet erre {}.",
"enableBackgroundUpdates": "Frissítések a háttérben",
"backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.", "backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.",
"backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.", "backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.",
"verifyLatestTag": "Ellenőrizze a „legújabb” címkét", "verifyLatestTag": "Ellenőrizze a „legújabb” címkét",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Szűrés egy 'köztes' látogatási linkre",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "A hivatkozások szűrése linkszöveg alapján",
"intermediateLinkNotFound": "Közvetítő link nem található", "intermediateLinkNotFound": "Köztes link nem található",
"intermediateLink": "Intermediate link", "intermediateLink": "Köztes link",
"exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)", "exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)",
"bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n", "bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n",
"autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása", "autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása",
@ -256,16 +262,15 @@
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat", "highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
"pickExportDir": "Válassza az Exportálási könyvtárat", "pickExportDir": "Válassza az Exportálási könyvtárat",
"autoExportOnChanges": "Auto-exportálás a változások után", "autoExportOnChanges": "Auto-exportálás a változások után",
"includeSettings": "Include settings", "includeSettings": "Tartalmazza a beállításokat",
"filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel", "filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t", "trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése", "dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
"reverseSort": "Fordított rendezés", "reverseSort": "Fordított rendezés",
"takeFirstLink": "Take first link", "takeFirstLink": "Vegye az első linket",
"skipSort": "Skip sorting", "skipSort": "A válogatás kihagyása",
"debugMenu": "Hibakereső menü", "debugMenu": "Hibakereső menü",
"bgTaskStarted": "A háttérfeladat elindult ellenőrizze a naplókat.", "bgTaskStarted": "A háttérfeladat elindult ellenőrizze a naplókat.",
"enableBackgroundUpdates": "Frissítések a háttérben",
"runBgCheckNow": "Futtassa a Háttérben frissítés ellenőrzését most", "runBgCheckNow": "Futtassa a Háttérben frissítés ellenőrzését most",
"versionExtractWholePage": "Alkalmazza a Version Extraction Regex-et az egész oldalra", "versionExtractWholePage": "Alkalmazza a Version Extraction Regex-et az egész oldalra",
"installing": "Telepítés", "installing": "Telepítés",
@ -282,14 +287,31 @@
"supportFixedAPKURL": "Támogatja a rögzített APK URL-eket", "supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
"selectX": "Kiválaszt {}", "selectX": "Kiválaszt {}",
"parallelDownloads": "Párhuzamos letöltéseket enged", "parallelDownloads": "Párhuzamos letöltéseket enged",
"installMethod": "Installation method", "useShizuku": "Használja Shizuku vagy Sui telepítéséhez",
"normal": "Normal", "shizukuBinderNotFound": "A Shizuku nem fut",
"shizuku": "Shizuku", "shizukuOld": "Régi Shizuku verzió (<11) - frissítsd!",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku fut Android < 8.1 ADB-vel - frissítse az Androidot vagy használja a Sui-t helyette",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuPretendToBeGooglePlay": "Állítsa be a Google Play-t telepítési forrásként (ha Shizuku-t használ)",
"useSystemFont": "Használja a rendszer betűtípusát",
"useVersionCodeAsOSVersion": "Az app verziókód használata a rendszer által észlelt verzióként",
"requestHeader": "Kérelem fejléc",
"useLatestAssetDateAsReleaseDate": "Használja a legújabb tartalomfeltöltést megjelenési dátumként",
"defaultPseudoVersioningMethod": "Alapértelmezett álverziós módszer",
"partialAPKHash": "Részleges APK Hash",
"APKLinkHash": "APK Link Hash",
"directAPKLink": "Közvetlen APK Link",
"pseudoVersionInUse": "Egy álverzió van használatban",
"installed": "Telepített",
"latest": "Legújabb",
"invertRegEx": "Invertált reguláris kifejezés",
"note": "Megjegyzés:",
"selfHostedNote": "A \"{}\" legördülő menü használható bármely forrás saját üzemeltetésű/egyéni példányainak eléréséhez.",
"badDownload": "Az APK-t nem lehetett elemezni (inkompatibilis vagy részleges letöltés)",
"beforeNewInstallsShareToAppVerifier": "Új alkalmazások megosztása az AppVerifierrel (ha elérhető)",
"appVerifierInstructionToast": "Ossza meg az AppVerifierrel, majd térjen vissza ide, ha kész.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?", "one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?" "other": "Eltávolítja az alkalmazásokat?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva", "one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva",
@ -335,8 +357,16 @@
"one": "A(z) {} és 1 további alkalmazás frissítve.", "one": "A(z) {} és 1 további alkalmazás frissítve.",
"other": "{} és {} további alkalmazás frissítve." "other": "{} és {} további alkalmazás frissítve."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Nem sikerült frissíteni {} és még 1 alkalmazást.",
"other": "Nem sikerült frissíteni {} és {} további alkalmazásokat."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} és 1 további alkalmazás is frissült.", "one": "{} és 1 további alkalmazás is frissült.",
"other": "{} és {} további alkalmazás is frissült." "other": "{} és {} további alkalmazás is frissült."
},
"apk": {
"one": "{} APK",
"other": "{} APK-k"
} }
} }

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(richiesto)", "requiredInBrackets": "(richiesto)",
"dropdownNoOptsError": "ERRORE: LA TENDINA DEVE AVERE ALMENO UN'OPZIONE", "dropdownNoOptsError": "ERRORE: LA TENDINA DEVE AVERE ALMENO UN'OPZIONE",
"colour": "Colore", "colour": "Colore",
"standard": "Standard",
"custom": "Personalizzato",
"useMaterialYou": "Utilizzate il materiale che avete a disposizione",
"githubStarredRepos": "repository stellati da GitHub", "githubStarredRepos": "repository stellati da GitHub",
"uname": "Nome utente", "uname": "Nome utente",
"wrongArgNum": "Numero di argomenti forniti errato", "wrongArgNum": "Numero di argomenti forniti errato",
@ -51,9 +54,8 @@
"percentProgress": "Avanzamento: {}%", "percentProgress": "Avanzamento: {}%",
"pleaseWait": "In attesa", "pleaseWait": "In attesa",
"updateAvailable": "Aggiornamento disponibile", "updateAvailable": "Aggiornamento disponibile",
"estimateInBracketsShort": "(stim.)",
"notInstalled": "Non installato", "notInstalled": "Non installato",
"estimateInBrackets": "(stimato)", "pseudoVersion": "pseudo-versione",
"selectAll": "Seleziona tutto", "selectAll": "Seleziona tutto",
"deselectX": "Deseleziona {}", "deselectX": "Deseleziona {}",
"xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.", "xWillBeRemovedButRemainInstalled": "Verà effettuata la rimozione di {}, ma non la disinstallazione.",
@ -66,20 +68,21 @@
"installUpdateApps": "Installa/Aggiorna app", "installUpdateApps": "Installa/Aggiorna app",
"installUpdateSelectedApps": "Installa/Aggiorna le app selezionate", "installUpdateSelectedApps": "Installa/Aggiorna le app selezionate",
"markXSelectedAppsAsUpdated": "Contrassegnare le {} app selezionate come aggiornate?", "markXSelectedAppsAsUpdated": "Contrassegnare le {} app selezionate come aggiornate?",
"no": "No", "no": "NO",
"yes": "Sì", "yes": "Sì",
"markSelectedAppsUpdated": "Contrassegna le app selezionate come aggiornate", "markSelectedAppsUpdated": "Contrassegna le app selezionate come aggiornate",
"pinToTop": "Fissa in alto", "pinToTop": "Fissa in alto",
"unpinFromTop": "Rimuovi dall'alto", "unpinFromTop": "Rimuovi dall'alto",
"resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle app selezionate?", "resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle app selezionate?",
"installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni app selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'app in Obtainium non sia corretta a causa di un aggiornamento fallito o di altri problemi.", "installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni app selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'app in Obtainium non sia corretta a causa di un aggiornamento fallito o di altri problemi.",
"customLinkMessage": "Questi collegamenti funzionano sui dispositivi con Obtainium installato",
"shareAppConfigLinks": "Condividi la configurazione dell'app come collegamento HTML",
"shareSelectedAppURLs": "Condividi gli URL delle app selezionate", "shareSelectedAppURLs": "Condividi gli URL delle app selezionate",
"resetInstallStatus": "Ripristina lo stato d'installazione", "resetInstallStatus": "Ripristina lo stato d'installazione",
"more": "Altro", "more": "Altro",
"removeOutdatedFilter": "Rimuovi il filtro per le app non aggiornate", "removeOutdatedFilter": "Rimuovi il filtro per le app non aggiornate",
"showOutdatedOnly": "Mostra solo le app non aggiornate", "showOutdatedOnly": "Mostra solo le app non aggiornate",
"filter": "Filtri", "filter": "Filtri",
"filterActive": "Filtri *",
"filterApps": "Filtra app", "filterApps": "Filtra app",
"appName": "Nome dell'app", "appName": "Nome dell'app",
"author": "Autore", "author": "Autore",
@ -110,9 +113,7 @@
"dark": "Scuro", "dark": "Scuro",
"light": "Chiaro", "light": "Chiaro",
"followSystem": "Segui il sistema", "followSystem": "Segui il sistema",
"obtainium": "Obtainium", "useBlackTheme": "Usa il tema nero puro",
"materialYou": "Material You",
"useBlackTheme": "Usa il tema Nero puro",
"appSortBy": "App ordinate per", "appSortBy": "App ordinate per",
"authorName": "Autore/Nome", "authorName": "Autore/Nome",
"nameAuthor": "Nome/Autore", "nameAuthor": "Nome/Autore",
@ -145,8 +146,10 @@
"noNewUpdates": "Nessun nuovo aggiornamento.", "noNewUpdates": "Nessun nuovo aggiornamento.",
"xHasAnUpdate": "Aggiornamento disponibile per {}", "xHasAnUpdate": "Aggiornamento disponibile per {}",
"appsUpdated": "App aggiornate", "appsUpdated": "App aggiornate",
"appsNotUpdated": "Impossibile aggiornare le applicazioni",
"appsUpdatedNotifDescription": "Notifica all'utente che una o più app sono state aggiornate in secondo piano", "appsUpdatedNotifDescription": "Notifica all'utente che una o più app sono state aggiornate in secondo piano",
"xWasUpdatedToY": "{} è stato aggiornato alla {}.", "xWasUpdatedToY": "{} è stato aggiornato alla {}.",
"xWasNotUpdatedToY": "Impossibile aggiornare {} a {}.",
"errorCheckingUpdates": "Controllo degli errori per gli aggiornamenti", "errorCheckingUpdates": "Controllo degli errori per gli aggiornamenti",
"errorCheckingUpdatesNotifDescription": "Una notifica che mostra quando il controllo degli aggiornamenti in secondo piano fallisce", "errorCheckingUpdatesNotifDescription": "Una notifica che mostra quando il controllo degli aggiornamenti in secondo piano fallisce",
"appsRemoved": "App rimosse", "appsRemoved": "App rimosse",
@ -175,9 +178,8 @@
"appWithIdOrNameNotFound": "Non è stata trovata alcuna app con quell'ID o nome", "appWithIdOrNameNotFound": "Non è stata trovata alcuna app con quell'ID o nome",
"reposHaveMultipleApps": "I repository possono contenere più app", "reposHaveMultipleApps": "I repository possono contenere più app",
"fdroidThirdPartyRepo": "Repository F-Droid di terze parti", "fdroidThirdPartyRepo": "Repository F-Droid di terze parti",
"steam": "Steam", "steamMobile": "Mobile a vapore",
"steamMobile": "Steam Mobile", "steamChat": "Chat di vapore",
"steamChat": "Steam Chat",
"install": "Installa", "install": "Installa",
"markInstalled": "Contrassegna come installata", "markInstalled": "Contrassegna come installata",
"update": "Aggiorna", "update": "Aggiorna",
@ -186,6 +188,9 @@
"disableVersionDetection": "Disattiva il rilevamento della versione", "disableVersionDetection": "Disattiva il rilevamento della versione",
"noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.", "noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.",
"downloadingX": "Scaricamento di {} in corso", "downloadingX": "Scaricamento di {} in corso",
"downloadX": "Scarica {}",
"downloadedX": "Scaricato {}",
"releaseAsset": "Rilascio Asset",
"downloadNotifDescription": "Notifica all'utente lo stato di avanzamento del download di un'app", "downloadNotifDescription": "Notifica all'utente lo stato di avanzamento del download di un'app",
"noAPKFound": "Nessun APK trovato", "noAPKFound": "Nessun APK trovato",
"noVersionDetection": "Disattiva rilevamento di versione", "noVersionDetection": "Disattiva rilevamento di versione",
@ -211,6 +216,7 @@
"changes": "Novità", "changes": "Novità",
"releaseDate": "Data di rilascio", "releaseDate": "Data di rilascio",
"importFromURLsInFile": "Importa da URL in file (come OPML)", "importFromURLsInFile": "Importa da URL in file (come OPML)",
"versionDetectionExplanation": "Riconcilia la stringa della versione con la versione rilevata dal sistema operativo",
"versionDetection": "Rilevamento di versione", "versionDetection": "Rilevamento di versione",
"standardVersionDetection": "Rilevamento di versione standard", "standardVersionDetection": "Rilevamento di versione standard",
"groupByCategory": "Raggruppa per categoria", "groupByCategory": "Raggruppa per categoria",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'", "dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'",
"dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK", "dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK",
"moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista", "moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista",
"gitlabPATLabel": "GitLab Personal Access Token\n(attiva la ricerca e migliora la rilevazione di apk)", "gitlabPATLabel": "GitLab Token di accesso personale",
"about": "Informazioni", "about": "Informazioni",
"requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)", "requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)",
"checkOnStart": "Controlla una volta all'avvio", "checkOnStart": "Controlla una volta all'avvio",
@ -234,8 +240,7 @@
"addInfoBelow": "Aggiungi questa info sotto.", "addInfoBelow": "Aggiungi questa info sotto.",
"addInfoInSettings": "Aggiungi questa info nelle impostazioni.", "addInfoInSettings": "Aggiungi questa info nelle impostazioni.",
"githubSourceNote": "Il limite di ricerca GitHub può essere evitato usando una chiave API.", "githubSourceNote": "Il limite di ricerca GitHub può essere evitato usando una chiave API.",
"gitlabSourceNote": "L'estrazione di APK da GitLab potrebbe non funzionare senza chiave API.", "sortByLastLinkSegment": "Ordina solo in base all'ultimo segmento del collegamento",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Filtra le note di rilascio con espressione regolare", "filterReleaseNotesByRegEx": "Filtra le note di rilascio con espressione regolare",
"customLinkFilterRegex": "Filtra link APK personalizzato con espressione regolare (predefinito '.apk$')", "customLinkFilterRegex": "Filtra link APK personalizzato con espressione regolare (predefinito '.apk$')",
"appsPossiblyUpdated": "Aggiornamenti app tentati", "appsPossiblyUpdated": "Aggiornamenti app tentati",
@ -245,10 +250,10 @@
"backgroundUpdateReqsExplanation": "Gli aggiornamenti in secondo piano potrebbero non essere possibili per tutte le app.", "backgroundUpdateReqsExplanation": "Gli aggiornamenti in secondo piano potrebbero non essere possibili per tutte le app.",
"backgroundUpdateLimitsExplanation": "La riuscita di un'installazione in secondo piano può essere determinata solo quando viene aperto Obtainium.", "backgroundUpdateLimitsExplanation": "La riuscita di un'installazione in secondo piano può essere determinata solo quando viene aperto Obtainium.",
"verifyLatestTag": "Verifica l'etichetta 'Latest'", "verifyLatestTag": "Verifica l'etichetta 'Latest'",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Filtro per un collegamento 'intermedio' da visitare",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filtra i collegamenti in base al testo del collegamento",
"intermediateLinkNotFound": "Link intermedio non trovato", "intermediateLinkNotFound": "Link intermedio non trovato",
"intermediateLink": "Intermediate link", "intermediateLink": "Collegamento intermedio",
"exemptFromBackgroundUpdates": "Esente da aggiornamenti in secondo piano (se attivo)", "exemptFromBackgroundUpdates": "Esente da aggiornamenti in secondo piano (se attivo)",
"bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa il WiFi", "bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa il WiFi",
"autoSelectHighestVersionCode": "Auto-seleziona APK con versionCode più alto", "autoSelectHighestVersionCode": "Auto-seleziona APK con versionCode più alto",
@ -282,11 +287,28 @@
"supportFixedAPKURL": "Supporta URL fissi di APK", "supportFixedAPKURL": "Supporta URL fissi di APK",
"selectX": "Seleziona {}", "selectX": "Seleziona {}",
"parallelDownloads": "Permetti download paralleli", "parallelDownloads": "Permetti download paralleli",
"installMethod": "Metodo d'installazione", "useShizuku": "Utilizzare Shizuku o Sui per installare",
"normal": "Normale",
"shizuku": "Shizuku",
"root": "Root",
"shizukuBinderNotFound": "Shizuku non è in esecuzione", "shizukuBinderNotFound": "Shizuku non è in esecuzione",
"shizukuOld": "Vecchia versione di Shizuku (<11) - aggiornarla",
"shizukuOldAndroidWithADB": "Shizuku funziona su Android < 8.1 con ADB - aggiornare Android o utilizzare Sui al suo posto",
"shizukuPretendToBeGooglePlay": "Impostare Google Play come fonte di installazione (se si usa Shizuku)",
"useSystemFont": "Usa i caratteri di sistema",
"useVersionCodeAsOSVersion": "Usa il codice versione dell'app come versione rilevata dal sistema operativo",
"requestHeader": "Intestazione della richiesta",
"useLatestAssetDateAsReleaseDate": "Usa l'ultimo caricamento della risorsa come data di rilascio",
"defaultPseudoVersioningMethod": "Metodo di pseudoversione predefinito",
"partialAPKHash": "Hash APK parziale",
"APKLinkHash": "Hash collegamento APK",
"directAPKLink": "Collegamento APK diretto",
"pseudoVersionInUse": "È in uso una pseudo-versione",
"installed": "Installato",
"latest": "Ultimo",
"invertRegEx": "Inverti espressione regolare",
"note": "Nota",
"selfHostedNote": "Il menu a tendina \"{}\" può essere usato per raggiungere istanze autogestite/personali di qualsiasi fonte.",
"badDownload": "Non è stato possibile analizzare l'APK (download incompatibile o parziale).",
"beforeNewInstallsShareToAppVerifier": "Condividere le nuove applicazioni con AppVerifier (se disponibile)",
"appVerifierInstructionToast": "Condividete con AppVerifier, quindi tornate qui quando siete pronti.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Rimuovere l'app?", "one": "Rimuovere l'app?",
"other": "Rimuovere le app?" "other": "Rimuovere le app?"
@ -335,8 +357,16 @@
"one": "{} e un'altra app sono state aggiornate.", "one": "{} e un'altra app sono state aggiornate.",
"other": "{} e altre {} app sono state aggiornate." "other": "{} e altre {} app sono state aggiornate."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Non è riuscito ad aggiornare {} e altre 1 app.",
"other": "Non è riuscito ad aggiornare {} e {} altre applicazioni."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} e un'altra app potrebbero essere state aggiornate.", "one": "{} e un'altra app potrebbero essere state aggiornate.",
"other": "{} e altre {} app potrebbero essere state aggiornate." "other": "{} e altre {} app potrebbero essere state aggiornate."
},
"apk": {
"one": "{} APK",
"other": "{} APK"
} }
} }

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(必須)", "requiredInBrackets": "(必須)",
"dropdownNoOptsError": "エラー: ドロップダウンには、少なくとも1つのオプションが必要です", "dropdownNoOptsError": "エラー: ドロップダウンには、少なくとも1つのオプションが必要です",
"colour": "カラー", "colour": "カラー",
"standard": "スタンダード",
"custom": "カスタム",
"useMaterialYou": "使用素材",
"githubStarredRepos": "Githubでスターしたリポジトリ", "githubStarredRepos": "Githubでスターしたリポジトリ",
"uname": "ユーザー名", "uname": "ユーザー名",
"wrongArgNum": "提供する引数の数が間違っています", "wrongArgNum": "提供する引数の数が間違っています",
@ -51,9 +54,8 @@
"percentProgress": "ダウンロード中: {}%", "percentProgress": "ダウンロード中: {}%",
"pleaseWait": "しばらくお待ちください", "pleaseWait": "しばらくお待ちください",
"updateAvailable": "アップデートが利用可能", "updateAvailable": "アップデートが利用可能",
"estimateInBracketsShort": "(推定)",
"notInstalled": "未インストール", "notInstalled": "未インストール",
"estimateInBrackets": "(推定)", "pseudoVersion": "疑似バージョン",
"selectAll": "すべて選択", "selectAll": "すべて選択",
"deselectX": "{}件の選択を解除", "deselectX": "{}件の選択を解除",
"xWillBeRemovedButRemainInstalled": "{} はObtainiumから削除されますが、デバイスにはインストールされたままです。", "xWillBeRemovedButRemainInstalled": "{} はObtainiumから削除されますが、デバイスにはインストールされたままです。",
@ -73,13 +75,14 @@
"unpinFromTop": "トップから固定解除", "unpinFromTop": "トップから固定解除",
"resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?", "resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?",
"installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗した場合など、Obtainiumに表示されるアプリのバージョンが正しくない場合に有効です。", "installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗した場合など、Obtainiumに表示されるアプリのバージョンが正しくない場合に有効です。",
"customLinkMessage": "これらのリンクはObtainiumがインストールされたデバイスで動作します",
"shareAppConfigLinks": "アプリの設定をHTMLリンクとして共有する",
"shareSelectedAppURLs": "選択したアプリのURLを共有する", "shareSelectedAppURLs": "選択したアプリのURLを共有する",
"resetInstallStatus": "インストール状態をリセットする", "resetInstallStatus": "インストール状態をリセットする",
"more": "もっと見る", "more": "もっと見る",
"removeOutdatedFilter": "アップデートが存在するアプリのフィルターを解除", "removeOutdatedFilter": "アップデートが存在するアプリのフィルターを解除",
"showOutdatedOnly": "アップデートが存在するアプリのみ表示する", "showOutdatedOnly": "アップデートが存在するアプリのみ表示する",
"filter": "フィルター", "filter": "フィルター",
"filterActive": "フィルター *",
"filterApps": "アプリをフィルタリングする", "filterApps": "アプリをフィルタリングする",
"appName": "アプリ名", "appName": "アプリ名",
"author": "作者", "author": "作者",
@ -110,8 +113,6 @@
"dark": "ダーク", "dark": "ダーク",
"light": "ライト", "light": "ライト",
"followSystem": "システムに従う", "followSystem": "システムに従う",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "ピュアブラックダークテーマを使用する", "useBlackTheme": "ピュアブラックダークテーマを使用する",
"appSortBy": "アプリの並び方", "appSortBy": "アプリの並び方",
"authorName": "作者名/アプリ名", "authorName": "作者名/アプリ名",
@ -145,16 +146,18 @@
"noNewUpdates": "新しいアップデートはありません", "noNewUpdates": "新しいアップデートはありません",
"xHasAnUpdate": "{} のアップデートが利用可能です。", "xHasAnUpdate": "{} のアップデートが利用可能です。",
"appsUpdated": "アプリをアップデートしました", "appsUpdated": "アプリをアップデートしました",
"appsNotUpdated": "アプリケーションの更新に失敗",
"appsUpdatedNotifDescription": "1つまたは複数のAppのアップデートがバックグラウンドで適用されたことをユーザーに通知する", "appsUpdatedNotifDescription": "1つまたは複数のAppのアップデートがバックグラウンドで適用されたことをユーザーに通知する",
"xWasUpdatedToY": "{} が {} にアップデートされました", "xWasUpdatedToY": "{} が {} にアップデートされました",
"xWasNotUpdatedToY": "への更新に失敗しました。",
"errorCheckingUpdates": "アップデート確認中のエラー", "errorCheckingUpdates": "アップデート確認中のエラー",
"errorCheckingUpdatesNotifDescription": "バックグラウンドでのアップデート確認に失敗した際に表示される通知", "errorCheckingUpdatesNotifDescription": "バックグラウンドでのアップデート確認に失敗した際に表示される通知",
"appsRemoved": "削除されたアプリ", "appsRemoved": "削除されたアプリ",
"appsRemovedNotifDescription": "アプリの読み込み中にエラーが発生したため、1つまたは複数のアプリが削除されたことをユーザーに通知する", "appsRemovedNotifDescription": "アプリの読み込み中にエラーが発生したため、1つまたは複数のアプリが削除されたことをユーザーに通知する",
"xWasRemovedDueToErrorY": "このエラーのため、{} は削除されました: {}", "xWasRemovedDueToErrorY": "このエラーのため、{} は削除されました: {}",
"completeAppInstallation": "アプリのインストールを完了する", "completeAppInstallation": "アプリのインストールを完了する",
"obtainiumMustBeOpenToInstallApps": "アプリをインストールするにはObtainium開いている必要があります", "obtainiumMustBeOpenToInstallApps": "アプリをインストールするにはObtainium開いている必要があります",
"completeAppInstallationNotifDescription": "アプリのインストールを完了するために、Obtainiumに戻る必要があります", "completeAppInstallationNotifDescription": "アプリのインストールを完了するために、Obtainiumに戻る必要があります",
"checkingForUpdates": "アップデートを確認中", "checkingForUpdates": "アップデートを確認中",
"checkingForUpdatesNotifDescription": "アップデートを確認する際に表示される一時的な通知", "checkingForUpdatesNotifDescription": "アップデートを確認する際に表示される一時的な通知",
"pleaseAllowInstallPerm": "Obtainiumによるアプリのインストールを許可してください。", "pleaseAllowInstallPerm": "Obtainiumによるアプリのインストールを許可してください。",
@ -169,15 +172,14 @@
"lastUpdateCheckX": "最終アップデート確認: {}", "lastUpdateCheckX": "最終アップデート確認: {}",
"remove": "削除", "remove": "削除",
"yesMarkUpdated": "はい、アップデート済みとしてマークします", "yesMarkUpdated": "はい、アップデート済みとしてマークします",
"fdroid": "F-Droid Official", "fdroid": "F-Droid公式",
"appIdOrName": "アプリのIDまたは名前", "appIdOrName": "アプリのIDまたは名前",
"appId": "App ID", "appId": "アプリID",
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした", "appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります", "reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
"fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ", "fdroidThirdPartyRepo": "F-Droid サードパーティリポジトリ",
"steam": "Steam", "steamMobile": "Steamモバイル",
"steamMobile": "Steam Mobile", "steamChat": "Steamチャット",
"steamChat": "Steam Chat",
"install": "インストール", "install": "インストール",
"markInstalled": "インストール済みとしてマークする", "markInstalled": "インストール済みとしてマークする",
"update": "アップデート", "update": "アップデート",
@ -186,6 +188,9 @@
"disableVersionDetection": "バージョン検出を無効にする", "disableVersionDetection": "バージョン検出を無効にする",
"noVersionDetectionExplanation": "このオプションは、バージョン検出が正しく機能しないアプリにのみ使用する必要があります。", "noVersionDetectionExplanation": "このオプションは、バージョン検出が正しく機能しないアプリにのみ使用する必要があります。",
"downloadingX": "{} をダウンロード中", "downloadingX": "{} をダウンロード中",
"downloadX": "ダウンロード",
"downloadedX": "ダウンロード",
"releaseAsset": "リリース資産",
"downloadNotifDescription": "アプリのダウンロード状況を通知する", "downloadNotifDescription": "アプリのダウンロード状況を通知する",
"noAPKFound": "APKが見つかりません", "noAPKFound": "APKが見つかりません",
"noVersionDetection": "バージョン検出を行わない", "noVersionDetection": "バージョン検出を行わない",
@ -211,16 +216,17 @@
"changes": "変更点", "changes": "変更点",
"releaseDate": "リリース日", "releaseDate": "リリース日",
"importFromURLsInFile": "ファイルOPMLなど内のURLからインポート", "importFromURLsInFile": "ファイルOPMLなど内のURLからインポート",
"versionDetectionExplanation": "バージョン文字列とOSから検出されたバージョンを照合する",
"versionDetection": "バージョン検出", "versionDetection": "バージョン検出",
"standardVersionDetection": "標準のバージョン検出", "standardVersionDetection": "標準のバージョン検出",
"groupByCategory": "カテゴリ別にグループ化する", "groupByCategory": "カテゴリ別にグループ化する",
"autoApkFilterByArch": "可能であればCPUアーキテクチャによるAPKのフィルタリングを試みる", "autoApkFilterByArch": "可能であればCPUアーキテクチャによるAPKのフィルタリングを試みる",
"overrideSource": "ソースの上書き", "overrideSource": "ソースの上書き",
"dontShowAgain": "二度と表示しない", "dontShowAgain": "二度と表示しない",
"dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない", "dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない",
"dontShowAPKOriginWarnings": "APKのダウンロード元の警告を表示しない", "dontShowAPKOriginWarnings": "APKのダウンロード元の警告を表示しない",
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる", "moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)", "gitlabPATLabel": "GitLab パーソナルアクセストークン",
"about": "概要", "about": "概要",
"requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)", "requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)",
"checkOnStart": "起動時にアップデートを確認する", "checkOnStart": "起動時にアップデートを確認する",
@ -234,8 +240,7 @@
"addInfoBelow": "下部でこの情報を追加してください。", "addInfoBelow": "下部でこの情報を追加してください。",
"addInfoInSettings": "設定でこの情報を追加してください。", "addInfoInSettings": "設定でこの情報を追加してください。",
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。", "githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。", "sortByLastLinkSegment": "リンクの最後のセグメントのみでソートする",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする", "filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')", "customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
"appsPossiblyUpdated": "アプリのアップデートを試行", "appsPossiblyUpdated": "アプリのアップデートを試行",
@ -245,25 +250,25 @@
"backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。", "backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。",
"backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。", "backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。",
"verifyLatestTag": "'latest'タグを確認する", "verifyLatestTag": "'latest'タグを確認する",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "訪問する「中間」リンクのフィルター",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "テキストでリンクをフィルタリングする",
"intermediateLinkNotFound": "中間リンクが見つかりませんでした", "intermediateLinkNotFound": "中間リンクが見つかりませんでした",
"intermediateLink": "Intermediate link", "intermediateLink": "中間リンク",
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)", "exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合バックグラウンドアップデートを無効にする", "bgUpdatesOnWiFiOnly": "WiFiを使用していない場合バックグラウンドアップデートを無効にする",
"autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する", "autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する",
"versionExtractionRegEx": "バージョン抽出の正規表現", "versionExtractionRegEx": "バージョン抽出の正規表現",
"matchGroupToUse": "使用するマッチしたグループ", "matchGroupToUse": "使用するマッチしたグループ",
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする", "highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
"pickExportDir": "エクスポートディレクトリを選択", "pickExportDir": "エクスポートディレクトリを選択",
"autoExportOnChanges": "変更があった際に自動でエクスポートする", "autoExportOnChanges": "変更があった際に自動でエクスポートする",
"includeSettings": "Include settings", "includeSettings": "設定を含む",
"filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする", "filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する", "trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
"dontSortReleasesList": "APIからのリリース順を保持する", "dontSortReleasesList": "APIからのリリース順を保持する",
"reverseSort": "逆順ソート", "reverseSort": "逆順ソート",
"takeFirstLink": "Take first link", "takeFirstLink": "最初のリンクを取得する",
"skipSort": "Skip sorting", "skipSort": "ソートをスキップする",
"debugMenu": "デバッグメニュー", "debugMenu": "デバッグメニュー",
"bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。", "bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
"runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する", "runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
@ -279,14 +284,31 @@
"completeAppInstallationNotifChannel": "アプリのインストールを完了する", "completeAppInstallationNotifChannel": "アプリのインストールを完了する",
"checkingForUpdatesNotifChannel": "アップデートを確認中", "checkingForUpdatesNotifChannel": "アップデートを確認中",
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する", "onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "固定されたAPKURLをサポートする",
"selectX": "Select {}", "selectX": "{} 選択",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "並行ダウンロードを許可する",
"installMethod": "Installation method", "useShizuku": "シズクまたはスイを使って設置する",
"normal": "Normal", "shizukuBinderNotFound": "Shizukuが起動していません",
"shizuku": "Shizuku", "shizukuOld": "古い雫バージョン (<11) - アップデートしてください。",
"root": "Root", "shizukuOldAndroidWithADB": "雫、Android < 8.1でADB動作 - Androidをアップデートするか、代わりにSuiを使うか",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuPretendToBeGooglePlay": "インストール元をGoogle Playに設定する雫を使用する場合",
"useSystemFont": "システムフォントを使用する",
"useVersionCodeAsOSVersion": "アプリのバージョンコードをOSで検出されたバージョンとして使用する",
"requestHeader": "リクエストヘッダー",
"useLatestAssetDateAsReleaseDate": "最新のアセットアップロードをリリース日として使用する",
"defaultPseudoVersioningMethod": "デフォルトの疑似バージョン管理方法",
"partialAPKHash": "部分的なAPKのハッシュ",
"APKLinkHash": "APKリンクのハッシュ",
"directAPKLink": "APKのダイレクトリンク",
"pseudoVersionInUse": "疑似バージョンが使用されています",
"installed": "インストール済み",
"latest": "最新",
"invertRegEx": "正規表現を反転",
"note": "注",
"selfHostedNote": "ドロップダウン\"{}\"を使用すると、あらゆるソースのセルフホスト/カスタムインスタンスにアクセスできます。",
"badDownload": "APK を解析できませんでした(互換性がないか、部分的にダウンロードされています)。",
"beforeNewInstallsShareToAppVerifier": "AppVerifierで新しいアプリを共有する利用可能な場合",
"appVerifierInstructionToast": "AppVerifierに共有し、準備ができたらここに戻ってください。",
"removeAppQuestion": { "removeAppQuestion": {
"one": "アプリを削除しますか?", "one": "アプリを削除しますか?",
"other": "アプリを削除しますか?" "other": "アプリを削除しますか?"
@ -335,8 +357,16 @@
"one": "{} とさらに {} 個のアプリがアップデートされました。", "one": "{} とさらに {} 個のアプリがアップデートされました。",
"other": "{} とさらに {} 個のアプリがアップデートされました。" "other": "{} とさらに {} 個のアプリがアップデートされました。"
}, },
"xAndNMoreUpdatesFailed": {
"one": "更新に失敗しました。",
"other": "アプリのアップデートに失敗しました。"
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。", "one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。",
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。" "other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。"
},
"apk": {
"one": "{}APK",
"other": "{}APK"
} }
} }

View File

@ -1,342 +1,372 @@
{ {
"invalidURLForSource": "Geen valide {} app URL", "invalidURLForSource": "Geen valide {} app URL",
"noReleaseFound": "Kan geen geschikte release vinden", "noReleaseFound": "Kan geen geschikte release vinden",
"noVersionFound": "Kan de versie niet bepalen", "noVersionFound": "Kan de versie niet bepalen",
"urlMatchesNoSource": "URL komt niet overeen met bekende bron", "urlMatchesNoSource": "URL komt niet overeen met bekende bron",
"cantInstallOlderVersion": "Kan geen oudere versie van de app installeren", "cantInstallOlderVersion": "Kan geen oudere versie van de app installeren",
"appIdMismatch": "Gedownloade pakket-ID komt niet overeen met de bestaande app-ID", "appIdMismatch": "Gedownloade pakket-ID komt niet overeen met de bestaande app-ID",
"functionNotImplemented": "Deze class heeft deze functie niet geïmplementeerd.", "functionNotImplemented": "Deze class heeft deze functie niet geïmplementeerd.",
"placeholder": "Plaatshouder", "placeholder": "Plaatshouder",
"someErrors": "Er zijn enkele fouten opgetreden", "someErrors": "Er zijn enkele fouten opgetreden",
"unexpectedError": "Onverwachte fout", "unexpectedError": "Onverwachte fout",
"ok": "Ok", "ok": "Ok",
"and": "en", "and": "en",
"githubPATLabel": "GitHub Personal Access Token\n(Verhoogt limiet aantal verzoeken)", "githubPATLabel": "GitHub Personal Access Token\n(Verhoogt limiet aantal verzoeken)",
"includePrereleases": "Bevat prereleases", "includePrereleases": "Bevat prereleases",
"fallbackToOlderReleases": "Terugvallen op oudere releases", "fallbackToOlderReleases": "Terugvallen op oudere releases",
"filterReleaseTitlesByRegEx": "Filter release-titels met reguliere expressies.", "filterReleaseTitlesByRegEx": "Filter release-titels met reguliere expressies.",
"invalidRegEx": "Ongeldige reguliere expressie", "invalidRegEx": "Ongeldige reguliere expressie",
"noDescription": "Geen omschrijving", "noDescription": "Geen omschrijving",
"cancel": "Annuleer", "cancel": "Annuleer",
"continue": "Ga verder", "continue": "Ga verder",
"requiredInBrackets": "(Verplicht)", "requiredInBrackets": "(Verplicht)",
"dropdownNoOptsError": "FOUTMELDING: DROPDOWN MOET TENMINSTE ÉÉN OPT HEBBEN", "dropdownNoOptsError": "FOUTMELDING: DROPDOWN MOET TENMINSTE ÉÉN OPT HEBBEN",
"colour": "Kleur", "colour": "Kleur",
"githubStarredRepos": "GitHub Starred Repos", "standard": "Standaard",
"uname": "Gebruikersnaam", "custom": "Aangepast",
"wrongArgNum": "Onjuist aantal argumenten verstrekt.", "useMaterialYou": "Gebruik materiaal",
"xIsTrackOnly": "{} is Track-Only", "githubStarredRepos": "GitHub-repo's met ster",
"source": "Bron", "uname": "Gebruikersnaam",
"app": "App", "wrongArgNum": "Onjuist aantal argumenten verstrekt.",
"appsFromSourceAreTrackOnly": "Apps van deze bron zijn 'Track-Only'.", "xIsTrackOnly": "{} is alleen tracken",
"youPickedTrackOnly": "Je hebt de 'Track-Only' optie geselecteerd.", "source": "Bron",
"trackOnlyAppDescription": "De app zal worden gevolgd voor updates, maar Obtainium zal niet in staat zijn om deze te downloaden of te installeren.", "app": "App",
"cancelled": "Geannuleerd", "appsFromSourceAreTrackOnly": "Apps van deze bron zijn 'Track-Only'.",
"appAlreadyAdded": "App al toegevoegd", "youPickedTrackOnly": "Je hebt de 'Track-Only' optie geselecteerd.",
"alreadyUpToDateQuestion": "Is de app al up-to-date?", "trackOnlyAppDescription": "De app zal worden gevolgd voor updates, maar Obtainium zal niet in staat zijn om deze te downloaden of te installeren.",
"addApp": "App toevoegen", "cancelled": "Geannuleerd",
"appSourceURL": "App bron URL", "appAlreadyAdded": "App al toegevoegd",
"error": "Foutmelding", "alreadyUpToDateQuestion": "Is de app al up-to-date?",
"add": "Toevoegen", "addApp": "App toevoegen",
"searchSomeSourcesLabel": "Zoeken (Alleen sommige bronnen)", "appSourceURL": "App bron URL",
"search": "Zoeken", "error": "Foutmelding",
"additionalOptsFor": "Aanvullende opties voor {}", "add": "Toevoegen",
"supportedSources": "Ondersteunde bronnen", "searchSomeSourcesLabel": "Zoeken (Alleen sommige bronnen)",
"trackOnlyInBrackets": "(Track-Only)", "search": "Zoeken",
"searchableInBrackets": "(Doorzoekbaar)", "additionalOptsFor": "Aanvullende opties voor {}",
"appsString": "Apps", "supportedSources": "Ondersteunde bronnen",
"noApps": "Geen Apps", "trackOnlyInBrackets": "(Alleen track)",
"noAppsForFilter": "Geen Apps voor filter", "searchableInBrackets": "(Doorzoekbaar)",
"byX": "Door {}", "appsString": "Apps",
"percentProgress": "Vooruitgang: {}%", "noApps": "Geen Apps",
"pleaseWait": "Even geduld", "noAppsForFilter": "Geen Apps voor filter",
"updateAvailable": "Update beschikbaar", "byX": "Door {}",
"estimateInBracketsShort": "(Ong.)", "percentProgress": "Vooruitgang: {}%",
"notInstalled": "Niet geinstalleerd", "pleaseWait": "Even geduld",
"estimateInBrackets": "(Ongeveer)", "updateAvailable": "Update beschikbaar",
"selectAll": "Selecteer alles", "notInstalled": "Niet geinstalleerd",
"deselectX": "Deselecteer {}", "pseudoVersion": "pseudo-versie",
"xWillBeRemovedButRemainInstalled": "{} zal worden verwijderd uit Obtainium, maar blijft geïnstalleerd op het apparaat.", "selectAll": "Selecteer alles",
"removeSelectedAppsQuestion": "Geselecteerde apps verwijderen??", "deselectX": "Deselecteer {}",
"removeSelectedApps": "Geselecteerde apps verwijderen", "xWillBeRemovedButRemainInstalled": "{} zal worden verwijderd uit Obtainium, maar blijft geïnstalleerd op het apparaat.",
"updateX": "Update {}", "removeSelectedAppsQuestion": "Geselecteerde apps verwijderen??",
"installX": "Installeer {}", "removeSelectedApps": "Geselecteerde apps verwijderen",
"markXTrackOnlyAsUpdated": "Markeer {}\n(Track-Only)\nals up-to-date", "updateX": "Update {}",
"changeX": "Verander {}", "installX": "Installeer {}",
"installUpdateApps": "Installeer/Update apps", "markXTrackOnlyAsUpdated": "Markeer {}\n(Track-Only)\nals up-to-date",
"installUpdateSelectedApps": "Installeer/Update geselecteerde apps", "changeX": "Verander {}",
"markXSelectedAppsAsUpdated": "{} geselecteerde apps markeren als up-to-date?", "installUpdateApps": "Installeer/Update apps",
"no": "Nee", "installUpdateSelectedApps": "Installeer/Update geselecteerde apps",
"yes": "Ja", "markXSelectedAppsAsUpdated": "{} geselecteerde apps markeren als up-to-date?",
"markSelectedAppsUpdated": "Markeer geselecteerde aps als up-to-date", "no": "Nee",
"pinToTop": "Vastzetten aan de bovenkant", "yes": "Ja",
"unpinFromTop": "Losmaken van de bovenkant", "markSelectedAppsUpdated": "Markeer geselecteerde aps als up-to-date",
"resetInstallStatusForSelectedAppsQuestion": "Installatiestatus resetten voor geselecteerde apps?", "pinToTop": "Vastzetten aan de bovenkant",
"installStatusOfXWillBeResetExplanation": "De installatiestatus van alle geselecteerde apps zal worden gereset.\n\nDit kan helpen wanneer de versie van de app die in Obtainium wordt weergegeven onjuist is vanwege mislukte updates of andere problemen.", "unpinFromTop": "Losmaken van de bovenkant",
"shareSelectedAppURLs": "Deel geselecteerde app URL's", "resetInstallStatusForSelectedAppsQuestion": "Installatiestatus resetten voor geselecteerde apps?",
"resetInstallStatus": "Reset installatiestatus", "installStatusOfXWillBeResetExplanation": "De installatiestatus van alle geselecteerde apps zal worden gereset.\n\nDit kan helpen wanneer de versie van de app die in Obtainium wordt weergegeven onjuist is vanwege mislukte updates of andere problemen.",
"more": "Meer", "customLinkMessage": "Deze links werken op apparaten waarop Obtainium is geïnstalleerd",
"removeOutdatedFilter": "Verwijder out-of-date app filter", "shareAppConfigLinks": "App-configuratie delen als HTML-link",
"showOutdatedOnly": "Toon alleen out-of-date apps", "shareSelectedAppURLs": "Deel geselecteerde app URL's",
"filter": "Filter", "resetInstallStatus": "Reset installatiestatus",
"filterActive": "Filter *", "more": "Meer",
"filterApps": "Filter apps", "removeOutdatedFilter": "Verwijder out-of-date app filter",
"appName": "App naam", "showOutdatedOnly": "Toon alleen out-of-date apps",
"author": "Auteur", "filter": "Filter",
"upToDateApps": "Up-to-date apps", "filterApps": "Filter apps",
"nonInstalledApps": "Niet-geïnstalleerde apps", "appName": "App naam",
"importExport": "Import/Export", "author": "Auteur",
"settings": "Instellingen", "upToDateApps": "Up-to-date apps",
"exportedTo": "Geëxporteerd naar {}", "nonInstalledApps": "Niet-geïnstalleerde apps",
"obtainiumExport": "Obtainium export", "importExport": "Importeren/Exporteren",
"invalidInput": "Ongeldige invoer", "settings": "Instellingen",
"importedX": "Geïmporteerd {}", "exportedTo": "Geëxporteerd naar {}",
"obtainiumImport": "Obtainium import", "obtainiumExport": "Obtainium export",
"importFromURLList": "Importeer van URL-lijsten", "invalidInput": "Ongeldige invoer",
"searchQuery": "Zoekopdracht", "importedX": "Geïmporteerd {}",
"appURLList": "App URL-lijst", "obtainiumImport": "Obtainium import",
"line": "Lijn", "importFromURLList": "Importeer van URL-lijsten",
"searchX": "Zoek {}", "searchQuery": "Zoekopdracht",
"noResults": "Geen resultaten gevonden", "appURLList": "App URL-lijst",
"importX": "Import {}", "line": "Lijn",
"importedAppsIdDisclaimer": "Geïmporteerde apps kunnen mogelijk onjuist worden weergegeven als \"Niet geïnstalleerd\".\nOm dit op te lossen, herinstalleer ze via Obtainium.\nDit zou geen invloed moeten hebben op app-gegevens.\n\nDit heeft alleen invloed op URL- en importmethoden van derden.", "searchX": "Zoek {}",
"importErrors": "Import foutmeldingen", "noResults": "Geen resultaten gevonden",
"importedXOfYApps": "{} van {} apps geïmporteerd.", "importX": "Importeer {}",
"followingURLsHadErrors": "De volgende URL's bevatten fouten:", "importedAppsIdDisclaimer": "Geïmporteerde apps kunnen mogelijk onjuist worden weergegeven als \"Niet geïnstalleerd\".\nOm dit op te lossen, herinstalleer ze via Obtainium.\nDit zou geen invloed moeten hebben op app-gegevens.\n\nDit heeft alleen invloed op URL- en importmethoden van derden.",
"selectURL": "Selecteer URL", "importErrors": "Import foutmeldingen",
"selectURLs": "Selecteer URL's", "importedXOfYApps": "{} van {} apps geïmporteerd.",
"pick": "Kies", "followingURLsHadErrors": "De volgende URL's bevatten fouten:",
"theme": "Thema", "selectURL": "Selecteer URL",
"dark": "Donker", "selectURLs": "Selecteer URL's",
"light": "Licht", "pick": "Kies",
"followSystem": "Volg systeem", "theme": "Thema",
"obtainium": "Obtainium", "dark": "Donker",
"materialYou": "Material You", "light": "Licht",
"useBlackTheme": "Gebruik zwart thema", "followSystem": "Volg systeem",
"appSortBy": "App sorteren op", "useBlackTheme": "Gebruik zwart thema",
"authorName": "Auteur/Naam", "appSortBy": "App sorteren op",
"nameAuthor": "Naam/Auteur", "authorName": "Auteur/Naam",
"asAdded": "Zoals toegevoegd", "nameAuthor": "Naam/Auteur",
"appSortOrder": "App sorteervolgorde", "asAdded": "Zoals toegevoegd",
"ascending": "Oplopend", "appSortOrder": "App sorteervolgorde",
"descending": "Aflopend", "ascending": "Oplopend",
"bgUpdateCheckInterval": "Frequentie voor achtergrondupdatecontrole", "descending": "Aflopend",
"neverManualOnly": "Nooit - Alleen handmatig", "bgUpdateCheckInterval": "Frequentie voor achtergrondupdatecontrole",
"appearance": "Weergave", "neverManualOnly": "Nooit - Alleen handmatig",
"showWebInAppView": "Toon de bronwebpagina in app-weergave", "appearance": "Weergave",
"pinUpdates": "Updates bovenaan in de apps-weergave vastpinnen", "showWebInAppView": "Toon de bronwebpagina in app-weergave",
"updates": "Updates", "pinUpdates": "Updates bovenaan in de apps-weergave vastpinnen",
"sourceSpecific": "Bron-specifiek", "updates": "Updates",
"appSource": "App bron", "sourceSpecific": "Bron-specifiek",
"noLogs": "Geen logs", "appSource": "App bron",
"appLogs": "App logs", "noLogs": "Geen logs",
"close": "Sluiten", "appLogs": "App logs",
"share": "Delen", "close": "Sluiten",
"appNotFound": "App niet gevonden", "share": "Delen",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "appNotFound": "App niet gevonden",
"pickAnAPK": "Kies een APK", "obtainiumExportHyphenatedLowercase": "obtainium-export",
"appHasMoreThanOnePackage": "{} heeft meer dan één package:", "pickAnAPK": "Kies een APK",
"deviceSupportsXArch": "Jouw apparaat support de {} CPU-architectuur.", "appHasMoreThanOnePackage": "{} heeft meer dan één package:",
"deviceSupportsFollowingArchs": "Je apparaat ondersteunt de volgende CPU-architecturen:", "deviceSupportsXArch": "Jouw apparaat support de {} CPU-architectuur.",
"warning": "Waarschuwing", "deviceSupportsFollowingArchs": "Je apparaat ondersteunt de volgende CPU-architecturen:",
"sourceIsXButPackageFromYPrompt": "De appbron is '{}' maar de release package komt van '{}'. Doorgaan?", "warning": "Waarschuwing",
"updatesAvailable": "Updates beschikbaar", "sourceIsXButPackageFromYPrompt": "De appbron is '{}' maar de release package komt van '{}'. Doorgaan?",
"updatesAvailableNotifDescription": "Stelt de gebruiker op de hoogte dat er updates beschikbaar zijn voor één of meer apps die worden bijgehouden door Obtainium.", "updatesAvailable": "Updates beschikbaar",
"noNewUpdates": "Geen nieuwe updates.", "updatesAvailableNotifDescription": "Stelt de gebruiker op de hoogte dat er updates beschikbaar zijn voor één of meer apps die worden bijgehouden door Obtainium.",
"xHasAnUpdate": "{} heeft een update.", "noNewUpdates": "Geen nieuwe updates.",
"appsUpdated": "Apps bijgewerkt", "xHasAnUpdate": "{} heeft een update.",
"appsUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps in de achtergrond zijn toegepast.", "appsUpdated": "Apps bijgewerkt",
"xWasUpdatedToY": "{} is bijgewerkt naar {}.", "appsNotUpdated": "Applicaties konden niet worden bijgewerkt",
"errorCheckingUpdates": "Fout bij het controleren op updates", "appsUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps in de achtergrond zijn toegepast.",
"errorCheckingUpdatesNotifDescription": "Een melding die verschijnt wanneer het controleren op updates in de achtergrond mislukt", "xWasUpdatedToY": "{} is bijgewerkt naar {}.",
"appsRemoved": "Apps verwijderd", "xWasNotUpdatedToY": "Het bijwerken van {} naar {} is mislukt.",
"appsRemovedNotifDescription": "Stelt de gebruiker op de hoogte dat één of meer apps zijn verwijderd vanwege fouten tijdens het laden ervan", "errorCheckingUpdates": "Fout bij het controleren op updates",
"xWasRemovedDueToErrorY": "{} is verwijderd vanwege deze foutmelding: {}", "errorCheckingUpdatesNotifDescription": "Een melding die verschijnt wanneer het controleren op updates in de achtergrond mislukt",
"completeAppInstallation": "Complete app installatie", "appsRemoved": "Apps verwijderd",
"obtainiumMustBeOpenToInstallApps": "Obtainium moet geopend zijn om apps te installeren", "appsRemovedNotifDescription": "Stelt de gebruiker op de hoogte dat één of meer apps zijn verwijderd vanwege fouten tijdens het laden ervan",
"completeAppInstallationNotifDescription": "Vraagt de gebruiker om terug te keren naar Obtainium om de installatie van een app af te ronden", "xWasRemovedDueToErrorY": "{} is verwijderd vanwege deze foutmelding: {}",
"checkingForUpdates": "Controleren op updates", "completeAppInstallation": "Complete app installatie",
"checkingForUpdatesNotifDescription": "Tijdelijke melding die verschijnt tijdens het controleren op updates", "obtainiumMustBeOpenToInstallApps": "Obtainium moet geopend zijn om apps te installeren",
"pleaseAllowInstallPerm": "Sta Obtainium toe om apps te installeren", "completeAppInstallationNotifDescription": "Vraagt de gebruiker om terug te keren naar Obtainium om de installatie van een app af te ronden",
"trackOnly": "Track-Only", "checkingForUpdates": "Controleren op updates",
"errorWithHttpStatusCode": "Foutmelding {}", "checkingForUpdatesNotifDescription": "Tijdelijke melding die verschijnt tijdens het controleren op updates",
"versionCorrectionDisabled": "Versiecorrectie uitgeschakeld (de plug-in lijkt niet te werken)", "pleaseAllowInstallPerm": "Sta Obtainium toe om apps te installeren",
"unknown": "Onbekend", "trackOnly": "Alleen track",
"none": "Geen", "errorWithHttpStatusCode": "Foutmelding {}",
"never": "Nooit", "versionCorrectionDisabled": "Versiecorrectie uitgeschakeld (de plug-in lijkt niet te werken)",
"latestVersionX": "Laatste versie: {}", "unknown": "Onbekend",
"installedVersionX": "Geïnstalleerde versie: {}", "none": "Geen",
"lastUpdateCheckX": "Laatste updatecontrole: {}", "never": "Nooit",
"remove": "Verwijderen", "latestVersionX": "Laatste versie: {}",
"yesMarkUpdated": "Ja, markeer als bijgewerkt", "installedVersionX": "Geïnstalleerde versie: {}",
"fdroid": "F-Droid Official", "lastUpdateCheckX": "Laatste updatecontrole: {}",
"appIdOrName": "App ID of naam", "remove": "Verwijderen",
"appId": "App ID", "yesMarkUpdated": "Ja, markeer als bijgewerkt",
"appWithIdOrNameNotFound": "Er werd geen app gevonden met dat ID of die naam", "fdroid": "F-Droid-ambtenaar",
"reposHaveMultipleApps": "Repositories kunnen meerdere apps bevatten", "appIdOrName": "App ID of naam",
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo", "appId": "App-ID",
"steam": "Steam", "appWithIdOrNameNotFound": "Er werd geen app gevonden met dat ID of die naam",
"steamMobile": "Steam Mobile", "reposHaveMultipleApps": "Repositories kunnen meerdere apps bevatten",
"steamChat": "Steam Chat", "fdroidThirdPartyRepo": "F-Droid Repository van derden",
"install": "Installeren", "steamMobile": "Stoommobiel",
"markInstalled": "Als geïnstalleerd markere", "steamChat": "Steamchat",
"update": "Update", "install": "Installeren",
"markUpdated": "Markeren als bijgewerkt", "markInstalled": "Als geïnstalleerd markere",
"additionalOptions": "Aanvullende opties", "update": "Update",
"disableVersionDetection": "Versieherkenning uitschakelen", "markUpdated": "Markeren als bijgewerkt",
"noVersionDetectionExplanation": "Deze optie moet alleen worden gebruikt voor apps waar versieherkenning niet correct werkt.", "additionalOptions": "Aanvullende opties",
"downloadingX": "Downloaden {}", "disableVersionDetection": "Versieherkenning uitschakelen",
"downloadNotifDescription": "Stelt de gebruiker op de hoogte van de voortgang bij het downloaden van een app", "noVersionDetectionExplanation": "Deze optie moet alleen worden gebruikt voor apps waar versieherkenning niet correct werkt.",
"noAPKFound": "Geen APK gevonden", "downloadingX": "Downloaden {}",
"noVersionDetection": "Geen versieherkenning", "downloadX": "Downloaden",
"categorize": "Categoriseren", "downloadedX": "Gedownload {}",
"categories": "Categorieën", "releaseAsset": "Release Activa",
"category": "Categorie", "downloadNotifDescription": "Stelt de gebruiker op de hoogte van de voortgang bij het downloaden van een app",
"noCategory": "Geen categorie", "noAPKFound": "Geen APK gevonden",
"noCategories": "Geen categorieën", "noVersionDetection": "Geen versieherkenning",
"deleteCategoriesQuestion": "Categorieën verwijderen?", "categorize": "Categoriseren",
"categoryDeleteWarning": "Alle apps in verwijderde categorieën worden teruggezet naar 'ongecategoriseerd'.", "categories": "Categorieën",
"addCategory": "Categorie toevoegen", "category": "Categorie",
"label": "Label", "noCategory": "Geen categorie",
"language": "Taal", "noCategories": "Geen categorieën",
"copiedToClipboard": "Gekopieerd naar klembord", "deleteCategoriesQuestion": "Categorieën verwijderen?",
"storagePermissionDenied": "Toegang tot opslag geweigerd", "categoryDeleteWarning": "Alle apps in verwijderde categorieën worden teruggezet naar 'ongecategoriseerd'.",
"selectedCategorizeWarning": "Dit zal eventuele bestaande categorie-instellingen voor de geselecteerde apps vervangen.", "addCategory": "Categorie toevoegen",
"filterAPKsByRegEx": "Filter APK's op reguliere expressie", "label": "Etiket",
"removeFromObtainium": "Verwijder van Obtainium", "language": "Taal",
"uninstallFromDevice": "Verwijder van apparaat", "copiedToClipboard": "Gekopieerd naar klembord",
"onlyWorksWithNonVersionDetectApps": "Werkt alleen voor apps waarbij versieherkenning is uitgeschakeld.", "storagePermissionDenied": "Toegang tot opslag geweigerd",
"releaseDateAsVersion": "Gebruik de releasedatum als versie", "selectedCategorizeWarning": "Dit zal eventuele bestaande categorie-instellingen voor de geselecteerde apps vervangen.",
"releaseDateAsVersionExplanation": "Deze optie moet alleen worden gebruikt voor apps waar versieherkenning niet correct werkt, maar waar wel een releasedatum beschikbaar is.", "filterAPKsByRegEx": "Filter APK's op reguliere expressie",
"changes": "Veranderingen", "removeFromObtainium": "Verwijder van Obtainium",
"releaseDate": "Releasedatum", "uninstallFromDevice": "Verwijder van apparaat",
"importFromURLsInFile": "Importeren vanaf URL's in een bestand (zoals OPML)", "onlyWorksWithNonVersionDetectApps": "Werkt alleen voor apps waarbij versieherkenning is uitgeschakeld.",
"versionDetection": "Versieherkenning", "releaseDateAsVersion": "Gebruik de releasedatum als versie",
"standardVersionDetection": "Standaard versieherkenning", "releaseDateAsVersionExplanation": "Deze optie moet alleen worden gebruikt voor apps waar versieherkenning niet correct werkt, maar waar wel een releasedatum beschikbaar is.",
"groupByCategory": "Groepeer op categorie", "changes": "Veranderingen",
"autoApkFilterByArch": "Poging om APK's te filteren op CPU-architectuur indien mogelijk", "releaseDate": "Releasedatum",
"overrideSource": "Bron overschrijven", "importFromURLsInFile": "Importeren vanaf URL's in een bestand (zoals OPML)",
"dontShowAgain": "Don't show this again", "versionDetectionExplanation": "Versiereeks afstemmen met versie gedetecteerd door besturingssysteem",
"dontShowTrackOnlyWarnings": "Geen waarschuwingen voor 'Track-Only' weergeven", "versionDetection": "Versieherkenning",
"dontShowAPKOriginWarnings": "APK-herkomstwaarschuwingen niet weergeven", "standardVersionDetection": "Standaard versieherkenning",
"moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave", "groupByCategory": "Groepeer op categorie",
"gitlabPATLabel": "GitLab Personal Access Token\n(Maakt het mogelijk beter te zoeken naar APK's)", "autoApkFilterByArch": "Poging om APK's te filteren op CPU-architectuur indien mogelijk",
"about": "Over", "overrideSource": "Bron overschrijven",
"requiresCredentialsInSettings": "{}: Dit vereist aanvullende referenties (in Instellingen)", "dontShowAgain": "Laat dit niet meer zien",
"checkOnStart": "Controleren op updates bij opstarten", "dontShowTrackOnlyWarnings": "Geen waarschuwingen voor 'Track-Only' weergeven",
"tryInferAppIdFromCode": "Probeer de app-ID af te leiden uit de broncode", "dontShowAPKOriginWarnings": "APK-herkomstwaarschuwingen niet weergeven",
"removeOnExternalUninstall": "Automatisch extern verwijderde apps verwijderen", "moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave",
"pickHighestVersionCode": "Automatisch de APK met de hoogste versiecode selecteren", "gitlabPATLabel": "GitLab persoonlijk toegangskenmerk",
"checkUpdateOnDetailPage": "Controleren op updates bij het openen van een app-detailpagina", "about": "Over",
"disablePageTransitions": "Schakel overgangsanimaties tussen pagina's uit", "requiresCredentialsInSettings": "{}: Dit vereist aanvullende referenties (in Instellingen)",
"reversePageTransitions": "Omgekeerde overgangsanimaties tussen pagina's", "checkOnStart": "Controleren op updates bij opstarten",
"minStarCount": "Minimale Github Stars", "tryInferAppIdFromCode": "Probeer de app-ID af te leiden uit de broncode",
"addInfoBelow": "Voeg deze informatie hieronder toe.", "removeOnExternalUninstall": "Automatisch extern verwijderde apps verwijderen",
"addInfoInSettings": "Voeg deze informatie toe in de instellingen.", "pickHighestVersionCode": "Automatisch de APK met de hoogste versiecode selecteren",
"githubSourceNote": "Beperkingen van GitHub kunnen worden vermeden door het gebruik van een API-sleutel.", "checkUpdateOnDetailPage": "Controleren op updates bij het openen van een app-detailpagina",
"gitlabSourceNote": "GitLab APK-extractie werkt mogelijk niet zonder een API-sleutel.", "disablePageTransitions": "Schakel overgangsanimaties tussen pagina's uit",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "reversePageTransitions": "Omgekeerde overgangsanimaties tussen pagina's",
"filterReleaseNotesByRegEx": "Filter release-opmerkingen met een reguliere expressie.", "minStarCount": "Minimale Github Stars",
"customLinkFilterRegex": "Aangepaste APK-linkfilter met een reguliere expressie (Standaard '.apk$').", "addInfoBelow": "Voeg deze informatie hieronder toe.",
"appsPossiblyUpdated": "Poging tot app-updates", "addInfoInSettings": "Voeg deze informatie toe in de instellingen.",
"appsPossiblyUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps mogelijk in de achtergrond zijn toegepast", "githubSourceNote": "Beperkingen van GitHub kunnen worden vermeden door het gebruik van een API-sleutel.",
"xWasPossiblyUpdatedToY": "{} mogelijk bijgewerkt naar {}.", "sortByLastLinkSegment": "Sorteren op alleen het laatste segment van de link",
"enableBackgroundUpdates": "Achtergrondupdates inschakelen", "filterReleaseNotesByRegEx": "Filter release-opmerkingen met een reguliere expressie.",
"backgroundUpdateReqsExplanation": "Achtergrondupdates zijn mogelijk niet voor alle apps mogelijk.", "customLinkFilterRegex": "Aangepaste APK-linkfilter met een reguliere expressie (Standaard '.apk$').",
"backgroundUpdateLimitsExplanation": "Het succes van een installatie in de achtergrond kan alleen worden bepaald wanneer Obtainium is geopend.", "appsPossiblyUpdated": "Poging tot app-updates",
"verifyLatestTag": "Verifieer de 'Laatste'-tag", "appsPossiblyUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps mogelijk in de achtergrond zijn toegepast",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "xWasPossiblyUpdatedToY": "{} mogelijk bijgewerkt naar {}.",
"filterByLinkText": "Filter links by link text", "enableBackgroundUpdates": "Achtergrondupdates inschakelen",
"intermediateLinkNotFound": "Tussenliggende link niet gevonden", "backgroundUpdateReqsExplanation": "Achtergrondupdates zijn mogelijk niet voor alle apps mogelijk.",
"intermediateLink": "Intermediate link", "backgroundUpdateLimitsExplanation": "Het succes van een installatie in de achtergrond kan alleen worden bepaald wanneer Obtainium is geopend.",
"exemptFromBackgroundUpdates": "Vrijgesteld van achtergrondupdates (indien ingeschakeld)", "verifyLatestTag": "Verifieer de 'Laatste'-tag",
"bgUpdatesOnWiFiOnly": "Achtergrondupdates uitschakelen wanneer niet verbonden met WiFi", "intermediateLinkRegex": "Filter voor een 'Intermediaire' link om te bezoeken",
"autoSelectHighestVersionCode": "Automatisch de APK met de hoogste versiecode selecteren", "filterByLinkText": "Links filteren op linktekst",
"versionExtractionRegEx": "Reguliere expressie voor versie-extractie", "intermediateLinkNotFound": "Tussenliggende link niet gevonden",
"matchGroupToUse": "Overeenkomende groep om te gebruiken voor de reguliere expressie voor versie-extractie", "intermediateLink": "Intermediaire link",
"highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.", "exemptFromBackgroundUpdates": "Vrijgesteld van achtergrondupdates (indien ingeschakeld)",
"pickExportDir": "Kies de exportmap", "bgUpdatesOnWiFiOnly": "Achtergrondupdates uitschakelen wanneer niet verbonden met WiFi",
"autoExportOnChanges": "Automatisch exporteren bij wijzigingen", "autoSelectHighestVersionCode": "Automatisch de APK met de hoogste versiecode selecteren",
"includeSettings": "Include settings", "versionExtractionRegEx": "Reguliere expressie voor versie-extractie",
"filterVersionsByRegEx": "Filter versies met een reguliere expressie", "matchGroupToUse": "Overeenkomende groep om te gebruiken voor de reguliere expressie voor versie-extractie",
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren", "highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API", "pickExportDir": "Kies de exportmap",
"reverseSort": "Sortering omkeren", "autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
"takeFirstLink": "Take first link", "includeSettings": "Instellingen opnemen",
"skipSort": "Skip sorting", "filterVersionsByRegEx": "Filter versies met een reguliere expressie",
"debugMenu": "Debug menu", "trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
"bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.", "dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
"runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit", "reverseSort": "Sortering omkeren",
"versionExtractWholePage": "De reguliere expressie voor versie-extractie toepassen op de hele pagina", "takeFirstLink": "Neem de eerste link",
"installing": "Installeren", "skipSort": "Sorteren overslaan",
"skipUpdateNotifications": "Updatemeldingen overslaan", "debugMenu": "Debug menu",
"updatesAvailableNotifChannel": "Updates beschikbaar", "bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
"appsUpdatedNotifChannel": "Apps bijgewerkt", "runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
"appsPossiblyUpdatedNotifChannel": "Poging tot app-updates", "versionExtractWholePage": "De reguliere expressie voor versie-extractie toepassen op de hele pagina",
"errorCheckingUpdatesNotifChannel": "Foutcontrole bij het zoeken naar updates", "installing": "Installeren",
"appsRemovedNotifChannel": "Apps verwijderd", "skipUpdateNotifications": "Updatemeldingen overslaan",
"downloadingXNotifChannel": "{} downloaden", "updatesAvailableNotifChannel": "Updates beschikbaar",
"completeAppInstallationNotifChannel": "Voltooien van de app-installatie", "appsUpdatedNotifChannel": "Apps bijgewerkt",
"checkingForUpdatesNotifChannel": "Controleren op updates", "appsPossiblyUpdatedNotifChannel": "Poging tot app-updates",
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates", "errorCheckingUpdatesNotifChannel": "Foutcontrole bij het zoeken naar updates",
"supportFixedAPKURL": "Support fixed APK URLs", "appsRemovedNotifChannel": "Apps verwijderd",
"selectX": "Select {}", "downloadingXNotifChannel": "{} downloaden",
"parallelDownloads": "Allow parallel downloads", "completeAppInstallationNotifChannel": "Voltooien van de app-installatie",
"installMethod": "Installation method", "checkingForUpdatesNotifChannel": "Controleren op updates",
"normal": "Normal", "onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
"shizuku": "Shizuku", "supportFixedAPKURL": "Ondersteuning vaste APK URL's",
"root": "Root", "selectX": "Selecteer {}",
"shizukuBinderNotFound": "Shizuku is not running", "parallelDownloads": "Parallelle downloads toestaan",
"removeAppQuestion": { "useShizuku": "Gebruik Shizuku of Sui om te installeren",
"one": "App verwijderen?", "shizukuBinderNotFound": "Shizuku draait niet",
"other": "Apps verwijderen?" "shizukuOld": "Oude Shizuku-versie (<11) - bijwerken",
}, "shizukuOldAndroidWithADB": "Shizuku draait op Android < 8.1 met ADB - update Android of gebruik Sui in plaats daarvan",
"tooManyRequestsTryAgainInMinutes": { "shizukuPretendToBeGooglePlay": "Google Play instellen als installatiebron (als Shizuku wordt gebruikt)",
"one": "Te veel verzoeken (aantal beperkt) - probeer het opnieuw in {} minuut", "useSystemFont": "Gebruik het systeemlettertype",
"other": "Te veel verzoeken (aantal beperkt) - probeer het opnieuw in {} minuten" "useVersionCodeAsOSVersion": "Gebruik app versieCode als door OS gedetecteerde versie",
}, "requestHeader": "Verzoekkoptekst",
"bgUpdateGotErrorRetryInMinutes": { "useLatestAssetDateAsReleaseDate": "Gebruik laatste upload als releasedatum",
"one": "Achtergrondupdatecontrole heeft een {}, zal een hercontrole plannen over {} minuut", "defaultPseudoVersioningMethod": "Standaard pseudo-versiebeheermethode",
"other": "Achtergrondupdatecontrole heeft een {}, zal een hercontrole plannen over {} minuten" "partialAPKHash": "Gedeeltelijke APK-hash",
}, "APKLinkHash": "APK-link-hash",
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "directAPKLink": "Directe APK-link",
"one": "Achtergrondupdatecontrole heeft {} update gevonden - zal de gebruiker op de hoogte stellen indien nodig", "pseudoVersionInUse": "Er is een pseudo-versie in gebruik",
"other": "Achtergrondupdatecontrole heeft {} updates gevonden - zal de gebruiker op de hoogte stellen indien nodig" "installed": "Geïnstalleerd",
}, "latest": "Laatste",
"apps": { "invertRegEx": "Reguliere expressie omkeren",
"one": "{} app", "note": "Opmerking",
"other": "{} apps" "selfHostedNote": "De \"{}\" dropdown kan gebruikt worden om zelf gehoste/aangepaste instanties van elke bron te bereiken.",
}, "badDownload": "De APK kon niet worden verwerkt (incompatibele of gedeeltelijke download)",
"url": { "beforeNewInstallsShareToAppVerifier": "Nieuwe Apps delen met AppVerifier (indien beschikbaar)",
"one": "{} URL", "appVerifierInstructionToast": "Deel naar AppVerifier en keer hier terug als je klaar bent.",
"other": "{} URLs" "removeAppQuestion": {
}, "one": "App verwijderen?",
"minute": { "other": "Apps verwijderen?"
"one": "{} minuut", },
"other": "{} minuten" "tooManyRequestsTryAgainInMinutes": {
}, "one": "Te veel verzoeken (aantal beperkt) - probeer het opnieuw in {} minuut",
"hour": { "other": "Te veel verzoeken (aantal beperkt) - probeer het opnieuw in {} minuten"
"one": "{} uur", },
"other": "{} uur" "bgUpdateGotErrorRetryInMinutes": {
}, "one": "Achtergrondupdatecontrole heeft een {}, zal een hercontrole plannen over {} minuut",
"day": { "other": "Achtergrondupdatecontrole heeft een {}, zal een hercontrole plannen over {} minuten"
"one": "{} dag", },
"other": "{} dagen" "bgCheckFoundUpdatesWillNotifyIfNeeded": {
}, "one": "Achtergrondupdatecontrole heeft {} update gevonden - zal de gebruiker op de hoogte stellen indien nodig",
"clearedNLogsBeforeXAfterY": { "other": "Achtergrondupdatecontrole heeft {} updates gevonden - zal de gebruiker op de hoogte stellen indien nodig"
"one": "{n} logboekitem gewist (voor = {before}, na = {after})", },
"other": "{n} logboekitems gewist (voor = {before}, na = {after})" "apps": {
}, "one": "{} app",
"xAndNMoreUpdatesAvailable": { "other": "{} apps"
"one": "{} en nog 1 app hebben updates.", },
"other": "{} en {} meer apps hebben updates." "url": {
}, "one": "{} URL",
"xAndNMoreUpdatesInstalled": { "other": "{} URL's"
"one": "{} en nog 1 app is bijgewerkt.", },
"other": "{} en {} meer apps zijn bijgewerkt." "minute": {
}, "one": "{} minuut",
"xAndNMoreUpdatesPossiblyInstalled": { "other": "{} minuten"
"one": "{} en nog 1 app zijn mogelijk bijgewerkt.", },
"other": "{} en {} meer apps zijn mogelijk bijgwerkt." "hour": {
} "one": "{} uur",
} "other": "{} uur"
},
"day": {
"one": "{} dag",
"other": "{} dagen"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} logboekitem gewist (voor = {before}, na = {after})",
"other": "{n} logboekitems gewist (voor = {before}, na = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} en nog 1 app hebben updates.",
"other": "{} en {} meer apps hebben updates."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} en nog 1 app is bijgewerkt.",
"other": "{} en {} meer apps zijn bijgewerkt."
},
"xAndNMoreUpdatesFailed": {
"one": "Bijwerken mislukt {} en nog 1 app.",
"other": "Mislukt om {} en {} meer apps bij te werken."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} en nog 1 app zijn mogelijk bijgewerkt.",
"other": "{} en {} meer apps zijn mogelijk bijgwerkt."
},
"apk": {
"one": "{} APK",
"other": "{} APK's"
}
}

View File

@ -6,7 +6,7 @@
"cantInstallOlderVersion": "Nie można zainstalować starszej wersji aplikacji", "cantInstallOlderVersion": "Nie można zainstalować starszej wersji aplikacji",
"appIdMismatch": "Pobrane ID pakietu nie pasuje do istniejącego ID aplikacji", "appIdMismatch": "Pobrane ID pakietu nie pasuje do istniejącego ID aplikacji",
"functionNotImplemented": "Ta klasa nie zaimplementowała tej funkcji", "functionNotImplemented": "Ta klasa nie zaimplementowała tej funkcji",
"placeholder": "Placeholder", "placeholder": "Zbiornik",
"someErrors": "Wystąpiły pewne błędy", "someErrors": "Wystąpiły pewne błędy",
"unexpectedError": "Nieoczekiwany błąd", "unexpectedError": "Nieoczekiwany błąd",
"ok": "Okej", "ok": "Okej",
@ -22,6 +22,9 @@
"requiredInBrackets": "(Wymagane)", "requiredInBrackets": "(Wymagane)",
"dropdownNoOptsError": "BŁĄD: LISTA ROZWIJANA MUSI MIEĆ CO NAJMNIEJ JEDNĄ OPCJĘ", "dropdownNoOptsError": "BŁĄD: LISTA ROZWIJANA MUSI MIEĆ CO NAJMNIEJ JEDNĄ OPCJĘ",
"colour": "Kolor", "colour": "Kolor",
"standard": "Standard",
"custom": "Niestandardowe",
"useMaterialYou": "Używaj materiałów",
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką", "githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
"uname": "Nazwa użytkownika", "uname": "Nazwa użytkownika",
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów", "wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
@ -51,9 +54,8 @@
"percentProgress": "Postęp: {}%", "percentProgress": "Postęp: {}%",
"pleaseWait": "Proszę czekać", "pleaseWait": "Proszę czekać",
"updateAvailable": "Dostępna aktualizacja", "updateAvailable": "Dostępna aktualizacja",
"estimateInBracketsShort": "(Szac.)",
"notInstalled": "Nie zainstalowano", "notInstalled": "Nie zainstalowano",
"estimateInBrackets": "(Szacunkowo)", "pseudoVersion": "pseudowersja",
"selectAll": "Zaznacz wszystkie", "selectAll": "Zaznacz wszystkie",
"deselectX": "Odznacz {}", "deselectX": "Odznacz {}",
"xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.", "xWillBeRemovedButRemainInstalled": "{} zostanie usunięty z Obtainium, ale pozostanie zainstalowany na urządzeniu.",
@ -73,13 +75,14 @@
"unpinFromTop": "Odepnij", "unpinFromTop": "Odepnij",
"resetInstallStatusForSelectedAppsQuestion": "Zresetować status instalacji dla wybranych aplikacji?", "resetInstallStatusForSelectedAppsQuestion": "Zresetować status instalacji dla wybranych aplikacji?",
"installStatusOfXWillBeResetExplanation": "Stan instalacji wybranych aplikacji zostanie zresetowany.\n\nMoże być to pomocne, gdy wersja aplikacji wyświetlana w Obtainium jest nieprawidłowa z powodu nieudanych aktualizacji lub innych problemów.", "installStatusOfXWillBeResetExplanation": "Stan instalacji wybranych aplikacji zostanie zresetowany.\n\nMoże być to pomocne, gdy wersja aplikacji wyświetlana w Obtainium jest nieprawidłowa z powodu nieudanych aktualizacji lub innych problemów.",
"customLinkMessage": "Te linki działają na urządzeniach z zainstalowanym Obtainium",
"shareAppConfigLinks": "Udostępnij konfigurację aplikacji jako link HTML",
"shareSelectedAppURLs": "Udostępnij wybrane adresy URL aplikacji", "shareSelectedAppURLs": "Udostępnij wybrane adresy URL aplikacji",
"resetInstallStatus": "Zresetuj stan instalacji", "resetInstallStatus": "Zresetuj stan instalacji",
"more": "Więcej", "more": "Więcej",
"removeOutdatedFilter": "Usuń filtr nieaktualnych aplikacji", "removeOutdatedFilter": "Usuń filtr nieaktualnych aplikacji",
"showOutdatedOnly": "Pokaż tylko nieaktualne aplikacje", "showOutdatedOnly": "Pokaż tylko nieaktualne aplikacje",
"filter": "FIltr", "filter": "FIltr",
"filterActive": "Filtruj *",
"filterApps": "Filtruj aplikacje", "filterApps": "Filtruj aplikacje",
"appName": "Nazwa aplikacji", "appName": "Nazwa aplikacji",
"author": "Autor", "author": "Autor",
@ -110,8 +113,6 @@
"dark": "Ciemny", "dark": "Ciemny",
"light": "Jasny", "light": "Jasny",
"followSystem": "Zgodny z systemem", "followSystem": "Zgodny z systemem",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Użyj czarnego motywu", "useBlackTheme": "Użyj czarnego motywu",
"appSortBy": "Sortuj aplikacje według", "appSortBy": "Sortuj aplikacje według",
"authorName": "Autor/Nazwa", "authorName": "Autor/Nazwa",
@ -145,8 +146,10 @@
"noNewUpdates": "Brak nowych aktualizacji.", "noNewUpdates": "Brak nowych aktualizacji.",
"xHasAnUpdate": "{} ma aktualizację.", "xHasAnUpdate": "{} ma aktualizację.",
"appsUpdated": "Zaktualizowano aplikacje", "appsUpdated": "Zaktualizowano aplikacje",
"appsNotUpdated": "Nie udało się zaktualizować aplikacji",
"appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle", "appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle",
"xWasUpdatedToY": "{} zaktualizowano do {}.", "xWasUpdatedToY": "{} zaktualizowano do {}.",
"xWasNotUpdatedToY": "Nie udało się zaktualizować {} do {}.",
"errorCheckingUpdates": "Błąd sprawdzania aktualizacji", "errorCheckingUpdates": "Błąd sprawdzania aktualizacji",
"errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się", "errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
"appsRemoved": "Usunięte aplikacje", "appsRemoved": "Usunięte aplikacje",
@ -175,9 +178,8 @@
"appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie", "appWithIdOrNameNotFound": "Nie znaleziono aplikacji o tym identyfikatorze lub nazwie",
"reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji", "reposHaveMultipleApps": "Repozytoria mogą zawierać wiele aplikacji",
"fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid", "fdroidThirdPartyRepo": "Zewnętrzne repo F-Droid",
"steam": "Steam",
"steamMobile": "Mobilny Steam", "steamMobile": "Mobilny Steam",
"steamChat": "Steam Chat", "steamChat": "Czat Steam",
"install": "Instaluj", "install": "Instaluj",
"markInstalled": "Oznacz jako zainstalowane", "markInstalled": "Oznacz jako zainstalowane",
"update": "Zaktualizuj", "update": "Zaktualizuj",
@ -186,6 +188,9 @@
"disableVersionDetection": "Wyłącz wykrywanie wersji", "disableVersionDetection": "Wyłącz wykrywanie wersji",
"noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.", "noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.",
"downloadingX": "Pobieranie {}", "downloadingX": "Pobieranie {}",
"downloadX": "Pobierz {}",
"downloadedX": "Pobrano {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "Informuje o postępach w pobieraniu aplikacji", "downloadNotifDescription": "Informuje o postępach w pobieraniu aplikacji",
"noAPKFound": "Nie znaleziono pakietu APK", "noAPKFound": "Nie znaleziono pakietu APK",
"noVersionDetection": "Bez wykrywania wersji", "noVersionDetection": "Bez wykrywania wersji",
@ -211,6 +216,7 @@
"changes": "Zmiany", "changes": "Zmiany",
"releaseDate": "Data wydania", "releaseDate": "Data wydania",
"importFromURLsInFile": "Importuj z adresów URL w pliku (typu OPML)", "importFromURLsInFile": "Importuj z adresów URL w pliku (typu OPML)",
"versionDetectionExplanation": "Uzgodnij ciąg wersji z wersją wykrytą w systemie operacyjnym",
"versionDetection": "Wykrywanie wersji", "versionDetection": "Wykrywanie wersji",
"standardVersionDetection": "Standardowe wykrywanie wersji", "standardVersionDetection": "Standardowe wykrywanie wersji",
"groupByCategory": "Grupuj według kategorii", "groupByCategory": "Grupuj według kategorii",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "Nie pokazuj ostrzeżeń \"Tylko obserwowana\"", "dontShowTrackOnlyWarnings": "Nie pokazuj ostrzeżeń \"Tylko obserwowana\"",
"dontShowAPKOriginWarnings": "Nie pokazuj ostrzeżeń o pochodzeniu APK", "dontShowAPKOriginWarnings": "Nie pokazuj ostrzeżeń o pochodzeniu APK",
"moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji", "moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji",
"gitlabPATLabel": "Osobisty token dostępu GitLab\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)", "gitlabPATLabel": "Osobisty token dostępu GitLab",
"about": "Więcej informacji", "about": "Więcej informacji",
"requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)", "requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu", "checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
@ -234,8 +240,7 @@
"addInfoBelow": "Dodaj tę informację poniżej.", "addInfoBelow": "Dodaj tę informację poniżej.",
"addInfoInSettings": "Dodaj tę informację w Ustawieniach.", "addInfoInSettings": "Dodaj tę informację w Ustawieniach.",
"githubSourceNote": "Limit żądań GitHub można ominąć za pomocą klucza API.", "githubSourceNote": "Limit żądań GitHub można ominąć za pomocą klucza API.",
"gitlabSourceNote": "Pozyskiwanie pliku APK z GitLab może nie działać bez klucza API.", "sortByLastLinkSegment": "Sortuj tylko według ostatniego segmentu łącza",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego", "filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")", "customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
"appsPossiblyUpdated": "Aplikacje mogły zostać zaktualizowane", "appsPossiblyUpdated": "Aplikacje mogły zostać zaktualizowane",
@ -245,10 +250,10 @@
"backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.", "backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
"backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.", "backgroundUpdateLimitsExplanation": "Powodzenie instalacji w tle można określić dopiero po otwarciu Obtainium.",
"verifyLatestTag": "Zweryfikuj najnowszy tag", "verifyLatestTag": "Zweryfikuj najnowszy tag",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Filtruj link \"pośredni\" do odwiedzenia",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filtruj linki według tekstu linku",
"intermediateLinkNotFound": "Nie znaleziono linku pośredniego", "intermediateLinkNotFound": "Nie znaleziono linku pośredniego",
"intermediateLink": "Intermediate link", "intermediateLink": "Łącze pośrednie",
"exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)", "exemptFromBackgroundUpdates": "Wyklucz z uaktualnień w tle (jeśli są włączone)",
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi", "bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK", "autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
@ -257,13 +262,13 @@
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe", "highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
"pickExportDir": "Wybierz katalog eksportu", "pickExportDir": "Wybierz katalog eksportu",
"autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian", "autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
"includeSettings": "Include settings", "includeSettings": "Dołącz ustawienia",
"filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego", "filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK", "trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API", "dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
"reverseSort": "Odwrotne sortowanie", "reverseSort": "Odwrotne sortowanie",
"takeFirstLink": "Take first link", "takeFirstLink": "Weź pierwszy link",
"skipSort": "Skip sorting", "skipSort": "Pomiń sortowanie",
"debugMenu": "Menu debugowania", "debugMenu": "Menu debugowania",
"bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.", "bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
"runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle", "runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
@ -281,12 +286,29 @@
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji", "onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK", "supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
"selectX": "Wybierz {}", "selectX": "Wybierz {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Zezwól na pobieranie równoległe",
"installMethod": "Installation method", "useShizuku": "Użyj Shizuku lub Sui, aby zainstalować",
"normal": "Normal",
"shizuku": "Shizuku",
"root": "Root",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizuku is not running",
"shizukuOld": "Stara wersja Shizuku (<11) - zaktualizuj ją",
"shizukuOldAndroidWithADB": "Shizuku działa na Androidzie < 8.1 z ADB - zaktualizuj Androida lub użyj zamiast tego Sui",
"shizukuPretendToBeGooglePlay": "Ustaw Google Play jako źródło instalacji (jeśli używana jest aplikacja Shizuku).",
"useSystemFont": "Użyj czcionki systemowej",
"useVersionCodeAsOSVersion": "Użyj kodu wersji aplikacji jako wersji wykrytej przez system operacyjny",
"requestHeader": "Nagłówek żądania",
"useLatestAssetDateAsReleaseDate": "Użyj najnowszego przesłanego zasobu jako daty wydania",
"defaultPseudoVersioningMethod": "Domyślna metoda pseudowersji",
"partialAPKHash": "Częściowy skrót APK",
"APKLinkHash": "Skrót łącza APK",
"directAPKLink": "Bezpośredni link APK",
"pseudoVersionInUse": "Pseudowersja jest w użyciu",
"installed": "Zainstalowano",
"latest": "Najnowszy",
"invertRegEx": "Odwróć wyrażenie regularne",
"note": "Uwaga",
"selfHostedNote": "Lista rozwijana \"{}\" może być używana do uzyskiwania dostępu do samodzielnie hostowanych / niestandardowych instancji dowolnego źródła.",
"badDownload": "Nie można przeanalizować pliku APK (niekompatybilny lub częściowo pobrany).",
"beforeNewInstallsShareToAppVerifier": "Udostępnianie nowych aplikacji za pomocą AppVerifier (jeśli dostępne)",
"appVerifierInstructionToast": "Udostępnij w AppVerifier, a następnie wróć tutaj, gdy będziesz gotowy.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Usunąć aplikację?", "one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?", "few": "Usunąć aplikacje?",
@ -359,10 +381,18 @@
"many": "{} i {} innych apek zostało zaktualizowanych.", "many": "{} i {} innych apek zostało zaktualizowanych.",
"other": "{} i {} inne apki zostały zaktualizowane." "other": "{} i {} inne apki zostały zaktualizowane."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Nie udało się zaktualizować {} i 1 innej aplikacji.",
"other": "Nie udało się zaktualizować {} i {} więcej aplikacji."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} i 1 inna apka mogły zostać zaktualizowane.", "one": "{} i 1 inna apka mogły zostać zaktualizowane.",
"few": "{} i {} inne apki mogły zostać zaktualizowane.", "few": "{} i {} inne apki mogły zostać zaktualizowane.",
"many": "{} i {} innych apek mogło zostać zaktualizowanych.", "many": "{} i {} innych apek mogło zostać zaktualizowanych.",
"other": "{} i {} inne apki mogły zostać zaktualizowane." "other": "{} i {} inne apki mogły zostać zaktualizowane."
},
"apk": {
"one": "{} APK",
"other": "{} APK"
} }
} }

View File

@ -1,107 +1,110 @@
{ {
"invalidURLForSource": "URL {} inválida", "invalidURLForSource": "URL {} inválida",
"noReleaseFound": "Não foi possivel encontrar uma versão adequada", "noReleaseFound": "Não foi possível encontrar uma versão adequada",
"noVersionFound": "Não foi possivel encontrar uma versão lançada", "noVersionFound": "Não foi possível encontrar uma versão",
"urlMatchesNoSource": "URL não corresponde a uma fonte conhecida", "urlMatchesNoSource": "A URL não corresponde a uma fonte conhecida",
"cantInstallOlderVersion": "Não pode instalar uma versão anterior de um App", "cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de um aplicativo",
"appIdMismatch": "ID do pacote baixado não é igual ao ID do App instalado", "appIdMismatch": "ID do pacote baixado não é igual ao ID do aplicativo instalado",
"functionNotImplemented": "Esta classe não implementou essa função", "functionNotImplemented": "Esta classe não implementou essa função",
"placeholder": "Espaço Reservado", "placeholder": "Espaço reservado",
"someErrors": "Alguns Erros Ocorreram", "someErrors": "Alguns erros ocorreram",
"unexpectedError": "Erro Inesperado", "unexpectedError": "Erro inesperado",
"ok": "Ok", "ok": "OK",
"and": "e", "and": "e",
"githubPATLabel": "Token de Acceso Pessoal do GitHub (Reduz tempos de espera)", "githubPATLabel": "Token de acesso pessoal do GitHub\n(Reduz tempos de espera)",
"includePrereleases": "Incluir pré-lançamentos", "includePrereleases": "Incluir pré-lançamentos",
"fallbackToOlderReleases": "Retornar para versões anteriores", "fallbackToOlderReleases": "Retornar para versões anteriores",
"filterReleaseTitlesByRegEx": "Filtrar Titulos de Versões por Expressão Regular", "filterReleaseTitlesByRegEx": "Filtrar tulos de versões usando expressão regular",
"invalidRegEx": "Expressão Regular Inválida", "invalidRegEx": "Expressão regular inválida",
"noDescription": "Sem descrição", "noDescription": "Sem descrição",
"cancel": "Cancelar", "cancel": "Cancelar",
"continue": "Continuar", "continue": "Continuar",
"requiredInBrackets": "(Necessário)", "requiredInBrackets": "(Necessário)",
"dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO", "dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO",
"colour": "Cor", "colour": "Cor",
"githubStarredRepos": "Favoritados no GitHub", "standard": "Padrão",
"custom": "Personalizado",
"useMaterialYou": "Utilizar o material que",
"githubStarredRepos": "repositórios favoritos no GitHub",
"uname": "Nome de usuário", "uname": "Nome de usuário",
"wrongArgNum": "Número de argumentos errado", "wrongArgNum": "Número de argumentos errado",
"xIsTrackOnly": "{} é 'Apenas Seguir'", "xIsTrackOnly": "{} é 'Apenas monitorar'",
"source": "Fonte", "source": "Fonte",
"app": "App", "app": "Aplicativo",
"appsFromSourceAreTrackOnly": "Os apps desta fonte são 'Apenas Seguir'.", "appsFromSourceAreTrackOnly": "Os aplicativos desta fonte são 'Apenas monitorar'.",
"youPickedTrackOnly": "Você selecionou a opção 'Apenas Seguir'.", "youPickedTrackOnly": "Você selecionou a opção 'Apenas monitorar'.",
"trackOnlyAppDescription": "Esse App vai ser seguido por atualizações, mais o Obtainium não poderá baixa-lo ou instala-lo.", "trackOnlyAppDescription": "As atualizações desse aplicativo serão monitoradas, mas o Obtainium não poderá baixá-lo ou instalá-lo.",
"cancelled": "Cancelado", "cancelled": "Cancelado",
"appAlreadyAdded": "App já adicionado", "appAlreadyAdded": "Aplicativo já adicionado",
"alreadyUpToDateQuestion": "App já atualizado?", "alreadyUpToDateQuestion": "Aplicativo já foi atualizado?",
"addApp": "Adicionar App", "addApp": "Adicionar aplicativo",
"appSourceURL": "URL de origem do App", "appSourceURL": "URL de origem do aplicativo",
"error": "Erro", "error": "Erro",
"add": "Adicionar", "add": "Adicionar",
"searchSomeSourcesLabel": "Procurar (Apenas Algumas Fontes)", "searchSomeSourcesLabel": "Procurar (apenas algumas fontes)",
"search": "Procurar", "search": "Procurar",
"additionalOptsFor": "Opções Adicionais para {}", "additionalOptsFor": "Opções adicionais para {}",
"supportedSources": "Fontes Compatíveis", "supportedSources": "Fontes compatíveis",
"trackOnlyInBrackets": "(Apenas Seguir)", "trackOnlyInBrackets": "(apenas monitorar)",
"searchableInBrackets": "(Pesquisável)", "searchableInBrackets": "(pesquisável)",
"appsString": "Apps", "appsString": "Aplicativos",
"noApps": "Sem Apps", "noApps": "Não há aplicativos",
"noAppsForFilter": "Sem Apps para Filtrar", "noAppsForFilter": "Sem aplicativos para filtrar",
"byX": "Por {}", "byX": "Por {}",
"percentProgress": "Progresso: {}%", "percentProgress": "Progresso: {}%",
"pleaseWait": "Por Favor Espere", "pleaseWait": "Por favor, espere",
"updateAvailable": "Atualização Disponível", "updateAvailable": "Atualização disponível",
"estimateInBracketsShort": "(Aprox.)", "notInstalled": "Não instalado",
"notInstalled": "Não Instalado", "pseudoVersion": "pseudo-versão",
"estimateInBrackets": "(Aproximado)", "selectAll": "Selecionar todos",
"selectAll": "Selecionar All",
"deselectX": "Deselecionar {}", "deselectX": "Deselecionar {}",
"xWillBeRemovedButRemainInstalled": "{} sera removido do Obtainium mais permanecerá instalado no dispositivo.", "xWillBeRemovedButRemainInstalled": "{} será removido do Obtainium mais permanecerá instalado no dispositivo.",
"removeSelectedAppsQuestion": "Remover Apps Selecionados?", "removeSelectedAppsQuestion": "Remover aplicativos selecionados?",
"removeSelectedApps": "Remover Apps Selecionados", "removeSelectedApps": "Remover aplicativos selecionados",
"updateX": "Atualizar {}", "updateX": "Atualizar {}",
"installX": "Instalar {}", "installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas Seguir)\ncomo Atualizado", "markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas monitorar)\ncomo Atualizado",
"changeX": "Mudar {}", "changeX": "Mudar {}",
"installUpdateApps": "Instalar/Atualizar Apps", "installUpdateApps": "Instalar/Atualizar aplicativos",
"installUpdateSelectedApps": "Instalar/Atualizar Apps Selecionados", "installUpdateSelectedApps": "Instalar/Atualizar aplicativos selecionados",
"markXSelectedAppsAsUpdated": "Marcar {} Apps Delecionados como Atualizados?", "markXSelectedAppsAsUpdated": "Marcar {} aplicativos selecionados como atualizados?",
"no": "Não", "no": "Não",
"yes": "Sim", "yes": "Sim",
"markSelectedAppsUpdated": "Marcar Apps Selecionados como Atualizados", "markSelectedAppsUpdated": "Marcar aplicativos selecionados como Atualizados",
"pinToTop": "Fixar no topo", "pinToTop": "Fixar no topo",
"unpinFromTop": "Desafixar do topo", "unpinFromTop": "Desafixar do topo",
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar Status de Instalação para Apps Seleciondos?", "resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação nos aplicativos selecionados?",
"installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer app selecionado sera reiniciado.\n\nIsso pode ajudar quando uma versão de um App mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.", "installStatusOfXWillBeResetExplanation": "O status de instalação de todos os aplicativos selecionados será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
"shareSelectedAppURLs": "Compartilhar URLs de Apps Selecionados", "customLinkMessage": "Esses links funcionam em dispositivos com o Obtainium instalado",
"resetInstallStatus": "Reiniciar Status de Instalação", "shareAppConfigLinks": "Compartilhar configuração do aplicativo como link HTML",
"shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados",
"resetInstallStatus": "Reiniciar status de instalação",
"more": "Mais", "more": "Mais",
"removeOutdatedFilter": "Remover Filtro de Apps Desatualizados", "removeOutdatedFilter": "Remover filtro de aplicativos desatualizados",
"showOutdatedOnly": "Mostrar Apenas Apps Desatualizados", "showOutdatedOnly": "Mostrar apenas aplicativos desatualizados",
"filter": "Filtro", "filter": "Filtro",
"filterActive": "Filtro *", "filterApps": "Filtrar aplicativos",
"filterApps": "Filtrar Apps", "appName": "Nome do aplicativo",
"appName": "Nome do App",
"author": "Autor", "author": "Autor",
"upToDateApps": "Apps Atualizados", "upToDateApps": "Aplicativos atualizados",
"nonInstalledApps": "Apps Não Instalados", "nonInstalledApps": "Aplicativos não instalados",
"importExport": "Importar/Exportar", "importExport": "Importar/Exportar",
"settings": "Configurações", "settings": "Configurações",
"exportedTo": "Exportado para {}", "exportedTo": "Exportado para {}",
"obtainiumExport": "Exportar Obtainium", "obtainiumExport": "Exportar dados do Obtainium",
"invalidInput": "Input Inválido", "invalidInput": "Entrada inválida",
"importedX": "Importado {}", "importedX": "Importado {}",
"obtainiumImport": "Importar Obtainium", "obtainiumImport": "Importar dados do Obtainium",
"importFromURLList": "Importar de Lista de URLs", "importFromURLList": "Importar de lista de URLs",
"searchQuery": "Pesquisa", "searchQuery": "Pesquisa",
"appURLList": "Lista de URLs de Apps", "appURLList": "Lista de URLs de aplicativos",
"line": "Linha", "line": "Linha",
"searchX": "Pesquisa {}", "searchX": "Pesquisar na/o {}",
"noResults": "Nenhum resultado encontrado", "noResults": "Nenhum resultado encontrado",
"importX": "Importar {}", "importX": "Importar {}",
"importedAppsIdDisclaimer": "Apps Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do App.\n\nAfeta apenas métodos de importação de URL e de terceiros.", "importedAppsIdDisclaimer": "Aplicativos Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do aplicativo.\n\nAfeta apenas métodos de importação de URL e de terceiros.",
"importErrors": "Erros de Importação", "importErrors": "Erros de importação",
"importedXOfYApps": "{} de {} Apps importados.", "importedXOfYApps": "{} de {} aplicativos importados.",
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:", "followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
"selectURL": "Selecionar URL", "selectURL": "Selecionar URL",
"selectURLs": "Selecionar URLs", "selectURLs": "Selecionar URLs",
@ -109,203 +112,222 @@
"theme": "Tema", "theme": "Tema",
"dark": "Escuro", "dark": "Escuro",
"light": "Claro", "light": "Claro",
"followSystem": "Seguir o Sistema", "followSystem": "Padrão do sistema",
"obtainium": "Obtainium", "useBlackTheme": "Usar tema preto AMOLED",
"materialYou": "Material You", "appSortBy": "Classificar aplicativo por",
"useBlackTheme": "Usar tema preto completamente escuro",
"appSortBy": "Classificar App por",
"authorName": "Autor/Nome", "authorName": "Autor/Nome",
"nameAuthor": "Nome/Autor", "nameAuthor": "Nome/Autor",
"asAdded": "Como Adicionado", "asAdded": "Como adicionado",
"appSortOrder": "Ordem de classificação de Apps", "appSortOrder": "Ordem de classificação de aplicativos",
"ascending": "Ascendente", "ascending": "Ascendente",
"descending": "Descendente", "descending": "Descendente",
"bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo plano", "bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo-plano",
"neverManualOnly": "Nunca - Apenas Manual", "neverManualOnly": "Nunca - apenas manual",
"appearance": "Aparência", "appearance": "Aparência",
"showWebInAppView": "Mostrar páginas da internet em App view", "showWebInAppView": "Mostrar página web do aplicativo em informações do aplicativo",
"pinUpdates": "Fixar atualizações no topo da visão de Apps", "pinUpdates": "Fixar atualizações no topo da janela de aplicativos",
"updates": "Atualizações", "updates": "Atualizações",
"sourceSpecific": "Específico a fonte", "sourceSpecific": "Token de acesso",
"appSource": "Fonte do App", "appSource": "Fonte do aplicativo",
"noLogs": "Sem Logs", "noLogs": "Sem logs",
"appLogs": "Logs do App", "appLogs": "Logs do aplicativo",
"close": "Fechar", "close": "Fechar",
"share": "Compartilhar", "share": "Compartilhar",
"appNotFound": "App não encontrado", "appNotFound": "Aplicativo não encontrado",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "exportação de obtainium",
"pickAnAPK": "Selecionar um APK", "pickAnAPK": "Selecionar um APK",
"appHasMoreThanOnePackage": "{} tem mais de um pacote:", "appHasMoreThanOnePackage": "{} tem mais de um pacote:",
"deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.", "deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.",
"deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:", "deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:",
"warning": "Aviso", "warning": "Aviso",
"sourceIsXButPackageFromYPrompt": "A Fonte do App é '{}' mais o pacote lançado vem de '{}'. Continuar?", "sourceIsXButPackageFromYPrompt": "A fonte do aplicativo é '{}' mas a origem do pacote é '{}'. Continuar?",
"updatesAvailable": "Atualizações Disponíveis", "updatesAvailable": "Atualizações disponíveis",
"updatesAvailableNotifDescription": "Notifica o usuário quando atualizações estão disponíveis um ou mais Apps seguidos pelo Obtainium", "updatesAvailableNotifDescription": "Notifica o usuário quando atualizações de um ou mais aplicativos monitorados pelo Obtainium estão disponíveis",
"noNewUpdates": "Sem novas atualizações.", "noNewUpdates": "Sem novas atualizações.",
"xHasAnUpdate": "{} tem uma atualização.", "xHasAnUpdate": "{} tem uma atualização.",
"appsUpdated": "Apps Atualizados", "appsUpdated": "Aplicativos atualizados",
"appsUpdatedNotifDescription": "Notifica o usuário quando atualizações para um ou mais Apps foram aplicadas em segundo plano", "appsNotUpdated": "Falha na atualização das aplicações",
"appsUpdatedNotifDescription": "Notifica o usuário quando atualizações foram aplicadas em segundo-plano para um ou mais aplicativos ",
"xWasUpdatedToY": "{} foi atualizado para {}.", "xWasUpdatedToY": "{} foi atualizado para {}.",
"errorCheckingUpdates": "Erro ao Procurar por Atualizações", "xWasNotUpdatedToY": "Falha ao atualizar {} para {}.",
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo plano falha", "errorCheckingUpdates": "Erro ao procurar por atualizações",
"appsRemoved": "Apps Removidos", "errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo-plano falha",
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais Apps foram removidos devido a erros ao carregá-los", "appsRemoved": "Aplicativos removidos",
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais aplicativos foram removidos devido a erros de carregamento",
"xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}", "xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}",
"completeAppInstallation": "Instalação completa do App", "completeAppInstallation": "Instalação do aplicativo concluída",
"obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar Apps", "obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar os aplicativos",
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um App", "completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um aplicativo",
"checkingForUpdates": "Checando por Atualizações", "checkingForUpdates": "Verificando atualizações",
"checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando checando por atualizações", "checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando o Obtainium está verificando se há atualizações",
"pleaseAllowInstallPerm": "Por favor, permita o Obtainium instalar Apps", "pleaseAllowInstallPerm": "Por favor, permita que o Obtainium possa instalar aplicativos",
"trackOnly": "Apenas Seguir", "trackOnly": "Apenas monitorar",
"errorWithHttpStatusCode": "Erro {}", "errorWithHttpStatusCode": "Erro {}",
"versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)", "versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)",
"unknown": "Desconhecido", "unknown": "Desconhecido",
"none": "Nenhum", "none": "Nenhum",
"never": "Nunca", "never": "Nunca",
"latestVersionX": "Última versão: {}", "latestVersionX": "Última versão: {}",
"installedVersionX": "Versão Instalada: {}", "installedVersionX": "Versão instalada: {}",
"lastUpdateCheckX": "Última Checagem por Atualização: {}", "lastUpdateCheckX": "Última verificação de atualizações: {}",
"remove": "Remover", "remove": "Remover",
"yesMarkUpdated": "Sim, Marcar como Atualizado", "yesMarkUpdated": "Sim, marcar como atualizado",
"fdroid": "F-Droid Official", "fdroid": "Oficial F-Droid",
"appIdOrName": "ID do App ou Nome", "appIdOrName": "ID do aplicativo ou nome",
"appId": "ID do App", "appId": "ID do aplicativo",
"appWithIdOrNameNotFound": "Nenhum App foi encontrado com esse ID ou nome", "appWithIdOrNameNotFound": "Nenhum aplicativo foi encontrado com esse ID ou nome",
"reposHaveMultipleApps": "Repositórios podem conter multiplos Apps", "reposHaveMultipleApps": "Repositórios podem conter múltiplos aplicativos",
"fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid", "fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid",
"steam": "Steam", "steamMobile": "Steam para celular",
"steamMobile": "Steam Mobile", "steamChat": "Chat do Steam",
"steamChat": "Steam Chat",
"install": "Instalar", "install": "Instalar",
"markInstalled": "Marcar Instalado", "markInstalled": "Marcar instalado",
"update": "Atualizar", "update": "Atualizar",
"markUpdated": "Marcar Atualizado", "markUpdated": "Marcar como atualizado",
"additionalOptions": "Opções Adicionais", "additionalOptions": "Opções adicionais",
"disableVersionDetection": "Desativar Detecção de Versão", "disableVersionDetection": "Desativar detecção de versão",
"noVersionDetectionExplanation": "Essa opção deve apenas ser usada por Apps onde detecção de versão não funciona corretamente.", "noVersionDetectionExplanation": "Essa opção deve apenas ser usada por aplicativos onde a detecção de versão não funciona corretamente.",
"downloadingX": "Baixando {}", "downloadingX": "Baixando {}",
"downloadNotifDescription": "Notifica o usuário do progresso ao baixar um App", "downloadX": "Descarregar {}",
"downloadedX": "Descarregado {}",
"releaseAsset": "Libertação de activos",
"downloadNotifDescription": "Notifica o usuário o progresso do download de um aplicativo",
"noAPKFound": "APK não encontrado", "noAPKFound": "APK não encontrado",
"noVersionDetection": "Sem Detecção de versão", "noVersionDetection": "Sem detecção de versão",
"categorize": "Categorizar", "categorize": "Categorizar",
"categories": "Categorias", "categories": "Categorias",
"category": "Categoria", "category": "Categoria",
"noCategory": "Sem Categoria", "noCategory": "Sem categoria",
"noCategories": "Sem Categoria", "noCategories": "Sem categorias",
"deleteCategoriesQuestion": "Deletar Categorias?", "deleteCategoriesQuestion": "Deletar categorias?",
"categoryDeleteWarning": "Todos os Apps em categorias removidas serão descategorizados.", "categoryDeleteWarning": "Todos os aplicativos em categorias removidas serão descategorizados.",
"addCategory": "Adicionar Categoria", "addCategory": "Adicionar categoria",
"label": "Etiqueta", "label": "Etiqueta",
"language": "Linguagem", "language": "Linguagem",
"copiedToClipboard": "Copiado para a área de transferência", "copiedToClipboard": "Copiado para a área de transferência",
"storagePermissionDenied": "Permição ao armazenamento negada", "storagePermissionDenied": "Permissão de armazenamento negada",
"selectedCategorizeWarning": "Isso vai substituir qualquer confirução de categoria para os Apps selecionados.", "selectedCategorizeWarning": "Isso vai substituir qualquer configuração de categoria para os aplicativos selecionados.",
"filterAPKsByRegEx": "Filtrar APKs por Expressão Regular", "filterAPKsByRegEx": "Filtrar APKs usando expressão regular",
"removeFromObtainium": "Remover do Obtainium", "removeFromObtainium": "Remover do Obtainium",
"uninstallFromDevice": "Desinstalar do dispositivo", "uninstallFromDevice": "Desinstalar do dispositivo",
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para Apps com detecção de versão desativada.", "onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicativos com detecção de versão desativada.",
"releaseDateAsVersion": "Usar Data de Lançamento como Versão", "releaseDateAsVersion": "Usar data de lançamento como versão",
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.", "releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.",
"changes": "Mudanças", "changes": "Alterações",
"releaseDate": "Data de Lançamento", "releaseDate": "Data de lançamento",
"importFromURLsInFile": "Importar de URLs em Arquivo (como OPML)", "importFromURLsInFile": "Importar de URLs em arquivo (formato OPML)",
"versionDetection": "Detecção de Versão", "versionDetectionExplanation": "Reconciliar string de versão com versão detectada no sistema operacional",
"versionDetection": "Detecção de versão",
"standardVersionDetection": "Detecção de versão padrão", "standardVersionDetection": "Detecção de versão padrão",
"groupByCategory": "Agroupar por Categoria", "groupByCategory": "Agroupar por categoria",
"autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível", "autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível",
"overrideSource": "Substituir Fonte", "overrideSource": "Substituir fonte",
"dontShowAgain": "Não mostrar isso novamente", "dontShowAgain": "Não mostrar isso novamente",
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas Seguir'", "dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas monitorar'",
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK", "dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
"moveNonInstalledAppsToBottom": "Mover Apps não instalados para o fundo da visão de Apps", "moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos",
"gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)", "gitlabPATLabel": "Token de acesso pessoal do Gitlab",
"about": "Sobre", "about": "Sobre",
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)", "requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
"checkOnStart": "Checar por atualizações ao iniciar ", "checkOnStart": "Verificar se há atualizações ao iniciar",
"tryInferAppIdFromCode": "Tente inferir o ID do App pelo código fonte", "tryInferAppIdFromCode": "Tente inferir o ID do aplicativo pelo código-fonte",
"removeOnExternalUninstall": "Remover automaticamente Apps desinstalados externamente", "removeOnExternalUninstall": "Remover automaticamente aplicativos desinstalados externamente",
"pickHighestVersionCode": "Auto-selecionar o maior numero de versão do APK", "pickHighestVersionCode": "Auto-selecionar o maior número de versão do APK",
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a pagina de detalhes de um App", "checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de um aplicativo",
"disablePageTransitions": "Desativar animações de transição de pagina", "disablePageTransitions": "Desativar animações de transição de página",
"reversePageTransitions": "Reverter animações de transição de pagina", "reversePageTransitions": "Animações de transição de página invertidas",
"minStarCount": "Contagem Minima de Estrelas", "minStarCount": "Contagem nima de estrelas",
"addInfoBelow": "Adicionar essa informação abaixo.", "addInfoBelow": "Adicionar essa informação abaixo.",
"addInfoInSettings": "Adicionar essa informação nas configurações.", "addInfoInSettings": "Adicionar essa informação nas configurações.",
"githubSourceNote": "A limitação de taxa do GitHub pode ser evitada usando uma chave de API.", "githubSourceNote": "A limitação de taxa do GitHub pode ser evitada usando uma chave de API.",
"gitlabSourceNote": "A extração de APK do GitLab pode não funcionar sem uma chave de API.", "sortByLastLinkSegment": "Ordenar apenas usando o último segmento do link",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "filterReleaseNotesByRegEx": "Filtrar notas de versão usando Regex",
"filterReleaseNotesByRegEx": "Filtrar Notas de Lançamento por Expressão Regular", "customLinkFilterRegex": "Filtro de link personalizado usando expressão regular (Padrão '.apk$')",
"customLinkFilterRegex": "Filtro de Link Personalizado por Expressão Regular (Padrão '.apk$')", "appsPossiblyUpdated": "Tentativas de atualização de aplicativos",
"appsPossiblyUpdated": "Tentativas de atualização de Apps", "appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais aplicativos foram potencialmente aplicadas em segundo-plano",
"appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais Apps foram potencialmente aplicadas em segundo plano",
"xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.", "xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.",
"enableBackgroundUpdates": "Ativar atualizações em segundo plano", "enableBackgroundUpdates": "Ativar atualizações em segundo-plano",
"backgroundUpdateReqsExplanation": "Atualizações em segundo plano podem não ser possíveis para todos os Apps.", "backgroundUpdateReqsExplanation": "Atualizações em segundo-plano podem não ser possíveis para todos os aplicativos.",
"backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo plano só pode ser determinado quando o Obtainium é aberto.", "backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo-plano só pode ser determinado quando o Obtainium é aberto.",
"verifyLatestTag": "Verifique a 'ultima' etiqueta", "verifyLatestTag": "Verifique a 'última' etiqueta",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Filtrar um link 'intermediário' para visitar",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filtrar links pelo texto do link",
"intermediateLinkNotFound": "Link intermediário não encontrado", "intermediateLinkNotFound": "Link intermediário não encontrado",
"intermediateLink": "Intermediate link", "intermediateLink": "Link intermediário",
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)", "exemptFromBackgroundUpdates": "Isento de atualizações em segundo-plano (se ativadas)",
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi", "bgUpdatesOnWiFiOnly": "Desative as atualizações em segundo-plano quando não estiver conectado no Wi-Fi",
"autoSelectHighestVersionCode": "Auto-selecionar o maior codigo de versão", "autoSelectHighestVersionCode": "Auto-selecionar a versão mais recente",
"versionExtractionRegEx": "RegEx para Extração de Versão", "versionExtractionRegEx": "Regex de extração de versão",
"matchGroupToUse": "Grupo de Seleção para Usar", "matchGroupToUse": "Grupo correspondente a ser usado no Regex de extração de versão",
"highlightTouchTargets": "Destaque areas de toque menos óbvias", "highlightTouchTargets": "Realçar áreas sensíveis ao toque que são menos óbvias",
"pickExportDir": "Escolher Diretorio de Exportação", "pickExportDir": "Escolher diretório para exportação",
"autoExportOnChanges": "Auto-exportar em mudanças", "autoExportOnChanges": "Auto-exportar em mudanças",
"includeSettings": "Include settings", "includeSettings": "Incluir configurações",
"filterVersionsByRegEx": "Filtrar Versões por Expressão Regular", "filterVersionsByRegEx": "Filtrar versões por expressão regular",
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida", "trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
"dontSortReleasesList": "Reter a ordem de lançamento da API", "dontSortReleasesList": "Reter a ordem de lançamento da API",
"reverseSort": "Ordenação reversa", "reverseSort": "Ordenação reversa",
"takeFirstLink": "Take first link", "takeFirstLink": "Obter primeiro link",
"skipSort": "Skip sorting", "skipSort": "Ignorar ordenação",
"debugMenu": "Menu Debug", "debugMenu": "Menu debug",
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.", "bgTaskStarted": "Tarefa em segundo-plano iniciada - verifique os logs.",
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora", "runBgCheckNow": "Execute agora em segundo-plano a verificação de atualizações",
"versionExtractWholePage": "Aplicar Regex de Extração de Versão à Página Inteira", "versionExtractWholePage": "Aplicar regex de extração de versão à página inteira",
"installing": "Instalando", "installing": "Instalando",
"skipUpdateNotifications": "Pular notificações de update", "skipUpdateNotifications": "Pular notificações de update",
"updatesAvailableNotifChannel": "Atualizações Disponíveis", "updatesAvailableNotifChannel": "Atualizações disponíveis",
"appsUpdatedNotifChannel": "Apps Atualizados", "appsUpdatedNotifChannel": "Aplicativos atualizados",
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de Apps", "appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de aplicativos",
"errorCheckingUpdatesNotifChannel": "Erro ao Procurar por Atualizações", "errorCheckingUpdatesNotifChannel": "Erro ao procurar por atualizações",
"appsRemovedNotifChannel": "Apps Removidos", "appsRemovedNotifChannel": "Aplicativos removidos",
"downloadingXNotifChannel": "Baixando {}", "downloadingXNotifChannel": "Baixando {}",
"completeAppInstallationNotifChannel": "Instalação completa do App", "completeAppInstallationNotifChannel": "Instalação completa do aplicativo",
"checkingForUpdatesNotifChannel": "Checando por Atualizações", "checkingForUpdatesNotifChannel": "Checando por atualizações",
"onlyCheckInstalledOrTrackOnlyApps": "Apenas checar apps instalados e 'Apenas Seguir' por updates", "onlyCheckInstalledOrTrackOnlyApps": "Apenas verificar atualizações de aplicativos instalados e 'Apenas monitorar'",
"supportFixedAPKURL": "Suporte APK com URLs fixas", "supportFixedAPKURL": "Suporte a APK com URLs fixas",
"selectX": "Selecionar {}", "selectX": "Selecionar {}",
"parallelDownloads": "Permitir downloads paralelos", "parallelDownloads": "Permitir downloads paralelos",
"installMethod": "Método de instalação", "useShizuku": "Utilizar Shizuku ou Sui para instalar",
"normal": "Normal", "shizukuBinderNotFound": "O Shizuku não está rodando",
"shizuku": "Shizuku", "shizukuOld": "Versão antiga do Shizuku (<11) - atualizar",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku a funcionar no Android < 8.1 com ADB - atualizar o Android ou utilizar o Sui",
"shizukuBinderNotFound": "Shizuku não esta rodando", "shizukuPretendToBeGooglePlay": "Definir o Google Play como fonte de instalação (se for utilizado o Shizuku)",
"useSystemFont": "Usar fonte padrão do sistema",
"useVersionCodeAsOSVersion": "Usar versionCode do aplicativo como versão detectada pelo sistema operacional",
"requestHeader": "Requisitar cabeçalho",
"useLatestAssetDateAsReleaseDate": "Use o último upload de recursos como data de lançamento",
"defaultPseudoVersioningMethod": "Método de pseudo-versionamento padrão",
"partialAPKHash": "Hash APK parcial",
"APKLinkHash": "Hash de links APK",
"directAPKLink": "Link direto do APK",
"pseudoVersionInUse": "Uma pseudo-versão está em uso",
"installed": "Instalado",
"latest": "Mais recente",
"invertRegEx": "Inverter expressão regular",
"note": "Nota",
"selfHostedNote": "O menu suspenso \"{}\" pode ser usado para acessar instâncias auto-hospedadas/personalizadas de qualquer fonte.",
"badDownload": "Não foi possível analisar o APK (transferência incompatível ou parcial)",
"beforeNewInstallsShareToAppVerifier": "Partilhar novas aplicações com o AppVerifier (se disponível)",
"appVerifierInstructionToast": "Partilhe com o AppVerifier e, em seguida, regresse aqui quando estiver pronto.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remover App?", "one": "Remover aplicativo?",
"other": "Remover Apps?" "other": "Remover aplicativos?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Muitas solicitações (taxa limitada) - tente novamente em {} minuto", "one": "Muitas solicitações (taxa de solicitações limitada) - tente novamente em {} minuto",
"other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos" "other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minuto", "one": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minuto",
"other": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minutos" "other": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minutos"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "A verificação de atualizações em segundo plano encontrou {} atualização, o usuário sera notificado caso necessário", "one": "A verificação de atualizações em segundo-plano encontrou {} atualização, o usuário sera notificado caso necessário",
"other": "A verificação de atualizações em segundo plano encontrou {} atualizações, o usuário sera notificado caso necessário" "other": "A verificação de atualizações em segundo-plano encontrou {} atualizações, o usuário sera notificado caso necessário"
}, },
"apps": { "apps": {
"one": "{} App", "one": "{} Aplicativo",
"other": "{} Apps" "other": "{} Aplicativos"
}, },
"url": { "url": {
"one": "{} URL", "one": "{} URL",
@ -324,19 +346,27 @@
"other": "{} Dias" "other": "{} Dias"
}, },
"clearedNLogsBeforeXAfterY": { "clearedNLogsBeforeXAfterY": {
"one": "Limpo {n} log (antes = {antes}, depois = {depois})", "one": "Foi limpo {n} log (antes = {antes}, depois = {depois})",
"other": "Limpados {n} logs (antes = {antes}, depois = {depois})" "other": "Foram limpos {n} logs (antes = {antes}, depois = {depois})"
}, },
"xAndNMoreUpdatesAvailable": { "xAndNMoreUpdatesAvailable": {
"one": "{} e 1 outro app tem atualizações.", "one": "{} e um outro aplicativo possui atualizações.",
"other": "{} e {} outros apps tem atualizações." "other": "{} e {} outros aplicativo possuem atualizações."
}, },
"xAndNMoreUpdatesInstalled": { "xAndNMoreUpdatesInstalled": {
"one": "{} e 1 outro app foi atualizado.", "one": "{} e um outro aplicativo foram atualizado.",
"other": "{} e {} outros apps foram atualizados." "other": "{} e {} outros aplicativos foram atualizados."
},
"xAndNMoreUpdatesFailed": {
"one": "Falha ao atualizar {} e mais 1 aplicação.",
"other": "Falha ao atualizar {} e {} mais aplicações."
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} e 1 outro app pode ter sido atualizado.", "one": "{} e um outro aplicativo podem ter sido atualizados.",
"other": "{} e {} outros apps podem ter sido atualizados." "other": "{} e {} outros aplicativos podem ter sido atualizados."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(обязательно)", "requiredInBrackets": "(обязательно)",
"dropdownNoOptsError": "Ошибка: в выпадающем списке должна быть выбрана хотя бы одна настройка", "dropdownNoOptsError": "Ошибка: в выпадающем списке должна быть выбрана хотя бы одна настройка",
"colour": "Цвет", "colour": "Цвет",
"standard": "Стандартный",
"custom": "Индивидуальный",
"useMaterialYou": "Использовать Material You",
"githubStarredRepos": "Избранные репозитории GitHub", "githubStarredRepos": "Избранные репозитории GitHub",
"uname": "Имя пользователя", "uname": "Имя пользователя",
"wrongArgNum": "Неправильное количество предоставленных аргументов", "wrongArgNum": "Неправильное количество предоставленных аргументов",
@ -51,9 +54,8 @@
"percentProgress": "Прогресс: {}%", "percentProgress": "Прогресс: {}%",
"pleaseWait": "Пожалуйста, подождите", "pleaseWait": "Пожалуйста, подождите",
"updateAvailable": "Доступно обновление", "updateAvailable": "Доступно обновление",
"estimateInBracketsShort": "(Оценка)",
"notInstalled": "Не установлено", "notInstalled": "Не установлено",
"estimateInBrackets": "(Оценка)", "pseudoVersion": "псевдоверсия",
"selectAll": "Выбрать всё", "selectAll": "Выбрать всё",
"deselectX": "Отменить выбор {}", "deselectX": "Отменить выбор {}",
"xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется на устройстве", "xWillBeRemovedButRemainInstalled": "{} будет удалено из Obtainium, но останется на устройстве",
@ -73,13 +75,14 @@
"unpinFromTop": "Открепить", "unpinFromTop": "Открепить",
"resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?", "resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?",
"installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная — из-за неудачных обновлений или других проблем", "installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная — из-за неудачных обновлений или других проблем",
"customLinkMessage": "Эти ссылки работают на устройствах с установленным Obtainium",
"shareAppConfigLinks": "Поделиться конфигурацией приложения в виде HTML-ссылки",
"shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений", "shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений",
"resetInstallStatus": "Сбросить статус установки", "resetInstallStatus": "Сбросить статус установки",
"more": "Ещё", "more": "Ещё",
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений", "removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
"showOutdatedOnly": "Показывать только устаревшие приложения", "showOutdatedOnly": "Показывать только устаревшие приложения",
"filter": "Фильтр", "filter": "Фильтр",
"filterActive": "Фильтр *",
"filterApps": "Фильтровать приложения", "filterApps": "Фильтровать приложения",
"appName": "Название приложения", "appName": "Название приложения",
"author": "Автор", "author": "Автор",
@ -110,8 +113,7 @@
"dark": "Тёмная", "dark": "Тёмная",
"light": "Светлая", "light": "Светлая",
"followSystem": "Системная", "followSystem": "Системная",
"obtainium": "Obtainium", "followSystemThemeExplanation": "Следование системной теме возможно только при использовании сторонних приложений",
"materialYou": "Material You",
"useBlackTheme": "Использовать чёрную тему", "useBlackTheme": "Использовать чёрную тему",
"appSortBy": "Сортировка приложений", "appSortBy": "Сортировка приложений",
"authorName": "Автор/Название", "authorName": "Автор/Название",
@ -133,7 +135,7 @@
"close": "Закрыть", "close": "Закрыть",
"share": "Поделиться", "share": "Поделиться",
"appNotFound": "Приложение не найдено", "appNotFound": "Приложение не найдено",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "получение-экспорт",
"pickAnAPK": "Выберите APK-файл", "pickAnAPK": "Выберите APK-файл",
"appHasMoreThanOnePackage": "{} имеет более одного пакета:", "appHasMoreThanOnePackage": "{} имеет более одного пакета:",
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}", "deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}",
@ -145,8 +147,10 @@
"noNewUpdates": "Нет новых обновлений", "noNewUpdates": "Нет новых обновлений",
"xHasAnUpdate": "{} есть обновление", "xHasAnUpdate": "{} есть обновление",
"appsUpdated": "Приложения обновлены", "appsUpdated": "Приложения обновлены",
"appsNotUpdated": "Не удалось обновить приложения",
"appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме", "appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме",
"xWasUpdatedToY": "{} была обновлена до версии {}", "xWasUpdatedToY": "{} была обновлена до версии {}",
"xWasNotUpdatedToY": "Не удалось обновить {} до версии {}",
"errorCheckingUpdates": "Ошибка при проверке обновлений", "errorCheckingUpdates": "Ошибка при проверке обновлений",
"errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой", "errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой",
"appsRemoved": "Приложение удалено", "appsRemoved": "Приложение удалено",
@ -175,9 +179,8 @@
"appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено", "appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено",
"reposHaveMultipleApps": "В хранилище несколько приложений", "reposHaveMultipleApps": "В хранилище несколько приложений",
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid", "fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
"steam": "Steam", "steamMobile": "Стим Мобайл",
"steamMobile": "Steam Mobile", "steamChat": "Стим-чат",
"steamChat": "Steam Chat",
"install": "Установить", "install": "Установить",
"markInstalled": "Пометить как установленное", "markInstalled": "Пометить как установленное",
"update": "Обновить", "update": "Обновить",
@ -186,6 +189,9 @@
"disableVersionDetection": "Отключить обнаружение версии", "disableVersionDetection": "Отключить обнаружение версии",
"noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где обнаружение версии не работает корректно", "noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где обнаружение версии не работает корректно",
"downloadingX": "Загрузка {}", "downloadingX": "Загрузка {}",
"downloadX": "Скачать {}",
"downloadedX": "Загружено {}",
"releaseAsset": "Освобождение актива",
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения", "downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
"noAPKFound": "APK не найден", "noAPKFound": "APK не найден",
"noVersionDetection": "Обнаружение версий отключено", "noVersionDetection": "Обнаружение версий отключено",
@ -211,6 +217,7 @@
"changes": "Изменения", "changes": "Изменения",
"releaseDate": "Дата выпуска", "releaseDate": "Дата выпуска",
"importFromURLsInFile": "Импорт из файла URL-адресов (например: OPML)", "importFromURLsInFile": "Импорт из файла URL-адресов (например: OPML)",
"versionDetectionExplanation": "Согласовать строку версии с версией, обнаруженной в ОС",
"versionDetection": "Определение версии", "versionDetection": "Определение версии",
"standardVersionDetection": "Стандартное", "standardVersionDetection": "Стандартное",
"groupByCategory": "Группировать по категориям", "groupByCategory": "Группировать по категориям",
@ -220,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях", "dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
"dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов", "dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов",
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка", "moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)", "gitlabPATLabel": "Персональный токен доступа GitLab",
"about": "Описание", "about": "Описание",
"requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)", "requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)",
"checkOnStart": "Проверять наличие обновлений при запуске", "checkOnStart": "Проверять наличие обновлений при запуске",
@ -234,8 +241,7 @@
"addInfoBelow": "Добавьте эту информацию ниже", "addInfoBelow": "Добавьте эту информацию ниже",
"addInfoInSettings": "Добавьте эту информацию в Настройки", "addInfoInSettings": "Добавьте эту информацию в Настройки",
"githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub", "githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub",
"gitlabSourceNote": "Без ключа API может не работать извлечение APK с GitLab", "sortByLastLinkSegment": "Сортировать только по последнему сегменту ссылки",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)", "filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)",
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')", "customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')",
"appsPossiblyUpdated": "Попытки обновления приложений", "appsPossiblyUpdated": "Попытки обновления приложений",
@ -245,10 +251,10 @@
"backgroundUpdateReqsExplanation": "Фоновые обновления могут быть возможны не для всех приложений", "backgroundUpdateReqsExplanation": "Фоновые обновления могут быть возможны не для всех приложений",
"backgroundUpdateLimitsExplanation": "Успешность фоновой установки можно определить только после открытия Obtainium", "backgroundUpdateLimitsExplanation": "Успешность фоновой установки можно определить только после открытия Obtainium",
"verifyLatestTag": "Проверять тег 'latest'", "verifyLatestTag": "Проверять тег 'latest'",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Фильтр для \"промежуточной\" ссылки для посещения",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Фильтрация ссылок по тексту ссылки",
"intermediateLinkNotFound": "Промежуточная ссылка не найдена", "intermediateLinkNotFound": "Промежуточная ссылка не найдена",
"intermediateLink": "Intermediate link", "intermediateLink": "Промежуточное звено",
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)", "exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi", "bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
"autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода", "autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода",
@ -257,13 +263,13 @@
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием", "highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
"pickExportDir": "Выбрать каталог для экспорта", "pickExportDir": "Выбрать каталог для экспорта",
"autoExportOnChanges": "Автоэкспорт при изменениях", "autoExportOnChanges": "Автоэкспорт при изменениях",
"includeSettings": "Include settings", "includeSettings": "Включить настройки",
"filterVersionsByRegEx": "Фильтровать версии по регулярному выражению", "filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK", "trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
"dontSortReleasesList": "Сохранить порядок релизов от API", "dontSortReleasesList": "Сохранить порядок релизов от API",
"reverseSort": "Обратная сортировка", "reverseSort": "Обратная сортировка",
"takeFirstLink": "Take first link", "takeFirstLink": "Взять первую ссылку",
"skipSort": "Skip sorting", "skipSort": "Пропустить сортировку",
"debugMenu": "Меню отладки", "debugMenu": "Меню отладки",
"bgTaskStarted": "Фоновая задача начата — проверьте журналы", "bgTaskStarted": "Фоновая задача начата — проверьте журналы",
"runBgCheckNow": "Запустить проверку фонового обновления сейчас", "runBgCheckNow": "Запустить проверку фонового обновления сейчас",
@ -282,13 +288,28 @@
"supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK", "supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK",
"selectX": "Выбрать {}", "selectX": "Выбрать {}",
"parallelDownloads": "Разрешить параллельные загрузки", "parallelDownloads": "Разрешить параллельные загрузки",
"installMethod": "Метод установки", "useShizuku": "Использовать Shizuku или Sui для установки",
"normal": "Нормальный", "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден, возможно он не запущен",
"shizuku": "Shizuku", "shizukuOld": "Устаревшая версия Shizuku (<11), обновите",
"root": "Суперпользователь", "shizukuOldAndroidWithADB": "Shizuku работает на Android < 8.1 с ADB, обновите Android или используйте Sui",
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден", "shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки (если используется Shizuku)",
"useSystemFont": "Использовать системный шрифт", "useSystemFont": "Использовать системный шрифт",
"systemFontError": "Ошибка загрузки системного шрифта: {}", "useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС",
"requestHeader": "Заголовок запроса",
"useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска",
"defaultPseudoVersioningMethod": "Метод псевдоверсионирования по умолчанию",
"partialAPKHash": "Частичный хэш APK",
"APKLinkHash": "Хеш ссылки APK",
"directAPKLink": "Прямая ссылка на APK",
"pseudoVersionInUse": "Используется псевдоверсия",
"installed": "Установлен",
"latest": "Последний",
"invertRegEx": "Инвертировать регулярное выражение",
"note": "Примечание",
"selfHostedNote": "Выпадающий список \"{}\" можно использовать для доступа к самостоятельно размещенным/настроенным экземплярам любого источника.",
"badDownload": "APK не удалось разобрать (несовместимая или неполная загрузка)",
"beforeNewInstallsShareToAppVerifier": "Поделитесь новыми приложениями с AppVerifier (если доступно)",
"appVerifierInstructionToast": "Поделитесь с AppVerifier, а затем вернитесь сюда, когда будете готовы.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Удалить приложение?", "one": "Удалить приложение?",
"other": "Удалить приложения?" "other": "Удалить приложения?"
@ -337,8 +358,16 @@
"one": "{} и ещё 1 приложение были обновлены", "one": "{} и ещё 1 приложение были обновлены",
"other": "{} и ещё {} приложений были обновлены" "other": "{} и ещё {} приложений были обновлены"
}, },
"xAndNMoreUpdatesFailed": {
"one": "Не удалось обновить {} и ещё 1 приложение",
"other": "Не удалось обновить {} и ещё {} приложений"
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} и ещё 1 приложение могли быть обновлены", "one": "{} и ещё 1 приложение могли быть обновлены",
"other": "{} и ещё {} приложений могли быть обновлены" "other": "{} и ещё {} приложений могли быть обновлены"
},
"apk": {
"one": "{} APK",
"other": "{} APKи"
} }
} }

View File

@ -0,0 +1,118 @@
// Take one (hardcoded) translation file and ensure that all other translation files have the same keys in the same order
// Then report which other translation files have identical items (or auto-translate them if a DeepL API key is provided)
const fs = require('fs')
const https = require('https')
const deeplAPIKey = process.argv[2]
const neverAutoTranslate = {
steamMobile: ['*'],
steamChat: ['*'],
root: ['*'],
obtainiumExportHyphenatedLowercase: ['*'],
theme: ['de'],
appId: ['de']
}
const translateText = async (text, targetLang, authKey) => {
return new Promise((resolve, reject) => {
const postData = `text=${encodeURIComponent(text)}&target_lang=${encodeURIComponent(targetLang)}&source_lang=EN`
const options = {
hostname: 'api-free.deepl.com',
port: 443,
path: '/v2/translate',
method: 'POST',
headers: {
'Authorization': `DeepL-Auth-Key ${authKey}`,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
}
const req = https.request(options, (res) => {
let responseData = ''
res.on('data', (chunk) => {
responseData += chunk
})
res.on('end', () => {
try {
const jsonResponse = JSON.parse(responseData)
resolve(jsonResponse)
} catch (error) {
reject(error)
}
})
})
req.on('error', (error) => {
reject(error)
})
req.write(postData)
req.end()
})
}
const main = async () => {
const translationsDir = __dirname
const templateFile = `${translationsDir}/en.json`
const otherFiles = fs.readdirSync(translationsDir).map(f => {
return `${translationsDir}/${f}`
}).filter(f => f.endsWith('.json') && f != templateFile)
const templateTranslation = JSON.parse(fs.readFileSync(templateFile).toString())
otherFiles.forEach(file => {
const thisTranslationOriginal = JSON.parse(fs.readFileSync((file).toString()))
const thisTranslationNew = {}
Object.keys(templateTranslation).forEach(k => {
thisTranslationNew[k] = thisTranslationOriginal[k] || templateTranslation[k]
})
fs.writeFileSync(file, `${JSON.stringify(thisTranslationNew, null, ' ')}\n`)
})
for (let i in otherFiles) {
const file = otherFiles[i]
const thisTranslation = JSON.parse(fs.readFileSync((file).toString()))
const translationKeys = Object.keys(templateTranslation)
for (let j in translationKeys) {
const k = translationKeys[j]
if (JSON.stringify(thisTranslation[k]) == JSON.stringify(templateTranslation[k])) {
const lang = file.split('/').pop().split('.')[0]
if (!neverAutoTranslate[k] || (neverAutoTranslate[k].indexOf('*') < 0 && neverAutoTranslate[k].indexOf(lang) < 0)) {
const reportLine = `${file} :::: ${k} :::: ${JSON.stringify(thisTranslation[k])}`
if (deeplAPIKey) {
const translateFunc = async (str) => {
const response = await translateText(str, lang, deeplAPIKey)
if (response.translations) {
return response.translations[0].text
} else {
throw JSON.stringify(response)
}
}
try {
if (typeof templateTranslation[k] == 'string') {
thisTranslation[k] = await translateFunc(thisTranslation[k])
} else {
const subKeys = Object.keys(templateTranslation[k])
for (let n in subKeys) {
const kk = subKeys[n]
thisTranslation[k][kk] = await translateFunc(thisTranslation[k][kk])
}
}
} catch (e) {
if (typeof e == 'string') {
console.log(`${reportLine} :::: ${e}`)
} else {
throw e
}
}
} else {
console.log(reportLine)
}
}
}
}
fs.writeFileSync(file, `${JSON.stringify(thisTranslation, null, ' ')}\n`)
}
}
main().catch(e => console.error)

View File

@ -5,26 +5,29 @@
"urlMatchesNoSource": "URL matchar inte känd källa", "urlMatchesNoSource": "URL matchar inte känd källa",
"cantInstallOlderVersion": "Kan inte installera en äldre version av en app", "cantInstallOlderVersion": "Kan inte installera en äldre version av en app",
"appIdMismatch": "Nerladdat paket-ID matchar inte nuvarande App-ID", "appIdMismatch": "Nerladdat paket-ID matchar inte nuvarande App-ID",
"functionNotImplemented": "This class has not implemented this function", "functionNotImplemented": "Den här klassen har inte implementerat den här funktionen",
"placeholder": "Platshållare", "placeholder": "Platshållare",
"someErrors": "Några fel uppstod", "someErrors": "Några fel uppstod",
"unexpectedError": "Oväntat fel", "unexpectedError": "Oväntat fel",
"ok": "Okej", "ok": "Okej",
"and": "och", "and": "och",
"githubPATLabel": "GitHub Personal Access Token (Increases Rate Limit)", "githubPATLabel": "GitHub Personal Access Token (ökar hastighetsgräns)",
"includePrereleases": "Inkludera förreleaser", "includePrereleases": "Inkludera förreleaser",
"fallbackToOlderReleases": "Fall tillbaka till äldre releaser", "fallbackToOlderReleases": "Fall tillbaka till äldre releaser",
"filterReleaseTitlesByRegEx": "Filter Release Titles by Regular Expression", "filterReleaseTitlesByRegEx": "Filtrera utgivningstitlar efter reguljärt uttryck",
"invalidRegEx": "Invalid regular expression", "invalidRegEx": "Ogiltigt reguljärt uttryck",
"noDescription": "Ingen beskrivning", "noDescription": "Ingen beskrivning",
"cancel": "Avbryt", "cancel": "Avbryt",
"continue": "Fortsätt", "continue": "Fortsätt",
"requiredInBrackets": "(Kräver)", "requiredInBrackets": "(Kräver)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT", "dropdownNoOptsError": "FEL: DROPDOWN MÅSTE HA MINST ETT OPT",
"colour": "Färg", "colour": "Färg",
"standard": "Standard",
"custom": "Anpassad",
"useMaterialYou": "Använd material Du",
"githubStarredRepos": "GitHub Stjärnmärkta Förråd", "githubStarredRepos": "GitHub Stjärnmärkta Förråd",
"uname": "Användarnamn", "uname": "Användarnamn",
"wrongArgNum": "Wrong number of arguments provided", "wrongArgNum": "Fel antal argument har angetts",
"xIsTrackOnly": "{} är 'Följ-Endast'", "xIsTrackOnly": "{} är 'Följ-Endast'",
"source": "Källa", "source": "Källa",
"app": "App", "app": "App",
@ -48,12 +51,11 @@
"noApps": "Inga Appar", "noApps": "Inga Appar",
"noAppsForFilter": "Inga Appar för Filter", "noAppsForFilter": "Inga Appar för Filter",
"byX": "Av {}", "byX": "Av {}",
"percentProgress": "Progress: {}%", "percentProgress": "Framsteg: {}%",
"pleaseWait": "Vänta", "pleaseWait": "Vänta",
"updateAvailable": "Uppdatering Tillgänglig", "updateAvailable": "Uppdatering Tillgänglig",
"estimateInBracketsShort": "(Est.)",
"notInstalled": "Inte Installerad", "notInstalled": "Inte Installerad",
"estimateInBrackets": "(Uppskattning)", "pseudoVersion": "pseudoversion",
"selectAll": "Välj Alla", "selectAll": "Välj Alla",
"deselectX": "Avmarkera {}", "deselectX": "Avmarkera {}",
"xWillBeRemovedButRemainInstalled": "{} kommer tas bort från Obtainium men kommer vara fortsatt installerad på enheten.", "xWillBeRemovedButRemainInstalled": "{} kommer tas bort från Obtainium men kommer vara fortsatt installerad på enheten.",
@ -73,13 +75,14 @@
"unpinFromTop": "Avnåla", "unpinFromTop": "Avnåla",
"resetInstallStatusForSelectedAppsQuestion": "Återställ Installationsstatus för valda Appar?", "resetInstallStatusForSelectedAppsQuestion": "Återställ Installationsstatus för valda Appar?",
"installStatusOfXWillBeResetExplanation": "Installationsstatusen för de markerade apparna kommer återställas.\n\n Detta kan hjälpa när appversionen visad i Obtanium är fel på grund av misslyckade uppdateringar eller andra orsaker.", "installStatusOfXWillBeResetExplanation": "Installationsstatusen för de markerade apparna kommer återställas.\n\n Detta kan hjälpa när appversionen visad i Obtanium är fel på grund av misslyckade uppdateringar eller andra orsaker.",
"customLinkMessage": "Dessa länkar fungerar på enheter med Obtainium installerat",
"shareAppConfigLinks": "Dela appkonfiguration som HTML-länk",
"shareSelectedAppURLs": "Dela Valda Appars URL:er", "shareSelectedAppURLs": "Dela Valda Appars URL:er",
"resetInstallStatus": "Återställ Installationstatus", "resetInstallStatus": "Återställ Installationstatus",
"more": "Mer", "more": "Mer",
"removeOutdatedFilter": "Ta bort Utgånga App-filtret", "removeOutdatedFilter": "Ta bort Utgånga App-filtret",
"showOutdatedOnly": "Visa Endast Utgånga Appar", "showOutdatedOnly": "Visa Endast Utgånga Appar",
"filter": "Filter", "filter": "Filtrera",
"filterActive": "Filter *",
"filterApps": "Filtrera Appar", "filterApps": "Filtrera Appar",
"appName": "Appnamn", "appName": "Appnamn",
"author": "Utvecklare", "author": "Utvecklare",
@ -94,7 +97,7 @@
"obtainiumImport": "Obtainium Import", "obtainiumImport": "Obtainium Import",
"importFromURLList": "Importera från URL-lista", "importFromURLList": "Importera från URL-lista",
"searchQuery": "Sökförfrågan", "searchQuery": "Sökförfrågan",
"appURLList": "App URL List", "appURLList": "App URL-lista",
"line": "Linje", "line": "Linje",
"searchX": "Sök {}", "searchX": "Sök {}",
"noResults": "Inga resultat", "noResults": "Inga resultat",
@ -110,18 +113,16 @@
"dark": "Mörkt", "dark": "Mörkt",
"light": "Ljust", "light": "Ljust",
"followSystem": "Följ System", "followSystem": "Följ System",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Använd svart tema", "useBlackTheme": "Använd svart tema",
"appSortBy": "Sortera Appar via", "appSortBy": "Sortera Appar via",
"authorName": "Utvecklare/Namn", "authorName": "Utvecklare/Namn",
"nameAuthor": "Namn/Utvecklare", "nameAuthor": "Namn/Utvecklare",
"asAdded": "As Added", "asAdded": "Som tillagd",
"appSortOrder": "App Sort Order", "appSortOrder": "Appsorteringsordning",
"ascending": "Stigande", "ascending": "Stigande",
"descending": "Fallande", "descending": "Fallande",
"bgUpdateCheckInterval": "Bakgrundsuppdateringskollfrekvens", "bgUpdateCheckInterval": "Bakgrundsuppdateringskollfrekvens",
"neverManualOnly": "Never - Manual Only", "neverManualOnly": "Aldrig - Endast manuell",
"appearance": "Utseende", "appearance": "Utseende",
"showWebInAppView": "Visa källans hemsida i appvyn", "showWebInAppView": "Visa källans hemsida i appvyn",
"pinUpdates": "Fäst uppdateringar högst upp i appvyn", "pinUpdates": "Fäst uppdateringar högst upp i appvyn",
@ -145,8 +146,10 @@
"noNewUpdates": "Inga nya uppdateringar.", "noNewUpdates": "Inga nya uppdateringar.",
"xHasAnUpdate": "{} har en uppdatering.", "xHasAnUpdate": "{} har en uppdatering.",
"appsUpdated": "Appar Uppdaterade", "appsUpdated": "Appar Uppdaterade",
"appsUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were applied in the background", "appsNotUpdated": "Misslyckades med att uppdatera applikationer",
"appsUpdatedNotifDescription": "Meddelar användaren att uppdateringar av en eller flera appar har tillämpats i bakgrunden",
"xWasUpdatedToY": "{} uppdaterades till {}.", "xWasUpdatedToY": "{} uppdaterades till {}.",
"xWasNotUpdatedToY": "Det gick inte att uppdatera {} till {}.",
"errorCheckingUpdates": "Fel vid uppdateringskoll", "errorCheckingUpdates": "Fel vid uppdateringskoll",
"errorCheckingUpdatesNotifDescription": "En aviserings som visar när bakgrundsuppdateringarkollar misslyckas", "errorCheckingUpdatesNotifDescription": "En aviserings som visar när bakgrundsuppdateringarkollar misslyckas",
"appsRemoved": "Appar borttagna", "appsRemoved": "Appar borttagna",
@ -156,13 +159,13 @@
"obtainiumMustBeOpenToInstallApps": "Obtainium måste vara öppet för att installera Appar", "obtainiumMustBeOpenToInstallApps": "Obtainium måste vara öppet för att installera Appar",
"completeAppInstallationNotifDescription": "Frågar användaren att återvända till Obtaiunium när appinstallation är klar", "completeAppInstallationNotifDescription": "Frågar användaren att återvända till Obtaiunium när appinstallation är klar",
"checkingForUpdates": "Kollar efter Uppdateringar", "checkingForUpdates": "Kollar efter Uppdateringar",
"checkingForUpdatesNotifDescription": "Transient notification that appears when checking for updates", "checkingForUpdatesNotifDescription": "Övergående meddelande som visas när du söker efter uppdateringar",
"pleaseAllowInstallPerm": "Tillåt Obtanium att installera Appar", "pleaseAllowInstallPerm": "Tillåt Obtanium att installera Appar",
"trackOnly": "Följ-Endast", "trackOnly": "Följ-Endast",
"errorWithHttpStatusCode": "Fel {}", "errorWithHttpStatusCode": "Fel {}",
"versionCorrectionDisabled": "Versionskorrigering inaktiverat (plugin verkar inte fungera)", "versionCorrectionDisabled": "Versionskorrigering inaktiverat (plugin verkar inte fungera)",
"unknown": "Okänd", "unknown": "Okänd",
"none": "None", "none": "Ingen",
"never": "Aldrig", "never": "Aldrig",
"latestVersionX": "Senaste Version: {}", "latestVersionX": "Senaste Version: {}",
"installedVersionX": "Installerad Version: {}", "installedVersionX": "Installerad Version: {}",
@ -175,7 +178,6 @@
"appWithIdOrNameNotFound": "Ingen App funnen med det namnet eller ID", "appWithIdOrNameNotFound": "Ingen App funnen med det namnet eller ID",
"reposHaveMultipleApps": "Förråd kan innehålla flera ApparR", "reposHaveMultipleApps": "Förråd kan innehålla flera ApparR",
"fdroidThirdPartyRepo": "F-Droid Tredjeparts Förråd", "fdroidThirdPartyRepo": "F-Droid Tredjeparts Förråd",
"steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam Chat",
"install": "Installera", "install": "Installera",
@ -184,9 +186,12 @@
"markUpdated": "Märk Uppdaterad", "markUpdated": "Märk Uppdaterad",
"additionalOptions": "Ytterligare Alternativ", "additionalOptions": "Ytterligare Alternativ",
"disableVersionDetection": "Inaktivera versionsdetektering", "disableVersionDetection": "Inaktivera versionsdetektering",
"noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", "noVersionDetectionExplanation": "Det här alternativet bör endast användas för appar där versionsidentifiering inte fungerar korrekt.",
"downloadingX": "Laddar ner {}", "downloadingX": "Laddar ner {}",
"downloadNotifDescription": "Notifies the user of the progress in downloading an App", "downloadX": "Ladda ner {}",
"downloadedX": "Nedladdad {}",
"releaseAsset": "Frigör tillgång",
"downloadNotifDescription": "Meddelar användaren om framstegen med att ladda ner en app",
"noAPKFound": "Ingen APK funnen", "noAPKFound": "Ingen APK funnen",
"noVersionDetection": "Ingen versiondetektering", "noVersionDetection": "Ingen versiondetektering",
"categorize": "Kategorisera", "categorize": "Kategorisera",
@ -197,97 +202,128 @@
"deleteCategoriesQuestion": "Ta Bort Kategorier?", "deleteCategoriesQuestion": "Ta Bort Kategorier?",
"categoryDeleteWarning": "Alla Appar i de borttagna kategorierna kommer att märkas som okategoriserade.", "categoryDeleteWarning": "Alla Appar i de borttagna kategorierna kommer att märkas som okategoriserade.",
"addCategory": "Lägg till Kategori", "addCategory": "Lägg till Kategori",
"label": "Label", "label": "Märka",
"language": "Språk", "language": "Språk",
"copiedToClipboard": "Kopierat till Urklipp", "copiedToClipboard": "Kopierat till Urklipp",
"storagePermissionDenied": "Lagringsbehörighet nekad", "storagePermissionDenied": "Lagringsbehörighet nekad",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "selectedCategorizeWarning": "Detta kommer att ersätta alla befintliga kategoriinställningar för de valda apparna.",
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "filterAPKsByRegEx": "Filtrera APK-filer efter reguljärt uttryck",
"removeFromObtainium": "Ta bort från Obtainium", "removeFromObtainium": "Ta bort från Obtainium",
"uninstallFromDevice": "Avinstallera från Enheten", "uninstallFromDevice": "Avinstallera från Enheten",
"onlyWorksWithNonVersionDetectApps": "Fungerar bara för Appar med versionsdetektering inaktiverat..", "onlyWorksWithNonVersionDetectApps": "Fungerar bara för Appar med versionsdetektering inaktiverat..",
"releaseDateAsVersion": "Använd releasedatum som version", "releaseDateAsVersion": "Använd releasedatum som version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.", "releaseDateAsVersionExplanation": "Det här alternativet bör endast användas för appar där versionsidentifiering inte fungerar korrekt, men ett releasedatum är tillgängligt.",
"changes": "Ändringar", "changes": "Ändringar",
"releaseDate": "Releasedatum", "releaseDate": "Releasedatum",
"importFromURLsInFile": "Importera från URL:er i fil (som OPML)", "importFromURLsInFile": "Importera från URL:er i fil (som OPML)",
"versionDetectionExplanation": "Sammanställ versionssträng med version upptäckt från OS",
"versionDetection": "Versionsdetektering", "versionDetection": "Versionsdetektering",
"standardVersionDetection": "Standardversionsdetektering", "standardVersionDetection": "Standardversionsdetektering",
"groupByCategory": "Gruppera via Kategori", "groupByCategory": "Gruppera via Kategori",
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible", "autoApkFilterByArch": "Försök att filtrera APK-filer efter CPU-arkitektur om möjligt",
"overrideSource": "Överskrid Källa", "overrideSource": "Överskrid Källa",
"dontShowAgain": "Visa inte detta igen", "dontShowAgain": "Visa inte detta igen",
"dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar", "dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar",
"dontShowAPKOriginWarnings": "Visa inte APK-ursprung varningar", "dontShowAPKOriginWarnings": "Visa inte APK-ursprung varningar",
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "moveNonInstalledAppsToBottom": "Flytta icke-installerade appar till botten av appvyn",
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "gitlabPATLabel": "Personligt åtkomsttoken för GitLab",
"about": "Om", "about": "Om",
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)", "requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
"checkOnStart": "Kolla efter uppdateringar vid start", "checkOnStart": "Kolla efter uppdateringar vid start",
"tryInferAppIdFromCode": "Try inferring App ID from source code", "tryInferAppIdFromCode": "Försök härleda app-id från källkoden",
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", "removeOnExternalUninstall": "Ta bort externt avinstallerade appar automatiskt",
"pickHighestVersionCode": "Auto-select highest version code APK", "pickHighestVersionCode": "Välj automatiskt högsta versionskod APK",
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page", "checkUpdateOnDetailPage": "Sök efter uppdateringar om att öppna en app-detaljsida",
"disablePageTransitions": "Disable page transition animations", "disablePageTransitions": "Inaktivera sidövergångsanimationer",
"reversePageTransitions": "Reverse page transition animations", "reversePageTransitions": "Animeringar för omvänd sidövergång",
"minStarCount": "Minsta antal stjärnmarkeringar", "minStarCount": "Minsta antal stjärnmarkeringar",
"addInfoBelow": "Lägg till denna information nedanför.", "addInfoBelow": "Lägg till denna information nedanför.",
"addInfoInSettings": "Lägg till denna information i Inställningar.", "addInfoInSettings": "Lägg till denna information i Inställningar.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.", "githubSourceNote": "GitHub-hastighetsbegränsning kan undvikas med en API-nyckel.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.", "sortByLastLinkSegment": "Sortera endast efter det sista segmentet av länken",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "filterReleaseNotesByRegEx": "Filtrera versionskommentarer efter reguljärt uttryck",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression", "customLinkFilterRegex": "Anpassad APK-länkfiltrera efter reguljärt uttryck (standard '.apk$')",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')", "appsPossiblyUpdated": "Appuppdateringar försökte",
"appsPossiblyUpdated": "App Updates Attempted", "appsPossiblyUpdatedNotifDescription": "Meddelar användaren om att uppdateringar av en eller flera appar potentiellt har tillämpats i bakgrunden",
"appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background", "xWasPossiblyUpdatedToY": "{} kan ha uppdaterats till {}.",
"xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
"enableBackgroundUpdates": "Aktivera Bakgrundsuppdateringar", "enableBackgroundUpdates": "Aktivera Bakgrundsuppdateringar",
"backgroundUpdateReqsExplanation": "Bakgrundsuppdateringar är inte möjligt för alla appar.", "backgroundUpdateReqsExplanation": "Bakgrundsuppdateringar är inte möjligt för alla appar.",
"backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.", "backgroundUpdateLimitsExplanation": "Framgången för en bakgrundsinstallation kan bara avgöras när Obtainium öppnas.",
"verifyLatestTag": "Verifiera 'senaste'-taggen", "verifyLatestTag": "Verifiera 'senaste'-taggen",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Filtrera för en \"mellanliggande\" länk att besöka",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filtrera länkar efter länktext",
"intermediateLinkNotFound": "Intermediate link not found", "intermediateLinkNotFound": "Mellanlänk hittades inte",
"intermediateLink": "Intermediate link", "intermediateLink": "Mellanlänk",
"exemptFromBackgroundUpdates": "Undta från bakgrundsuppdateringar (om aktiverad)", "exemptFromBackgroundUpdates": "Undta från bakgrundsuppdateringar (om aktiverad)",
"bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utan WiFi", "bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utan WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK", "autoSelectHighestVersionCode": "Välj automatiskt högsta versionskod APK",
"versionExtractionRegEx": "Version Extraction RegEx", "versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use", "matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Highlight less obvious touch targets", "highlightTouchTargets": "Markera mindre uppenbara beröringsobjekt",
"pickExportDir": "Välj Exportsökväg", "pickExportDir": "Välj Exportsökväg",
"autoExportOnChanges": "Automatisk export vid ändringar", "autoExportOnChanges": "Automatisk export vid ändringar",
"includeSettings": "Include settings", "includeSettings": "Inkludera inställningar",
"filterVersionsByRegEx": "Filter Versions by Regular Expression", "filterVersionsByRegEx": "Filtrera versioner efter reguljärt uttryck",
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK", "trySelectingSuggestedVersionCode": "Försök att välja föreslagen versionCode APK",
"dontSortReleasesList": "Retain release order from API", "dontSortReleasesList": "Behåll släpporder från API",
"reverseSort": "Omvänd sortering", "reverseSort": "Omvänd sortering",
"takeFirstLink": "Take first link", "takeFirstLink": "Ta första länken",
"skipSort": "Skip sorting", "skipSort": "Hoppa över sortering",
"debugMenu": "Felsökningsmeny", "debugMenu": "Felsökningsmeny",
"bgTaskStarted": "Background task started - check logs.", "bgTaskStarted": "Bakgrundsuppgift startat - kontrollera loggar.",
"runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu", "runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
"parallelDownloads": "Allow parallel downloads", "versionExtractWholePage": "Tillämpa Regex för versionssträngextraktion på hela sidan",
"installMethod": "Installation method", "installing": "Installerar",
"normal": "Normal", "skipUpdateNotifications": "Hoppa över uppdateringsmeddelanden",
"shizuku": "Shizuku", "updatesAvailableNotifChannel": "Uppdateringar tillgängliga",
"root": "Root", "appsUpdatedNotifChannel": "Appar uppdaterade",
"appsPossiblyUpdatedNotifChannel": "Appuppdateringar försökte",
"errorCheckingUpdatesNotifChannel": "Fel vid sökning efter uppdateringar",
"appsRemovedNotifChannel": "Appar borttagna",
"downloadingXNotifChannel": "Laddar ner {}",
"completeAppInstallationNotifChannel": "Fullständig appinstallation",
"checkingForUpdatesNotifChannel": "Kollar efter uppdateringar",
"onlyCheckInstalledOrTrackOnlyApps": "Kontrollera bara installerade appar och endast spårbara appar för uppdateringar",
"supportFixedAPKURL": "Stöd fasta APK-webbadresser",
"selectX": "Välj {}",
"parallelDownloads": "Tillåt parallella nedladdningar",
"useShizuku": "Använd Shizuku eller Sui för att installera",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizuku is not running",
"shizukuOld": "Gammal Shizuku-version (<11) - uppdatera den",
"shizukuOldAndroidWithADB": "Shizuku körs på Android < 8.1 med ADB - uppdatera Android eller använd Sui istället",
"shizukuPretendToBeGooglePlay": "Ange Google Play som installationskälla (om Shizuku används)",
"useSystemFont": "Använd systemteckensnittet",
"useVersionCodeAsOSVersion": "Använd appversionskoden som OS-upptäckt version",
"requestHeader": "Rubrik för begäran",
"useLatestAssetDateAsReleaseDate": "Använd senaste tillgångsuppladdning som releasedatum",
"defaultPseudoVersioningMethod": "Standard pseudoversionsmetod",
"partialAPKHash": "Delvis APK-hash",
"APKLinkHash": "APK-länk Hash",
"directAPKLink": "Direkt APK-länk",
"pseudoVersionInUse": "En pseudoversion används",
"installed": "Installerad",
"latest": "Senast",
"invertRegEx": "Invertera reguljärt uttryck",
"note": "Anmärkning",
"selfHostedNote": "Rullgardinsmenyn \"{}\" kan användas för att nå självhostade/anpassade instanser av valfri källa.",
"badDownload": "APK kunde inte analyseras (inkompatibel eller partiell nedladdning)",
"beforeNewInstallsShareToAppVerifier": "Dela nya appar med AppVerifier (om tillgängligt)",
"appVerifierInstructionToast": "Dela till AppVerifier och återvänd sedan hit när du är klar.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Ta Bort App?", "one": "Ta Bort App?",
"other": "Ta Bort Appar?" "other": "Ta Bort Appar?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Too many requests (rate limited) - try again in {} minute", "one": "För många förfrågningar (begränsat pris) - försök igen om {} minut",
"other": "Too many requests (rate limited) - try again in {} minutes" "other": "För många förfrågningar (begränsat pris) - försök igen om {} minuter"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "BG update checking encountered a {}, will schedule a retry check in {} minute", "one": "BG-uppdateringskontroll påträffade en {}, kommer att schemalägga ett nytt försök om {} minut",
"other": "BG update checking encountered a {}, will schedule a retry check in {} minutes" "other": "BG-uppdateringskontroll påträffade en {}, kommer att schemalägga ett nytt försök om {} minuter"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "BG update checking found {} update - will notify user if needed", "one": "BG-uppdateringskontroll hittades {} uppdatering - kommer att meddela användaren om det behövs",
"other": "BG update checking found {} updates - will notify user if needed" "other": "BG-uppdateringskontroll hittades {} uppdateringar - kommer att meddela användaren om det behövs"
}, },
"apps": { "apps": {
"one": "{} App", "one": "{} App",
@ -321,8 +357,16 @@
"one": "{} och 1 till app uppdaterades.", "one": "{} och 1 till app uppdaterades.",
"other": "{} och {} appar till uppdaterades." "other": "{} och {} appar till uppdaterades."
}, },
"xAndNMoreUpdatesFailed": {
"one": "Misslyckades med att uppdatera {} och ytterligare 1 app.",
"other": "Det gick inte att uppdatera {} och {} fler appar."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} och 1 till app kan ha uppdaterats.", "one": "{} och 1 till app kan ha uppdaterats.",
"other": "{} och {} appar till kan ha uppdaterats." "other": "{} och {} appar till kan ha uppdaterats."
},
"apk": {
"one": "{} APK",
"other": "{} APK:er"
} }
} }

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(Gerekli)", "requiredInBrackets": "(Gerekli)",
"dropdownNoOptsError": "HATA: DİPLOMADA EN AZ BİR SEÇENEK OLMALI", "dropdownNoOptsError": "HATA: DİPLOMADA EN AZ BİR SEÇENEK OLMALI",
"colour": "Renk", "colour": "Renk",
"standard": "Standart",
"custom": "Özel",
"useMaterialYou": "Sizin Malzemenizi Kullanın",
"githubStarredRepos": "GitHub'a Yıldızlı Depolar", "githubStarredRepos": "GitHub'a Yıldızlı Depolar",
"uname": "Kullanıcı Adı", "uname": "Kullanıcı Adı",
"wrongArgNum": "Hatalı argüman sayısı sağlandı", "wrongArgNum": "Hatalı argüman sayısı sağlandı",
@ -51,9 +54,8 @@
"percentProgress": "İlerleme: {}%", "percentProgress": "İlerleme: {}%",
"pleaseWait": "Lütfen Bekleyin", "pleaseWait": "Lütfen Bekleyin",
"updateAvailable": "Güncelleme Var", "updateAvailable": "Güncelleme Var",
"estimateInBracketsShort": "(Est.)",
"notInstalled": "Yüklenmedi", "notInstalled": "Yüklenmedi",
"estimateInBrackets": "(Tahmini)", "pseudoVersion": "sözde versiyon",
"selectAll": "Hepsini Seç", "selectAll": "Hepsini Seç",
"deselectX": "{}'yi Seçimden Kaldır", "deselectX": "{}'yi Seçimden Kaldır",
"xWillBeRemovedButRemainInstalled": "{} Obtainium'dan kaldırılacak ancak cihazınızda yüklü kalacaktır.", "xWillBeRemovedButRemainInstalled": "{} Obtainium'dan kaldırılacak ancak cihazınızda yüklü kalacaktır.",
@ -73,13 +75,14 @@
"unpinFromTop": "Üstten Kaldır", "unpinFromTop": "Üstten Kaldır",
"resetInstallStatusForSelectedAppsQuestion": "Seçilen Uygulamaların Yükleme Durumunu Sıfırlamak İstiyor musunuz?", "resetInstallStatusForSelectedAppsQuestion": "Seçilen Uygulamaların Yükleme Durumunu Sıfırlamak İstiyor musunuz?",
"installStatusOfXWillBeResetExplanation": "Seçilen Uygulamaların yükleme durumu sıfırlanacak.\n\nBu, Obtainium'da gösterilen uygulama sürümünün başarısız güncellemeler veya diğer sorunlar nedeniyle yanlış olması durumunda yardımcı olabilir.", "installStatusOfXWillBeResetExplanation": "Seçilen Uygulamaların yükleme durumu sıfırlanacak.\n\nBu, Obtainium'da gösterilen uygulama sürümünün başarısız güncellemeler veya diğer sorunlar nedeniyle yanlış olması durumunda yardımcı olabilir.",
"customLinkMessage": "Bu bağlantılar Obtainium'un kurulu olduğu cihazlarda çalışır",
"shareAppConfigLinks": "Uygulama yapılandırmasını HTML bağlantısı olarak paylaş",
"shareSelectedAppURLs": "Seçilen Uygulama URL'larını Paylaş", "shareSelectedAppURLs": "Seçilen Uygulama URL'larını Paylaş",
"resetInstallStatus": "Yükleme Durumunu Sıfırla", "resetInstallStatus": "Yükleme Durumunu Sıfırla",
"more": "Daha Fazla", "more": "Daha Fazla",
"removeOutdatedFilter": "Güncel Olmayan Uygulama Filtresini Kaldır", "removeOutdatedFilter": "Güncel Olmayan Uygulama Filtresini Kaldır",
"showOutdatedOnly": "Yalnızca Güncel Olmayan Uygulamaları Göster", "showOutdatedOnly": "Yalnızca Güncel Olmayan Uygulamaları Göster",
"filter": "Filtre", "filter": "Filtre",
"filterActive": "Filtre *",
"filterApps": "Uygulamaları Filtrele", "filterApps": "Uygulamaları Filtrele",
"appName": "Uygulama Adı", "appName": "Uygulama Adı",
"author": "Yazar", "author": "Yazar",
@ -110,8 +113,6 @@
"dark": "Koyu", "dark": "Koyu",
"light": "Aydınlık", "light": "Aydınlık",
"followSystem": "Sistemi Takip Et", "followSystem": "Sistemi Takip Et",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "Saf siyah koyu temasını kullan", "useBlackTheme": "Saf siyah koyu temasını kullan",
"appSortBy": "Uygulama Sıralama Ölçütü", "appSortBy": "Uygulama Sıralama Ölçütü",
"authorName": "Yazar/Ad", "authorName": "Yazar/Ad",
@ -145,8 +146,10 @@
"noNewUpdates": "Yeni güncelleme yok.", "noNewUpdates": "Yeni güncelleme yok.",
"xHasAnUpdate": "{} güncelleme alıyor.", "xHasAnUpdate": "{} güncelleme alıyor.",
"appsUpdated": "Uygulamalar Güncellendi", "appsUpdated": "Uygulamalar Güncellendi",
"appsNotUpdated": "Uygulamalar güncellenemedi",
"appsUpdatedNotifDescription": "Kullanıcıya bir veya daha fazla uygulamanın arka planda güncellendiğine dair bilgi verir", "appsUpdatedNotifDescription": "Kullanıcıya bir veya daha fazla uygulamanın arka planda güncellendiğine dair bilgi verir",
"xWasUpdatedToY": "{} şu sürüme güncellendi: {}.", "xWasUpdatedToY": "{} şu sürüme güncellendi: {}.",
"xWasNotUpdatedToY": "{} öğesi {} olarak güncellenemedi.",
"errorCheckingUpdates": "Güncellemeler Kontrol Edilirken Hata Oluştu", "errorCheckingUpdates": "Güncellemeler Kontrol Edilirken Hata Oluştu",
"errorCheckingUpdatesNotifDescription": "Arka planda güncelleme kontrolü sırasında hata oluştuğunda görünen bir bildirim", "errorCheckingUpdatesNotifDescription": "Arka planda güncelleme kontrolü sırasında hata oluştuğunda görünen bir bildirim",
"appsRemoved": "Uygulamalar Kaldırıldı", "appsRemoved": "Uygulamalar Kaldırıldı",
@ -175,8 +178,7 @@
"appWithIdOrNameNotFound": "Bu kimlik veya ada sahip bir uygulama bulunamadı", "appWithIdOrNameNotFound": "Bu kimlik veya ada sahip bir uygulama bulunamadı",
"reposHaveMultipleApps": "Depolar birden fazla uygulama içerebilir", "reposHaveMultipleApps": "Depolar birden fazla uygulama içerebilir",
"fdroidThirdPartyRepo": "F-Droid Üçüncü Taraf Depo", "fdroidThirdPartyRepo": "F-Droid Üçüncü Taraf Depo",
"steam": "Steam", "steamMobile": "Buhar Mobil",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Sohbet", "steamChat": "Steam Sohbet",
"install": "Yükle", "install": "Yükle",
"markInstalled": "Yüklendi olarak İşaretle", "markInstalled": "Yüklendi olarak İşaretle",
@ -186,6 +188,9 @@
"disableVersionDetection": "Sürüm Algılama Devre Dışı", "disableVersionDetection": "Sürüm Algılama Devre Dışı",
"noVersionDetectionExplanation": "Bu seçenek, sürüm algılamanın doğru çalışmadığı uygulamalar için kullanılmalıdır.", "noVersionDetectionExplanation": "Bu seçenek, sürüm algılamanın doğru çalışmadığı uygulamalar için kullanılmalıdır.",
"downloadingX": "{} İndiriliyor", "downloadingX": "{} İndiriliyor",
"downloadX": "İndir {}",
"downloadedX": "İndirildi {}",
"releaseAsset": "Varlık Serbest Bırakma",
"downloadNotifDescription": "Bir uygulamanın indirme sürecinde ilerlemeyi bildiren bir bildirim", "downloadNotifDescription": "Bir uygulamanın indirme sürecinde ilerlemeyi bildiren bir bildirim",
"noAPKFound": "APK bulunamadı", "noAPKFound": "APK bulunamadı",
"noVersionDetection": "Sürüm Algılanamıyor", "noVersionDetection": "Sürüm Algılanamıyor",
@ -211,6 +216,7 @@
"changes": "Değişiklikler", "changes": "Değişiklikler",
"releaseDate": "Yayın Tarihi", "releaseDate": "Yayın Tarihi",
"importFromURLsInFile": "Dosyadaki URL'lerden İçe Aktar", "importFromURLsInFile": "Dosyadaki URL'lerden İçe Aktar",
"versionDetectionExplanation": "Sürüm dizesini işletim sistemi tarafından algılanan sürümle uzlaştır",
"versionDetection": "Sürüm Tespiti", "versionDetection": "Sürüm Tespiti",
"standardVersionDetection": "Standart sürüm tespiti", "standardVersionDetection": "Standart sürüm tespiti",
"groupByCategory": "Kategoriye Göre Grupla", "groupByCategory": "Kategoriye Göre Grupla",
@ -220,7 +226,7 @@
"dontShowTrackOnlyWarnings": "'Yalnızca Takip Edilen' uyarılarını gösterme", "dontShowTrackOnlyWarnings": "'Yalnızca Takip Edilen' uyarılarını gösterme",
"dontShowAPKOriginWarnings": "APK kaynağı uyarılarını gösterme", "dontShowAPKOriginWarnings": "APK kaynağı uyarılarını gösterme",
"moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı", "moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı",
"gitlabPATLabel": "GitLab Kişisel Erişim Belirteci\n(Arama ve Daha İyi APK Keşfi İçin)", "gitlabPATLabel": "GitLab Kişisel Erişim Belirteci",
"about": "Hakkında", "about": "Hakkında",
"requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)", "requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)",
"checkOnStart": "Başlangıçta güncellemeleri kontrol et", "checkOnStart": "Başlangıçta güncellemeleri kontrol et",
@ -234,8 +240,7 @@
"addInfoBelow": "Bu bilgiyi aşağıya ekle.", "addInfoBelow": "Bu bilgiyi aşağıya ekle.",
"addInfoInSettings": "Bu bilgiyi Ayarlar'da ekleyin.", "addInfoInSettings": "Bu bilgiyi Ayarlar'da ekleyin.",
"githubSourceNote": "GitHub hız sınırlaması bir API anahtarı kullanılarak atlanabilir.", "githubSourceNote": "GitHub hız sınırlaması bir API anahtarı kullanılarak atlanabilir.",
"gitlabSourceNote": "GitLab APK çıkarma işlemi bir API anahtarı olmadan çalışmayabilir.", "sortByLastLinkSegment": "Bağlantının yalnızca son bölümüne göre sırala",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Sürüm Notlarını Düzenli İfade ile Filtrele", "filterReleaseNotesByRegEx": "Sürüm Notlarını Düzenli İfade ile Filtrele",
"customLinkFilterRegex": "Özel APK Bağlantı Filtresi Düzenli İfade ile (Varsayılan '.apk$')", "customLinkFilterRegex": "Özel APK Bağlantı Filtresi Düzenli İfade ile (Varsayılan '.apk$')",
"appsPossiblyUpdated": "Uygulama Güncellemeleri Denendi", "appsPossiblyUpdated": "Uygulama Güncellemeleri Denendi",
@ -245,10 +250,10 @@
"backgroundUpdateReqsExplanation": "Arka plan güncellemeleri tüm uygulamalar için mümkün olmayabilir.", "backgroundUpdateReqsExplanation": "Arka plan güncellemeleri tüm uygulamalar için mümkün olmayabilir.",
"backgroundUpdateLimitsExplanation": "Arka plan kurulumunun başarısı, Obtainium'un açıldığında ancak belirlenebilir.", "backgroundUpdateLimitsExplanation": "Arka plan kurulumunun başarısı, Obtainium'un açıldığında ancak belirlenebilir.",
"verifyLatestTag": "'latest' etiketini doğrula", "verifyLatestTag": "'latest' etiketini doğrula",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Ziyaret Edilecek 'Orta Düzey' Bağlantıyı Filtrele",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Bağlantıları bağlantı metnine göre filtrele",
"intermediateLinkNotFound": "Ara bağlantı bulunamadı", "intermediateLinkNotFound": "Ara bağlantı bulunamadı",
"intermediateLink": "Intermediate link", "intermediateLink": "Ara bağlantı",
"exemptFromBackgroundUpdates": "Arka plan güncellemelerinden muaf tut (etkinse)", "exemptFromBackgroundUpdates": "Arka plan güncellemelerinden muaf tut (etkinse)",
"bgUpdatesOnWiFiOnly": "WiFi olmadığında arka plan güncellemelerini devre dışı bırak", "bgUpdatesOnWiFiOnly": "WiFi olmadığında arka plan güncellemelerini devre dışı bırak",
"autoSelectHighestVersionCode": "Otomatik olarak en yüksek sürüm kodunu seç", "autoSelectHighestVersionCode": "Otomatik olarak en yüksek sürüm kodunu seç",
@ -257,13 +262,13 @@
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula", "highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
"pickExportDir": "Dışa Aktarılacak Klasörü Seç", "pickExportDir": "Dışa Aktarılacak Klasörü Seç",
"autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar", "autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
"includeSettings": "Include settings", "includeSettings": "Ayarları dahil et",
"filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele", "filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene", "trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
"dontSortReleasesList": "API'den sıralama düzenini koru", "dontSortReleasesList": "API'den sıralama düzenini koru",
"reverseSort": "Ters sıralama", "reverseSort": "Ters sıralama",
"takeFirstLink": "Take first link", "takeFirstLink": "İlk bağlantıyı al",
"skipSort": "Skip sorting", "skipSort": "Sıralamayı atla",
"debugMenu": "Hata Ayıklama Menüsü", "debugMenu": "Hata Ayıklama Menüsü",
"bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.", "bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
"runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır", "runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
@ -279,14 +284,31 @@
"completeAppInstallationNotifChannel": "Uygulama Kurulumu Tamamlandı", "completeAppInstallationNotifChannel": "Uygulama Kurulumu Tamamlandı",
"checkingForUpdatesNotifChannel": "Güncellemeler Kontrol Ediliyor", "checkingForUpdatesNotifChannel": "Güncellemeler Kontrol Ediliyor",
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme", "onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "Sabit APK URL'lerini destekleyin",
"selectX": "Select {}", "selectX": "Seçme {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Paralel indirmelere izin ver",
"installMethod": "Installation method", "useShizuku": "Yüklemek için Shizuku veya Sui'yi kullanın",
"normal": "Normal",
"shizuku": "Shizuku",
"root": "Root",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizuku is not running",
"shizukuOld": "Eski Shizuku sürümü (<11) - güncelleyin",
"shizukuOldAndroidWithADB": "Shizuku ADB ile Android < 8.1 üzerinde çalışıyor - Android'i güncelleyin veya bunun yerine Sui kullanın",
"shizukuPretendToBeGooglePlay": "Google Play'i yükleme kaynağı olarak ayarlayın (Shizuku kullanılıyorsa)",
"useSystemFont": "Sistem yazı tipini kullan",
"useVersionCodeAsOSVersion": "Uygulama versionCode'unu işletim sistemi tarafından algılanan sürüm olarak kullan",
"requestHeader": "Başlık talep et",
"useLatestAssetDateAsReleaseDate": "Yayın tarihi olarak en son öğe yüklemesini kullan",
"defaultPseudoVersioningMethod": "Varsayılan Sözde Sürüm Oluşturma Yöntemi",
"partialAPKHash": "Kısmi APK Karması",
"APKLinkHash": "APK Bağlantı Karması",
"directAPKLink": "Doğrudan APK Bağlantısı",
"pseudoVersionInUse": "Sözde Versiyon Kullanımda",
"installed": "Kurulmuş",
"latest": "En sonuncu",
"invertRegEx": "Normal ifadeyi ters çevir",
"note": "Not",
"selfHostedNote": "\"{}\" açılır menüsü, herhangi bir kaynağın kendi kendine barındırılan/özel örneklerine ulaşmak için kullanılabilir.",
"badDownload": "APK ayrıştırılamadı (uyumsuz veya kısmi indirme)",
"beforeNewInstallsShareToAppVerifier": "Yeni Uygulamaları AppVerifier ile paylaşın (varsa)",
"appVerifierInstructionToast": "AppVerifier ile paylaşın, hazır olduğunuzda buraya dönün.",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Uygulamayı Kaldır?", "one": "Uygulamayı Kaldır?",
"other": "Uygulamaları Kaldır?" "other": "Uygulamaları Kaldır?"
@ -335,8 +357,16 @@
"one": "{} ve 1 diğer uygulama güncellendi.", "one": "{} ve 1 diğer uygulama güncellendi.",
"other": "{} ve {} daha fazla uygulama güncellendi." "other": "{} ve {} daha fazla uygulama güncellendi."
}, },
"xAndNMoreUpdatesFailed": {
"one": "{} ve 1 uygulama daha güncellenemedi.",
"other": "{} ve {} daha fazla uygulama güncellenemedi."
},
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} ve 1 diğer uygulama muhtemelen güncellendi.", "one": "{} ve 1 diğer uygulama muhtemelen güncellendi.",
"other": "{} ve {} daha fazla uygulama muhtemelen güncellendi." "other": "{} ve {} daha fazla uygulama muhtemelen güncellendi."
},
"apk": {
"one": "{} APK",
"other": "{} APK'lar"
} }
} }

372
assets/translations/uk.json Normal file
View File

@ -0,0 +1,372 @@
{
"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": "Застосунок буде відстежуватися для оновлень, але Obtainium не зможе його завантажити або встановити.",
"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": "{} буде видалено з 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": "Поділитися вибраними URL-адресами застосунків",
"resetInstallStatus": "Скинути статус встановлення",
"more": "Більше",
"removeOutdatedFilter": "Видалити фільтр застарілих застосунків",
"showOutdatedOnly": "Показати лише застарілі застосунки",
"filter": "Фільтр",
"filterApps": "Фільтрувати застосунки",
"appName": "Назва застосунку",
"author": "Автор",
"upToDateApps": "Актуальні застосунки",
"nonInstalledApps": "Невстановлені застосунки",
"importExport": "Імпорт/Експорт",
"settings": "Налаштування",
"exportedTo": "Експортовано в {}",
"obtainiumExport": "Експорт з Obtainium",
"invalidInput": "Недійсний ввід",
"importedX": "Імпортовано {}",
"obtainiumImport": "Імпорт в Obtainium",
"importFromURLList": "Імпорт зі списку URL-адрес",
"searchQuery": "Пошуковий запит",
"appURLList": "Список URL-адрес застосунків",
"line": "Лінія",
"searchX": "Пошук {}",
"noResults": "Результати відсутні",
"importX": "Імпорт {}",
"importedAppsIdDisclaimer": "Імпортовані застосунки можуть неправильно відображатися як \"Не встановлені\".\nДля виправлення цього перевстановіть їх через Obtainium.\nЦе не повинно вплинути на дані застосунків.\n\nПов'язано лише з URL-адресами та імпортом від третіх сторін.",
"importErrors": "Помилки імпорту",
"importedXOfYApps": "Імпортовано {} з {} застосунків.",
"followingURLsHadErrors": "Помилки в наступних URL-адресах:",
"selectURL": "Вибрати URL",
"selectURLs": "Вибрати URL-адреси",
"pick": "Вибрати",
"theme": "Тема",
"dark": "Темна",
"light": "Світла",
"followSystem": "Дотримуватися системи",
"useBlackTheme": "Використовувати чорну тему (Amoled)",
"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": "Помилка {} HTTP-коду",
"versionCorrectionDisabled": "Виправлення версії вимкнено (здається, плагін не працює)",
"unknown": "Невідомо",
"none": "Нічого",
"never": "Ніколи",
"latestVersionX": "Остання версія: {}",
"installedVersionX": "Встановлено: {}",
"lastUpdateCheckX": "Остання перевірка оновлень: {}",
"remove": "Видалити",
"yesMarkUpdated": "Так, позначити як оновлене",
"fdroid": "F-Droid Офіційний",
"appIdOrName": "Ідентифікатор або назва застосунку",
"appId": "Ідентифікатор застосунку",
"appWithIdOrNameNotFound": "Застосунок з таким ідентифікатором або назвою не знайдено",
"reposHaveMultipleApps": "Сховища можуть містити кілька застосунків",
"fdroidThirdPartyRepo": "F-Droid Стороннє сховище",
"steamMobile": "Мобільний Steam",
"steamChat": "Чат Steam",
"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": "Використовувати дату випуску як рядок версії",
"releaseDateAsVersionExplanation": "Цю опцію слід використовувати лише для застосунків, де визначення версії працює неправильно, але є дата випуску.",
"changes": "Зміни",
"releaseDate": "Дата випуску",
"importFromURLsInFile": "Імпорт з URL-адрес у файлі (наприклад, OPML)",
"versionDetectionExplanation": "Порівняти рядок версії з версією, визначеною операційною системою",
"versionDetection": "Визначення версії",
"standardVersionDetection": "Стандартне визначення версії",
"groupByCategory": "Групувати за категоріями",
"autoApkFilterByArch": "Спробувати фільтрувати APK за архітектурою ЦП, якщо можливо",
"overrideSource": "Перевизначити джерело",
"dontShowAgain": "Не показувати це знову",
"dontShowTrackOnlyWarnings": "Не показувати попередження про 'Тільки відстеження'",
"dontShowAPKOriginWarnings": "Не показувати попередження про походження APK",
"moveNonInstalledAppsToBottom": "Перемістити невстановлені застосунки вниз у перегляді застосунків",
"gitlabPATLabel": "Особистий токен GitLab (Увімкнення пошуку та краще виявлення APK)",
"about": "Про програму",
"requiresCredentialsInSettings": "{} потребує додаткових облікових даних (у налаштуваннях)",
"checkOnStart": "Перевірити наявність оновлень при запуску",
"tryInferAppIdFromCode": "Спробувати вивести ідентифікатор застосунку з вихідного коду",
"removeOnExternalUninstall": "Автоматично видаляти застосунки, які було видалено зовнішнім чином",
"pickHighestVersionCode": "Автоматично вибрати APK з найвищим кодом версії",
"checkUpdateOnDetailPage": "Перевіряти наявність оновлень при відкритті сторінки деталей застосунку",
"disablePageTransitions": "Вимкнути анімації переходів між сторінками",
"reversePageTransitions": "Зворотні анімації переходів між сторінками",
"minStarCount": "Мінімальна кількість зірок",
"addInfoBelow": "Додати цю інформацію нижче.",
"addInfoInSettings": "Додати цю інформацію у налаштуваннях.",
"githubSourceNote": "Лімітування швидкості GitHub можна уникнути, використовуючи ключ API.",
"sortByLastLinkSegment": "Сортувати лише за останнім сегментом посилання",
"filterReleaseNotesByRegEx": "Фільтрувати примітки до релізу за регулярним виразом",
"customLinkFilterRegex": "Фільтр кастомного посилання на APK за регулярним виразом (за замовчуванням '.apk$')",
"appsPossiblyUpdated": "Спроб оновлення застосунків",
"appsPossiblyUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного або декількох застосунків можливо були застосовані в фоновому режимі",
"xWasPossiblyUpdatedToY": "{} можливо було оновлено до {}.",
"enableBackgroundUpdates": "Увімкнути оновлення в фоновому режимі",
"backgroundUpdateReqsExplanation": "Оновлення в фоновому режимі може бути неможливим для всіх застосунків.",
"backgroundUpdateLimitsExplanation": "Успіх фонової установки може бути визначений лише після відкриття Obtainium.",
"verifyLatestTag": "Перевірити тег 'latest'",
"intermediateLinkRegex": "Фільтр для 'Проміжного' Посилання для Відвідування",
"filterByLinkText": "Фільтрувати посилання за текстом посилання",
"intermediateLinkNotFound": "Проміжне посилання не знайдено",
"intermediateLink": "Проміжне посилання",
"exemptFromBackgroundUpdates": "Виключено з фонових оновлень (якщо ввімкнено)",
"bgUpdatesOnWiFiOnly": "Вимкнути фонові оновлення поза Wi-Fi",
"autoSelectHighestVersionCode": "Автоматичний вибір APK з найвищим кодом версії",
"versionExtractionRegEx": "Регулярний вираз для вилучення рядка версії",
"matchGroupToUse": "Група співпадінь для використання в регулярному виразі вилучення версії",
"highlightTouchTargets": "Підсвічувати менш очевидні області дотику",
"pickExportDir": "Вибрати каталог експорту",
"autoExportOnChanges": "Автоматичний експорт при змінах",
"includeSettings": "Включити налаштування",
"filterVersionsByRegEx": "Фільтрувати версії за регулярним виразом",
"trySelectingSuggestedVersionCode": "Спробуйте вибрати запропонований код версії APK",
"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 працює на Android < 8.1 з ADB - оновіть Android або використовуйте Sui замість нього",
"shizukuPretendToBeGooglePlay": "Виберіть Google Play як джерело встановлення (якщо використовується Shizuku)",
"useSystemFont": "Використовувати системний шрифт",
"useVersionCodeAsOSVersion": "Використовувати код версії застосунку як версію, визначену операційною системою",
"requestHeader": "Заголовок запиту",
"useLatestAssetDateAsReleaseDate": "Використовувати останню дату завантаження ресурсу як дату випуску",
"defaultPseudoVersioningMethod": "Метод за замовчуванням псевдо-версіонування",
"partialAPKHash": "Хеш часткового APK",
"APKLinkHash": "Хеш посилання на APK",
"directAPKLink": "Пряме посилання на APK",
"pseudoVersionInUse": "Використовується псевдо-версія",
"installed": "Встановлено",
"latest": "Остання",
"invertRegEx": "Інвертувати регулярний вираз",
"note": "Примітка",
"selfHostedNote": "Випадаючий список \"{}\" може використовуватися для доступу до власних/призначених для самостійного використання екземплярів будь-якого джерела.",
"badDownload": "APK не вдалося розпарсити (несумісний або часткове завантаження)",
"beforeNewInstallsShareToAppVerifier": "Діліться новими додатками з AppVerifier (якщо доступно)",
"appVerifierInstructionToast": "Надішліть на AppVerifier, а потім поверніться сюди, коли будете готові.",
"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-файли"
}
}

View File

@ -9,32 +9,35 @@
"placeholder": "Giữ chỗ", "placeholder": "Giữ chỗ",
"someErrors": "Đã xảy ra một số lỗi", "someErrors": "Đã xảy ra một số lỗi",
"unexpectedError": "Lỗi không mong đợi", "unexpectedError": "Lỗi không mong đợi",
"ok": "Ôkê", "ok": "OK",
"and": "và", "and": "và",
"githubPATLabel": "Mã thông báo truy cập cá nhân GitHub (Tăng tốc độ giới hạn)", "githubPATLabel": "Token truy cập cá nhân GitHub (Cải thiện tốc độ giới hạn)",
"includePrereleases": "Bao gồm các bản phát hành trước", "includePrereleases": "Bao gồm các bản phát hành trước",
"fallbackToOlderReleases": "Dự phòng về bản phát hành cũ hơn", "fallbackToOlderReleases": "Dự phòng về bản phát hành cũ hơn",
"filterReleaseTitlesByRegEx": "Lọc tiêu đề bản phát hành theo biểu thức chính quy", "filterReleaseTitlesByRegEx": "Lọc tiêu đề bản phát hành theo biểu thức chính quy",
"invalidRegEx": "Biểu thức chính quy không hợp lệ", "invalidRegEx": "Biểu thức chính quy không hợp lệ",
"noDescription": "Không có mô tả", "noDescription": "Không có mô tả",
"cancel": "Hủy bỏ", "cancel": "Hủy",
"continue": "Tiếp tục", "continue": "Tiếp tục",
"requiredInBrackets": "(Yêu cầu)", "requiredInBrackets": "(Yêu cầu)",
"dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN", "dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN",
"colour": "Màu sắc", "colour": "Màu sắc",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Use Material You",
"githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub", "githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub",
"uname": "Tên người dùng", "uname": "Tên người dùng",
"wrongArgNum": "Số lượng đối số được cung cấp sai", "wrongArgNum": "Số lượng đối số được cung cấp sai",
"xIsTrackOnly": "{}là Chỉ-Theo dõi", "xIsTrackOnly": "{} là Chỉ theo dõi",
"source": "Nguồn", "source": "Nguồn",
"app": "Ứng dụng", "app": "Ứng dụng",
"appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ-Theo dõi'.", "appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ theo dõi'.",
"youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ-Theo dõi'.", "youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ theo dõi'.",
"trackOnlyAppDescription": "Ứng dụng sẽ được theo dõi để cập nhật, nhưng Obtainium sẽ không thể tải xuống hoặc cài đặt nó.", "trackOnlyAppDescription": "Ứng dụng sẽ được theo dõi để cập nhật, nhưng Obtainium sẽ không thể tải xuống hoặc cài đặt nó.",
"cancelled": "Đã hủy", "cancelled": "Đã hủy",
"appAlreadyAdded": "Ứng dụng được thêm rồi", "appAlreadyAdded": "Ứng dụng được thêm rồi",
"alreadyUpToDateQuestion": "Ứng dụng đã được cập nhật?", "alreadyUpToDateQuestion": "Ứng dụng đã được cập nhật?",
"addApp": "Thêm ứng dụng", "addApp": "Thêm",
"appSourceURL": "URL nguồn ứng dụng", "appSourceURL": "URL nguồn ứng dụng",
"error": "Lỗi", "error": "Lỗi",
"add": "Thêm", "add": "Thêm",
@ -42,18 +45,17 @@
"search": "Tìm kiếm", "search": "Tìm kiếm",
"additionalOptsFor": "Tùy chọn bổ sung cho {}", "additionalOptsFor": "Tùy chọn bổ sung cho {}",
"supportedSources": "Nguồn được hỗ trợ", "supportedSources": "Nguồn được hỗ trợ",
"trackOnlyInBrackets": "(Chỉ-Theo dõi)", "trackOnlyInBrackets": "(Chỉ theo dõi)",
"searchableInBrackets": "(Có thể tìm kiếm)", "searchableInBrackets": "(Có thể tìm kiếm)",
"appsString": "Ứng dụng", "appsString": "Ứng dụng",
"noApps": "Không có ứng dụng", "noApps": "Không có ứng dụng",
"noAppsForFilter": "Không có ứng dụng cho bộ lọc", "noAppsForFilter": "Không có ứng dụng cho bộ lọc",
"byX": "Bởi {}", "byX": "Bởi {}",
"percentProgress": "Tiến triển: {}%", "percentProgress": "Đang tải {}%",
"pleaseWait": "Vui lòng chờ", "pleaseWait": "Vui lòng chờ",
"updateAvailable": "Có sẵn bản cập nhật", "updateAvailable": "Có sẵn bản cập nhật",
"estimateInBracketsShort": "(Ước lượng.)",
"notInstalled": "Chưa cài đặt", "notInstalled": "Chưa cài đặt",
"estimateInBrackets": "(Ước lượng)", "pseudoVersion": "phiên bản giả",
"selectAll": "Chọn tất cả", "selectAll": "Chọn tất cả",
"deselectX": "Bỏ chọn {}", "deselectX": "Bỏ chọn {}",
"xWillBeRemovedButRemainInstalled": "{} sẽ bị xóa khỏi Obtainium nhưng vẫn còn cài đặt trên thiết bị.", "xWillBeRemovedButRemainInstalled": "{} sẽ bị xóa khỏi Obtainium nhưng vẫn còn cài đặt trên thiết bị.",
@ -61,7 +63,7 @@
"removeSelectedApps": "Xóa ứng dụng đã chọn", "removeSelectedApps": "Xóa ứng dụng đã chọn",
"updateX": "Cập nhật {}", "updateX": "Cập nhật {}",
"installX": "Cài đặt {}", "installX": "Cài đặt {}",
"markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ-Theo dõi)\nnhư là đã cập nhật", "markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ theo dõi)\nnhư là đã cập nhật",
"changeX": "Thay đổi {}", "changeX": "Thay đổi {}",
"installUpdateApps": "Cài đặt/Cập nhật ứng dụng", "installUpdateApps": "Cài đặt/Cập nhật ứng dụng",
"installUpdateSelectedApps": "Cài đặt/Cập nhật ứng dụng đã chọn", "installUpdateSelectedApps": "Cài đặt/Cập nhật ứng dụng đã chọn",
@ -73,25 +75,26 @@
"unpinFromTop": "Bỏ ghim khỏi đầu trang", "unpinFromTop": "Bỏ ghim khỏi đầu trang",
"resetInstallStatusForSelectedAppsQuestion": "Đặt lại trạng thái cài đặt cho ứng dụng đã chọn?", "resetInstallStatusForSelectedAppsQuestion": "Đặt lại trạng thái cài đặt cho ứng dụng đã chọn?",
"installStatusOfXWillBeResetExplanation": "Trạng thái cài đặt của mọi Ứng dụng đã chọn sẽ được đặt lại.\n\nĐiều này có thể hữu ích khi phiên bản Ứng dụng hiển thị trong Obtainium không chính xác do cập nhật không thành công hoặc các sự cố khác.", "installStatusOfXWillBeResetExplanation": "Trạng thái cài đặt của mọi Ứng dụng đã chọn sẽ được đặt lại.\n\nĐiều này có thể hữu ích khi phiên bản Ứng dụng hiển thị trong Obtainium không chính xác do cập nhật không thành công hoặc các sự cố khác.",
"customLinkMessage": "Các liên kết này hoạt động trên các thiết bị có cài đặt Obtainium",
"shareAppConfigLinks": "Chia sẻ cấu hình ứng dụng dưới dạng liên kết HTML",
"shareSelectedAppURLs": "Chia sẻ URL ứng dụng đã chọn", "shareSelectedAppURLs": "Chia sẻ URL ứng dụng đã chọn",
"resetInstallStatus": "Đặt lại trạng thái cài đặt", "resetInstallStatus": "Đặt lại trạng thái cài đặt",
"more": "Nhiều hơn", "more": "Nhiều hơn",
"removeOutdatedFilter": "Xóa bộ lọc ứng dụng lỗi thời", "removeOutdatedFilter": "Xóa bộ lọc ứng dụng lỗi thời",
"showOutdatedOnly": "Chỉ hiển thị các ứng dụng lỗi thời", "showOutdatedOnly": "Chỉ hiển thị các ứng dụng lỗi thời",
"filter": "Lọc", "filter": "Lọc",
"filterActive": "Lọc *",
"filterApps": "Lọc ứng dụng", "filterApps": "Lọc ứng dụng",
"appName": "Tên ứng dụng", "appName": "Tên ứng dụng",
"author": "Tác giả", "author": "Tác giả",
"upToDateApps": "Ứng dụng cập nhật", "upToDateApps": "Ứng dụng cập nhật",
"nonInstalledApps": "Ứng dụng chưa được cài đặt", "nonInstalledApps": "Ứng dụng chưa được cài đặt",
"importExport": "Nhập/Xuất", "importExport": "Nhập/Xuất",
"settings": "Cài đặt", "settings": "Thiết đặt",
"exportedTo": "Đã xuất sang {}", "exportedTo": "Đã xuất sang {}",
"obtainiumExport": "Xuất Obtainium", "obtainiumExport": "Xuất",
"invalidInput": "Đầu vào không hợp lệ", "invalidInput": "Đầu vào không hợp lệ",
"importedX": "Đã nhập {}", "importedX": "Đã nhập {}",
"obtainiumImport": "Nhập Obtainium", "obtainiumImport": "Nhập",
"importFromURLList": "Nhập từ danh sách URL", "importFromURLList": "Nhập từ danh sách URL",
"searchQuery": "Truy vấn tìm kiếm", "searchQuery": "Truy vấn tìm kiếm",
"appURLList": "Danh sách URL ứng dụng", "appURLList": "Danh sách URL ứng dụng",
@ -110,30 +113,28 @@
"dark": "Tối", "dark": "Tối",
"light": "Sáng", "light": "Sáng",
"followSystem": "Theo hệ thống", "followSystem": "Theo hệ thống",
"obtainium": "Obtainium", "useBlackTheme": "Nền đen",
"materialYou": "Material You", "appSortBy": "Sắp xếp ứng dụng",
"useBlackTheme": "Sử dụng chủ đề tối màu đen thuần túy", "authorName": "Tác giả",
"appSortBy": "Sắp xếp ứng dụng theo", "nameAuthor": "Tên",
"authorName": "Tác giả/Tên",
"nameAuthor": "Tên/Tác giả",
"asAdded": "Như đã thêm", "asAdded": "Như đã thêm",
"appSortOrder": "Thứ tự sắp xếp ứng dụng", "appSortOrder": "Thứ tự sắp xếp",
"ascending": "Tăng dần", "ascending": "Tăng dần",
"descending": "Giảm dần", "descending": "Giảm dần",
"bgUpdateCheckInterval": "Khoảng thời gian kiểm tra cập nhật nền", "bgUpdateCheckInterval": "Thời gian tự động kiểm tra cập nhật",
"neverManualOnly": "Không bao giờ - Chỉ thủ công", "neverManualOnly": "Không bao giờ",
"appearance": "Vẻ ngoài", "appearance": "Hiển thị",
"showWebInAppView": "Hiển thị trang web Nguồn trong chế độ xem Ứng dụng", "showWebInAppView": "Hiển thị trang web Nguồn trong chế độ xem chi tiết Ứng dụng",
"pinUpdates": "Ghim nội dung cập nhật lên đầu chế độ xem Ứng dụng", "pinUpdates": "Chuyển ứng dng có phiên bản mới lên đầu danh sách",
"updates": "Cập nhật", "updates": "Cập nhật",
"sourceSpecific": "Nguồn cụ thể", "sourceSpecific": "Cài đặt Nguồn",
"appSource": "Nguồn ứng dụng", "appSource": "Nguồn ứng dụng",
"noLogs": "Không có nhật ký", "noLogs": "Không có nhật ký",
"appLogs": "Nhật ký ứng dụng", "appLogs": "Nhật ký ứng dụng",
"close": "Đóng", "close": "Đóng",
"share": "Chia sẻ", "share": "Chia sẻ",
"appNotFound": "Không tìm thấy ứng dụng", "appNotFound": "Không tìm thấy ứng dụng",
"obtainiumExportHyphenatedLowercase": "xuất khẩu-obtainium", "obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Chọn một APK", "pickAnAPK": "Chọn một APK",
"appHasMoreThanOnePackage": "{} có nhiều gói:", "appHasMoreThanOnePackage": "{} có nhiều gói:",
"deviceSupportsXArch": "Thiết bị của bạn hỗ trợ kiến trúc CPU {}.", "deviceSupportsXArch": "Thiết bị của bạn hỗ trợ kiến trúc CPU {}.",
@ -145,8 +146,10 @@
"noNewUpdates": "Không có bản cập nhật mới.", "noNewUpdates": "Không có bản cập nhật mới.",
"xHasAnUpdate": "{} có bản cập nhật.", "xHasAnUpdate": "{} có bản cập nhật.",
"appsUpdated": "Ứng dụng đã cập nhật ", "appsUpdated": "Ứng dụng đã cập nhật ",
"appsNotUpdated": "Failed to update applications",
"appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền", "appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền",
"xWasUpdatedToY": "{} đã được cập nhật thành {}.", "xWasUpdatedToY": "{} đã được cập nhật thành {}.",
"xWasNotUpdatedToY": "Failed to update {} to {}.",
"errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật", "errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật",
"errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công", "errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công",
"appsRemoved": "Ứng dụng đã loại bỏ", "appsRemoved": "Ứng dụng đã loại bỏ",
@ -158,7 +161,7 @@
"checkingForUpdates": "Đang kiểm tra cập nhật", "checkingForUpdates": "Đang kiểm tra cập nhật",
"checkingForUpdatesNotifDescription": "Thông báo tạm thời xuất hiện khi kiểm tra bản cập nhật", "checkingForUpdatesNotifDescription": "Thông báo tạm thời xuất hiện khi kiểm tra bản cập nhật",
"pleaseAllowInstallPerm": "Vui lòng cho phép Obtainium cài đặt Ứng dụng", "pleaseAllowInstallPerm": "Vui lòng cho phép Obtainium cài đặt Ứng dụng",
"trackOnly": "Chỉ-Theo dõi", "trackOnly": "Chỉ theo dõi",
"errorWithHttpStatusCode": "Lỗi {}", "errorWithHttpStatusCode": "Lỗi {}",
"versionCorrectionDisabled": "Tính năng sửa phiên bản bị vô hiệu hóa (plugin dường như không hoạt động)", "versionCorrectionDisabled": "Tính năng sửa phiên bản bị vô hiệu hóa (plugin dường như không hoạt động)",
"unknown": "Không xác định", "unknown": "Không xác định",
@ -169,15 +172,14 @@
"lastUpdateCheckX": "Kiểm tra cập nhật lần cuối: {}", "lastUpdateCheckX": "Kiểm tra cập nhật lần cuối: {}",
"remove": "Loại bỏ", "remove": "Loại bỏ",
"yesMarkUpdated": "Có, Đánh dấu là đã cập nhật", "yesMarkUpdated": "Có, Đánh dấu là đã cập nhật",
"fdroid": "Chính thức của F-Droid", "fdroid": "F-Droid Chính thức",
"appIdOrName": "ID hoặc tên ứng dụng", "appIdOrName": "ID hoặc tên ứng dụng",
"appId": "ID ứng dụng", "appId": "ID ứng dụng",
"appWithIdOrNameNotFound": "Không tìm thấy ứng dụng nào có ID hoặc tên đó", "appWithIdOrNameNotFound": "Không tìm thấy ứng dụng nào có ID hoặc tên đó",
"reposHaveMultipleApps": "Kho có thể chứa nhiều Ứng dụng", "reposHaveMultipleApps": "Kho có thể chứa nhiều Ứng dụng",
"fdroidThirdPartyRepo": "Kho lưu trữ bên thứ ba F-Droid", "fdroidThirdPartyRepo": "Kho lưu trữ bên thứ ba F-Droid",
"steam": "Steam", "steamMobile": "Điện thoại di động hơi nước",
"steamMobile": "Steam Mobile", "steamChat": "Trò chuyện hơi nước",
"steamChat": "Steam Chat",
"install": "Cài đặt", "install": "Cài đặt",
"markInstalled": "Đánh dấu là đã cài đặt", "markInstalled": "Đánh dấu là đã cài đặt",
"update": "Cập nhật", "update": "Cập nhật",
@ -186,22 +188,25 @@
"disableVersionDetection": "Tắt tính năng phát hiện phiên bản", "disableVersionDetection": "Tắt tính năng phát hiện phiên bản",
"noVersionDetectionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng mà tính năng phát hiện phiên bản không hoạt động chính xác.", "noVersionDetectionExplanation": "Chỉ nên sử dụng tùy chọn này cho Ứng dụng mà tính năng phát hiện phiên bản không hoạt động chính xác.",
"downloadingX": "Đang tải xuống {}", "downloadingX": "Đang tải xuống {}",
"downloadX": "Download {}",
"downloadedX": "Downloaded {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "Thông báo cho người dùng về tiến trình tải xuống Ứng dụng", "downloadNotifDescription": "Thông báo cho người dùng về tiến trình tải xuống Ứng dụng",
"noAPKFound": "Không tìm thấy APK", "noAPKFound": "Không tìm thấy APK",
"noVersionDetection": "Không phát hiện phiên bản", "noVersionDetection": "Không phát hiện phiên bản",
"categorize": "Phân loại", "categorize": "Phân loại",
"categories": "Thể loại", "categories": "Danh mục",
"category": "Thể loại", "category": "Danh mục",
"noCategory": "Không thể loại", "noCategory": "Không danh mục",
"noCategories": "Không thể loại", "noCategories": "Không danh mục",
"deleteCategoriesQuestion": "Xóa thể loại?", "deleteCategoriesQuestion": "Xóa danh mục?",
"categoryDeleteWarning": "Tất cả ứng dụng trong thể loại đã xóa sẽ được đặt thành chưa được phân loại.", "categoryDeleteWarning": "Tất cả ứng dụng trong danh mục đã xóa sẽ được đặt thành chưa được phân loại.",
"addCategory": "Thêm thể loại", "addCategory": "Thêm thể loại",
"label": "Nhãn", "label": "Nhãn",
"language": "Ngôn ngữ", "language": "Ngôn ngữ",
"copiedToClipboard": "Sao chép vào clipboard", "copiedToClipboard": "Sao chép vào clipboard",
"storagePermissionDenied": "Quyền lưu trữ bị từ chối", "storagePermissionDenied": "Quyền lưu trữ bị từ chối",
"selectedCategorizeWarning": "Điều này sẽ thay thế mọi cài đặt thể loại hiện có cho Ứng dụng đã chọn.", "selectedCategorizeWarning": "Điều này sẽ thay thế mọi thiết đặt danh mục hiện có cho Ứng dụng đã chọn.",
"filterAPKsByRegEx": "Lọc APK theo biểu thức chính quy", "filterAPKsByRegEx": "Lọc APK theo biểu thức chính quy",
"removeFromObtainium": "Loại khỏi Obtainium", "removeFromObtainium": "Loại khỏi Obtainium",
"uninstallFromDevice": "Gỡ cài đặt khỏi thiết bị", "uninstallFromDevice": "Gỡ cài đặt khỏi thiết bị",
@ -211,44 +216,44 @@
"changes": "Thay đổi", "changes": "Thay đổi",
"releaseDate": "Ngày phát hành", "releaseDate": "Ngày phát hành",
"importFromURLsInFile": "Nhập từ URL trong Tệp (như OPML)", "importFromURLsInFile": "Nhập từ URL trong Tệp (như OPML)",
"versionDetectionExplanation": "Đối chiếu chuỗi phiên bản với phiên bản được phát hiện từ hệ điều hành",
"versionDetection": "Phát hiện phiên bản", "versionDetection": "Phát hiện phiên bản",
"standardVersionDetection": "Phát hiện phiên bản tiêu chuẩn", "standardVersionDetection": "Phát hiện phiên bản tiêu chuẩn",
"groupByCategory": "Nhóm theo thể loại", "groupByCategory": "Nhóm theo danh mục",
"autoApkFilterByArch": "Cố gắng lọc APK theo kiến trúc CPU nếu có thể", "autoApkFilterByArch": "Cố gắng lọc APK theo kiến trúc CPU nếu có thể",
"overrideSource": "Ghi đè nguồn", "overrideSource": "Ghi đè nguồn",
"dontShowAgain": "Đừng hiển thị thông tin này nữa", "dontShowAgain": "Đừng hiển thị thông tin này nữa",
"dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ-Theo dõi'", "dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ theo dõi'",
"dontShowAPKOriginWarnings": "Không hiển thị cảnh báo nguồn gốc APK", "dontShowAPKOriginWarnings": "Không hiển thị cảnh báo nguồn gốc APK",
"moveNonInstalledAppsToBottom": "Di chuyển Ứng dụng chưa được cài đặt xuống cuối chế độ xem Ứng dụng", "moveNonInstalledAppsToBottom": "Chuyển Ứng dụng chưa được cài đặt xuống cuối danh sách",
"gitlabPATLabel": "Mã thông báo truy cập cá nhân GitLab\n(Cho phép tìm kiếm và khám phá APK tốt hơn)", "gitlabPATLabel": "Token truy cập cá nhân GitLab",
"about": "Giới thiệu", "about": "Giới thiệu",
"requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Cài đặt)", "requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Thiết đặt)",
"checkOnStart": "Kiểm tra các bản cập nhật khi khởi động", "checkOnStart": "Kiểm tra các bản cập nhật khi khởi động",
"tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn", "tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn",
"removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài", "removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài",
"pickHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất", "pickHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất",
"checkUpdateOnDetailPage": "Kiểm tra các bản cập nhật khi mở trang chi tiết Ứng dụng", "checkUpdateOnDetailPage": "Kiểm tra các bản cập nhật khi mở trang chi tiết Ứng dụng",
"disablePageTransitions": "Tắt hoạt ảnh chuyển trang", "disablePageTransitions": "Tắt hiệu ứng chuyển trang",
"reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược", "reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược",
"minStarCount": "Số lượng sao tối thiểu", "minStarCount": "Số lượng sao tối thiểu",
"addInfoBelow": "Thêm thông tin này vào bên dưới.", "addInfoBelow": "Thêm thông tin này vào bên dưới.",
"addInfoInSettings": "Thêm thông tin này vào Cài đặt.", "addInfoInSettings": "Thêm thông tin này vào Thiết đặt.",
"githubSourceNote": "Có thể tránh được việc giới hạn tốc độ GitHub bằng cách sử dụng khóa API.", "githubSourceNote": "Có thể tránh được việc giới hạn tốc độ GitHub bằng cách sử dụng khóa API.",
"gitlabSourceNote": "Trích xuất APK GitLab có thể không hoạt động nếu không có khóa API.", "sortByLastLinkSegment": "Chỉ sắp xếp theo đoạn cuối của liên kết",
"sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Lọc ghi chú phát hành theo biểu thức chính quy", "filterReleaseNotesByRegEx": "Lọc ghi chú phát hành theo biểu thức chính quy",
"customLinkFilterRegex": "Bộ lọc liên kết APK tùy chỉnh theo biểu thức chính quy (Mặc định '.apk$')", "customLinkFilterRegex": "Bộ lọc liên kết APK tùy chỉnh theo biểu thức chính quy (Mặc định '.apk$')",
"appsPossiblyUpdated": "Đã cố gắng cập nhật ứng dụng", "appsPossiblyUpdated": "Đã cố gắng cập nhật ứng dụng",
"appsPossiblyUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng có khả năng được áp dụng trong nền", "appsPossiblyUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng có khả năng được áp dụng trong nền",
"xWasPossiblyUpdatedToY": "{} có thể đã được cập nhật thành {}.", "xWasPossiblyUpdatedToY": "{} có thể đã được cập nhật thành {}.",
"enableBackgroundUpdates": "Bật cập nhật nền", "enableBackgroundUpdates": "Tự động cập nhật trong nền",
"backgroundUpdateReqsExplanation": "Có thể không thực hiện được cập nhật nền cho tất cả ứng dụng.", "backgroundUpdateReqsExplanation": "Có thể không thực hiện được cập nhật trong nền cho tất cả ứng dụng.",
"backgroundUpdateLimitsExplanation": "Sự thành công của cài đặt nền chỉ có thể được xác định khi mở Obtainium.", "backgroundUpdateLimitsExplanation": "Sự thành công của cài đặt trong nền chỉ có thể được xác định khi mở Obtainium.",
"verifyLatestTag": "Xác minh thẻ 'mới nhất'", "verifyLatestTag": "Xác minh thẻ 'mới nhất'",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Lọc tìm liên kết 'Trung cấp' để truy cập",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Lọc liên kết theo văn bản liên kết",
"intermediateLinkNotFound": "Không tìm thấy liên kết trung gian", "intermediateLinkNotFound": "Không tìm thấy liên kết trung gian",
"intermediateLink": "Intermediate link", "intermediateLink": "Liên kết trung gian",
"exemptFromBackgroundUpdates": "Miễn cập nhật nền (nếu được bật)", "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ó WiFi", "bgUpdatesOnWiFiOnly": "Tắt cập nhật nền khi không có WiFi",
"autoSelectHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất", "autoSelectHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất",
@ -256,14 +261,14 @@
"matchGroupToUse": "Nhóm đối sánh để sử dụng cho Regex trích xuất phiên bản", "matchGroupToUse": "Nhóm đối sánh để sử dụng cho Regex trích xuất phiên bản",
"highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn", "highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
"pickExportDir": "Chọn thư mục xuất", "pickExportDir": "Chọn thư mục xuất",
"autoExportOnChanges": "Tự động xuất khi thay đổi", "autoExportOnChanges": "Tự động xuất",
"includeSettings": "Include settings", "includeSettings": "Bao gồm thiết đặt",
"filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy", "filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất", "trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API", "dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
"reverseSort": "Sắp xếp ngược", "reverseSort": "Sắp xếp ngược",
"takeFirstLink": "Take first link", "takeFirstLink": "Lấy liên kết đầu tiên",
"skipSort": "Skip sorting", "skipSort": "Bỏ qua việc sắp xếp",
"debugMenu": "Danh sách gỡ lỗi", "debugMenu": "Danh sách gỡ lỗi",
"bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.", "bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
"runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ", "runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
@ -278,65 +283,90 @@
"downloadingXNotifChannel": "Đang tải xuống {}", "downloadingXNotifChannel": "Đang tải xuống {}",
"completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng", "completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng",
"checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật", "checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật",
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật", "onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra cập nhật các ứng dụng đã cài đặt và Chỉ theo dõi",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "Hỗ trợ URL APK cố định",
"selectX": "Select {}", "selectX": "Lựa chọn {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Cho phép tải đa luồng",
"installMethod": "Installation method", "useShizuku": "Use Shizuku or Sui to install",
"normal": "Normal", "shizukuBinderNotFound": "Shizuku chưa khởi động",
"shizuku": "Shizuku", "shizukuOld": "Old Shizuku version (<11) - update it",
"root": "Root", "shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB - update Android or use Sui instead",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
"removeAppQuestion":{ "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",
"useLatestAssetDateAsReleaseDate": "Sử dụng nội dung tải lên mới nhất làm ngày phát hành",
"defaultPseudoVersioningMethod": "Phương pháp tạo phiên bản giả mặc định",
"partialAPKHash": "Băm APK một phần",
"APKLinkHash": "Băm liên kết APK",
"directAPKLink": "Liên kết APK trực tiếp",
"pseudoVersionInUse": "Phiên bản giả đang được sử dụng",
"installed": "Đã cài đặt",
"latest": "Mới nhất",
"invertRegEx": "Đảo ngược biểu thức chính quy",
"note": "Ghi chú",
"selfHostedNote": "Trình đơn thả xuống \"{}\" có thể được dùng để tiếp cận các phiên bản tự lưu trữ/tùy chỉnh của bất kỳ nguồn nào.",
"badDownload": "Không thể phân tích cú pháp APK (tải xuống một phần hoặc không tương thích)",
"beforeNewInstallsShareToAppVerifier": "Chia sẻ ứng dụng mới với AppVerifier (nếu có)",
"appVerifierInstructionToast": "Chia sẻ lên AppVerifier, sau đó quay lại đây khi sẵn sàng.",
"removeAppQuestion": {
"one": "Gỡ ứng dụng?", "one": "Gỡ ứng dụng?",
"other": "Gỡ ứng dụng?" "other": "Gỡ ứng dụng?"
}, },
"tooManyRequestsTryAgainInMinutes":{ "tooManyRequestsTryAgainInMinutes": {
"one": "Quá nhiều yêu cầu (tốc độ giới hạn) - hãy thử lại sau {} phút", "one": "Quá nhiều yêu cầu (tốc độ giới hạn) - hãy thử lại sau {} phút",
"other": "Quá nhiều yêu cầu (tốc độ giới hạn) - hãy thử lại sau {} phút" "other": "Quá nhiều yêu cầu (tốc độ giới hạn) - hãy thử lại sau {} phút"
}, },
"bgUpdateGotErrorRetryInMinutes":{ "bgUpdateGotErrorRetryInMinutes": {
"one": "Việc kiểm tra bản cập nhật BG gặp phải {}, sẽ lên lịch kiểm tra lại sau {} phút", "one": "Việc kiểm tra bản cập nhật BG gặp phải {}, sẽ lên lịch kiểm tra lại sau {} phút",
"other": "Việc kiểm tra bản cập nhật BG gặp phải {}, sẽ lên lịch kiểm tra lại sau {} phút" "other": "Việc kiểm tra bản cập nhật BG gặp phải {}, sẽ lên lịch kiểm tra lại sau {} phút"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded":{ "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "Đang kiểm tra bản cập nhật BG tìm thấy {} bản cập nhật - sẽ thông báo cho người dùng nếu cần", "one": "Đang kiểm tra bản cập nhật BG tìm thấy {} bản cập nhật - sẽ thông báo cho người dùng nếu cần",
"other": "Đang kiểm tra bản cập nhật BG tìm thấy {} bản cập nhật - sẽ thông báo cho người dùng nếu cần" "other": "Đang kiểm tra bản cập nhật BG tìm thấy {} bản cập nhật - sẽ thông báo cho người dùng nếu cần"
}, },
"apps":{ "apps": {
"one": "{} Ứng dụng", "one": "{} Ứng dụng",
"other": "{} Ứng dụng" "other": "{} Ứng dụng"
}, },
"url":{ "url": {
"one": "{} URL", "one": "{} URL",
"other": "{} URL" "other": "{} URL"
}, },
"minute":{ "minute": {
"one": "{} Phút", "one": "{} Phút",
"other": "{} Phút" "other": "{} Phút"
}, },
"hour":{ "hour": {
"one": "{} Giờ", "one": "{} Giờ",
"other": "{} Giờ" "other": "{} Giờ"
}, },
"day":{ "day": {
"one": "{} Ngày", "one": "{} Ngày",
"other": "{} ngày" "other": "{} ngày"
}, },
"clearedNLogsBeforeXAfterY":{ "clearedNLogsBeforeXAfterY": {
"one": "Đã xóa {n} nhật ký (trước = {trước}, sau = {sau})", "one": "Đã xóa {n} nhật ký (trước = {trước}, sau = {sau})",
"other": "Đã xóa {n} nhật ký (trước = {trước}, sau = {sau})" "other": "Đã xóa {n} nhật ký (trước = {trước}, sau = {sau})"
}, },
"xAndNMoreUpdatesAvailable":{ "xAndNMoreUpdatesAvailable": {
"one": "{} và 1 ứng dụng khác có bản cập nhật.", "one": "{} và 1 ứng dụng khác có bản cập nhật.",
"other": "{} và {} ứng dụng khác có bản cập nhật." "other": "{} và {} ứng dụng khác có bản cập nhật."
}, },
"xAndNMoreUpdatesInstalled":{ "xAndNMoreUpdatesInstalled": {
"one": "{} và 1 ứng dụng khác đã được cập nhật.", "one": "{} và 1 ứng dụng khác đã được cập nhật.",
"other": "{} và {} ứng dụng khác đã được cập nhật." "other": "{} và {} ứng dụng khác đã được cập nhật."
}, },
"xAndNMoreUpdatesPossiblyInstalled":{ "xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.",
"other": "Failed to update {} and {} more apps."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} và 1 ứng dụng khác có thể đã được cập nhật.", "one": "{} và 1 ứng dụng khác có thể đã được cập nhật.",
"other": "{} và {} ứng dụng khác có thể đã được cập nhật." "other": "{} và {} ứng dụng khác có thể đã được cập nhật."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
} }
} }

View File

@ -22,11 +22,14 @@
"requiredInBrackets": "(必填)", "requiredInBrackets": "(必填)",
"dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项", "dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项",
"colour": "配色", "colour": "配色",
"githubStarredRepos": "GitHub 已星标仓库", "standard": "标准",
"custom": "定制",
"useMaterialYou": "使用 Material You",
"githubStarredRepos": "已星标的 GitHub 仓库",
"uname": "用户名", "uname": "用户名",
"wrongArgNum": "参数数量错误", "wrongArgNum": "参数数量错误",
"xIsTrackOnly": "{}为“仅追踪”模式", "xIsTrackOnly": "{}为“仅追踪”模式",
"source": "源代码", "source": "源",
"app": "应用", "app": "应用",
"appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。", "appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。",
"youPickedTrackOnly": "您选择了“仅追踪”。", "youPickedTrackOnly": "您选择了“仅追踪”。",
@ -51,17 +54,16 @@
"percentProgress": "进度:{}%", "percentProgress": "进度:{}%",
"pleaseWait": "请稍候", "pleaseWait": "请稍候",
"updateAvailable": "更新可用", "updateAvailable": "更新可用",
"estimateInBracketsShort": "(推测)",
"notInstalled": "未安装", "notInstalled": "未安装",
"estimateInBrackets": "(推测)", "pseudoVersion": "虚拟版本号",
"selectAll": "全选", "selectAll": "全选",
"deselectX": "取消选择 {}", "deselectX": "取消选择 {}",
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。", "xWillBeRemovedButRemainInstalled": "{}将从 Obtainium 中删除,但仍安装在您的设备中。",
"removeSelectedAppsQuestion": "是否删除选中的应用?", "removeSelectedAppsQuestion": "是否删除选中的应用?",
"removeSelectedApps": "删除选中的应用", "removeSelectedApps": "删除选中的应用",
"updateX": "更新 {}", "updateX": "更新 {}",
"installX": "安装 {}", "installX": "安装 {}",
"markXTrackOnlyAsUpdated": "将 {}\n仅追踪\n标记为已更新", "markXTrackOnlyAsUpdated": "将{}\n仅追踪\n标记为已更新",
"changeX": "更改 {}", "changeX": "更改 {}",
"installUpdateApps": "安装/更新应用", "installUpdateApps": "安装/更新应用",
"installUpdateSelectedApps": "安装/更新选中的应用", "installUpdateSelectedApps": "安装/更新选中的应用",
@ -73,13 +75,14 @@
"unpinFromTop": "取消置顶", "unpinFromTop": "取消置顶",
"resetInstallStatusForSelectedAppsQuestion": "是否重置选中应用的安装状态?", "resetInstallStatusForSelectedAppsQuestion": "是否重置选中应用的安装状态?",
"installStatusOfXWillBeResetExplanation": "选中应用的安装状态将会被重置。\n\n当更新安装失败或其他问题导致 Obtainium 中的应用版本显示错误时,可以尝试通过此方法解决。", "installStatusOfXWillBeResetExplanation": "选中应用的安装状态将会被重置。\n\n当更新安装失败或其他问题导致 Obtainium 中的应用版本显示错误时,可以尝试通过此方法解决。",
"customLinkMessage": "这些链接适用于安装了 Gettingium 的设备",
"shareAppConfigLinks": "将应用程序配置共享为 HTML 链接",
"shareSelectedAppURLs": "分享选中应用的 URL", "shareSelectedAppURLs": "分享选中应用的 URL",
"resetInstallStatus": "重置安装状态", "resetInstallStatus": "重置安装状态",
"more": "更多", "more": "更多",
"removeOutdatedFilter": "删除失效的应用筛选", "removeOutdatedFilter": "删除失效的应用筛选",
"showOutdatedOnly": "只显示待更新应用", "showOutdatedOnly": "只显示待更新应用",
"filter": "筛选", "filter": "筛选",
"filterActive": "筛选 *",
"filterApps": "筛选应用", "filterApps": "筛选应用",
"appName": "应用名称", "appName": "应用名称",
"author": "作者", "author": "作者",
@ -88,17 +91,17 @@
"importExport": "导入/导出", "importExport": "导入/导出",
"settings": "设置", "settings": "设置",
"exportedTo": "已导出至 {}", "exportedTo": "已导出至 {}",
"obtainiumExport": "Obtainium 导出", "obtainiumExport": "导出 Obtainium",
"invalidInput": "无效的输入", "invalidInput": "无效的输入",
"importedX": "已导入 {}", "importedX": "已导入 {}",
"obtainiumImport": "Obtainium 导入", "obtainiumImport": "导入 Obtainium",
"importFromURLList": "从 URL 列表导入", "importFromURLList": "从 URL 列表导入",
"searchQuery": "搜索查询", "searchQuery": "搜索查询",
"appURLList": "应用 URL 列表", "appURLList": "应用 URL 列表",
"line": "行", "line": "行",
"searchX": "搜索 {}", "searchX": "搜索{}",
"noResults": "无结果", "noResults": "无结果",
"importX": "导入 {}", "importX": "导入{}",
"importedAppsIdDisclaimer": "导入的应用可能会错误地显示为“未安装”状态。\n请通过 Obtainium 重新安装这些应用来解决此问题。", "importedAppsIdDisclaimer": "导入的应用可能会错误地显示为“未安装”状态。\n请通过 Obtainium 重新安装这些应用来解决此问题。",
"importErrors": "导入错误", "importErrors": "导入错误",
"importedXOfYApps": "已导入 {} 中的 {} 个应用。", "importedXOfYApps": "已导入 {} 中的 {} 个应用。",
@ -110,8 +113,6 @@
"dark": "深色", "dark": "深色",
"light": "浅色", "light": "浅色",
"followSystem": "跟随系统", "followSystem": "跟随系统",
"obtainium": "Obtainium",
"materialYou": "Material You",
"useBlackTheme": "使用纯黑深色主题", "useBlackTheme": "使用纯黑深色主题",
"appSortBy": "排序依据", "appSortBy": "排序依据",
"authorName": "作者 / 应用名称", "authorName": "作者 / 应用名称",
@ -123,7 +124,7 @@
"bgUpdateCheckInterval": "后台更新检查间隔", "bgUpdateCheckInterval": "后台更新检查间隔",
"neverManualOnly": "手动", "neverManualOnly": "手动",
"appearance": "外观", "appearance": "外观",
"showWebInAppView": "应用详情页显示来源网页", "showWebInAppView": "应用详情页显示来源网页",
"pinUpdates": "将待更新应用置顶", "pinUpdates": "将待更新应用置顶",
"updates": "更新", "updates": "更新",
"sourceSpecific": "来源", "sourceSpecific": "来源",
@ -133,9 +134,9 @@
"close": "关闭", "close": "关闭",
"share": "分享", "share": "分享",
"appNotFound": "未找到应用", "appNotFound": "未找到应用",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "获取出口",
"pickAnAPK": "选择一个 APK 文件", "pickAnAPK": "选择一个 APK 文件",
"appHasMoreThanOnePackage": "{} 有多个架构可用:", "appHasMoreThanOnePackage": "{}有多个架构可用:",
"deviceSupportsXArch": "您的设备支持 {} 架构。", "deviceSupportsXArch": "您的设备支持 {} 架构。",
"deviceSupportsFollowingArchs": "您的设备支持下列架构:", "deviceSupportsFollowingArchs": "您的设备支持下列架构:",
"warning": "警告", "warning": "警告",
@ -143,15 +144,17 @@
"updatesAvailable": "更新可用", "updatesAvailable": "更新可用",
"updatesAvailableNotifDescription": "Obtainium 追踪的应用有更新时发送通知", "updatesAvailableNotifDescription": "Obtainium 追踪的应用有更新时发送通知",
"noNewUpdates": "全部应用已是最新。", "noNewUpdates": "全部应用已是最新。",
"xHasAnUpdate": "{} 可以更新了。", "xHasAnUpdate": "{}可以更新了。",
"appsUpdated": "应用已更新", "appsUpdated": "应用已更新",
"appsNotUpdated": "更新应用程序失败",
"appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知", "appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知",
"xWasUpdatedToY": "{} 已更新至 {}。", "xWasUpdatedToY": "{}已更新至 {}。",
"xWasNotUpdatedToY": "未能将 {} 更新为 {}。",
"errorCheckingUpdates": "检查更新出错", "errorCheckingUpdates": "检查更新出错",
"errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知", "errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知",
"appsRemoved": "应用已删除", "appsRemoved": "应用已删除",
"appsRemovedNotifDescription": "当应用因加载出错而被删除时发送通知", "appsRemovedNotifDescription": "当应用因加载出错而被删除时发送通知",
"xWasRemovedDueToErrorY": "{} 由于以下错误被删除:{}", "xWasRemovedDueToErrorY": "{}由于以下错误被删除:{}",
"completeAppInstallation": "完成应用安装", "completeAppInstallation": "完成应用安装",
"obtainiumMustBeOpenToInstallApps": "必须启动 Obtainium 才能安装应用", "obtainiumMustBeOpenToInstallApps": "必须启动 Obtainium 才能安装应用",
"completeAppInstallationNotifDescription": "提示返回 Obtainium 以完成应用的安装", "completeAppInstallationNotifDescription": "提示返回 Obtainium 以完成应用的安装",
@ -159,7 +162,7 @@
"checkingForUpdatesNotifDescription": "检查更新时短暂显示的通知", "checkingForUpdatesNotifDescription": "检查更新时短暂显示的通知",
"pleaseAllowInstallPerm": "请授予 Obtainium 安装应用的权限", "pleaseAllowInstallPerm": "请授予 Obtainium 安装应用的权限",
"trackOnly": "仅追踪", "trackOnly": "仅追踪",
"errorWithHttpStatusCode": "错误 {}", "errorWithHttpStatusCode": "{} 错误",
"versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)", "versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)",
"unknown": "未知", "unknown": "未知",
"none": "无", "none": "无",
@ -175,9 +178,8 @@
"appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用", "appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用",
"reposHaveMultipleApps": "存储库中可能包含多个应用", "reposHaveMultipleApps": "存储库中可能包含多个应用",
"fdroidThirdPartyRepo": "F-Droid 第三方存储库", "fdroidThirdPartyRepo": "F-Droid 第三方存储库",
"steam": "Steam", "steamMobile": "蒸汽手机",
"steamMobile": "Steam Mobile", "steamChat": "蒸汽聊天",
"steamChat": "Steam Chat",
"install": "安装", "install": "安装",
"markInstalled": "标记为已安装", "markInstalled": "标记为已安装",
"update": "更新", "update": "更新",
@ -185,11 +187,14 @@
"additionalOptions": "附加选项", "additionalOptions": "附加选项",
"disableVersionDetection": "禁用版本检测", "disableVersionDetection": "禁用版本检测",
"noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。", "noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。",
"downloadingX": "正在下载{}", "downloadingX": "正在下载{}",
"downloadX": "下载 {}",
"downloadedX": "下载 {}",
"releaseAsset": "发行版",
"downloadNotifDescription": "提示应用的下载进度", "downloadNotifDescription": "提示应用的下载进度",
"noAPKFound": "未找到 APK 文件", "noAPKFound": "未找到 APK 文件",
"noVersionDetection": "禁用版本检测", "noVersionDetection": "禁用版本检测",
"categorize": "类", "categorize": "类",
"categories": "类别", "categories": "类别",
"category": "类别", "category": "类别",
"noCategory": "无类别", "noCategory": "无类别",
@ -211,30 +216,30 @@
"changes": "更新日志", "changes": "更新日志",
"releaseDate": "发行日期", "releaseDate": "发行日期",
"importFromURLsInFile": "从文件中的 URL 导入(如 OPML", "importFromURLsInFile": "从文件中的 URL 导入(如 OPML",
"versionDetectionExplanation": "使发行版本号与应用定义的版本号一致",
"versionDetection": "版本检测", "versionDetection": "版本检测",
"standardVersionDetection": "常规版本检测", "standardVersionDetection": "常规版本检测",
"groupByCategory": "按类别分组显示", "groupByCategory": "按类别分组显示",
"autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件", "autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件",
"overrideSource": "覆盖来源", "overrideSource": "覆盖来源",
"dontShowAgain": "不再显示", "dontShowAgain": "不再显示",
"dontShowTrackOnlyWarnings": "不显示“仅追踪”模式警告", "dontShowTrackOnlyWarnings": "忽略“仅追踪”模式警告",
"dontShowAPKOriginWarnings": "不显示 APK 文件来源警告", "dontShowAPKOriginWarnings": "忽略 APK 文件来源警告",
"moveNonInstalledAppsToBottom": "将未安装应用置底", "moveNonInstalledAppsToBottom": "将未安装应用置底",
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)", "gitlabPATLabel": "GitLab 个人访问令牌",
"about": "相关文档", "about": "相关文档",
"requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)", "requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)",
"checkOnStart": "启动时进行一次检查", "checkOnStart": "启动时进行一次检查",
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID", "tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
"removeOnExternalUninstall": "自动删除已卸载的外部应用", "removeOnExternalUninstall": "自动删除列表中已卸载的应用",
"pickHighestVersionCode": "自动选版本号最高的 APK 文件", "pickHighestVersionCode": "自动选取内部版本号最高的 APK 文件",
"checkUpdateOnDetailPage": "打开应用详情页时检查更新", "checkUpdateOnDetailPage": "打开应用详情页时进行检查",
"disablePageTransitions": "禁用页面过渡动画效果", "disablePageTransitions": "禁用页面过渡动画效果",
"reversePageTransitions": "反转页面过渡动画效果", "reversePageTransitions": "反转页面过渡动画效果",
"minStarCount": "最小星标数", "minStarCount": "最小星标数",
"addInfoBelow": "在下方添加此凭据。", "addInfoBelow": "在下方添加此凭据。",
"addInfoInSettings": "在“设置”中添加此凭据。", "addInfoInSettings": "在“设置”中添加此凭据。",
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。", "githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
"sortByLastLinkSegment": "仅根据链接的末尾部分进行筛选", "sortByLastLinkSegment": "仅根据链接的末尾部分进行筛选",
"filterReleaseNotesByRegEx": "筛选发行说明(正则表达式)", "filterReleaseNotesByRegEx": "筛选发行说明(正则表达式)",
"customLinkFilterRegex": "筛选自定义来源的 APK 文件链接\n正则表达式默认匹配模式为“.apk$”)", "customLinkFilterRegex": "筛选自定义来源的 APK 文件链接\n正则表达式默认匹配模式为“.apk$”)",
@ -251,9 +256,9 @@
"intermediateLink": "中转链接", "intermediateLink": "中转链接",
"exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用)", "exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用)",
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新", "bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件", "autoSelectHighestVersionCode": "自动选择内部版本号最高的 APK 文件",
"versionExtractionRegEx": "版本号提取规则(正则表达式)", "versionExtractionRegEx": "版本号匹配组规则(正则表达式)",
"matchGroupToUse": "引用的捕获组", "matchGroupToUse": "引用匹配组规则,将用于版本字符串提取",
"highlightTouchTargets": "突出展示不明显的触摸区域", "highlightTouchTargets": "突出展示不明显的触摸区域",
"pickExportDir": "选择导出文件夹", "pickExportDir": "选择导出文件夹",
"autoExportOnChanges": "数据变更时自动导出", "autoExportOnChanges": "数据变更时自动导出",
@ -275,18 +280,35 @@
"appsPossiblyUpdatedNotifChannel": "已尝试更新应用", "appsPossiblyUpdatedNotifChannel": "已尝试更新应用",
"errorCheckingUpdatesNotifChannel": "检查更新出错", "errorCheckingUpdatesNotifChannel": "检查更新出错",
"appsRemovedNotifChannel": "应用已删除", "appsRemovedNotifChannel": "应用已删除",
"downloadingXNotifChannel": "正在下载{}", "downloadingXNotifChannel": "正在下载{}",
"completeAppInstallationNotifChannel": "完成应用安装", "completeAppInstallationNotifChannel": "完成应用安装",
"checkingForUpdatesNotifChannel": "正在检查更新", "checkingForUpdatesNotifChannel": "正在检查更新",
"onlyCheckInstalledOrTrackOnlyApps": "只已安装和“仅追踪”的应用进行更新检查", "onlyCheckInstalledOrTrackOnlyApps": "只检查已安装和“仅追踪”的应用",
"supportFixedAPKURL": "支持固定的 APK 文件链接", "supportFixedAPKURL": "支持固定的 APK 文件链接",
"selectX": "选择 {}", "selectX": "选择{}",
"parallelDownloads": "启用并行下载", "parallelDownloads": "启用并行下载",
"installMethod": "安装方式", "useShizuku": "使用 Shizuku 或 Sui 安装",
"normal": "常规", "shizukuBinderNotFound": "未发现兼容的 Shizuku 服务",
"shizuku": "Shizuku", "shizukuOld": "旧的 Shizuku 版本 (<11) - 更新它",
"root": "Root", "shizukuOldAndroidWithADB": "使用 ADB 在 Android < 8.1 上运行 Shizuku - 更新 Android 或使用 Sui 代替",
"shizukuBinderNotFound": "Shizuku 服务未运行", "shizukuPretendToBeGooglePlay": "将 Google Play 设置为安装源(如果使用 Shizuku",
"useSystemFont": "使用系统字体",
"useVersionCodeAsOSVersion": "使用内部版本号代替应用定义的版本号",
"requestHeader": "请求标头",
"useLatestAssetDateAsReleaseDate": "使用最近文件上传时间作为发行日期",
"defaultPseudoVersioningMethod": "默认虚拟版本方案",
"partialAPKHash": "APK 文件散列值片段",
"APKLinkHash": "APK 文件链接散列值",
"directAPKLink": "APK 文件直链",
"pseudoVersionInUse": "正在使用虚拟版本号",
"installed": "当前版本",
"latest": "最新版本",
"invertRegEx": "反转匹配",
"note": "备注",
"selfHostedNote": "可以通过“{}”下拉菜单来指向任意来源的自托管/自定义实例。",
"badDownload": "无法解析 APK 文件(不兼容或文件不完整)",
"beforeNewInstallsShareToAppVerifier": "与 AppVerifier 共享新应用程序(如有)",
"appVerifierInstructionToast": "分享到 AppVerifier准备就绪后返回此处。",
"removeAppQuestion": { "removeAppQuestion": {
"one": "是否删除应用?", "one": "是否删除应用?",
"other": "是否删除应用?" "other": "是否删除应用?"
@ -328,15 +350,23 @@
"other": "清除了 {n} 个日志({before} 之前,{after} 之后)" "other": "清除了 {n} 个日志({before} 之前,{after} 之后)"
}, },
"xAndNMoreUpdatesAvailable": { "xAndNMoreUpdatesAvailable": {
"one": "{} 和另外 1 个应用可以更新了。", "one": "{}和另外 1 个应用可以更新了。",
"other": "{} 和另外 {} 个应用可以更新了。" "other": "{}和另外 {} 个应用可以更新了。"
}, },
"xAndNMoreUpdatesInstalled": { "xAndNMoreUpdatesInstalled": {
"one": "{} 和另外 1 个应用已更新。", "one": "{} 和另外 1 个应用已更新。",
"other": "{} 和另外 {} 个应用已更新。" "other": "{}和另外 {} 个应用已更新。"
},
"xAndNMoreUpdatesFailed": {
"one": "更新 {} 和另外 1 个应用程序失败。",
"other": "未能更新 {} 和 {} 更多应用程序。"
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} 和另外 1 个应用已尝试更新。", "one": "{} 和另外 1 个应用已尝试更新。",
"other": "{} 和另外 {} 个应用已尝试更新。" "other": "{}和另外 {} 个应用已尝试更新。"
},
"apk": {
"one": "{}APK",
"other": "{}APK"
} }
} }

View File

@ -7,6 +7,10 @@ trap "cd "$CURR_DIR"" EXIT
if [ -z "$1" ]; then 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 git fetch && git merge origin/main && git push # Typically run after a PR to main, so bring dev up to date
fi fi
cd .flutter
git fetch
git checkout "$(flutter --version | head -2 | tail -1 | awk '{print $4}')" # Ensure included Flutter submodule version equals my environment
cd ..
rm ./build/app/outputs/flutter-apk/* 2>/dev/null # Get rid of older builds if any 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) 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 for file in ./build/app/outputs/flutter-apk/app-*normal*.apk*; do mv "$file" "${file//-normal/}"; done

View File

@ -0,0 +1,54 @@
<p>Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.</p>
<p>Read the <a href="https://github.com/ImranR98/Obtainium/wiki">Wiki</a></p>
<p>
<b>Currently supported App sources:</b>
</p>
<ul>
<li>
<p>Open Source - General:</p>
<ul>
<li>GitHub</li>
<li>GitLab</li>
<li>Codeberg</li>
<li>F-Droid</li>
<li>Third Party F-Droid Repos</li>
<li>IzzyOnDroid</li>
<li>SourceForge</li>
<li>SourceHut</li>
</ul>
</li>
<li>
<p>Other - General:</p>
<ul>
<li>APKPure</li>
<li>Aptoide</li>
<li>Uptodowng</li>
<li>APKMirror (Track-Only)</li>
<li>Huawei AppGallery</li>
<li>Jenkins Jobs</li>
</ul>
</li>
<li>
<p>Open Source - App-Specific:</p>
<ul>
<li>Mullvad</li>
<li>Signal</li>
<li>VLC</li>
</ul>
</li>
<li>
<p>Other - App-Specific:</p>
<ul>
<li>WhatsApp</li>
<li>Telegram App</li>
<li>Neutron Code</li>
</ul>
</li>
<li><p>"HTML" (Fallback): Any other URL that returns an HTML page with links to APK files</p></li>
</ul>
<p>
<b>Limitations:</b>
</p>
<p>
For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable.
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

View File

@ -0,0 +1 @@
Get android app updates directly from the source

View File

@ -0,0 +1 @@
Obtainium

View File

@ -0,0 +1,54 @@
<p>Obtainium позволяет вам устанавливать и обновлять приложения прямо с их объявлений о выпусках и получать уведомления о новых выпусках.</p>
<p>Для деталей читайте <a href="https://github.com/ImranR98/Obtainium/wiki">Вики</a></p>
<p>
<b>Поддерживаемые источники приложений:</b>
</p>
<ul>
<li>
<p>Свободное ПО - Общие:</p>
<ul>
<li>GitHub</li>
<li>GitLab</li>
<li>Codeberg</li>
<li>F-Droid</li>
<li>Third Party F-Droid Repos</li>
<li>IzzyOnDroid</li>
<li>SourceForge</li>
<li>SourceHut</li>
</ul>
</li>
<li>
<p>Другие - Общие:</p>
<ul>
<li>APKPure</li>
<li>Aptoide</li>
<li>Uptodowng</li>
<li>APKMirror (Track-Only)</li>
<li>Huawei AppGallery</li>
<li>Jenkins Jobs</li>
</ul>
</li>
<li>
<p>Свободное ПО - Для отдельных приложений:</p>
<ul>
<li>Mullvad</li>
<li>Signal</li>
<li>VLC</li>
</ul>
</li>
<li>
<p>Другие - Для отдельных приложений:</p>
<ul>
<li>WhatsApp</li>
<li>Telegram App</li>
<li>Neutron Code</li>
</ul>
</li>
<li><p>"HTML" (Подстраховка): Любой другой URL-адрес, который возвращает HTML-страницу со ссылками на APK-файлы.</p></li>
</ul>
<p>
<b>Ограничения:</b>
</p>
<p>
Для некоторых источников данные собираются с помощью веб-скрапинга и могут легко сломаться из-за изменений в дизайне веб-сайта. В таких случаях более надежные методы могут быть недоступны.
</p>

View File

@ -0,0 +1 @@
Получайте обновления приложений прямо из источника

View File

@ -5,17 +5,20 @@ import 'package:obtainium/providers/source_provider.dart';
class APKCombo extends AppSource { class APKCombo extends AppSource {
APKCombo() { APKCombo() {
host = 'apkcombo.com'; hosts = ['apkcombo.com'];
showReleaseDateAsVersionToggle = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/+[^/]+/+[^/]+'); RegExp standardUrlRegEx = RegExp(
var match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+',
caseSensitive: false);
var match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -26,18 +29,19 @@ class APKCombo extends AppSource {
@override @override
Future<Map<String, String>?> getRequestHeaders( Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{}, Map<String, dynamic> additionalSettings,
bool forAPKDownload = false}) async { {bool forAPKDownload = false}) async {
return { return {
"User-Agent": "curl/8.0.1", "User-Agent": "curl/8.0.1",
"Accept": "*/*", "Accept": "*/*",
"Connection": "keep-alive", "Connection": "keep-alive",
"Host": "$host" "Host": hosts[0]
}; };
} }
Future<List<MapEntry<String, String>>> getApkUrls(String standardUrl) async { Future<List<MapEntry<String, String>>> getApkUrls(
var res = await sourceRequest('$standardUrl/download/apk'); String standardUrl, Map<String, dynamic> additionalSettings) async {
var res = await sourceRequest('$standardUrl/download/apk', {});
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
@ -70,9 +74,9 @@ class APKCombo extends AppSource {
} }
@override @override
Future<String> apkUrlPrefetchModifier( Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
String apkUrl, String standardUrl) async { Map<String, dynamic> additionalSettings) async {
var freshURLs = await getApkUrls(standardUrl); var freshURLs = await getApkUrls(standardUrl, additionalSettings);
var path2Match = Uri.parse(apkUrl).path; var path2Match = Uri.parse(apkUrl).path;
for (var url in freshURLs) { for (var url in freshURLs) {
if (Uri.parse(url.value).path == path2Match) { if (Uri.parse(url.value).path == path2Match) {
@ -88,7 +92,7 @@ class APKCombo extends AppSource {
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String appId = (await tryInferringAppId(standardUrl))!; String appId = (await tryInferringAppId(standardUrl))!;
var preres = await sourceRequest(standardUrl); var preres = await sourceRequest(standardUrl, additionalSettings);
if (preres.statusCode != 200) { if (preres.statusCode != 200) {
throw getObtainiumHttpError(preres); throw getObtainiumHttpError(preres);
} }
@ -112,7 +116,9 @@ class APKCombo extends AppSource {
} }
} }
return APKDetails( return APKDetails(
version, await getApkUrls(standardUrl), AppNames(author, appName), version,
await getApkUrls(standardUrl, additionalSettings),
AppNames(author, appName),
releaseDate: releaseDate); releaseDate: releaseDate);
} }
} }

View File

@ -9,8 +9,9 @@ import 'package:obtainium/providers/source_provider.dart';
class APKMirror extends AppSource { class APKMirror extends AppSource {
APKMirror() { APKMirror() {
host = 'apkmirror.com'; hosts = ['apkmirror.com'];
enforceTrackOnly = true; enforceTrackOnly = true;
showReleaseDateAsVersionToggle = true;
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
@ -32,13 +33,14 @@ class APKMirror extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp standardUrlRegEx = RegExp(
RegExp('^https?://(www\\.)?$host/apk/[^/]+/[^/]+'); '^https?://(www\\.)?${getSourceRegex(hosts)}/apk/[^/]+/[^/]+',
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -58,7 +60,7 @@ class APKMirror extends AppSource {
true true
? additionalSettings['filterReleaseTitlesByRegEx'] ? additionalSettings['filterReleaseTitlesByRegEx']
: null; : null;
Response res = await sourceRequest('$standardUrl/feed'); Response res = await sourceRequest('$standardUrl/feed', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var items = parse(res.body).querySelectorAll('item'); var items = parse(res.body).querySelectorAll('item');
dynamic targetRelease; dynamic targetRelease;
@ -84,7 +86,7 @@ class APKMirror extends AppSource {
dateString != null ? HttpDate.parse('$dateString GMT') : null; dateString != null ? HttpDate.parse('$dateString GMT') : null;
String? version = titleString String? version = titleString
?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0, ?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0,
RegExp(' by ').firstMatch(titleString)?.start ?? 0) RegExp(' by ').allMatches(titleString).last.start)
.trim(); .trim();
if (version == null || version.isEmpty) { if (version == null || version.isEmpty) {
version = titleString; version = titleString;

View File

@ -20,26 +20,29 @@ parseDateTimeMMMddCommayyyy(String? dateString) {
class APKPure extends AppSource { class APKPure extends AppSource {
APKPure() { APKPure() {
host = 'apkpure.com'; hosts = ['apkpure.net', 'apkpure.com'];
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
showReleaseDateAsVersionToggle = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp standardUrlRegExB = RegExp(
RegExp('^https?://m.$host/+[^/]+/+[^/]+(/+[^/]+)?'); '^https?://m.${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?',
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase()); caseSensitive: false);
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
if (match != null) { if (match != null) {
url = 'https://$host${Uri.parse(url).path}'; url = 'https://${getSourceRegex(hosts)}${Uri.parse(url).path}';
} }
RegExp standardUrlRegExA = RegExp standardUrlRegExA = RegExp(
RegExp('^https?://(www\\.)?$host/+[^/]+/+[^/]+(/+[^/]+)?'); '^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?',
match = standardUrlRegExA.firstMatch(url.toLowerCase()); caseSensitive: false);
match = standardUrlRegExA.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -55,8 +58,8 @@ class APKPure extends AppSource {
) async { ) async {
String appId = (await tryInferringAppId(standardUrl))!; String appId = (await tryInferringAppId(standardUrl))!;
String host = Uri.parse(standardUrl).host; String host = Uri.parse(standardUrl).host;
var res = await sourceRequest('$standardUrl/download'); var res = await sourceRequest('$standardUrl/download', additionalSettings);
var resChangelog = await sourceRequest(standardUrl); var resChangelog = await sourceRequest(standardUrl, additionalSettings);
if (res.statusCode == 200 && resChangelog.statusCode == 200) { if (res.statusCode == 200 && resChangelog.statusCode == 200) {
var html = parse(res.body); var html = parse(res.body);
var htmlChangelog = parse(resChangelog.body); var htmlChangelog = parse(resChangelog.body);
@ -69,7 +72,8 @@ class APKPure extends AppSource {
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString); DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString);
String type = html.querySelector('a.info-tag')?.text.trim() ?? 'APK'; String type = html.querySelector('a.info-tag')?.text.trim() ?? 'APK';
List<MapEntry<String, String>> apkUrls = [ List<MapEntry<String, String>> apkUrls = [
MapEntry('$appId.apk', 'https://d.$host/b/$type/$appId?version=latest') MapEntry('$appId.apk',
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?version=latest')
]; ];
String author = html String author = html
.querySelector('span.info-sdk') .querySelector('span.info-sdk')

View File

@ -6,30 +6,35 @@ import 'package:obtainium/providers/source_provider.dart';
class Aptoide extends AppSource { class Aptoide extends AppSource {
Aptoide() { Aptoide() {
host = 'aptoide.com'; hosts = ['aptoide.com'];
name = 'Aptoide'; name = 'Aptoide';
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
showReleaseDateAsVersionToggle = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
Future<String?> tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async { {Map<String, dynamic> additionalSettings = const {}}) async {
return (await getAppDetailsJSON(standardUrl))['package']; return (await getAppDetailsJSON(
standardUrl, additionalSettings))['package'];
} }
Future<Map<String, dynamic>> getAppDetailsJSON(String standardUrl) async { Future<Map<String, dynamic>> getAppDetailsJSON(
var res = await sourceRequest(standardUrl); String standardUrl, Map<String, dynamic> additionalSettings) async {
var res = await sourceRequest(standardUrl, additionalSettings);
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
@ -40,8 +45,8 @@ class Aptoide extends AppSource {
} else { } else {
throw NoReleasesError(); throw NoReleasesError();
} }
var res2 = var res2 = await sourceRequest(
await sourceRequest('https://ws2.aptoide.com/api/7/getApp/app_id/$id'); 'https://ws2.aptoide.com/api/7/getApp/app_id/$id', additionalSettings);
if (res2.statusCode != 200) { if (res2.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
@ -53,7 +58,7 @@ class Aptoide extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
var appDetails = await getAppDetailsJSON(standardUrl); var appDetails = await getAppDetailsJSON(standardUrl, additionalSettings);
String appName = appDetails['name'] ?? tr('app'); String appName = appDetails['name'] ?? tr('app');
String author = appDetails['developer']?['name'] ?? name; String author = appDetails['developer']?['name'] ?? name;
String? dateStr = appDetails['updated']; String? dateStr = appDetails['updated'];

View File

@ -5,7 +5,7 @@ import 'package:obtainium/providers/source_provider.dart';
class Codeberg extends AppSource { class Codeberg extends AppSource {
GitHub gh = GitHub(); GitHub gh = GitHub();
Codeberg() { Codeberg() {
host = 'codeberg.org'; hosts = ['codeberg.org'];
additionalSourceAppSpecificSettingFormItems = additionalSourceAppSpecificSettingFormItems =
gh.additionalSourceAppSpecificSettingFormItems; gh.additionalSourceAppSpecificSettingFormItems;
@ -16,12 +16,14 @@ class Codeberg extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -35,7 +37,7 @@ class Codeberg extends AppSource {
) async { ) async {
return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings, return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
(bool useTagUrl) async { (bool useTagUrl) async {
return 'https://$host/api/v1/repos${standardUrl.substring('https://$host'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100'; return 'https://${hosts[0]}/api/v1/repos${standardUrl.substring('https://${hosts[0]}'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
}, null); }, null);
} }
@ -50,7 +52,7 @@ class Codeberg extends AppSource {
{Map<String, dynamic> querySettings = const {}}) async { {Map<String, dynamic> querySettings = const {}}) async {
return gh.searchCommon( return gh.searchCommon(
query, query,
'https://$host/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100', 'https://${hosts[0]}/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100',
'data', 'data',
querySettings: querySettings); querySettings: querySettings);
} }

View File

@ -0,0 +1,44 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/providers/source_provider.dart';
class DirectAPKLink extends AppSource {
HTML html = HTML();
DirectAPKLink() {
neverAutoSelect = true;
name = tr('directAPKLink');
additionalSourceAppSpecificSettingFormItems = html
.additionalSourceAppSpecificSettingFormItems
.where((element) => element
.where((element) => element.key == 'requestHeader')
.isNotEmpty)
.toList();
excludeCommonSettingKeys = [
'versionExtractionRegEx',
'matchGroupToUse',
'versionDetection',
'useVersionCodeAsOSVersion',
'apkFilterRegEx',
'autoApkFilterByArch'
];
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
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;
return html.getLatestAPKDetails(standardUrl, additionalSettingsNew);
}
}

View File

@ -3,13 +3,15 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/app_sources/gitlab.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
class FDroid extends AppSource { class FDroid extends AppSource {
FDroid() { FDroid() {
host = 'f-droid.org'; hosts = ['f-droid.org'];
name = tr('fdroid'); name = tr('fdroid');
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
canSearch = true; canSearch = true;
@ -37,20 +39,22 @@ class FDroid extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp standardUrlRegExB = RegExp(
RegExp('^https?://(www\\.)?$host/+[^/]+/+packages/+[^/]+'); '^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+packages/+[^/]+',
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase()); caseSensitive: false);
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
if (match != null) { if (match != null) {
url = url =
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}'; 'https://${Uri.parse(match.group(0)!).host}/packages/${Uri.parse(url).pathSegments.last}';
} }
RegExp standardUrlRegExA = RegExp standardUrlRegExA = RegExp(
RegExp('^https?://(www\\.)?$host/+packages/+[^/]+'); '^https?://(www\\.)?${getSourceRegex(hosts)}/+packages/+[^/]+',
match = standardUrlRegExA.firstMatch(url.toLowerCase()); caseSensitive: false);
match = standardUrlRegExA.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -67,35 +71,56 @@ class FDroid extends AppSource {
String? appId = await tryInferringAppId(standardUrl); String? appId = await tryInferringAppId(standardUrl);
String host = Uri.parse(standardUrl).host; String host = Uri.parse(standardUrl).host;
var details = getAPKUrlsFromFDroidPackagesAPIResponse( var details = getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'), await sourceRequest(
'https://$host/api/v1/packages/$appId', additionalSettings),
'https://$host/repo/$appId', 'https://$host/repo/$appId',
standardUrl, standardUrl,
name, name,
autoSelectHighestVersionCode: additionalSettings: additionalSettings);
additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode:
additionalSettings['trySelectingSuggestedVersionCode'] == true,
filterVersionsByRegEx:
(additionalSettings['filterVersionsByRegEx'] as String?)
?.isNotEmpty ==
true
? additionalSettings['filterVersionsByRegEx']
: null);
if (!hostChanged) { if (!hostChanged) {
try { try {
var res = await sourceRequest( var res = await sourceRequest(
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml'); 'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml',
String author = res.body additionalSettings);
.split('\n') var lines = res.body.split('\n');
.where((l) => l.startsWith('AuthorName: ')) var authorLines = lines.where((l) => l.startsWith('AuthorName: '));
.first if (authorLines.isNotEmpty) {
.split(': ') details.names.author =
.sublist(1) authorLines.first.split(': ').sublist(1).join(': ');
.join(': '); }
details.names.author = author; var changelogUrls = lines
.where((l) => l.startsWith('Changelog: '))
.map((e) => e.split(' ').sublist(1).join(' '));
if (changelogUrls.isNotEmpty) {
details.changeLog = changelogUrls.first;
bool isGitHub = false;
bool isGitLab = false;
try {
GitHub().sourceSpecificStandardizeURL(details.changeLog!);
isGitHub = true;
} catch (e) {
//
}
try {
GitLab().sourceSpecificStandardizeURL(details.changeLog!);
isGitLab = true;
} catch (e) {
//
}
if ((isGitHub || isGitLab) &&
(details.changeLog?.indexOf('/blob/') ?? -1) >= 0) {
details.changeLog = (await sourceRequest(
details.changeLog!.replaceFirst('/blob/', '/raw/'),
additionalSettings))
.body;
}
}
} catch (e) { } catch (e) {
// Fail silently // Fail silently
} }
if ((details.changeLog?.length ?? 0) > 2048) {
details.changeLog = '${details.changeLog!.substring(0, 2048)}...';
}
} }
return details; return details;
} }
@ -104,7 +129,7 @@ class FDroid extends AppSource {
Future<Map<String, List<String>>> search(String query, Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async { {Map<String, dynamic> querySettings = const {}}) async {
Response res = await sourceRequest( Response res = await sourceRequest(
'https://search.$host/?q=${Uri.encodeQueryComponent(query)}'); 'https://search.${hosts[0]}/?q=${Uri.encodeQueryComponent(query)}', {});
if (res.statusCode == 200) { if (res.statusCode == 200) {
Map<String, List<String>> urlsWithDescriptions = {}; Map<String, List<String>> urlsWithDescriptions = {};
parse(res.body).querySelectorAll('.package-header').forEach((e) { parse(res.body).querySelectorAll('.package-header').forEach((e) {
@ -132,12 +157,30 @@ class FDroid extends AppSource {
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse( APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl, String sourceName, Response res, String apkUrlPrefix, String standardUrl, String sourceName,
{bool autoSelectHighestVersionCode = false, {Map<String, dynamic> additionalSettings = const {}}) {
bool trySelectingSuggestedVersionCode = false, var autoSelectHighestVersionCode =
String? filterVersionsByRegEx}) { additionalSettings['autoSelectHighestVersionCode'] == true;
var trySelectingSuggestedVersionCode =
additionalSettings['trySelectingSuggestedVersionCode'] == true;
var filterVersionsByRegEx =
(additionalSettings['filterVersionsByRegEx'] as String?)?.isNotEmpty ==
true
? additionalSettings['filterVersionsByRegEx']
: null;
var apkFilterRegEx =
(additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty == true
? additionalSettings['apkFilterRegEx']
: null;
if (res.statusCode == 200) { if (res.statusCode == 200) {
var response = jsonDecode(res.body); var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? []; 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;
}).toList();
}
if (releases.isEmpty) { if (releases.isEmpty) {
throw NoReleasesError(); throw NoReleasesError();
} }

View File

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
@ -10,6 +11,7 @@ class FDroidRepo extends AppSource {
canSearch = true; canSearch = true;
excludeFromMassSearch = true; excludeFromMassSearch = true;
neverAutoSelect = true; neverAutoSelect = true;
showReleaseDateAsVersionToggle = true;
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
@ -44,7 +46,7 @@ class FDroidRepo extends AppSource {
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
var standardUri = Uri.parse(url); var standardUri = Uri.parse(url);
var pathSegments = standardUri.pathSegments; var pathSegments = standardUri.pathSegments;
if (pathSegments.last == 'index.xml') { if (pathSegments.isNotEmpty && pathSegments.last == 'index.xml') {
pathSegments.removeLast(); pathSegments.removeLast();
standardUri = standardUri.replace(path: pathSegments.join('/')); standardUri = standardUri.replace(path: pathSegments.join('/'));
} }
@ -59,7 +61,7 @@ class FDroidRepo extends AppSource {
throw NoReleasesError(); throw NoReleasesError();
} }
url = removeQueryParamsFromUrl(standardizeUrl(url)); url = removeQueryParamsFromUrl(standardizeUrl(url));
var res = await sourceRequest('$url/index.xml'); var res = await sourceRequestWithURLVariants(url, {});
if (res.statusCode == 200) { if (res.statusCode == 200) {
var body = parse(res.body); var body = parse(res.body);
Map<String, List<String>> results = {}; Map<String, List<String>> results = {};
@ -71,7 +73,11 @@ class FDroidRepo extends AppSource {
appId.contains(query) || appId.contains(query) ||
appName.contains(query) || appName.contains(query) ||
appDesc.contains(query)) { appDesc.contains(query)) {
results['$url?appId=$appId'] = [appName, appDesc]; results[
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}?appId=$appId'] = [
appName,
appDesc
];
} }
}); });
return results; return results;
@ -101,6 +107,26 @@ class FDroidRepo extends AppSource {
return app; return app;
} }
Future<Response> sourceRequestWithURLVariants(
String url,
Map<String, dynamic> additionalSettings,
) async {
var res = await sourceRequest(
'$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('/')
: url;
res = await sourceRequest('$base/repo/index.xml', additionalSettings);
if (res.statusCode != 200) {
res = await sourceRequest(
'$base/fdroid/repo/index.xml', additionalSettings);
}
}
return res;
}
@override @override
Future<APKDetails> getLatestAPKDetails( Future<APKDetails> getLatestAPKDetails(
String standardUrl, String standardUrl,
@ -116,8 +142,8 @@ class FDroidRepo extends AppSource {
if (appIdOrName == null) { if (appIdOrName == null) {
throw NoReleasesError(); throw NoReleasesError();
} }
var res = await sourceRequest( var res =
'$standardUrl${standardUrl.endsWith('/index.xml') ? '' : '/index.xml'}'); await sourceRequestWithURLVariants(standardUrl, additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var body = parse(res.body); var body = parse(res.body);
var foundApps = body.querySelectorAll('application').where((element) { var foundApps = body.querySelectorAll('application').where((element) {
@ -166,7 +192,8 @@ class FDroidRepo extends AppSource {
latestVersionReleases = [latestVersionReleases[0]]; latestVersionReleases = [latestVersionReleases[0]];
} }
List<String> apkUrls = latestVersionReleases List<String> apkUrls = latestVersionReleases
.map((e) => '$standardUrl/${e.querySelector('apkname')!.innerHtml}') .map((e) =>
'${res.request!.url.toString().split('/').reversed.toList().sublist(1).reversed.join('/')}/${e.querySelector('apkname')!.innerHtml}')
.toList(); .toList();
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls), return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
AppNames(authorName, appName), AppNames(authorName, appName),

View File

@ -14,8 +14,9 @@ import 'package:url_launcher/url_launcher_string.dart';
class GitHub extends AppSource { class GitHub extends AppSource {
GitHub() { GitHub() {
host = 'github.com'; hosts = ['github.com'];
appIdInferIsOptional = true; appIdInferIsOptional = true;
showReleaseDateAsVersionToggle = true;
sourceConfigSettingFormItems = [ sourceConfigSettingFormItems = [
GeneratedFormTextField('github-creds', GeneratedFormTextField('github-creds',
@ -76,6 +77,10 @@ class GitHub extends AppSource {
[ [
GeneratedFormSwitch('dontSortReleasesList', GeneratedFormSwitch('dontSortReleasesList',
label: tr('dontSortReleasesList')) label: tr('dontSortReleasesList'))
],
[
GeneratedFormSwitch('useLatestAssetDateAsReleaseDate',
label: tr('useLatestAssetDateAsReleaseDate'), defaultValue: false)
] ]
]; ];
@ -108,7 +113,8 @@ class GitHub extends AppSource {
for (var path in possibleBuildGradleLocations) { for (var path in possibleBuildGradleLocations) {
try { try {
var res = await sourceRequest( var res = await sourceRequest(
'${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/contents/$path'); '${await convertStandardUrlToAPIUrl(standardUrl, additionalSettings)}/contents/$path',
additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
try { try {
var body = jsonDecode(res.body); var body = jsonDecode(res.body);
@ -149,18 +155,20 @@ class GitHub extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
Future<Map<String, String>?> getRequestHeaders( Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{}, Map<String, dynamic> additionalSettings,
bool forAPKDownload = false}) async { {bool forAPKDownload = false}) async {
var token = await getTokenIfAny(additionalSettings); var token = await getTokenIfAny(additionalSettings);
var headers = <String, String>{}; var headers = <String, String>{};
if (token != null) { if (token != null) {
@ -203,11 +211,11 @@ class GitHub extends AppSource {
} }
Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async => Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async =>
'https://api.$host'; 'https://api.${hosts[0]}';
Future<String> convertStandardUrlToAPIUrl( Future<String> convertStandardUrlToAPIUrl(
String standardUrl, Map<String, dynamic> additionalSettings) async => String standardUrl, Map<String, dynamic> additionalSettings) async =>
'${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://$host'.length)}'; '${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://${hosts[0]}'.length)}';
@override @override
String? changeLogPageFromStandardUrl(String standardUrl) => String? changeLogPageFromStandardUrl(String standardUrl) =>
@ -234,11 +242,14 @@ class GitHub extends AppSource {
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true; bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
bool dontSortReleasesList = bool dontSortReleasesList =
additionalSettings['dontSortReleasesList'] == true; additionalSettings['dontSortReleasesList'] == true;
bool useLatestAssetDateAsReleaseDate =
additionalSettings['useLatestAssetDateAsReleaseDate'] == true;
dynamic latestRelease; dynamic latestRelease;
if (verifyLatestTag) { if (verifyLatestTag) {
var temp = requestUrl.split('?'); var temp = requestUrl.split('?');
Response res = await sourceRequest( Response res = await sourceRequest(
'${temp[0]}/latest${temp.length > 1 ? '?${temp.sublist(1).join('?')}' : ''}'); '${temp[0]}/latest${temp.length > 1 ? '?${temp.sublist(1).join('?')}' : ''}',
additionalSettings);
if (res.statusCode != 200) { if (res.statusCode != 200) {
if (onHttpErrorCode != null) { if (onHttpErrorCode != null) {
onHttpErrorCode(res); onHttpErrorCode(res);
@ -247,7 +258,7 @@ class GitHub extends AppSource {
} }
latestRelease = jsonDecode(res.body); latestRelease = jsonDecode(res.body);
} }
Response res = await sourceRequest(requestUrl); Response res = await sourceRequest(requestUrl, additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var releases = jsonDecode(res.body) as List<dynamic>; var releases = jsonDecode(res.body) as List<dynamic>;
if (latestRelease != null) { if (latestRelease != null) {
@ -260,23 +271,41 @@ class GitHub extends AppSource {
} }
} }
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) => List<MapEntry<String, String>> getReleaseAssetUrls(dynamic release) =>
(release['assets'] as List<dynamic>?) (release['assets'] as List<dynamic>?)?.map((e) {
?.map((e) { return (e['name'] != null) &&
return (e['name'] != null) && ((e['url'] ?? e['browser_download_url']) != null)
((e['url'] ?? e['browser_download_url']) != null) ? MapEntry(e['name'] as String,
? MapEntry(e['name'] as String, (e['url'] ?? e['browser_download_url']) as String)
(e['url'] ?? e['browser_download_url']) as String) : const MapEntry('', '');
: const MapEntry('', ''); }).toList() ??
})
.where((element) => element.key.toLowerCase().endsWith('.apk'))
.toList() ??
[]; [];
DateTime? getReleaseDateFromRelease(dynamic rel) => DateTime? getPublishDateFromRelease(dynamic rel) =>
rel?['published_at'] != null rel?['published_at'] != null
? DateTime.parse(rel['published_at']) ? DateTime.parse(rel['published_at'])
: null; : null;
DateTime? getNewestAssetDateFromRelease(dynamic rel) {
var t = (rel['assets'] as List<dynamic>?)
?.map((e) {
return e?['updated_at'] != null
? DateTime.parse(e['updated_at'])
: null;
})
.where((e) => e != null)
.toList();
t?.sort((a, b) => b!.compareTo(a!));
if (t?.isNotEmpty == true) {
return t!.first;
}
return null;
}
DateTime? getReleaseDateFromRelease(dynamic rel, bool useAssetDate) =>
!useAssetDate
? getPublishDateFromRelease(rel)
: getNewestAssetDateFromRelease(rel);
if (dontSortReleasesList) { if (dontSortReleasesList) {
releases = releases.reversed.toList(); releases = releases.reversed.toList();
} else { } else {
@ -301,19 +330,25 @@ class GitHub extends AppSource {
(nameA as String).substring(matchA!.start, matchA.end), (nameA as String).substring(matchA!.start, matchA.end),
(nameB as String).substring(matchB!.start, matchB.end)); (nameB as String).substring(matchB!.start, matchB.end));
} else { } else {
return (getReleaseDateFromRelease(a) ?? DateTime(1)) return (getReleaseDateFromRelease(
.compareTo(getReleaseDateFromRelease(b) ?? DateTime(0)); a, useLatestAssetDateAsReleaseDate) ??
DateTime(1))
.compareTo(getReleaseDateFromRelease(
b, useLatestAssetDateAsReleaseDate) ??
DateTime(0));
} }
} }
}); });
} }
if (latestRelease != null && if (latestRelease != null &&
(latestRelease['tag_name'] ?? latestRelease['name']) != null &&
releases.isNotEmpty && releases.isNotEmpty &&
latestRelease != latestRelease !=
(releases[releases.length - 1]['tag_name'] ?? (releases[releases.length - 1]['tag_name'] ??
releases[0]['name'])) { releases[0]['name'])) {
var ind = releases.indexWhere((element) => var ind = releases.indexWhere((element) =>
latestRelease == (element['tag_name'] ?? element['name'])); (latestRelease['tag_name'] ?? latestRelease['name']) ==
(element['tag_name'] ?? element['name']));
if (ind >= 0) { if (ind >= 0) {
releases.add(releases.removeAt(ind)); releases.add(releases.removeAt(ind));
} }
@ -345,34 +380,51 @@ class GitHub extends AppSource {
.hasMatch(((releases[i]['body'] as String?) ?? '').trim())) { .hasMatch(((releases[i]['body'] as String?) ?? '').trim())) {
continue; continue;
} }
var apkUrls = getReleaseAPKUrls(releases[i]); var allAssetUrls = getReleaseAssetUrls(releases[i]);
if (additionalSettings['apkFilterRegEx'] != null) { List<MapEntry<String, String>> apkUrls = allAssetUrls
var reg = RegExp(additionalSettings['apkFilterRegEx']); .where((element) => element.key.toLowerCase().endsWith('.apk'))
apkUrls = .toList();
apkUrls.where((element) => reg.hasMatch(element.key)).toList();
} apkUrls = filterApks(apkUrls, additionalSettings['apkFilterRegEx'],
additionalSettings['invertAPKFilter']);
if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) { if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) {
continue; continue;
} }
targetRelease = releases[i]; targetRelease = releases[i];
targetRelease['apkUrls'] = apkUrls; targetRelease['apkUrls'] = apkUrls;
targetRelease['version'] =
targetRelease['tag_name'] ?? targetRelease['name'];
if (targetRelease['tarball_url'] != null) {
allAssetUrls.add(MapEntry(
(targetRelease['version'] ?? 'source') + '.tar.gz',
targetRelease['tarball_url']));
}
if (targetRelease['zipball_url'] != null) {
allAssetUrls.add(MapEntry(
(targetRelease['version'] ?? 'source') + '.zip',
targetRelease['zipball_url']));
}
targetRelease['allAssetUrls'] = allAssetUrls;
break; break;
} }
if (targetRelease == null) { if (targetRelease == null) {
throw NoReleasesError(); throw NoReleasesError();
} }
String? version = targetRelease['tag_name'] ?? targetRelease['name']; String? version = targetRelease['version'];
DateTime? releaseDate = getReleaseDateFromRelease(targetRelease); DateTime? releaseDate = getReleaseDateFromRelease(
targetRelease, useLatestAssetDateAsReleaseDate);
if (version == null) { if (version == null) {
throw NoVersionError(); throw NoVersionError();
} }
var changeLog = targetRelease['body'].toString(); var changeLog = (targetRelease['body'] ?? '').toString();
return APKDetails( return APKDetails(
version, version,
targetRelease['apkUrls'] as List<MapEntry<String, String>>, targetRelease['apkUrls'] as List<MapEntry<String, String>>,
getAppNames(standardUrl), getAppNames(standardUrl),
releaseDate: releaseDate, releaseDate: releaseDate,
changeLog: changeLog.isEmpty ? null : changeLog); changeLog: changeLog.isEmpty ? null : changeLog,
allAssetUrls:
targetRelease['allAssetUrls'] as List<MapEntry<String, String>>);
} else { } else {
if (onHttpErrorCode != null) { if (onHttpErrorCode != null) {
onHttpErrorCode(res); onHttpErrorCode(res);
@ -424,7 +476,7 @@ class GitHub extends AppSource {
String query, String requestUrl, String rootProp, String query, String requestUrl, String rootProp,
{Function(Response)? onHttpErrorCode, {Function(Response)? onHttpErrorCode,
Map<String, dynamic> querySettings = const {}}) async { Map<String, dynamic> querySettings = const {}}) async {
Response res = await sourceRequest(requestUrl); Response res = await sourceRequest(requestUrl, {});
if (res.statusCode == 200) { if (res.statusCode == 200) {
int minStarCount = querySettings['minStarCount'] != null int minStarCount = querySettings['minStarCount'] != null
? int.parse(querySettings['minStarCount']) ? int.parse(querySettings['minStarCount'])

View File

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:obtainium/app_sources/github.dart'; import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
@ -13,8 +13,9 @@ import 'package:url_launcher/url_launcher_string.dart';
class GitLab extends AppSource { class GitLab extends AppSource {
GitLab() { GitLab() {
host = 'gitlab.com'; hosts = ['gitlab.com'];
canSearch = true; canSearch = true;
showReleaseDateAsVersionToggle = true;
sourceConfigSettingFormItems = [ sourceConfigSettingFormItems = [
GeneratedFormTextField('gitlab-creds', GeneratedFormTextField('gitlab-creds',
@ -52,12 +53,14 @@ class GitLab extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async { Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async {
@ -69,27 +72,19 @@ class GitLab extends AppSource {
return creds != null && creds.isNotEmpty ? creds : null; return creds != null && creds.isNotEmpty ? creds : null;
} }
@override
Future<String?> getSourceNote() async {
if ((await getPATIfAny({})) == null) {
return '${tr('gitlabSourceNote')} ${hostChanged ? tr('addInfoBelow') : tr('addInfoInSettings')}';
}
return null;
}
@override @override
Future<Map<String, List<String>>> search(String query, Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async { {Map<String, dynamic> querySettings = const {}}) async {
var url = var url =
'https://$host/api/v4/projects?search=${Uri.encodeQueryComponent(query)}'; 'https://${hosts[0]}/api/v4/projects?search=${Uri.encodeQueryComponent(query)}';
var res = await sourceRequest(url); var res = await sourceRequest(url, {});
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
var json = jsonDecode(res.body) as List<dynamic>; var json = jsonDecode(res.body) as List<dynamic>;
Map<String, List<String>> results = {}; Map<String, List<String>> results = {};
for (var element in json) { for (var element in json) {
results['https://$host/${element['path_with_namespace']}'] = [ results['https://${hosts[0]}/${element['path_with_namespace']}'] = [
element['name_with_namespace'], element['name_with_namespace'],
element['description'] ?? tr('noDescription') element['description'] ?? tr('noDescription')
]; ];
@ -101,103 +96,100 @@ class GitLab extends AppSource {
String? changeLogPageFromStandardUrl(String standardUrl) => String? changeLogPageFromStandardUrl(String standardUrl) =>
'$standardUrl/-/releases'; '$standardUrl/-/releases';
@override
Future<Map<String, String>?> getRequestHeaders(
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>{};
headers[HttpHeaders.refererHeader] = 'https://${hosts[0]}';
if (headers.isNotEmpty) {
return headers;
} else {
return null;
}
}
@override @override
Future<APKDetails> getLatestAPKDetails( Future<APKDetails> getLatestAPKDetails(
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
// Prepare request params
var names = GitHub().getAppNames(standardUrl);
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
bool trackOnly = additionalSettings['trackOnly'] == true;
// Request data from REST API
Response res = await sourceRequest(
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/${trackOnly ? 'repository/tags' : 'releases'}?$optionalAuth',
additionalSettings);
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
// Extract .apk details from received data
Iterable<APKDetails> apkDetailsList = [];
var json = jsonDecode(res.body) as List<dynamic>;
apkDetailsList = json.map((e) {
var apkUrlsFromAssets = (e['assets']?['links'] as List<dynamic>? ?? [])
.map((e) {
return (e['direct_asset_url'] ?? e['url'] ?? '') as String;
})
.where((s) => s.isNotEmpty)
.toList();
List<String> uploadedAPKsFromDescription =
((e['description'] ?? '') as String)
.split('](')
.join('\n')
.split('.apk)')
.join('.apk\n')
.split('\n')
.where((s) => s.startsWith('/uploads/') && s.endsWith('apk'))
.map((s) => '$standardUrl$s')
.toList();
var apkUrlsSet = apkUrlsFromAssets.toSet();
apkUrlsSet.addAll(uploadedAPKsFromDescription);
var releaseDateString = e['released_at'] ?? e['created_at'];
DateTime? releaseDate =
releaseDateString != null ? DateTime.parse(releaseDateString) : null;
return APKDetails(
e['tag_name'] ?? e['name'],
getApkUrlsFromUrls(apkUrlsSet.toList()),
GitHub().getAppNames(standardUrl),
releaseDate: releaseDate);
});
if (apkDetailsList.isEmpty) {
throw NoReleasesError();
}
var finalResult = apkDetailsList.first;
// Fallback procedure
bool fallbackToOlderReleases = bool fallbackToOlderReleases =
additionalSettings['fallbackToOlderReleases'] == true; additionalSettings['fallbackToOlderReleases'] == true;
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {}); if (finalResult.apkUrls.isEmpty && fallbackToOlderReleases && !trackOnly) {
Iterable<APKDetails> apkDetailsList = []; apkDetailsList =
if (PAT != null) { apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
var names = GitHub().getAppNames(standardUrl); finalResult = apkDetailsList.first;
Response res = await sourceRequest(
'https://$host/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT');
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var json = jsonDecode(res.body) as List<dynamic>;
apkDetailsList = json.map((e) {
var apkUrlsFromAssets = (e['assets']?['links'] as List<dynamic>? ?? [])
.map((e) {
return (e['direct_asset_url'] ?? e['url'] ?? '') as String;
})
.where((s) => s.isNotEmpty)
.toList();
List<String> uploadedAPKsFromDescription =
((e['description'] ?? '') as String)
.split('](')
.join('\n')
.split('.apk)')
.join('.apk\n')
.split('\n')
.where((s) => s.startsWith('/uploads/') && s.endsWith('apk'))
.map((s) => '$standardUrl$s')
.toList();
var apkUrlsSet = apkUrlsFromAssets.toSet();
apkUrlsSet.addAll(uploadedAPKsFromDescription);
var releaseDateString = e['released_at'] ?? e['created_at'];
DateTime? releaseDate = releaseDateString != null
? DateTime.parse(releaseDateString)
: null;
return APKDetails(
e['tag_name'] ?? e['name'],
getApkUrlsFromUrls(apkUrlsSet.toList()),
GitHub().getAppNames(standardUrl),
releaseDate: releaseDate);
});
} else {
Response res = await sourceRequest('$standardUrl/-/tags?format=atom');
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var standardUri = Uri.parse(standardUrl);
var parsedHtml = parse(res.body);
apkDetailsList = parsedHtml.querySelectorAll('entry').map((entry) {
var entryContent = parse(
parseFragment(entry.querySelector('content')!.innerHtml).text);
var apkUrls = [
...getLinksFromParsedHTML(
entryContent,
RegExp(
'^${standardUri.path.replaceAllMapped(RegExp(r'[.*+?^${}()|[\]\\]'), (x) {
return '\\${x[0]}';
})}/uploads/[^/]+/[^/]+\\.apk\$',
caseSensitive: false),
standardUri.origin),
// GitLab releases may contain links to externally hosted APKs
...getLinksFromParsedHTML(entryContent,
RegExp('/[^/]+\\.apk\$', caseSensitive: false), '')
.where((element) => Uri.parse(element).host != '')
];
var entryId = entry.querySelector('id')?.innerHtml;
var version =
entryId == null ? null : Uri.parse(entryId).pathSegments.last;
var releaseDateString = entry.querySelector('updated')?.innerHtml;
DateTime? releaseDate = releaseDateString != null
? DateTime.parse(releaseDateString)
: null;
if (version == null) {
throw NoVersionError();
}
return APKDetails(version, getApkUrlsFromUrls(apkUrls),
GitHub().getAppNames(standardUrl),
releaseDate: releaseDate);
});
} }
if (apkDetailsList.isEmpty) {
throw NoReleasesError(note: tr('gitlabSourceNote')); if (finalResult.apkUrls.isEmpty && !trackOnly) {
throw NoAPKError();
} }
if (fallbackToOlderReleases) {
if (additionalSettings['trackOnly'] != true) { finalResult.apkUrls = finalResult.apkUrls.map((apkUrl) {
apkDetailsList = if (RegExp('^$standardUrl/-/jobs/[0-9]+/artifacts/file/[^/]+\$')
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList(); .hasMatch(apkUrl.value)) {
return MapEntry(
apkUrl.key, apkUrl.value.replaceFirst('/file/', '/raw/'));
} else {
return apkUrl;
} }
if (apkDetailsList.isEmpty) { }).toList();
throw NoReleasesError(note: tr('gitlabSourceNote'));
} return finalResult;
}
return apkDetailsList.first;
} }
} }

View File

@ -17,13 +17,19 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
.split('/') .split('/')
.where((element) => element.trim().isNotEmpty) .where((element) => element.trim().isNotEmpty)
.toList(); .toList();
String absoluteUrl;
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) { if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
return '${referenceAbsoluteUrl.origin}/$ambiguousUrl'; absoluteUrl = '${referenceAbsoluteUrl.origin}$ambiguousUrl';
} else if (currPathSegments.isEmpty) {
absoluteUrl = '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) { } else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl'; absoluteUrl =
'${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
} else { } else {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl'; absoluteUrl =
'${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl';
} }
return Uri.parse(absoluteUrl).toString();
} }
int compareAlphaNumeric(String a, String b) { int compareAlphaNumeric(String a, String b) {
@ -102,11 +108,7 @@ class HTML extends AppSource {
[ [
GeneratedFormSwitch('versionExtractWholePage', GeneratedFormSwitch('versionExtractWholePage',
label: tr('versionExtractWholePage')) label: tr('versionExtractWholePage'))
], ]
[
GeneratedFormSwitch('supportFixedAPKURL',
defaultValue: true, label: tr('supportFixedAPKURL')),
],
]; ];
var commonFormItems = [ var commonFormItems = [
[GeneratedFormSwitch('filterByLinkText', label: tr('filterByLinkText'))], [GeneratedFormSwitch('filterByLinkText', label: tr('filterByLinkText'))],
@ -135,20 +137,72 @@ class HTML extends AppSource {
], ],
finalStepFormitems[0], finalStepFormitems[0],
...commonFormItems, ...commonFormItems,
...finalStepFormitems.sublist(1) ...finalStepFormitems.sublist(1),
[
GeneratedFormSubForm(
'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;
}
])
]
],
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')
]
]; ];
overrideVersionDetectionFormDefault('noVersionDetection',
disableStandard: false, disableRelDate: true);
} }
@override @override
Future<Map<String, String>?> getRequestHeaders( Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{}, Map<String, dynamic> additionalSettings,
bool forAPKDownload = false}) async { {bool forAPKDownload = false}) async {
return { if (additionalSettings.isNotEmpty) {
"User-Agent": if (additionalSettings['requestHeader']?.isNotEmpty != true) {
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36" additionalSettings['requestHeader'] = [];
}; }
additionalSettings['requestHeader'] = additionalSettings['requestHeader']
.where((l) => l['requestHeader'].isNotEmpty == true)
.toList();
Map<String, String> requestHeaders = {};
for (int i = 0; i < (additionalSettings['requestHeader'].length); i++) {
var temp =
(additionalSettings['requestHeader'][i]['requestHeader'] as String)
.split(':');
requestHeaders[temp[0].trim()] = temp.sublist(1).join(':').trim();
}
return requestHeaders;
}
return null;
} }
@override @override
@ -172,6 +226,8 @@ class HTML extends AppSource {
? element.text ? element.text
: (element.attributes['href'] ?? '').split('/').last)) : (element.attributes['href'] ?? '').split('/').last))
.where((element) => element.key.isNotEmpty) .where((element) => element.key.isNotEmpty)
.map((e) =>
MapEntry(ensureAbsoluteUrl(e.key, res.request!.url), e.value))
.toList(); .toList();
if (allLinks.isEmpty) { if (allLinks.isEmpty) {
allLinks = RegExp( allLinks = RegExp(
@ -187,18 +243,28 @@ class HTML extends AppSource {
if ((additionalSettings['customLinkFilterRegex'] as String?)?.isNotEmpty == if ((additionalSettings['customLinkFilterRegex'] as String?)?.isNotEmpty ==
true) { true) {
var reg = RegExp(additionalSettings['customLinkFilterRegex']); var reg = RegExp(additionalSettings['customLinkFilterRegex']);
links = allLinks links = allLinks.where((element) {
.where((element) => var link = element.key;
reg.hasMatch(filterLinkByText ? element.value : element.key)) try {
.toList(); link = Uri.decodeFull(element.key);
} catch (e) {
// Some links may not have valid encoding
}
return reg.hasMatch(filterLinkByText ? element.value : link);
}).toList();
} else { } else {
links = allLinks links = allLinks.where((element) {
.where((element) => var link = element.key;
Uri.parse(filterLinkByText ? element.value : element.key) try {
.path link = Uri.decodeFull(element.key);
.toLowerCase() } catch (e) {
.endsWith('.apk')) // Some links may not have valid encoding
.toList(); }
return Uri.parse(filterLinkByText ? element.value : link)
.path
.toLowerCase()
.endsWith('.apk');
}).toList();
} }
if (!skipSort) { if (!skipSort) {
links.sort((a, b) => additionalSettings['sortByLastLinkSegment'] == true links.sort((a, b) => additionalSettings['sortByLastLinkSegment'] == true
@ -227,7 +293,8 @@ class HTML extends AppSource {
.where((l) => l['customLinkFilterRegex'].isNotEmpty == true) .where((l) => l['customLinkFilterRegex'].isNotEmpty == true)
.toList(); .toList();
for (int i = 0; i < (additionalSettings['intermediateLink'].length); i++) { for (int i = 0; i < (additionalSettings['intermediateLink'].length); i++) {
var intLinks = await grabLinksCommon(await sourceRequest(currentUrl), var intLinks = await grabLinksCommon(
await sourceRequest(currentUrl, additionalSettings),
additionalSettings['intermediateLink'][i]); additionalSettings['intermediateLink'][i]);
if (intLinks.isEmpty) { if (intLinks.isEmpty) {
throw NoReleasesError(); throw NoReleasesError();
@ -235,31 +302,40 @@ class HTML extends AppSource {
currentUrl = intLinks.last.key; currentUrl = intLinks.last.key;
} }
} }
var uri = Uri.parse(currentUrl); var uri = Uri.parse(currentUrl);
Response res = await sourceRequest(currentUrl); List<MapEntry<String, String>> links = [];
var links = await grabLinksCommon(res, additionalSettings); String versionExtractionWholePageString = currentUrl;
if (additionalSettings['directAPKLink'] != true) {
if ((additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty == true) { Response res = await sourceRequest(currentUrl, additionalSettings);
var reg = RegExp(additionalSettings['apkFilterRegEx']); versionExtractionWholePageString =
links = links.where((element) => reg.hasMatch(element.key)).toList(); res.body.split('\r\n').join('\n').split('\n').join('\\n');
} links = await grabLinksCommon(res, additionalSettings);
if (links.isEmpty) { links = filterApks(links, additionalSettings['apkFilterRegEx'],
throw NoReleasesError(); additionalSettings['invertAPKFilter']);
if (links.isEmpty) {
throw NoReleasesError();
}
} else {
links = [MapEntry(currentUrl, currentUrl)];
} }
var rel = links.last.key; var rel = links.last.key;
String? version; var relDecoded = rel;
if (additionalSettings['supportFixedAPKURL'] != true) { try {
version = rel.hashCode.toString(); relDecoded = Uri.decodeFull(rel);
} catch (e) {
// Some links may not have valid encoding
} }
String? version;
version = extractVersion( version = extractVersion(
additionalSettings['versionExtractionRegEx'] as String?, additionalSettings['versionExtractionRegEx'] as String?,
additionalSettings['matchGroupToUse'] as String?, additionalSettings['matchGroupToUse'] as String?,
additionalSettings['versionExtractWholePage'] == true additionalSettings['versionExtractWholePage'] == true
? res.body.split('\r\n').join('\n').split('\n').join('\\n') ? versionExtractionWholePageString
: rel); : relDecoded);
rel = ensureAbsoluteUrl(rel, uri); version ??=
version ??= (await checkDownloadHash(rel)).toString(); additionalSettings['defaultPseudoVersioningMethod'] == 'APKLinkHash'
? rel.hashCode.toString()
: (await checkPartialDownloadHashDynamic(rel)).toString();
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(), return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
AppNames(uri.host, tr('app'))); AppNames(uri.host, tr('app')));
} }

View File

@ -6,26 +6,30 @@ import 'package:obtainium/providers/source_provider.dart';
class HuaweiAppGallery extends AppSource { class HuaweiAppGallery extends AppSource {
HuaweiAppGallery() { HuaweiAppGallery() {
name = 'Huawei AppGallery'; name = 'Huawei AppGallery';
host = 'appgallery.huawei.com'; hosts = ['appgallery.huawei.com'];
overrideVersionDetectionFormDefault('releaseDateAsVersion', versionDetectionDisallowed = true;
disableStandard: true); showReleaseDateAsVersionToggle = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/app/[^/]+'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}/app/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
getDlUrl(String standardUrl) => getDlUrl(String standardUrl) =>
'https://${host!.replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}'; 'https://${hosts[0].replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}';
requestAppdlRedirect(String dlUrl) async { requestAppdlRedirect(
Response res = await sourceRequest(dlUrl, followRedirects: false); String dlUrl, Map<String, dynamic> additionalSettings) async {
Response res =
await sourceRequest(dlUrl, additionalSettings, followRedirects: false);
if (res.statusCode == 200 || if (res.statusCode == 200 ||
res.statusCode == 302 || res.statusCode == 302 ||
res.statusCode == 304) { res.statusCode == 304) {
@ -52,7 +56,7 @@ class HuaweiAppGallery extends AppSource {
Future<String?> tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async { {Map<String, dynamic> additionalSettings = const {}}) async {
String dlUrl = getDlUrl(standardUrl); String dlUrl = getDlUrl(standardUrl);
Response res = await requestAppdlRedirect(dlUrl); Response res = await requestAppdlRedirect(dlUrl, additionalSettings);
return res.headers['location'] != null return res.headers['location'] != null
? appIdFromRedirectDlUrl(res.headers['location']!) ? appIdFromRedirectDlUrl(res.headers['location']!)
: null; : null;
@ -64,7 +68,7 @@ class HuaweiAppGallery extends AppSource {
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String dlUrl = getDlUrl(standardUrl); String dlUrl = getDlUrl(standardUrl);
Response res = await requestAppdlRedirect(dlUrl); Response res = await requestAppdlRedirect(dlUrl, additionalSettings);
if (res.headers['location'] == null) { if (res.headers['location'] == null) {
throw NoReleasesError(); throw NoReleasesError();
} }
@ -80,7 +84,7 @@ class HuaweiAppGallery extends AppSource {
} }
var relDate = relDateStrAdj == null var relDate = relDateStrAdj == null
? null ? null
: DateFormat('yy-MM-dd-HH-mm').parse(relDateStrAdj.join('')); : DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
if (relDateStr == null) { if (relDateStr == null) {
throw NoVersionError(); throw NoVersionError();
} }

View File

@ -6,7 +6,7 @@ class IzzyOnDroid extends AppSource {
late FDroid fd; late FDroid fd;
IzzyOnDroid() { IzzyOnDroid() {
host = 'izzysoft.de'; hosts = ['izzysoft.de'];
fd = FDroid(); fd = FDroid();
additionalSourceAppSpecificSettingFormItems = additionalSourceAppSpecificSettingFormItems =
fd.additionalSourceAppSpecificSettingFormItems; fd.additionalSourceAppSpecificSettingFormItems;
@ -15,17 +15,20 @@ class IzzyOnDroid extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExA = RegExp('^https?://android.$host/repo/apk/[^/]+'); RegExp standardUrlRegExA = RegExp(
RegExpMatch? match = standardUrlRegExA.firstMatch(url.toLowerCase()); '^https?://android.${getSourceRegex(hosts)}/repo/apk/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegExA.firstMatch(url);
if (match == null) { if (match == null) {
RegExp standardUrlRegExB = RegExp standardUrlRegExB = RegExp(
RegExp('^https?://apt.$host/fdroid/index/apk/[^/]+'); '^https?://apt.${getSourceRegex(hosts)}/fdroid/index/apk/[^/]+',
match = standardUrlRegExB.firstMatch(url.toLowerCase()); caseSensitive: false);
match = standardUrlRegExB.firstMatch(url);
} }
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -42,14 +45,11 @@ class IzzyOnDroid extends AppSource {
String? appId = await tryInferringAppId(standardUrl); String? appId = await tryInferringAppId(standardUrl);
return fd.getAPKUrlsFromFDroidPackagesAPIResponse( return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest( await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'), 'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId',
additionalSettings),
'https://android.izzysoft.de/frepo/$appId', 'https://android.izzysoft.de/frepo/$appId',
standardUrl, standardUrl,
name, name,
autoSelectHighestVersionCode: additionalSettings: additionalSettings);
additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode:
additionalSettings['trySelectingSuggestedVersionCode'] == true,
filterVersionsByRegEx: additionalSettings['filterVersionsByRegEx']);
} }
} }

View File

@ -6,8 +6,9 @@ import 'package:obtainium/providers/source_provider.dart';
class Jenkins extends AppSource { class Jenkins extends AppSource {
Jenkins() { Jenkins() {
overrideVersionDetectionFormDefault('releaseDateAsVersion', versionDetectionDisallowed = true;
disableStandard: true); neverAutoSelect = true;
showReleaseDateAsVersionToggle = true;
} }
String trimJobUrl(String url) { String trimJobUrl(String url) {
@ -16,7 +17,7 @@ class Jenkins extends AppSource {
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -29,8 +30,8 @@ class Jenkins extends AppSource {
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
standardUrl = trimJobUrl(standardUrl); standardUrl = trimJobUrl(standardUrl);
Response res = Response res = await sourceRequest(
await sourceRequest('$standardUrl/lastSuccessfulBuild/api/json'); '$standardUrl/lastSuccessfulBuild/api/json', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var json = jsonDecode(res.body); var json = jsonDecode(res.body);
var releaseDate = json['timestamp'] == null var releaseDate = json['timestamp'] == null

View File

@ -6,17 +6,19 @@ import 'package:obtainium/providers/source_provider.dart';
class Mullvad extends AppSource { class Mullvad extends AppSource {
Mullvad() { Mullvad() {
host = 'mullvad.net'; hosts = ['mullvad.net'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -28,7 +30,8 @@ class Mullvad extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = await sourceRequest('$standardUrl/en/download/android'); Response res = await sourceRequest(
'$standardUrl/en/download/android', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var versions = parse(res.body) var versions = parse(res.body)
.querySelectorAll('p') .querySelectorAll('p')

View File

@ -5,18 +5,20 @@ import 'package:obtainium/providers/source_provider.dart';
class NeutronCode extends AppSource { class NeutronCode extends AppSource {
NeutronCode() { NeutronCode() {
host = 'neutroncode.com'; hosts = ['neutroncode.com'];
showReleaseDateAsVersionToggle = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp standardUrlRegEx = RegExp(
RegExp('^https?://(www\\.)?$host/downloads/file/[^/]+'); '^https?://(www\\.)?${getSourceRegex(hosts)}/downloads/file/[^/]+',
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -79,7 +81,7 @@ class NeutronCode extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = await sourceRequest(standardUrl); Response res = await sourceRequest(standardUrl, additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var http = parse(res.body); var http = parse(res.body);
var name = http.querySelector('.pd-title')?.innerHtml; var name = http.querySelector('.pd-title')?.innerHtml;
@ -92,7 +94,7 @@ class NeutronCode extends AppSource {
if (version == null) { if (version == null) {
throw NoVersionError(); throw NoVersionError();
} }
String? apkUrl = 'https://$host/download/$filename'; String? apkUrl = 'https://${hosts[0]}/download/$filename';
var dateStringOriginal = var dateStringOriginal =
http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml; http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml;
var dateString = dateStringOriginal != null var dateString = dateStringOriginal != null

View File

@ -5,12 +5,12 @@ import 'package:obtainium/providers/source_provider.dart';
class Signal extends AppSource { class Signal extends AppSource {
Signal() { Signal() {
host = 'signal.org'; hosts = ['signal.org'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override
@ -18,8 +18,8 @@ class Signal extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = Response res = await sourceRequest(
await sourceRequest('https://updates.$host/android/latest.json'); 'https://updates.${hosts[0]}/android/latest.json', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var json = jsonDecode(res.body); var json = jsonDecode(res.body);
String? apkUrl = json['url']; String? apkUrl = json['url'];

View File

@ -5,24 +5,34 @@ import 'package:obtainium/providers/source_provider.dart';
class SourceForge extends AppSource { class SourceForge extends AppSource {
SourceForge() { SourceForge() {
host = 'sourceforge.net'; hosts = ['sourceforge.net'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp('^https?://(www\\.)?$host/p/[^/]+'); var sourceRegex = getSourceRegex(hosts);
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase()); RegExp standardUrlRegExC =
RegExp('^https?://(www\\.)?$sourceRegex/p/.+', caseSensitive: false);
RegExpMatch? match = standardUrlRegExC.firstMatch(url);
if (match != null) { if (match != null) {
url = url =
'https://${Uri.parse(url.substring(0, match.end)).host}/projects/${url.substring(Uri.parse(url.substring(0, match.end)).host.length + '/projects/'.length + 1)}'; 'https://${Uri.parse(match.group(0)!).host}/projects/${url.substring(Uri.parse(match.group(0)!).host.length + '/projects/'.length + 1)}';
} }
RegExp standardUrlRegExA = RegExp standardUrlRegExB = RegExp(
RegExp('^https?://(www\\.)?$host/projects/[^/]+'); '^https?://(www\\.)?$sourceRegex/projects/[^/]+',
match = standardUrlRegExA.firstMatch(url.toLowerCase()); 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);
match = standardUrlRegExA.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -30,37 +40,79 @@ class SourceForge extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = await sourceRequest('$standardUrl/rss?path=/'); var standardUri = Uri.parse(standardUrl);
if (standardUri.pathSegments.length == 2) {
standardUrl = '$standardUrl/files';
standardUri = Uri.parse(standardUrl);
}
Response res = await sourceRequest(
'${standardUri.origin}/${standardUri.pathSegments.sublist(0, 2).join('/')}/rss?path=/',
additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var parsedHtml = parse(res.body); var parsedHtml = parse(res.body);
var allDownloadLinks = var allDownloadLinks = parsedHtml
parsedHtml.querySelectorAll('guid').map((e) => e.innerHtml).toList(); .querySelectorAll('guid')
.map((e) => e.innerHtml)
.where((element) => element.startsWith(standardUrl))
.toList();
getVersion(String url) { getVersion(String url) {
try { try {
var tokens = url.split('/'); var segments = url
var fi = tokens.indexOf('files'); .substring(standardUrl.length)
return tokens[tokens[fi + 2] == 'download' ? fi - 1 : fi + 1]; .split('/')
.where((element) => element.isNotEmpty)
.toList()
.reversed
.toList()
.sublist(1)
.reversed
.toList();
segments = segments.length > 1
? segments.reversed.toList().sublist(1).reversed.toList()
: segments;
var version = segments.isNotEmpty ? segments.join('/') : null;
if (version != null) {
try {
var extractedVersion = extractVersion(
additionalSettings['versionExtractionRegEx'] as String?,
additionalSettings['matchGroupToUse'] as String?,
version);
if (extractedVersion != null) {
version = extractedVersion;
}
} catch (e) {
if (e is NoVersionError) {
version = null;
} else {
rethrow;
}
}
}
return version;
} catch (e) { } catch (e) {
return null; return null;
} }
} }
String? version = getVersion(allDownloadLinks[0]); var apkUrlListAllReleases = allDownloadLinks
.where((element) => element.toLowerCase().endsWith('.apk/download'))
.where((element) => getVersion(element) != null)
.toList();
if (apkUrlListAllReleases.isEmpty) {
throw NoReleasesError();
}
String? version = getVersion(apkUrlListAllReleases[0]);
if (version == null) { if (version == null) {
throw NoVersionError(); throw NoVersionError();
} }
var apkUrlListAllReleases = allDownloadLinks
.where((element) => element.toLowerCase().endsWith('.apk/download'))
.toList();
var apkUrlList = var apkUrlList =
apkUrlListAllReleases // This can be used skipped for fallback support later apkUrlListAllReleases // This can be used skipped for fallback support later
.where((element) => getVersion(element) == version) .where((element) => getVersion(element) == version)
.toList(); .toList();
return APKDetails( var segments = standardUrl.split('/');
version, return APKDetails(version, getApkUrlsFromUrls(apkUrlList),
getApkUrlsFromUrls(apkUrlList), AppNames(name, segments[segments.indexOf('files') - 1]));
AppNames(
name, standardUrl.substring(standardUrl.lastIndexOf('/') + 1)));
} else { } else {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }

View File

@ -8,7 +8,8 @@ import 'package:easy_localization/easy_localization.dart';
class SourceHut extends AppSource { class SourceHut extends AppSource {
SourceHut() { SourceHut() {
host = 'git.sr.ht'; hosts = ['git.sr.ht'];
showReleaseDateAsVersionToggle = true;
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
@ -20,12 +21,14 @@ class SourceHut extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@ -36,11 +39,21 @@ class SourceHut extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
if (standardUrl.endsWith('/refs')) {
standardUrl = standardUrl
.split('/')
.reversed
.toList()
.sublist(1)
.reversed
.join('/');
}
Uri standardUri = Uri.parse(standardUrl); Uri standardUri = Uri.parse(standardUrl);
String appName = standardUri.pathSegments.last; String appName = standardUri.pathSegments.last;
bool fallbackToOlderReleases = bool fallbackToOlderReleases =
additionalSettings['fallbackToOlderReleases'] == true; additionalSettings['fallbackToOlderReleases'] == true;
Response res = await sourceRequest('$standardUrl/refs/rss.xml'); Response res =
await sourceRequest('$standardUrl/refs/rss.xml', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var parsedHtml = parse(res.body); var parsedHtml = parse(res.body);
List<APKDetails> apkDetailsList = []; List<APKDetails> apkDetailsList = [];
@ -69,7 +82,7 @@ class SourceHut extends AppSource {
} catch (e) { } catch (e) {
// ignore // ignore
} }
var res2 = await sourceRequest(releasePage); var res2 = await sourceRequest(releasePage, additionalSettings);
List<MapEntry<String, String>> apkUrls = []; List<MapEntry<String, String>> apkUrls = [];
if (res2.statusCode == 200) { if (res2.statusCode == 200) {
apkUrls = getApkUrlsFromUrls(parse(res2.body) apkUrls = getApkUrlsFromUrls(parse(res2.body)

View File

@ -7,8 +7,8 @@ import 'package:obtainium/providers/source_provider.dart';
class SteamMobile extends AppSource { class SteamMobile extends AppSource {
SteamMobile() { SteamMobile() {
host = 'store.steampowered.com'; hosts = ['store.steampowered.com'];
name = tr('steam'); name = 'Steam';
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
GeneratedFormDropdown('app', apks.entries.toList(), GeneratedFormDropdown('app', apks.entries.toList(),
@ -21,7 +21,7 @@ class SteamMobile extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override
@ -29,7 +29,8 @@ class SteamMobile extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = await sourceRequest('https://$host/mobile'); Response res =
await sourceRequest('https://${hosts[0]}/mobile', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var apkNamePrefix = additionalSettings['app'] as String?; var apkNamePrefix = additionalSettings['app'] as String?;
if (apkNamePrefix == null) { if (apkNamePrefix == null) {

View File

@ -6,13 +6,13 @@ import 'package:obtainium/providers/source_provider.dart';
class TelegramApp extends AppSource { class TelegramApp extends AppSource {
TelegramApp() { TelegramApp() {
host = 'telegram.org'; hosts = ['telegram.org'];
name = 'Telegram ${tr('app')}'; name = 'Telegram ${tr('app')}';
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override
@ -20,7 +20,8 @@ class TelegramApp extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = await sourceRequest('https://t.me/s/TAndroidAPK'); Response res =
await sourceRequest('https://t.me/s/TAndroidAPK', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var http = parse(res.body); var http = parse(res.body);
var messages = var messages =

View File

@ -6,29 +6,34 @@ import 'package:obtainium/providers/source_provider.dart';
class Uptodown extends AppSource { class Uptodown extends AppSource {
Uptodown() { Uptodown() {
host = 'uptodown.com'; hosts = ['uptodown.com'];
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
showReleaseDateAsVersionToggle = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host'); RegExp standardUrlRegEx = RegExp(
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); '^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return '${url.substring(0, match.end)}/android/download'; return '${match.group(0)!}/android/download';
} }
@override @override
Future<String?> tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async { {Map<String, dynamic> additionalSettings = const {}}) async {
return (await getAppDetailsFromPage(standardUrl))['appId']; return (await getAppDetailsFromPage(
standardUrl, additionalSettings))['appId'];
} }
Future<Map<String, String?>> getAppDetailsFromPage(String standardUrl) async { Future<Map<String, String?>> getAppDetailsFromPage(
var res = await sourceRequest(standardUrl); String standardUrl, Map<String, dynamic> additionalSettings) async {
var res = await sourceRequest(standardUrl, additionalSettings);
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
@ -56,7 +61,8 @@ class Uptodown extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
var appDetails = await getAppDetailsFromPage(standardUrl); var appDetails =
await getAppDetailsFromPage(standardUrl, additionalSettings);
var version = appDetails['version']; var version = appDetails['version'];
var apkUrl = appDetails['apkUrl']; var apkUrl = appDetails['apkUrl'];
var appId = appDetails['appId']; var appId = appDetails['appId'];
@ -82,9 +88,9 @@ class Uptodown extends AppSource {
} }
@override @override
Future<String> apkUrlPrefetchModifier( Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
String apkUrl, String standardUrl) async { Map<String, dynamic> additionalSettings) async {
var res = await sourceRequest(apkUrl); var res = await sourceRequest(apkUrl, additionalSettings);
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }
@ -94,6 +100,6 @@ class Uptodown extends AppSource {
if (finalUrlKey == null) { if (finalUrlKey == null) {
throw NoAPKError(); throw NoAPKError();
} }
return 'https://dw.$host/dwn/$finalUrlKey'; return 'https://dw.${hosts[0]}/dwn/$finalUrlKey';
} }
} }

View File

@ -1,31 +1,33 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
class VLC extends AppSource { class VLC extends AppSource {
VLC() { VLC() {
host = 'videolan.org'; hosts = ['videolan.org'];
} }
get dwUrlBase => 'https://get.$host/vlc-android/'; get dwUrlBase => 'https://get.${hosts[0]}/vlc-android/';
@override @override
Future<Map<String, String>?> getRequestHeaders( Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{}, Map<String, dynamic> additionalSettings,
bool forAPKDownload = false}) => {bool forAPKDownload = false}) async {
HTML().getRequestHeaders( return {
additionalSettings: additionalSettings, "User-Agent":
forAPKDownload: forAPKDownload); "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"
};
}
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
Future<String?> getLatestVersion(String standardUrl) async { Future<String?> getLatestVersion(
Response res = await sourceRequest(dwUrlBase); String standardUrl, Map<String, dynamic> additionalSettings) async {
Response res = await sourceRequest(dwUrlBase, additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var dwLinks = parse(res.body) var dwLinks = parse(res.body)
.querySelectorAll('a') .querySelectorAll('a')
@ -77,9 +79,9 @@ class VLC extends AppSource {
} }
@override @override
Future<String> apkUrlPrefetchModifier( Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
String apkUrl, String standardUrl) async { Map<String, dynamic> additionalSettings) async {
Response res = await sourceRequest(apkUrl); Response res = await sourceRequest(apkUrl, additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
String? apkUrl = String? apkUrl =
parse(res.body).querySelector('#alt_link')?.attributes['href']; parse(res.body).querySelector('#alt_link')?.attributes['href'];

View File

@ -5,20 +5,20 @@ import 'package:obtainium/providers/source_provider.dart';
class WhatsApp extends AppSource { class WhatsApp extends AppSource {
WhatsApp() { WhatsApp() {
host = 'whatsapp.com'; hosts = ['whatsapp.com'];
overrideVersionDetectionFormDefault('noVersionDetection', versionDetectionDisallowed = true;
disableStandard: true, disableRelDate: true);
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override
Future<String> apkUrlPrefetchModifier( Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
String apkUrl, String standardUrl) async { Map<String, dynamic> additionalSettings) async {
Response res = await sourceRequest('$standardUrl/android'); Response res =
await sourceRequest('$standardUrl/android', additionalSettings);
if (res.statusCode == 200) { if (res.statusCode == 200) {
var targetLinks = parse(res.body) var targetLinks = parse(res.body)
.querySelectorAll('a') .querySelectorAll('a')
@ -42,8 +42,8 @@ class WhatsApp extends AppSource {
) async { ) async {
// This is a CDN link that is consistent per version // This is a CDN link that is consistent per version
// But it has query params that change constantly // But it has query params that change constantly
Uri apkUri = Uri apkUri = Uri.parse(await apkUrlPrefetchModifier(
Uri.parse(await apkUrlPrefetchModifier(standardUrl, standardUrl)); standardUrl, standardUrl, additionalSettings));
var unusableApkUrl = '${apkUri.origin}/${apkUri.path}'; var unusableApkUrl = '${apkUri.origin}/${apkUri.path}';
// So we use the param-less URL is a pseudo-version to add the app and check for updates // So we use the param-less URL is a pseudo-version to add the app and check for updates
// See #357 for why we can't scrape the version number directly // See #357 for why we can't scrape the version number directly

View File

@ -13,6 +13,7 @@ abstract class GeneratedFormItem {
late dynamic defaultValue; late dynamic defaultValue;
List<dynamic> additionalValidators; List<dynamic> additionalValidators;
dynamic ensureType(dynamic val); dynamic ensureType(dynamic val);
GeneratedFormItem clone();
GeneratedFormItem(this.key, GeneratedFormItem(this.key,
{this.label = 'Input', {this.label = 'Input',
@ -44,6 +45,20 @@ class GeneratedFormTextField extends GeneratedFormItem {
String ensureType(val) { String ensureType(val) {
return val.toString(); return val.toString();
} }
@override
GeneratedFormTextField clone() {
return GeneratedFormTextField(key,
label: label,
belowWidgets: belowWidgets,
defaultValue: defaultValue,
additionalValidators: List.from(additionalValidators),
required: required,
max: max,
hint: hint,
password: password,
textInputType: textInputType);
}
} }
class GeneratedFormDropdown extends GeneratedFormItem { class GeneratedFormDropdown extends GeneratedFormItem {
@ -64,14 +79,31 @@ class GeneratedFormDropdown extends GeneratedFormItem {
String ensureType(val) { String ensureType(val) {
return val.toString(); return val.toString();
} }
@override
GeneratedFormDropdown clone() {
return GeneratedFormDropdown(
key,
opts?.map((e) => MapEntry(e.key, e.value)).toList(),
label: label,
belowWidgets: belowWidgets,
defaultValue: defaultValue,
disabledOptKeys:
disabledOptKeys != null ? List.from(disabledOptKeys!) : null,
additionalValidators: List.from(additionalValidators),
);
}
} }
class GeneratedFormSwitch extends GeneratedFormItem { class GeneratedFormSwitch extends GeneratedFormItem {
bool disabled = false;
GeneratedFormSwitch( GeneratedFormSwitch(
super.key, { super.key, {
super.label, super.label,
super.belowWidgets, super.belowWidgets,
bool super.defaultValue = false, bool super.defaultValue = false,
bool disabled = false,
List<String? Function(bool value)> super.additionalValidators = const [], List<String? Function(bool value)> super.additionalValidators = const [],
}); });
@ -79,6 +111,16 @@ class GeneratedFormSwitch extends GeneratedFormItem {
bool ensureType(val) { bool ensureType(val) {
return val == true || val == 'true'; return val == true || val == 'true';
} }
@override
GeneratedFormSwitch clone() {
return GeneratedFormSwitch(key,
label: label,
belowWidgets: belowWidgets,
defaultValue: defaultValue,
disabled: false,
additionalValidators: List.from(additionalValidators));
}
} }
class GeneratedFormTagInput extends GeneratedFormItem { class GeneratedFormTagInput extends GeneratedFormItem {
@ -103,6 +145,20 @@ class GeneratedFormTagInput extends GeneratedFormItem {
Map<String, MapEntry<int, bool>> ensureType(val) { Map<String, MapEntry<int, bool>> ensureType(val) {
return val is Map<String, MapEntry<int, bool>> ? val : {}; return val is Map<String, MapEntry<int, bool>> ? val : {};
} }
@override
GeneratedFormTagInput clone() {
return GeneratedFormTagInput(key,
label: label,
belowWidgets: belowWidgets,
defaultValue: defaultValue,
additionalValidators: List.from(additionalValidators),
deleteConfirmationMessage: deleteConfirmationMessage,
singleSelect: singleSelect,
alignment: alignment,
emptyMessage: emptyMessage,
showLabelWhenNotEmpty: showLabelWhenNotEmpty);
}
} }
typedef OnValueChanges = void Function( typedef OnValueChanges = void Function(
@ -119,16 +175,35 @@ class GeneratedForm extends StatefulWidget {
State<GeneratedForm> createState() => _GeneratedFormState(); State<GeneratedForm> createState() => _GeneratedFormState();
} }
List<List<GeneratedFormItem>> cloneFormItems(
List<List<GeneratedFormItem>> items) {
List<List<GeneratedFormItem>> clonedItems = [];
for (var row in items) {
List<GeneratedFormItem> clonedRow = [];
for (var it in row) {
clonedRow.add(it.clone());
}
clonedItems.add(clonedRow);
}
return clonedItems;
}
class GeneratedFormSubForm extends GeneratedFormItem { class GeneratedFormSubForm extends GeneratedFormItem {
final List<List<GeneratedFormItem>> items; final List<List<GeneratedFormItem>> items;
GeneratedFormSubForm(super.key, this.items, GeneratedFormSubForm(super.key, this.items,
{super.label, super.belowWidgets, super.defaultValue}); {super.label, super.belowWidgets, super.defaultValue = const []});
@override @override
ensureType(val) { ensureType(val) {
return val; // Not easy to validate List<Map<String, dynamic>> return val; // Not easy to validate List<Map<String, dynamic>>
} }
@override
GeneratedFormSubForm clone() {
return GeneratedFormSubForm(key, cloneFormItems(items),
label: label, belowWidgets: belowWidgets, defaultValue: defaultValue);
}
} }
// Generates a color in the HSLuv (Pastel) color space // Generates a color in the HSLuv (Pastel) color space
@ -170,8 +245,8 @@ class _GeneratedFormState extends State<GeneratedForm> {
void someValueChanged({bool isBuilding = false, bool forceInvalid = false}) { void someValueChanged({bool isBuilding = false, bool forceInvalid = false}) {
Map<String, dynamic> returnValues = values; Map<String, dynamic> returnValues = values;
var valid = true; var valid = true;
for (int r = 0; r < widget.items.length; r++) { for (int r = 0; r < formInputs.length; r++) {
for (int i = 0; i < widget.items[r].length; i++) { for (int i = 0; i < formInputs[r].length; i++) {
if (formInputs[r][i] is TextFormField) { if (formInputs[r][i] is TextFormField) {
valid = valid && validateTextField(formInputs[r][i] as TextFormField); valid = valid && validateTextField(formInputs[r][i] as TextFormField);
} }
@ -297,15 +372,51 @@ class _GeneratedFormState extends State<GeneratedForm> {
), ),
Switch( Switch(
value: values[fieldKey], value: values[fieldKey],
onChanged: (value) { onChanged:
setState(() { (widget.items[r][e] as GeneratedFormSwitch).disabled
values[fieldKey] = value; ? null
someValueChanged(); : (value) {
}); setState(() {
}) values[fieldKey] = value;
someValueChanged();
});
})
], ],
); );
} else if (widget.items[r][e] is GeneratedFormTagInput) { } else if (widget.items[r][e] is GeneratedFormTagInput) {
onAddPressed() {
showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: widget.items[r][e].label,
items: [
[GeneratedFormTextField('label', label: tr('label'))]
]);
}).then((value) {
String? label = value?['label'];
if (label != null) {
setState(() {
var temp =
values[fieldKey] as Map<String, MapEntry<int, bool>>?;
temp ??= {};
if (temp[label] == null) {
var singleSelect =
(widget.items[r][e] as GeneratedFormTagInput)
.singleSelect;
var someSelected = temp.entries
.where((element) => element.value.value)
.isNotEmpty;
temp[label] = MapEntry(generateRandomLightColor().value,
!(someSelected && singleSelect));
values[fieldKey] = temp;
someValueChanged();
}
});
}
});
}
formInputs[r][e] = formInputs[r][e] =
Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
if ((values[fieldKey] as Map<String, MapEntry<int, bool>>?) if ((values[fieldKey] as Map<String, MapEntry<int, bool>>?)
@ -331,14 +442,14 @@ class _GeneratedFormState extends State<GeneratedForm> {
(widget.items[r][e] as GeneratedFormTagInput).alignment, (widget.items[r][e] as GeneratedFormTagInput).alignment,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
(values[fieldKey] as Map<String, MapEntry<int, bool>>?) // (values[fieldKey] as Map<String, MapEntry<int, bool>>?)
?.isEmpty == // ?.isEmpty ==
true // true
? Text( // ? Text(
(widget.items[r][e] as GeneratedFormTagInput) // (widget.items[r][e] as GeneratedFormTagInput)
.emptyMessage, // .emptyMessage,
) // )
: const SizedBox.shrink(), // : const SizedBox.shrink(),
...(values[fieldKey] as Map<String, MapEntry<int, bool>>?) ...(values[fieldKey] as Map<String, MapEntry<int, bool>>?)
?.entries ?.entries
.map((e2) { .map((e2) {
@ -462,62 +573,37 @@ class _GeneratedFormState extends State<GeneratedForm> {
tooltip: tr('remove'), tooltip: tr('remove'),
)) ))
: const SizedBox.shrink(), : const SizedBox.shrink(),
Padding( (values[fieldKey] as Map<String, MapEntry<int, bool>>?)
padding: const EdgeInsets.symmetric(horizontal: 4), ?.isEmpty ==
child: IconButton( true
onPressed: () { ? Padding(
showDialog<Map<String, dynamic>?>( padding: const EdgeInsets.symmetric(horizontal: 4),
context: context, child: TextButton.icon(
builder: (BuildContext ctx) { onPressed: onAddPressed,
return GeneratedFormModal( icon: const Icon(Icons.add),
title: widget.items[r][e].label, label: Text(
items: [ (widget.items[r][e] as GeneratedFormTagInput)
[ .label),
GeneratedFormTextField('label', ))
label: tr('label')) : Padding(
] padding: const EdgeInsets.symmetric(horizontal: 4),
]); child: IconButton(
}).then((value) { onPressed: onAddPressed,
String? label = value?['label']; icon: const Icon(Icons.add),
if (label != null) { visualDensity: VisualDensity.compact,
setState(() { tooltip: tr('add'),
var temp = values[fieldKey] )),
as Map<String, MapEntry<int, bool>>?;
temp ??= {};
if (temp[label] == null) {
var singleSelect = (widget.items[r][e]
as GeneratedFormTagInput)
.singleSelect;
var someSelected = temp.entries
.where((element) => element.value.value)
.isNotEmpty;
temp[label] = MapEntry(
generateRandomLightColor().value,
!(someSelected && singleSelect));
values[fieldKey] = temp;
someValueChanged();
}
});
}
});
},
icon: const Icon(Icons.add),
visualDensity: VisualDensity.compact,
tooltip: tr('add'),
)),
], ],
) )
]); ]);
} else if (widget.items[r][e] is GeneratedFormSubForm) { } else if (widget.items[r][e] is GeneratedFormSubForm) {
List<Widget> subformColumn = []; List<Widget> subformColumn = [];
var compact = (widget.items[r][e] as GeneratedFormSubForm)
.items
.length ==
1 &&
(widget.items[r][e] as GeneratedFormSubForm).items[0].length == 1;
for (int i = 0; i < values[fieldKey].length; i++) { for (int i = 0; i < values[fieldKey].length; i++) {
var items = (widget.items[r][e] as GeneratedFormSubForm)
.items
.map((x) => x.map((y) {
y.defaultValue = values[fieldKey]?[i]?[y.key];
return y;
}).toList())
.toList();
var internalFormKey = ValueKey(generateRandomNumber( var internalFormKey = ValueKey(generateRandomNumber(
values[fieldKey].length, values[fieldKey].length,
seed2: i, seed2: i,
@ -525,18 +611,28 @@ class _GeneratedFormState extends State<GeneratedForm> {
subformColumn.add(Column( subformColumn.add(Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Divider(), if (!compact)
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
Text( if (!compact)
'${(widget.items[r][e] as GeneratedFormSubForm).label} (${i + 1})', Text(
style: const TextStyle(fontWeight: FontWeight.bold), '${(widget.items[r][e] as GeneratedFormSubForm).label} (${i + 1})',
), style: const TextStyle(fontWeight: FontWeight.bold),
),
GeneratedForm( GeneratedForm(
key: internalFormKey, key: internalFormKey,
items: items, items: cloneFormItems(
(widget.items[r][e] as GeneratedFormSubForm).items)
.map((x) => x.map((y) {
y.defaultValue = values[fieldKey]?[i]?[y.key];
y.key = '${y.key.toString()},$internalFormKey';
return y;
}).toList())
.toList(),
onValueChanges: (values, valid, isBuilding) { onValueChanges: (values, valid, isBuilding) {
values = values.map(
(key, value) => MapEntry(key.split(',')[0], value));
if (valid) { if (valid) {
this.values[fieldKey]?[i] = values; this.values[fieldKey]?[i] = values;
} }
@ -567,13 +663,12 @@ class _GeneratedFormState extends State<GeneratedForm> {
Icons.delete_outline_rounded, Icons.delete_outline_rounded,
)) ))
], ],
), )
], ],
)); ));
} }
subformColumn.add(Padding( subformColumn.add(Padding(
padding: EdgeInsets.only( padding: const EdgeInsets.only(bottom: 0, top: 8),
bottom: values[fieldKey].length > 0 ? 24 : 0, top: 8),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@ -591,9 +686,6 @@ class _GeneratedFormState extends State<GeneratedForm> {
], ],
), ),
)); ));
if (values[fieldKey].length > 0) {
subformColumn.add(const Divider());
}
formInputs[r][e] = Column(children: subformColumn); formInputs[r][e] = Column(children: subformColumn);
} }
} }

View File

@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:obtainium/pages/home.dart'; import 'package:obtainium/pages/home.dart';
import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/logs_provider.dart';
import 'package:obtainium/providers/native_provider.dart';
import 'package:obtainium/providers/notifications_provider.dart'; import 'package:obtainium/providers/notifications_provider.dart';
import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
@ -19,12 +20,6 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports // ignore: implementation_imports
import 'package:easy_localization/src/localization.dart'; import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.15.4';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
const int bgUpdateCheckAlarmId = 666;
List<MapEntry<Locale, String>> supportedLocales = const [ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('en'), 'English'), MapEntry(Locale('en'), 'English'),
MapEntry(Locale('zh'), '简体中文'), MapEntry(Locale('zh'), '简体中文'),
@ -44,6 +39,7 @@ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('nl'), 'Nederlands'), MapEntry(Locale('nl'), 'Nederlands'),
MapEntry(Locale('vi'), 'Tiếng Việt'), MapEntry(Locale('vi'), 'Tiếng Việt'),
MapEntry(Locale('tr'), 'Türkçe'), MapEntry(Locale('tr'), 'Türkçe'),
MapEntry(Locale('uk'), 'Українська'),
]; ];
const fallbackLocale = Locale('en'); const fallbackLocale = Locale('en');
const localeDir = 'assets/translations'; const localeDir = 'assets/translations';
@ -123,8 +119,6 @@ void main() async {
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask); BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
} }
var defaultThemeColour = Colors.deepPurple;
class Obtainium extends StatefulWidget { class Obtainium extends StatefulWidget {
const Obtainium({super.key}); const Obtainium({super.key});
@ -146,6 +140,7 @@ class _ObtainiumState extends State<Obtainium> {
BackgroundFetchConfig( BackgroundFetchConfig(
minimumFetchInterval: 15, minimumFetchInterval: 15,
stopOnTerminate: false, stopOnTerminate: false,
startOnBoot: true,
enableHeadless: true, enableHeadless: true,
requiresBatteryNotLow: false, requiresBatteryNotLow: false,
requiresCharging: false, requiresCharging: false,
@ -176,20 +171,30 @@ class _ObtainiumState extends State<Obtainium> {
// If this is the first run, ask for notification permissions and add Obtainium to the Apps list // If this is the first run, ask for notification permissions and add Obtainium to the Apps list
Permission.notification.request(); Permission.notification.request();
if (!fdroid) { if (!fdroid) {
appsProvider.saveApps([ getInstalledInfo(obtainiumId).then((value) {
App( if (value?.versionName != null) {
obtainiumId, appsProvider.saveApps([
'https://github.com/ImranR98/Obtainium', App(
'ImranR98', obtainiumId,
'Obtainium', obtainiumUrl,
currentReleaseTag, 'ImranR98',
currentReleaseTag, 'Obtainium',
[], value!.versionName,
0, value.versionName!,
{'includePrereleases': true}, [],
null, 0,
false) {
], onlyIfExists: false); 'versionDetection': true,
'apkFilterRegEx': 'fdroid',
'invertAPKFilter': true
},
null,
false)
], onlyIfExists: false);
}
}).catchError((err) {
print(err);
});
} }
} }
if (!supportedLocales if (!supportedLocales
@ -207,15 +212,13 @@ class _ObtainiumState extends State<Obtainium> {
// Decide on a colour/brightness scheme based on OS and user settings // Decide on a colour/brightness scheme based on OS and user settings
ColorScheme lightColorScheme; ColorScheme lightColorScheme;
ColorScheme darkColorScheme; ColorScheme darkColorScheme;
if (lightDynamic != null && if (lightDynamic != null && darkDynamic != null && settingsProvider.useMaterialYou) {
darkDynamic != null &&
settingsProvider.colour == ColourSettings.materialYou) {
lightColorScheme = lightDynamic.harmonized(); lightColorScheme = lightDynamic.harmonized();
darkColorScheme = darkDynamic.harmonized(); darkColorScheme = darkDynamic.harmonized();
} else { } else {
lightColorScheme = ColorScheme.fromSeed(seedColor: defaultThemeColour); lightColorScheme = ColorScheme.fromSeed(seedColor: settingsProvider.themeColor);
darkColorScheme = ColorScheme.fromSeed( darkColorScheme = ColorScheme.fromSeed(
seedColor: defaultThemeColour, brightness: Brightness.dark); seedColor: settingsProvider.themeColor, brightness: Brightness.dark);
} }
// set the background and surface colors to pure black in the amoled theme // set the background and surface colors to pure black in the amoled theme
@ -225,6 +228,8 @@ class _ObtainiumState extends State<Obtainium> {
.harmonized(); .harmonized();
} }
if (settingsProvider.useSystemFont) NativeFeatures.loadSystemFont();
return MaterialApp( return MaterialApp(
title: 'Obtainium', title: 'Obtainium',
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
@ -236,17 +241,15 @@ class _ObtainiumState extends State<Obtainium> {
colorScheme: settingsProvider.theme == ThemeSettings.dark colorScheme: settingsProvider.theme == ThemeSettings.dark
? darkColorScheme ? darkColorScheme
: lightColorScheme, : lightColorScheme,
fontFamily: settingsProvider.useSystemFont fontFamily:
? 'SystemFont' settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
: 'Metropolis'),
darkTheme: ThemeData( darkTheme: ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.light colorScheme: settingsProvider.theme == ThemeSettings.light
? lightColorScheme ? lightColorScheme
: darkColorScheme, : darkColorScheme,
fontFamily: settingsProvider.useSystemFont fontFamily:
? 'SystemFont' settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
: 'Metropolis'),
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{ home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
}, child: const HomePage())); }, child: const HomePage()));

View File

@ -18,7 +18,7 @@ class GitHubStars implements MassAppUrlSource {
Response res = await get( Response res = await get(
Uri.parse( Uri.parse(
'https://api.github.com/users/$username/starred?per_page=100&page=$page'), 'https://api.github.com/users/$username/starred?per_page=100&page=$page'),
headers: await GitHub().getRequestHeaders()); headers: await GitHub().getRequestHeaders({}));
if (res.statusCode == 200) { if (res.statusCode == 200) {
Map<String, List<String>> urlsWithDescriptions = {}; Map<String, List<String>> urlsWithDescriptions = {};
for (var e in (jsonDecode(res.body) as List<dynamic>)) { for (var e in (jsonDecode(res.body) as List<dynamic>)) {

View File

@ -1,7 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart'; import 'package:obtainium/components/generated_form_modal.dart';
@ -59,25 +58,15 @@ class AddAppPageState extends State<AddAppPage> {
if (updateUrlInput) { if (updateUrlInput) {
urlInputKey++; urlInputKey++;
} }
var prevHost = pickedSource?.host; var prevHost = pickedSource?.hosts.isNotEmpty == true
try { ? pickedSource?.hosts[0]
var naturalSource = : null;
valid ? sourceProvider.getSource(userInput) : null;
if (naturalSource != null &&
naturalSource.runtimeType.toString() !=
HTML().runtimeType.toString()) {
// If input has changed to match a regular source, reset the override
pickedSourceOverride = null;
}
} catch (e) {
// ignore
}
var source = valid var source = valid
? sourceProvider.getSource(userInput, ? sourceProvider.getSource(userInput,
overrideSource: pickedSourceOverride) overrideSource: pickedSourceOverride)
: null; : null;
if (pickedSource.runtimeType != source.runtimeType || if (pickedSource.runtimeType != source.runtimeType ||
(prevHost != null && prevHost != source?.host)) { (prevHost != null && prevHost != source?.hosts[0])) {
pickedSource = source; pickedSource = source;
additionalSettings = source != null additionalSettings = source != null
? getDefaultValuesFromFormItems( ? getDefaultValuesFromFormItems(
@ -133,8 +122,7 @@ class AddAppPageState extends State<AddAppPage> {
getReleaseDateAsVersionConfirmationIfNeeded( getReleaseDateAsVersionConfirmationIfNeeded(
bool userPickedTrackOnly) async { bool userPickedTrackOnly) async {
return (!(additionalSettings['versionDetection'] == return (!(additionalSettings['releaseDateAsVersion'] == true &&
'releaseDateAsVersion' &&
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
await showDialog( await showDialog(
context: context, context: context,
@ -162,12 +150,13 @@ class AddAppPageState extends State<AddAppPage> {
app = await sourceProvider.getApp( app = await sourceProvider.getApp(
pickedSource!, userInput.trim(), additionalSettings, pickedSource!, userInput.trim(), additionalSettings,
trackOnlyOverride: trackOnly, trackOnlyOverride: trackOnly,
overrideSource: pickedSourceOverride, sourceIsOverriden: pickedSourceOverride != null,
inferAppIdIfOptional: inferAppIdIfOptional); inferAppIdIfOptional: inferAppIdIfOptional);
// Only download the APK here if you need to for the package ID // Only download the APK here if you need to for the package ID
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) { if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
var apkUrl = await appsProvider.confirmApkUrl(app, context); var apkUrl =
await appsProvider.confirmAppFileUrl(app, context, false);
if (apkUrl == null) { if (apkUrl == null) {
throw ObtainiumError(tr('cancelled')); throw ObtainiumError(tr('cancelled'));
} }
@ -190,8 +179,7 @@ class AddAppPageState extends State<AddAppPage> {
throw ObtainiumError(tr('appAlreadyAdded')); throw ObtainiumError(tr('appAlreadyAdded'));
} }
if (app.additionalSettings['trackOnly'] == true || if (app.additionalSettings['trackOnly'] == true ||
app.additionalSettings['versionDetection'] != app.additionalSettings['versionDetection'] != true) {
'standardVersionDetection') {
app.installedVersion = app.latestVersion; app.installedVersion = app.latestVersion;
} }
app.categories = pickedCategories; app.categories = pickedCategories;
@ -361,8 +349,9 @@ class AddAppPageState extends State<AddAppPage> {
[ [
GeneratedFormDropdown( GeneratedFormDropdown(
'overrideSource', 'overrideSource',
defaultValue: HTML().runtimeType.toString(), defaultValue: '',
[ [
MapEntry('', tr('none')),
...sourceProvider.sources.map( ...sourceProvider.sources.map(
(s) => MapEntry(s.runtimeType.toString(), s.name)) (s) => MapEntry(s.runtimeType.toString(), s.name))
], ],
@ -496,36 +485,74 @@ class AddAppPageState extends State<AddAppPage> {
], ],
); );
Widget getSourcesListWidget() => Column( Widget getSourcesListWidget() => Padding(
crossAxisAlignment: CrossAxisAlignment.center, padding: const EdgeInsets.all(16),
mainAxisAlignment: MainAxisAlignment.center, child: Row(
children: [ children: [
Text( GestureDetector(
tr('supportedSources'), onTap: () {
style: const TextStyle(fontWeight: FontWeight.bold), showDialog(
), context: context,
const SizedBox( builder: (context) {
height: 16, return GeneratedFormModal(
), singleNullReturnButton: tr('ok'),
...sourceProvider.sources.map((e) => GestureDetector( title: tr('supportedSources'),
onTap: e.host != null items: const [],
? () { additionalWidgets: [
launchUrlString('https://${e.host}', ...sourceProvider.sources.map(
mode: LaunchMode.externalApplication); (e) => Padding(
} padding:
: null, const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: e.hosts.isNotEmpty
? () {
launchUrlString(
'https://${e.hosts[0]}',
mode: LaunchMode
.externalApplication);
}
: null,
child: Text(
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}',
style: TextStyle(
decoration: e.hosts.isNotEmpty
? TextDecoration.underline
: TextDecoration.none),
))),
),
const SizedBox(
height: 16,
),
Text(
'${tr('note')}:',
style:
const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 4,
),
Text(tr('selfHostedNote',
args: [tr('overrideSource')])),
],
);
},
);
},
child: Text( child: Text(
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}', tr('supportedSources'),
style: TextStyle( style: const TextStyle(
decoration: e.host != null fontWeight: FontWeight.bold,
? TextDecoration.underline decoration: TextDecoration.underline,
: TextDecoration.none,
fontStyle: FontStyle.italic), fontStyle: FontStyle.italic),
))) ))
]); ],
),
);
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
bottomNavigationBar:
pickedSource == null ? getSourcesListWidget() : null,
body: CustomScrollView(shrinkWrap: true, slivers: <Widget>[ body: CustomScrollView(shrinkWrap: true, slivers: <Widget>[
CustomAppBar(title: tr('addApp')), CustomAppBar(title: tr('addApp')),
SliverToBoxAdapter( SliverToBoxAdapter(
@ -539,11 +566,7 @@ class AddAppPageState extends State<AddAppPage> {
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
if (pickedSourceOverride != null || if (pickedSource != null) getHTMLSourceOverrideDropdown(),
(pickedSource != null &&
pickedSource.runtimeType.toString() ==
HTML().runtimeType.toString()))
getHTMLSourceOverrideDropdown(),
if (shouldShowSearchBar()) getSearchBarRow(), if (shouldShowSearchBar()) getSearchBarRow(),
if (pickedSource != null) if (pickedSource != null)
FutureBuilder( FutureBuilder(
@ -557,18 +580,7 @@ class AddAppPageState extends State<AddAppPage> {
: const SizedBox(); : const SizedBox();
}, },
future: pickedSource?.getSourceNote()), future: pickedSource?.getSourceNote()),
SizedBox(
height: pickedSource != null ? 16 : 96,
),
if (pickedSource != null) getAdditionalOptsCol(), if (pickedSource != null) getAdditionalOptsCol(),
if (pickedSource == null)
const Divider(
height: 48,
),
if (pickedSource == null) getSourcesListWidget(),
SizedBox(
height: pickedSource != null ? 8 : 2,
),
])), ])),
) )
])); ]));

View File

@ -1,10 +1,10 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart'; import 'package:obtainium/components/generated_form_modal.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/main.dart'; import 'package:obtainium/main.dart';
import 'package:obtainium/pages/apps.dart';
import 'package:obtainium/pages/settings.dart'; import 'package:obtainium/pages/settings.dart';
import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/settings_provider.dart';
@ -24,16 +24,35 @@ class AppPage extends StatefulWidget {
class _AppPageState extends State<AppPage> { class _AppPageState extends State<AppPage> {
AppInMemory? prevApp; AppInMemory? prevApp;
bool updating = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var appsProvider = context.watch<AppsProvider>(); var appsProvider = context.watch<AppsProvider>();
var settingsProvider = context.watch<SettingsProvider>(); var settingsProvider = context.watch<SettingsProvider>();
getUpdate(String id) { getUpdate(String id, {bool resetVersion = false}) async {
appsProvider.checkUpdate(id).catchError((e) { try {
showError(e, context); setState(() {
return null; updating = true;
}); });
await appsProvider.checkUpdate(id);
if (resetVersion) {
appsProvider.apps[id]?.app.additionalSettings['versionDetection'] =
true;
if (appsProvider.apps[id]?.app.installedVersion != null) {
appsProvider.apps[id]?.app.installedVersion =
appsProvider.apps[id]?.app.latestVersion;
}
appsProvider.saveApps([appsProvider.apps[id]!.app]);
}
} catch (err) {
// ignore: use_build_context_synchronously
showError(err, context);
} finally {
setState(() {
updating = false;
});
}
} }
bool areDownloadsRunning = appsProvider.areDownloadsRunning(); bool areDownloadsRunning = appsProvider.areDownloadsRunning();
@ -54,122 +73,154 @@ class _AppPageState extends State<AppPage> {
var trackOnly = app?.app.additionalSettings['trackOnly'] == true; var trackOnly = app?.app.additionalSettings['trackOnly'] == true;
bool isVersionDetectionStandard = bool isVersionDetectionStandard =
app?.app.additionalSettings['versionDetection'] == app?.app.additionalSettings['versionDetection'] == true;
'standardVersionDetection';
bool installedVersionIsEstimate = trackOnly || bool installedVersionIsEstimate = trackOnly ||
(app?.app.installedVersion != null && (app?.app.installedVersion != null &&
app?.app.additionalSettings['versionDetection'] == app?.app.additionalSettings['versionDetection'] != true);
'noVersionDetection');
getInfoColumn() => Column( getInfoColumn() {
mainAxisAlignment: MainAxisAlignment.center, String versionLines = '';
crossAxisAlignment: CrossAxisAlignment.stretch, bool installed = app?.app.installedVersion != null;
children: [ bool upToDate = app?.app.installedVersion == app?.app.latestVersion;
GestureDetector( if (installed) {
onTap: () { versionLines = '${app?.app.installedVersion} ${tr('installed')}';
if (app?.app.url != null) { if (upToDate) {
launchUrlString(app?.app.url ?? '', versionLines += '/${tr('latest')}';
mode: LaunchMode.externalApplication); }
} } else {
}, versionLines = tr('notInstalled');
onLongPress: () { }
Clipboard.setData(ClipboardData(text: app?.app.url ?? '')); if (!upToDate) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar( versionLines += '\n${app?.app.latestVersion} ${tr('latest')}';
content: Text(tr('copiedToClipboard')), }
)); String infoLines = tr('lastUpdateCheckX', args: [
}, app?.app.lastUpdateCheck == null
child: Text( ? tr('never')
app?.app.url ?? '', : '${app?.app.lastUpdateCheck?.toLocal()}'
textAlign: TextAlign.center, ]);
style: const TextStyle( if (trackOnly) {
decoration: TextDecoration.underline, infoLines = '${tr('xIsTrackOnly', args: [tr('app')])}\n$infoLines';
fontStyle: FontStyle.italic, }
fontSize: 12), if (installedVersionIsEstimate) {
)), infoLines = '${tr('pseudoVersionInUse')}\n$infoLines';
const SizedBox( }
height: 32, if ((app?.app.apkUrls.length ?? 0) > 0) {
), infoLines =
Column( '$infoLines\n${app?.app.apkUrls.length == 1 ? app?.app.apkUrls[0].key : plural('apk', app?.app.apkUrls.length ?? 0)}';
}
var changeLogFn = app != null ? getChangeLogFn(context, app.app) : null;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 24),
child: Column(
children: [ children: [
Text( const SizedBox(
'${tr('latestVersionX', args: [ height: 8,
app?.app.latestVersion ?? tr('unknown') ),
])}\n${tr('installedVersionX', args: [ Text(versionLines,
app?.app.installedVersion ?? tr('none') textAlign: TextAlign.start,
])}${installedVersionIsEstimate ? '\n${tr('estimateInBrackets')}' : ''}', style: Theme.of(context)
textAlign: TextAlign.end, .textTheme
style: Theme.of(context).textTheme.bodyLarge!, .bodyLarge!
.copyWith(fontWeight: FontWeight.bold)),
changeLogFn != null || app?.app.releaseDate != null
? GestureDetector(
onTap: changeLogFn,
child: Text(
app?.app.releaseDate == null
? tr('changes')
: app!.app.releaseDate.toString(),
textAlign: TextAlign.center,
style:
Theme.of(context).textTheme.labelSmall!.copyWith(
decoration: changeLogFn != null
? TextDecoration.underline
: null,
fontStyle: changeLogFn != null
? FontStyle.italic
: null,
),
),
)
: const SizedBox.shrink(),
const SizedBox(
height: 8,
), ),
], ],
), ),
if (app?.app.installedVersion != null && ),
!isVersionDetectionStandard) Text(
Column( infoLines,
children: [ textAlign: TextAlign.center,
const SizedBox( style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
height: 16, ),
), if (app?.app.apkUrls.isNotEmpty == true ||
Text( app?.app.otherAssetUrls.isNotEmpty == true)
'${trackOnly ? '${tr('xIsTrackOnly', args: [ GestureDetector(
tr('app') onTap: app?.app == null || updating
])}\n' : ''}${tr('noVersionDetection')}', ? null
style: Theme.of(context).textTheme.labelSmall, : () async {
textAlign: TextAlign.center, try {
) await appsProvider
], .downloadAppAssets([app!.app.id], context);
), } catch (e) {
const SizedBox( showError(e, context);
height: 32, }
),
Text(
tr('lastUpdateCheckX', args: [
app?.app.lastUpdateCheck == null
? tr('never')
: '\n${app?.app.lastUpdateCheck?.toLocal()}'
]),
textAlign: TextAlign.center,
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
),
const SizedBox(
height: 48,
),
CategoryEditorSelector(
alignment: WrapAlignment.center,
preselected: app?.app.categories != null
? app!.app.categories.toSet()
: {},
onSelected: (categories) {
if (app != null) {
app.app.categories = categories;
appsProvider.saveApps([app.app]);
}
}),
if (app?.app.additionalSettings['about'] is String &&
app?.app.additionalSettings['about'].isNotEmpty)
Column(
children: [
const SizedBox(
height: 48,
),
GestureDetector(
onLongPress: () {
Clipboard.setData(ClipboardData(
text: app?.app.additionalSettings['about'] ?? ''));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(tr('copiedToClipboard')),
));
}, },
child: Text( child: Text(
app?.app.additionalSettings['about'], tr('downloadX', args: [tr('releaseAsset').toLowerCase()]),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle(fontStyle: FontStyle.italic), style: Theme.of(context).textTheme.labelSmall!.copyWith(
decoration:
changeLogFn != null ? TextDecoration.underline : null,
fontStyle: changeLogFn != null ? FontStyle.italic : null,
), ),
)
],
), ),
], ),
); const SizedBox(
height: 48,
),
CategoryEditorSelector(
alignment: WrapAlignment.center,
preselected: app?.app.categories != null
? app!.app.categories.toSet()
: {},
onSelected: (categories) {
if (app != null) {
app.app.categories = categories;
appsProvider.saveApps([app.app]);
}
}),
if (app?.app.additionalSettings['about'] is String &&
app?.app.additionalSettings['about'].isNotEmpty)
Column(
children: [
const SizedBox(
height: 48,
),
GestureDetector(
onLongPress: () {
Clipboard.setData(ClipboardData(
text: app?.app.additionalSettings['about'] ?? ''));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(tr('copiedToClipboard')),
));
},
child: Text(
app?.app.additionalSettings['about'],
textAlign: TextAlign.center,
style: const TextStyle(fontStyle: FontStyle.italic),
),
)
],
),
],
);
}
getFullInfoColumn() => Column( getFullInfoColumn() => Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -196,29 +247,37 @@ class _AppPageState extends State<AppPage> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.displayLarge, style: Theme.of(context).textTheme.displayLarge,
), ),
Text( Text(tr('byX', args: [app?.app.author ?? tr('unknown')]),
tr('byX', args: [app?.app.author ?? tr('unknown')]), textAlign: TextAlign.center,
textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineMedium),
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox( const SizedBox(
height: 8, height: 24,
), ),
GestureDetector(
onTap: () {
if (app?.app.url != null) {
launchUrlString(app?.app.url ?? '',
mode: LaunchMode.externalApplication);
}
},
onLongPress: () {
Clipboard.setData(ClipboardData(text: app?.app.url ?? ''));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(tr('copiedToClipboard')),
));
},
child: Text(
app?.app.url ?? '',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall!.copyWith(
decoration: TextDecoration.underline,
fontStyle: FontStyle.italic),
)),
Text( Text(
app?.app.id ?? '', app?.app.id ?? '',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(context).textTheme.labelSmall,
), ),
app?.app.releaseDate == null
? const SizedBox.shrink()
: Text(
app!.app.releaseDate.toString(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall,
),
const SizedBox(
height: 32,
),
getInfoColumn(), getInfoColumn(),
const SizedBox(height: 150) const SizedBox(height: 150)
], ],
@ -287,25 +346,6 @@ class _AppPageState extends State<AppPage> {
return row; return row;
}).toList(); }).toList();
items = items.map((row) {
row = row.map((e) {
if (e.key == 'versionDetection' && e is GeneratedFormDropdown) {
e.disabledOptKeys ??= [];
if (app?.app.installedVersion != null &&
app?.app.additionalSettings['versionDetection'] !=
'releaseDateAsVersion' &&
!appsProvider.isVersionDetectionPossible(app)) {
e.disabledOptKeys!.add('standardVersionDetection');
}
if (app?.app.releaseDate == null) {
e.disabledOptKeys!.add('releaseDateAsVersion');
}
}
return e;
}).toList();
return row;
}).toList();
return GeneratedFormModal( return GeneratedFormModal(
title: tr('additionalOptions'), items: items); title: tr('additionalOptions'), items: items);
}); });
@ -320,56 +360,56 @@ class _AppPageState extends State<AppPage> {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showMessage(tr('appsFromSourceAreTrackOnly'), context); showMessage(tr('appsFromSourceAreTrackOnly'), context);
} }
if (app.app.additionalSettings['versionDetection'] == var versionDetectionEnabled =
'releaseDateAsVersion') { app.app.additionalSettings['versionDetection'] == true &&
if (originalSettings['versionDetection'] != 'releaseDateAsVersion') { originalSettings['versionDetection'] != true;
if (app.app.releaseDate != null) { var releaseDateVersionEnabled =
bool isUpdated = app.app.additionalSettings['releaseDateAsVersion'] == true &&
app.app.installedVersion == app.app.latestVersion; originalSettings['releaseDateAsVersion'] != true;
app.app.latestVersion = var releaseDateVersionDisabled =
app.app.releaseDate!.microsecondsSinceEpoch.toString(); app.app.additionalSettings['releaseDateAsVersion'] != true &&
if (isUpdated) { originalSettings['releaseDateAsVersion'] == true;
app.app.installedVersion = app.app.latestVersion; if (releaseDateVersionEnabled) {
} if (app.app.releaseDate != null) {
bool isUpdated = app.app.installedVersion == app.app.latestVersion;
app.app.latestVersion =
app.app.releaseDate!.microsecondsSinceEpoch.toString();
if (isUpdated) {
app.app.installedVersion = app.app.latestVersion;
} }
} }
} else if (originalSettings['versionDetection'] == } else if (releaseDateVersionDisabled) {
'releaseDateAsVersion') {
app.app.installedVersion = app.app.installedVersion =
app.installedInfo?.versionName ?? app.app.installedVersion; app.installedInfo?.versionName ?? app.app.installedVersion;
} }
if (versionDetectionEnabled) {
app.app.additionalSettings['versionDetection'] = true;
app.app.additionalSettings['releaseDateAsVersion'] = false;
}
appsProvider.saveApps([app.app]).then((value) { appsProvider.saveApps([app.app]).then((value) {
getUpdate(app.app.id); getUpdate(app.app.id, resetVersion: versionDetectionEnabled);
}); });
} }
} }
getResetInstallStatusButton() => TextButton(
onPressed: app?.app == null
? null
: () {
app!.app.installedVersion = null;
appsProvider.saveApps([app.app]);
},
child: Text(
tr('resetInstallStatus'),
textAlign: TextAlign.center,
));
getInstallOrUpdateButton() => TextButton( getInstallOrUpdateButton() => TextButton(
onPressed: (app?.app.installedVersion == null || onPressed: !updating &&
(app?.app.installedVersion == null ||
app?.app.installedVersion != app?.app.latestVersion) && app?.app.installedVersion != app?.app.latestVersion) &&
!areDownloadsRunning !areDownloadsRunning
? () async { ? () async {
try { try {
var successMessage = app?.app.installedVersion == null
? tr('installed')
: tr('appsUpdated');
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
var res = await appsProvider.downloadAndInstallLatestApps( var res = await appsProvider.downloadAndInstallLatestApps(
app?.app.id != null ? [app!.app.id] : [], app?.app.id != null ? [app!.app.id] : [],
globalNavigatorKey.currentContext, globalNavigatorKey.currentContext,
); );
if (app?.app.installedVersion != null && !trackOnly) { if (res.isNotEmpty && !trackOnly) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
showMessage(tr('appsUpdated'), context); showMessage(successMessage, context);
} }
if (res.isNotEmpty && mounted) { if (res.isNotEmpty && mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -399,20 +439,10 @@ class _AppPageState extends State<AppPage> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
if (app?.app.installedVersion != null &&
app?.app.installedVersion != app?.app.latestVersion &&
!isVersionDetectionStandard &&
!trackOnly)
IconButton(
onPressed: app?.downloadProgress != null
? null
: showMarkUpdatedDialog,
tooltip: tr('markUpdated'),
icon: const Icon(Icons.done)),
if (source != null && if (source != null &&
source.combinedAppSpecificSettingFormItems.isNotEmpty) source.combinedAppSpecificSettingFormItems.isNotEmpty)
IconButton( IconButton(
onPressed: app?.downloadProgress != null onPressed: app?.downloadProgress != null || updating
? null ? null
: () async { : () async {
var values = var values =
@ -454,17 +484,33 @@ class _AppPageState extends State<AppPage> {
}, },
icon: const Icon(Icons.more_horiz), icon: const Icon(Icons.more_horiz),
tooltip: tr('more')), tooltip: tr('more')),
if (app?.app.installedVersion != null &&
app?.app.installedVersion != app?.app.latestVersion &&
!isVersionDetectionStandard &&
!trackOnly)
IconButton(
onPressed: app?.downloadProgress != null || updating
? null
: showMarkUpdatedDialog,
tooltip: tr('markUpdated'),
icon: const Icon(Icons.done)),
if ((!isVersionDetectionStandard || trackOnly) &&
app?.app.installedVersion != null &&
app?.app.installedVersion == app?.app.latestVersion)
IconButton(
onPressed: app?.app == null || updating
? null
: () {
app!.app.installedVersion = null;
appsProvider.saveApps([app.app]);
},
icon: const Icon(Icons.restore_rounded),
tooltip: tr('resetInstallStatus')),
const SizedBox(width: 16.0), const SizedBox(width: 16.0),
Expanded( Expanded(child: getInstallOrUpdateButton()),
child: (!isVersionDetectionStandard || trackOnly) &&
app?.app.installedVersion != null &&
app?.app.installedVersion ==
app?.app.latestVersion
? getResetInstallStatusButton()
: getInstallOrUpdateButton()),
const SizedBox(width: 16.0), const SizedBox(width: 16.0),
IconButton( IconButton(
onPressed: app?.downloadProgress != null onPressed: app?.downloadProgress != null || updating
? null ? null
: () { : () {
appsProvider appsProvider

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -24,6 +26,92 @@ class AppsPage extends StatefulWidget {
State<AppsPage> createState() => AppsPageState(); State<AppsPage> createState() => AppsPageState();
} }
showChangeLogDialog(BuildContext context, App app, String? changesUrl,
AppSource appSource, String changeLog) {
showDialog(
context: context,
builder: (BuildContext context) {
return GeneratedFormModal(
title: tr('changes'),
items: const [],
message: app.latestVersion,
additionalWidgets: [
changesUrl != null
? GestureDetector(
child: Text(
changesUrl,
style: const TextStyle(
decoration: TextDecoration.underline,
fontStyle: FontStyle.italic),
),
onTap: () {
launchUrlString(changesUrl,
mode: LaunchMode.externalApplication);
},
)
: const SizedBox.shrink(),
changesUrl != null
? const SizedBox(
height: 16,
)
: const SizedBox.shrink(),
appSource.changeLogIfAnyIsMarkDown
? SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - 350,
child: Markdown(
data: changeLog,
onTapLink: (text, href, title) {
if (href != null) {
launchUrlString(
href.startsWith('http://') ||
href.startsWith('https://')
? href
: '${Uri.parse(app.url).origin}/$href',
mode: LaunchMode.externalApplication);
}
},
extensionSet: md.ExtensionSet(
md.ExtensionSet.gitHubFlavored.blockSyntaxes,
[
md.EmojiSyntax(),
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
],
),
))
: Text(changeLog),
],
singleNullReturnButton: tr('ok'),
);
});
}
getChangeLogFn(BuildContext context, App app) {
AppSource appSource =
SourceProvider().getSource(app.url, overrideSource: app.overrideSource);
String? changesUrl = appSource.changeLogPageFromStandardUrl(app.url);
String? changeLog = app.changeLog;
if (changeLog?.split('\n').length == 1) {
if (RegExp(
'(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?')
.hasMatch(changeLog!)) {
if (changesUrl == null) {
changesUrl = changeLog;
changeLog = null;
}
}
}
return (changeLog == null && changesUrl == null)
? null
: () {
if (changeLog != null) {
showChangeLogDialog(context, app, changesUrl, appSource, changeLog);
} else {
launchUrlString(changesUrl!, mode: LaunchMode.externalApplication);
}
};
}
class AppsPageState extends State<AppsPage> { class AppsPageState extends State<AppsPage> {
AppsFilter filter = AppsFilter(); AppsFilter filter = AppsFilter();
final AppsFilter neutralFilter = AppsFilter(); final AppsFilter neutralFilter = AppsFilter();
@ -260,66 +348,6 @@ class AppsPageState extends State<AppsPage> {
.where((a) => selectedAppIds.contains(a.id)) .where((a) => selectedAppIds.contains(a.id))
.toSet(); .toSet();
showChangeLogDialog(
String? changesUrl, AppSource appSource, String changeLog, int index) {
showDialog(
context: context,
builder: (BuildContext context) {
return GeneratedFormModal(
title: tr('changes'),
items: const [],
message: listedApps[index].app.latestVersion,
additionalWidgets: [
changesUrl != null
? GestureDetector(
child: Text(
changesUrl,
style: const TextStyle(
decoration: TextDecoration.underline,
fontStyle: FontStyle.italic),
),
onTap: () {
launchUrlString(changesUrl,
mode: LaunchMode.externalApplication);
},
)
: const SizedBox.shrink(),
changesUrl != null
? const SizedBox(
height: 16,
)
: const SizedBox.shrink(),
appSource.changeLogIfAnyIsMarkDown
? SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - 350,
child: Markdown(
data: changeLog,
onTapLink: (text, href, title) {
if (href != null) {
launchUrlString(
href.startsWith('http://') ||
href.startsWith('https://')
? href
: '${Uri.parse(listedApps[index].app.url).origin}/$href',
mode: LaunchMode.externalApplication);
}
},
extensionSet: md.ExtensionSet(
md.ExtensionSet.gitHubFlavored.blockSyntaxes,
[
md.EmojiSyntax(),
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
],
),
))
: Text(changeLog),
],
singleNullReturnButton: tr('ok'),
);
});
}
getLoadingWidgets() { getLoadingWidgets() {
return [ return [
if (listedApps.isEmpty) if (listedApps.isEmpty)
@ -349,25 +377,6 @@ class AppsPageState extends State<AppsPage> {
]; ];
} }
getChangeLogFn(int appIndex) {
AppSource appSource = SourceProvider().getSource(
listedApps[appIndex].app.url,
overrideSource: listedApps[appIndex].app.overrideSource);
String? changesUrl =
appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
String? changeLog = listedApps[appIndex].app.changeLog;
return (changeLog == null && changesUrl == null)
? null
: () {
if (changeLog != null) {
showChangeLogDialog(changesUrl, appSource, changeLog, appIndex);
} else {
launchUrlString(changesUrl!,
mode: LaunchMode.externalApplication);
}
};
}
getUpdateButton(int appIndex) { getUpdateButton(int appIndex) {
return IconButton( return IconButton(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
@ -419,7 +428,7 @@ class AppsPageState extends State<AppsPage> {
} }
getVersionText(int appIndex) { getVersionText(int appIndex) {
return '${listedApps[appIndex].app.installedVersion ?? tr('notInstalled')}${listedApps[appIndex].app.additionalSettings['trackOnly'] == true ? ' ${tr('estimateInBrackets')}' : ''}'; return '${listedApps[appIndex].app.installedVersion ?? tr('notInstalled')}${listedApps[appIndex].app.additionalSettings['trackOnly'] == true ? ' ${tr('pseudoVersion')}' : ''}';
} }
getChangesButtonString(int appIndex, bool hasChangeLogFn) { getChangesButtonString(int appIndex, bool hasChangeLogFn) {
@ -432,7 +441,7 @@ class AppsPageState extends State<AppsPage> {
} }
getSingleAppHorizTile(int index) { getSingleAppHorizTile(int index) {
var showChangesFn = getChangeLogFn(index); var showChangesFn = getChangeLogFn(context, listedApps[index].app);
var hasUpdate = listedApps[index].app.installedVersion != null && var hasUpdate = listedApps[index].app.installedVersion != null &&
listedApps[index].app.installedVersion != listedApps[index].app.installedVersion !=
listedApps[index].app.latestVersion; listedApps[index].app.latestVersion;
@ -694,7 +703,7 @@ class AppsPageState extends State<AppsPage> {
showError(e, context); showError(e, context);
return <String>[]; return <String>[];
}).then((value) { }).then((value) {
if (shouldInstallUpdates) { if (value.isNotEmpty && shouldInstallUpdates) {
showMessage(tr('appsUpdated'), context); showMessage(tr('appsUpdated'), context);
} }
}); });
@ -845,47 +854,75 @@ class AppsPageState extends State<AppsPage> {
scrollable: true, scrollable: true,
content: Padding( content: Padding(
padding: const EdgeInsets.only(top: 6), padding: const EdgeInsets.only(top: 6),
child: Row( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
IconButton( TextButton(
onPressed: pinSelectedApps,
child: Text(selectedApps
.where((element) => element.pinned)
.isEmpty
? tr('pinToTop')
: tr('unpinFromTop'))),
const Divider(),
TextButton(
onPressed: () {
String urls = '';
for (var a in selectedApps) {
urls += '${a.url}\n';
}
urls = urls.substring(0, urls.length - 1);
Share.share(urls,
subject: 'Obtainium - ${tr('appsString')}');
Navigator.of(context).pop();
},
child: Text(tr('shareSelectedAppURLs'))),
const Divider(),
TextButton(
onPressed: selectedAppIds.isEmpty
? null
: () {
String urls = '';
for (var a in selectedApps) {
urls +=
'https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/${Uri.encodeComponent(jsonEncode({
'id': a.id,
'url': a.url,
'author': a.author,
'name': a.name,
'preferredApkIndex':
a.preferredApkIndex,
'additionalSettings':
jsonEncode(a.additionalSettings)
}))}\n\n';
}
Share.share(urls,
subject:
'Obtainium - ${tr('appsString')}');
},
child: Text(tr('shareAppConfigLinks'))),
const Divider(),
TextButton(
onPressed: () {
appsProvider
.downloadAppAssets(
selectedApps.map((e) => e.id).toList(),
globalNavigatorKey.currentContext ??
context)
.catchError((e) => showError(
e,
globalNavigatorKey.currentContext ??
context));
Navigator.of(context).pop();
},
child: Text(tr('downloadX',
args: [tr('releaseAsset').toLowerCase()]))),
const Divider(),
TextButton(
onPressed: appsProvider.areDownloadsRunning() onPressed: appsProvider.areDownloadsRunning()
? null ? null
: showMassMarkDialog, : showMassMarkDialog,
tooltip: tr('markSelectedAppsUpdated'), child: Text(tr('markSelectedAppsUpdated'))),
icon: const Icon(Icons.done)),
IconButton(
onPressed: pinSelectedApps,
tooltip: selectedApps
.where((element) => element.pinned)
.isEmpty
? tr('pinToTop')
: tr('unpinFromTop'),
icon: Icon(selectedApps
.where((element) => element.pinned)
.isEmpty
? Icons.bookmark_outline_rounded
: Icons.bookmark_remove_outlined),
),
IconButton(
onPressed: () {
String urls = '';
for (var a in selectedApps) {
urls += 'obtainium://add/${a.url}\n';
}
urls = urls.substring(0, urls.length - 1);
Share.share(urls,
subject: tr('selectedAppURLsFromObtainium'));
Navigator.of(context).pop();
},
tooltip: tr('shareSelectedAppURLs'),
icon: const Icon(Icons.share),
),
IconButton(
onPressed: resetSelectedAppsInstallStatuses,
tooltip: tr('resetInstallStatus'),
icon: const Icon(Icons.restore_page_outlined),
),
]), ]),
), ),
); );
@ -1004,7 +1041,7 @@ class AppsPageState extends State<AppsPage> {
IconButton( IconButton(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
style: const ButtonStyle(visualDensity: VisualDensity.compact), style: const ButtonStyle(visualDensity: VisualDensity.compact),
tooltip: isFilterOff ? tr('filter') : tr('filterActive'), tooltip: '${tr('filter')}${isFilterOff ? '' : ' *'}',
onPressed: isFilterOff onPressed: isFilterOff
? showFilterDialog ? showFilterDialog
: () { : () {

View File

@ -199,10 +199,11 @@ class _ImportExportPageState extends State<ImportExportPage> {
...source.searchQuerySettingFormItems.map((e) => [e]), ...source.searchQuerySettingFormItems.map((e) => [e]),
[ [
GeneratedFormTextField('url', GeneratedFormTextField('url',
label: source.host != null label: source.hosts.isNotEmpty
? tr('overrideSource') ? tr('overrideSource')
: plural('url', 1).substring(2), : plural('url', 1).substring(2),
defaultValue: source.host ?? '', defaultValue:
source.hosts.isNotEmpty ? source.hosts[0] : '',
required: true) required: true)
], ],
], ],
@ -212,7 +213,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
setState(() { setState(() {
importInProgress = true; importInProgress = true;
}); });
if (values['url'] != source.host) { if (source.hosts.isEmpty || values['url'] != source.hosts[0]) {
source = sourceProvider.getSource(values['url'], source = sourceProvider.getSource(values['url'],
overrideSource: source.runtimeType.toString()); overrideSource: source.runtimeType.toString());
} }

View File

@ -1,5 +1,7 @@
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:equations/equations.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
@ -12,6 +14,7 @@ import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:shizuku_apk_installer/shizuku_apk_installer.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
class SettingsPage extends StatefulWidget { class SettingsPage extends StatefulWidget {
@ -22,78 +25,178 @@ class SettingsPage extends StatefulWidget {
} }
class _SettingsPageState extends State<SettingsPage> { class _SettingsPageState extends State<SettingsPage> {
List<int> updateIntervalNodes = [
15, 30, 60, 120, 180, 360, 720, 1440, 4320, 10080, 20160, 43200];
int updateInterval = 0;
late SplineInterpolation updateIntervalInterpolator; // 🤓
String updateIntervalLabel = tr('neverManualOnly');
bool showIntervalLabel = true;
final Map<ColorSwatch<Object>, String> colorsNameMap =
<ColorSwatch<Object>, String> {
ColorTools.createPrimarySwatch(obtainiumThemeColor): 'Obtainium'
};
void initUpdateIntervalInterpolator() {
List<InterpolationNode> nodes = [];
for (final (index, element) in updateIntervalNodes.indexed) {
nodes.add(InterpolationNode(x: index.toDouble()+1, y: element.toDouble()));
}
updateIntervalInterpolator = SplineInterpolation(nodes: nodes);
}
void processIntervalSliderValue(double val) {
if (val < 0.5) {
updateInterval = 0;
updateIntervalLabel = tr('neverManualOnly');
return;
}
int valInterpolated = 0;
if (val < 1) {
valInterpolated = 15;
} else {
valInterpolated = updateIntervalInterpolator.compute(val).round();
}
if (valInterpolated < 60) {
updateInterval = valInterpolated;
updateIntervalLabel = plural('minute', valInterpolated);
} else if (valInterpolated < 8 * 60) {
int valRounded = (valInterpolated / 15).floor() * 15;
updateInterval = valRounded;
updateIntervalLabel = plural('hour', valRounded ~/ 60);
int mins = valRounded % 60;
if (mins != 0) updateIntervalLabel += " ${plural('minute', mins)}";
} else if (valInterpolated < 24 * 60) {
int valRounded = (valInterpolated / 30).floor() * 30;
updateInterval = valRounded;
updateIntervalLabel = plural('hour', valRounded / 60);
} else if (valInterpolated < 7 * 24 * 60){
int valRounded = (valInterpolated / (12 * 60)).floor() * 12 * 60;
updateInterval = valRounded;
updateIntervalLabel = plural('day', valRounded / (24 * 60));
} else {
int valRounded = (valInterpolated / (24 * 60)).floor() * 24 * 60;
updateInterval = valRounded;
updateIntervalLabel = plural('day', valRounded ~/ (24 * 60));
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SettingsProvider settingsProvider = context.watch<SettingsProvider>(); SettingsProvider settingsProvider = context.watch<SettingsProvider>();
SourceProvider sourceProvider = SourceProvider(); SourceProvider sourceProvider = SourceProvider();
if (settingsProvider.prefs == null) { if (settingsProvider.prefs == null) settingsProvider.initializeSettings();
settingsProvider.initializeSettings(); initUpdateIntervalInterpolator();
processIntervalSliderValue(settingsProvider.updateIntervalSliderVal);
var followSystemThemeExplanation = FutureBuilder(
builder: (ctx, val) {
return ((val.data?.version.sdkInt ?? 30) < 29) ?
Text(tr('followSystemThemeExplanation'),
style: Theme.of(context).textTheme.labelSmall)
: const SizedBox.shrink();
},
future: DeviceInfoPlugin().androidInfo
);
Future<bool> colorPickerDialog() async {
return ColorPicker(
color: settingsProvider.themeColor,
onColorChanged: (Color color) =>
setState(() =>
settingsProvider.themeColor = color
),
actionButtons: const ColorPickerActionButtons(
okButton: true,
closeButton: true,
dialogActionButtons: false,
),
pickersEnabled: const <ColorPickerType, bool>{
ColorPickerType.both: false,
ColorPickerType.primary: false,
ColorPickerType.accent: false,
ColorPickerType.bw: false,
ColorPickerType.custom: true,
ColorPickerType.wheel: true,
},
pickerTypeLabels: <ColorPickerType, String>{
ColorPickerType.custom: tr('standard'),
ColorPickerType.wheel: tr('custom')
},
title: Text(tr('selectX', args: [tr('colour')]),
style: Theme.of(context).textTheme.titleLarge),
wheelDiameter: 192,
wheelSquareBorderRadius: 32,
width: 48,
height: 48,
borderRadius: 24,
spacing: 8,
runSpacing: 8,
enableShadesSelection: false,
customColorSwatchesAndNames: colorsNameMap,
showMaterialName: true,
showColorName: true,
materialNameTextStyle: Theme.of(context).textTheme.bodySmall,
colorNameTextStyle: Theme.of(context).textTheme.bodySmall,
copyPasteBehavior: const ColorPickerCopyPasteBehavior(longPressMenu: true),
).showPickerDialog(
context,
transitionBuilder: (BuildContext context,
Animation<double> a1, Animation<double> a2, Widget widget) {
final double curvedValue = Curves.easeInCubic.transform(a1.value);
return Transform(
alignment: Alignment.center,
transform: Matrix4.diagonal3Values(curvedValue, curvedValue, 1),
child: Opacity(
opacity: curvedValue,
child: widget
),
);
},
transitionDuration: const Duration(milliseconds: 250),
);
} }
var installMethodDropdown = DropdownButtonFormField( var colorPicker = ListTile(
decoration: InputDecoration(labelText: tr('installMethod')), dense: true,
value: settingsProvider.installMethod, contentPadding: EdgeInsets.zero,
items: [ title: Text(tr('selectX', args: [tr('colour')])),
DropdownMenuItem( subtitle: Text("${ColorTools.nameThatColor(settingsProvider.themeColor)} "
value: InstallMethodSettings.normal, "(${ColorTools.materialNameAndCode(settingsProvider.themeColor,
child: Text(tr('normal')), colorSwatchNameMap: colorsNameMap)})"),
), trailing: ColorIndicator(
DropdownMenuItem( width: 40,
value: InstallMethodSettings.shizuku, height: 40,
child: Text(tr('shizuku')), borderRadius: 20,
), color: settingsProvider.themeColor,
DropdownMenuItem( onSelectFocus: false,
value: InstallMethodSettings.root, onSelect: () async {
child: Text(tr('root')), final Color colorBeforeDialog = settingsProvider.themeColor;
) if (!(await colorPickerDialog())) {
], setState(() {
onChanged: (value) { settingsProvider.themeColor = colorBeforeDialog;
if (value != null) { });
settingsProvider.installMethod = value;
} }
}); }
)
);
var themeDropdown = DropdownButtonFormField( var useMaterialThemeSwitch = FutureBuilder(
decoration: InputDecoration(labelText: tr('theme')), builder: (ctx, val) {
value: settingsProvider.theme, return ((val.data?.version.sdkInt ?? 0) >= 31) ?
items: [ Row(
DropdownMenuItem( mainAxisAlignment: MainAxisAlignment.spaceBetween,
value: ThemeSettings.dark, children: [
child: Text(tr('dark')), Flexible(child: Text(tr('useMaterialYou'))),
), Switch(
DropdownMenuItem( value: settingsProvider.useMaterialYou,
value: ThemeSettings.light, onChanged: (value) {
child: Text(tr('light')), settingsProvider.useMaterialYou = value;
), })
DropdownMenuItem( ],
value: ThemeSettings.system, ) : const SizedBox.shrink();
child: Text(tr('followSystem')), },
) future: DeviceInfoPlugin().androidInfo
], );
onChanged: (value) {
if (value != null) {
settingsProvider.theme = value;
}
});
var colourDropdown = DropdownButtonFormField(
decoration: InputDecoration(labelText: tr('colour')),
value: settingsProvider.colour,
items: [
DropdownMenuItem(
value: ColourSettings.basic,
child: Text(tr('obtainium')),
),
DropdownMenuItem(
value: ColourSettings.materialYou,
child: Text(tr('materialYou')),
)
],
onChanged: (value) {
if (value != null) {
settingsProvider.colour = value;
}
});
var sortDropdown = DropdownButtonFormField( var sortDropdown = DropdownButtonFormField(
isExpanded: true, isExpanded: true,
@ -165,30 +268,29 @@ class _SettingsPageState extends State<SettingsPage> {
} }
}); });
var intervalDropdown = DropdownButtonFormField( var intervalSlider = Slider(
decoration: InputDecoration(labelText: tr('bgUpdateCheckInterval')), value: settingsProvider.updateIntervalSliderVal,
value: settingsProvider.updateInterval, max: updateIntervalNodes.length.toDouble(),
items: updateIntervals.map((e) { divisions: updateIntervalNodes.length * 20,
int displayNum = (e < 60 label: updateIntervalLabel,
? e onChanged: (double value) {
: e < 1440 setState(() {
? e / 60 settingsProvider.updateIntervalSliderVal = value;
: e / 1440) processIntervalSliderValue(value);
.round();
String display = e == 0
? tr('neverManualOnly')
: (e < 60
? plural('minute', displayNum)
: e < 1440
? plural('hour', displayNum)
: plural('day', displayNum));
return DropdownMenuItem(value: e, child: Text(display));
}).toList(),
onChanged: (value) {
if (value != null) {
settingsProvider.updateInterval = value;
}
}); });
},
onChangeStart: (double value) {
setState(() {
showIntervalLabel = false;
});
},
onChangeEnd: (double value) {
setState(() {
showIntervalLabel = true;
settingsProvider.updateInterval = updateInterval;
});
},
);
var sourceSpecificFields = sourceProvider.sources.map((e) { var sourceSpecificFields = sourceProvider.sources.map((e) {
if (e.sourceConfigSettingFormItems.isNotEmpty) { if (e.sourceConfigSettingFormItems.isNotEmpty) {
@ -239,15 +341,19 @@ class _SettingsPageState extends State<SettingsPage> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary), color: Theme.of(context).colorScheme.primary),
), ),
intervalDropdown, //intervalDropdown,
height16,
if (showIntervalLabel) SizedBox(
child: Text("${tr('bgUpdateCheckInterval')}: $updateIntervalLabel")
) else const SizedBox(height: 16),
intervalSlider,
FutureBuilder( FutureBuilder(
builder: (ctx, val) { builder: (ctx, val) {
return (val.data?.version.sdkInt ?? 0) >= 30 return (settingsProvider.updateInterval > 0) && (((val.data?.version.sdkInt ?? 0) >= 30) || settingsProvider.useShizuku)
? Column( ? Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
height16,
Row( Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment MainAxisAlignment
@ -351,6 +457,22 @@ class _SettingsPageState extends State<SettingsPage> {
], ],
), ),
height16, height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child:
Text(tr('removeOnExternalUninstall'))),
Switch(
value: settingsProvider
.removeOnExternalUninstall,
onChanged: (value) {
settingsProvider
.removeOnExternalUninstall = value;
})
],
),
height16,
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -363,7 +485,71 @@ class _SettingsPageState extends State<SettingsPage> {
}) })
], ],
), ),
installMethodDropdown, height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(tr(
'beforeNewInstallsShareToAppVerifier')),
GestureDetector(
onTap: () {
launchUrlString(
'https://github.com/soupslurpr/AppVerifier',
mode: LaunchMode
.externalApplication);
},
child: Text(
tr('about'),
style: const TextStyle(
decoration:
TextDecoration.underline,
fontSize: 12),
)),
],
)),
Switch(
value: settingsProvider
.beforeNewInstallsShareToAppVerifier,
onChanged: (value) {
settingsProvider
.beforeNewInstallsShareToAppVerifier =
value;
})
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(tr('useShizuku'))),
Switch(
value: settingsProvider.useShizuku,
onChanged: (useShizuku) {
if (useShizuku) {
ShizukuApkInstaller.checkPermission().then((resCode) {
settingsProvider.useShizuku = resCode!.startsWith('granted');
switch(resCode){
case 'binder_not_found':
showError(ObtainiumError(tr('shizukuBinderNotFound')), context);
case 'old_shizuku':
showError(ObtainiumError(tr('shizukuOld')), context);
case 'old_android_with_adb':
showError(ObtainiumError(tr('shizukuOldAndroidWithADB')), context);
case 'denied':
showError(ObtainiumError(tr('cancelled')), context);
}
});
} else {
settingsProvider.useShizuku = false;
}
})
],
),
height32, height32,
Text( Text(
tr('sourceSpecific'), tr('sourceSpecific'),
@ -379,9 +565,34 @@ class _SettingsPageState extends State<SettingsPage> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary), color: Theme.of(context).colorScheme.primary),
), ),
themeDropdown, DropdownButtonFormField(
decoration: InputDecoration(labelText: tr('theme')),
value: settingsProvider.theme,
items: [
DropdownMenuItem(
value: ThemeSettings.system,
child: Text(tr('followSystem')),
),
DropdownMenuItem(
value: ThemeSettings.light,
child: Text(tr('light')),
),
DropdownMenuItem(
value: ThemeSettings.dark,
child: Text(tr('dark')),
)
],
onChanged: (value) {
if (value != null) {
settingsProvider.theme = value;
}
}),
height8,
if (settingsProvider.theme == ThemeSettings.system)
followSystemThemeExplanation,
height16, height16,
Row( if (settingsProvider.theme != ThemeSettings.light)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Flexible(child: Text(tr('useBlackTheme'))), Flexible(child: Text(tr('useBlackTheme'))),
@ -389,11 +600,13 @@ class _SettingsPageState extends State<SettingsPage> {
value: settingsProvider.useBlackTheme, value: settingsProvider.useBlackTheme,
onChanged: (value) { onChanged: (value) {
settingsProvider.useBlackTheme = value; settingsProvider.useBlackTheme = value;
}) }
], )
), ]
colourDropdown, ),
height16, height8,
useMaterialThemeSwitch,
if (!settingsProvider.useMaterialYou) colorPicker,
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -407,30 +620,35 @@ class _SettingsPageState extends State<SettingsPage> {
), ),
height16, height16,
localeDropdown, localeDropdown,
height16, FutureBuilder(
Row( builder: (ctx, val) {
mainAxisAlignment: MainAxisAlignment.spaceBetween, return (val.data?.version.sdkInt ?? 0) >= 34
children: [ ? Column(
Flexible(child: Text(tr('useSystemFont'))), crossAxisAlignment: CrossAxisAlignment.start,
Switch( children: [
value: settingsProvider.useSystemFont, height16,
onChanged: (useSystemFont) { Row(
if (useSystemFont) { mainAxisAlignment: MainAxisAlignment.spaceBetween,
NativeFeatures.loadSystemFont().then((fontLoadRes) { children: [
if (fontLoadRes == 'ok') { Flexible(child: Text(tr('useSystemFont'))),
settingsProvider.useSystemFont = true; Switch(
} else { value: settingsProvider.useSystemFont,
showError(ObtainiumError( onChanged: (useSystemFont) {
tr('systemFontError', args: [fontLoadRes]) if (useSystemFont) {
), context); NativeFeatures.loadSystemFont().then((val) {
} settingsProvider.useSystemFont = true;
}); });
} else { } else {
settingsProvider.useSystemFont = false; settingsProvider.useSystemFont = false;
} }
}) })
], ]
), )
]
)
: const SizedBox.shrink();
},
future: DeviceInfoPlugin().androidInfo),
height16, height16,
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -470,22 +688,6 @@ class _SettingsPageState extends State<SettingsPage> {
], ],
), ),
height16, height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child:
Text(tr('removeOnExternalUninstall'))),
Switch(
value: settingsProvider
.removeOnExternalUninstall,
onChanged: (value) {
settingsProvider
.removeOnExternalUninstall = value;
})
],
),
height16,
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -628,38 +830,9 @@ class _SettingsPageState extends State<SettingsPage> {
label: Text(tr('appLogs'))), label: Text(tr('appLogs'))),
], ],
), ),
const Divider( const SizedBox(
height: 32, height: 16,
), )
// Padding(
// padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
// child: Column(children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Flexible(child: Text(tr('debugMenu'))),
// Switch(
// value: settingsProvider.showDebugOpts,
// onChanged: (value) {
// settingsProvider.showDebugOpts = value;
// })
// ],
// ),
// if (settingsProvider.showDebugOpts)
// Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
// children: [
// height16,
// TextButton(
// onPressed: () {
// bgUpdateCheck('taskId', null);
// showMessage(tr('bgTaskStarted'), context);
// },
// child: Text(tr('runBgCheckNow')))
// ],
// ),
// ]),
// ),
], ],
), ),
) )

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +1,22 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:android_system_font/android_system_font.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
class NativeFeatures { class NativeFeatures {
static const MethodChannel _channel = MethodChannel('native');
static bool _systemFontLoaded = false; static bool _systemFontLoaded = false;
static bool _callbacksApplied = false;
static int _resPermShizuku = -2; // not set
static Future<ByteData> _readFileBytes(String path) async { static Future<ByteData> _readFileBytes(String path) async {
var file = File(path); var bytes = await File(path).readAsBytes();
var bytes = await file.readAsBytes();
return ByteData.view(bytes.buffer); return ByteData.view(bytes.buffer);
} }
static Future _handleCalls(MethodCall call) async { static Future loadSystemFont() async {
if (call.method == 'resPermShizuku') { if (_systemFontLoaded) return;
_resPermShizuku = call.arguments['res'];
}
}
static Future _waitWhile(bool Function() test,
[Duration pollInterval = const Duration(milliseconds: 250)]) {
var completer = Completer();
check() {
if (test()) {
Timer(pollInterval, check);
} else {
completer.complete();
}
}
check();
return completer.future;
}
static Future<String> loadSystemFont() async {
if (_systemFontLoaded) { return "ok"; }
var getFontRes = await _channel.invokeMethod('getSystemFont');
if (getFontRes[0] != '/') { return getFontRes; } // Error
var fontLoader = FontLoader('SystemFont'); var fontLoader = FontLoader('SystemFont');
fontLoader.addFont(_readFileBytes(getFontRes)); var fontFilePath = await AndroidSystemFont().getFilePath();
await fontLoader.load(); fontLoader.addFont(_readFileBytes(fontFilePath!));
fontLoader.load();
_systemFontLoaded = true; _systemFontLoaded = true;
return "ok";
}
static Future<int> checkPermissionShizuku() async {
if (!_callbacksApplied) {
_channel.setMethodCallHandler(_handleCalls);
_callbacksApplied = true;
}
int res = await _channel.invokeMethod('checkPermissionShizuku');
if (res == -2) {
await _waitWhile(() => _resPermShizuku == -2);
res = _resPermShizuku;
_resPermShizuku = -2;
}
return res;
}
static Future<bool> checkPermissionRoot() async {
return await _channel.invokeMethod('checkPermissionRoot');
}
static Future<bool> installWithShizuku({required String apkFileUri}) async {
return await _channel.invokeMethod(
'installWithShizuku', {'apkFileUri': apkFileUri});
}
static Future<bool> installWithRoot({required String apkFilePath}) async {
return await _channel.invokeMethod(
'installWithRoot', {'apkFilePath': apkFilePath});
} }
} }

View File

@ -41,20 +41,26 @@ class UpdateNotification extends ObtainiumNotification {
} }
class SilentUpdateNotification extends ObtainiumNotification { class SilentUpdateNotification extends ObtainiumNotification {
SilentUpdateNotification(List<App> updates, {int? id}) SilentUpdateNotification(List<App> updates, bool succeeded, {int? id})
: super( : super(
id ?? 3, id ?? 3,
tr('appsUpdated'), succeeded
? tr('appsUpdated')
: tr('appsNotUpdated'),
'', '',
'APPS_UPDATED', 'APPS_UPDATED',
tr('appsUpdatedNotifChannel'), tr('appsUpdatedNotifChannel'),
tr('appsUpdatedNotifDescription'), tr('appsUpdatedNotifDescription'),
Importance.defaultImportance) { Importance.defaultImportance) {
message = updates.length == 1 message = updates.length == 1
? tr('xWasUpdatedToY', ? tr(succeeded
args: [updates[0].finalName, updates[0].latestVersion]) ? 'xWasUpdatedToY'
: plural('xAndNMoreUpdatesInstalled', updates.length - 1, : 'xWasNotUpdatedToY',
args: [updates[0].finalName, (updates.length - 1).toString()]); args: [updates[0].finalName, updates[0].latestVersion])
: plural(succeeded
? 'xAndNMoreUpdatesInstalled'
: "xAndNMoreUpdatesFailed",
updates.length - 1, args: [updates[0].finalName, (updates.length - 1).toString()]);
} }
} }
@ -120,6 +126,18 @@ class DownloadNotification extends ObtainiumNotification {
progPercent: progPercent); progPercent: progPercent);
} }
class DownloadedNotification extends ObtainiumNotification {
DownloadedNotification(String fileName, String downloadUrl)
: super(
downloadUrl.hashCode,
tr('downloadedX', args: [fileName]),
'',
'FILE_DOWNLOADED',
tr('downloadedXNotifChannel', args: [tr('app')]),
tr('downloadedX', args: [tr('app')]),
Importance.defaultImportance);
}
final completeInstallationNotification = ObtainiumNotification( final completeInstallationNotification = ObtainiumNotification(
1, 1,
tr('completeAppInstallation'), tr('completeAppInstallation'),

View File

@ -14,29 +14,17 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:shared_storage/shared_storage.dart' as saf; import 'package:shared_storage/shared_storage.dart' as saf;
String obtainiumTempId = 'imranr98_obtainium_${GitHub().host}'; String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}';
String obtainiumId = 'dev.imranr.obtainium'; String obtainiumId = 'dev.imranr.obtainium';
String obtainiumUrl = 'https://github.com/ImranR98/Obtainium';
enum InstallMethodSettings { normal, shizuku, root } Color obtainiumThemeColor = const Color(0xFF6438B5);
enum ThemeSettings { system, light, dark } enum ThemeSettings { system, light, dark }
enum ColourSettings { basic, materialYou }
enum SortColumnSettings { added, nameAuthor, authorName, releaseDate } enum SortColumnSettings { added, nameAuthor, authorName, releaseDate }
enum SortOrderSettings { ascending, descending } enum SortOrderSettings { ascending, descending }
const maxAPIRateLimitMinutes = 30;
const minUpdateIntervalMinutes = maxAPIRateLimitMinutes + 30;
const maxUpdateIntervalMinutes = 4320;
List<int> updateIntervals = [15, 30, 60, 120, 180, 360, 720, 1440, 4320, 0]
.where((element) =>
(element >= minUpdateIntervalMinutes &&
element <= maxUpdateIntervalMinutes) ||
element == 0)
.toList();
class SettingsProvider with ChangeNotifier { class SettingsProvider with ChangeNotifier {
SharedPreferences? prefs; SharedPreferences? prefs;
String? defaultAppDir; String? defaultAppDir;
@ -60,13 +48,12 @@ class SettingsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
InstallMethodSettings get installMethod { bool get useShizuku{
return InstallMethodSettings.values[ return prefs?.getBool('useShizuku') ?? false;
prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];
} }
set installMethod(InstallMethodSettings t) { set useShizuku(bool useShizuku) {
prefs?.setInt('installMethod', t.index); prefs?.setBool('useShizuku', useShizuku);
notifyListeners(); notifyListeners();
} }
@ -80,13 +67,23 @@ class SettingsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
ColourSettings get colour { Color get themeColor {
return ColourSettings int? colorCode = prefs?.getInt('themeColor');
.values[prefs?.getInt('colour') ?? ColourSettings.basic.index]; return (colorCode != null) ?
Color(colorCode) : obtainiumThemeColor;
} }
set colour(ColourSettings t) { set themeColor(Color themeColor) {
prefs?.setInt('colour', t.index); prefs?.setInt('themeColor', themeColor.value);
notifyListeners();
}
bool get useMaterialYou {
return prefs?.getBool('useMaterialYou') ?? false;
}
set useMaterialYou(bool useMaterialYou) {
prefs?.setBool('useMaterialYou', useMaterialYou);
notifyListeners(); notifyListeners();
} }
@ -100,21 +97,20 @@ class SettingsProvider with ChangeNotifier {
} }
int get updateInterval { int get updateInterval {
var min = prefs?.getInt('updateInterval') ?? 360; return prefs?.getInt('updateInterval') ?? 360;
if (!updateIntervals.contains(min)) {
var temp = updateIntervals[0];
for (var i in updateIntervals) {
if (min > i && i != 0) {
temp = i;
}
}
min = temp;
}
return min;
} }
set updateInterval(int min) { set updateInterval(int min) {
prefs?.setInt('updateInterval', (min < 15 && min != 0) ? 15 : min); prefs?.setInt('updateInterval', min);
notifyListeners();
}
double get updateIntervalSliderVal {
return prefs?.getDouble('updateIntervalSliderVal') ?? 6.0;
}
set updateIntervalSliderVal(double val) {
prefs?.setDouble('updateIntervalSliderVal', val);
notifyListeners(); notifyListeners();
} }
@ -464,4 +460,13 @@ class SettingsProvider with ChangeNotifier {
prefs?.setStringList('searchDeselected', list); prefs?.setStringList('searchDeselected', list);
notifyListeners(); notifyListeners();
} }
bool get beforeNewInstallsShareToAppVerifier {
return prefs?.getBool('beforeNewInstallsShareToAppVerifier') ?? true;
}
set beforeNewInstallsShareToAppVerifier(bool val) {
prefs?.setBool('beforeNewInstallsShareToAppVerifier', val);
notifyListeners();
}
} }

View File

@ -11,6 +11,7 @@ import 'package:obtainium/app_sources/apkmirror.dart';
import 'package:obtainium/app_sources/apkpure.dart'; import 'package:obtainium/app_sources/apkpure.dart';
import 'package:obtainium/app_sources/aptoide.dart'; import 'package:obtainium/app_sources/aptoide.dart';
import 'package:obtainium/app_sources/codeberg.dart'; import 'package:obtainium/app_sources/codeberg.dart';
import 'package:obtainium/app_sources/directAPKLink.dart';
import 'package:obtainium/app_sources/fdroid.dart'; import 'package:obtainium/app_sources/fdroid.dart';
import 'package:obtainium/app_sources/fdroidrepo.dart'; import 'package:obtainium/app_sources/fdroidrepo.dart';
import 'package:obtainium/app_sources/github.dart'; import 'package:obtainium/app_sources/github.dart';
@ -19,7 +20,6 @@ import 'package:obtainium/app_sources/huaweiappgallery.dart';
import 'package:obtainium/app_sources/izzyondroid.dart'; import 'package:obtainium/app_sources/izzyondroid.dart';
import 'package:obtainium/app_sources/html.dart'; import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/app_sources/jenkins.dart'; import 'package:obtainium/app_sources/jenkins.dart';
import 'package:obtainium/app_sources/mullvad.dart';
import 'package:obtainium/app_sources/neutroncode.dart'; import 'package:obtainium/app_sources/neutroncode.dart';
import 'package:obtainium/app_sources/signal.dart'; import 'package:obtainium/app_sources/signal.dart';
import 'package:obtainium/app_sources/sourceforge.dart'; import 'package:obtainium/app_sources/sourceforge.dart';
@ -47,9 +47,10 @@ class APKDetails {
late AppNames names; late AppNames names;
late DateTime? releaseDate; late DateTime? releaseDate;
late String? changeLog; late String? changeLog;
late List<MapEntry<String, String>> allAssetUrls;
APKDetails(this.version, this.apkUrls, this.names, APKDetails(this.version, this.apkUrls, this.names,
{this.releaseDate, this.changeLog}); {this.releaseDate, this.changeLog, this.allAssetUrls = const []});
} }
stringMapListTo2DList(List<MapEntry<String, String>> mapList) => stringMapListTo2DList(List<MapEntry<String, String>> mapList) =>
@ -104,6 +105,21 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
additionalSettings.remove('releaseDateAsVersion'); additionalSettings.remove('releaseDateAsVersion');
} }
} }
// Convert dropdown style version detection options back into bool style
if (additionalSettings['versionDetection'] == 'standardVersionDetection') {
additionalSettings['versionDetection'] = true;
} else if (additionalSettings['versionDetection'] == 'noVersionDetection') {
additionalSettings['versionDetection'] = false;
} else if (additionalSettings['versionDetection'] == 'releaseDateAsVersion') {
additionalSettings['versionDetection'] = false;
additionalSettings['releaseDateAsVersion'] = true;
}
// Convert bool style pseudo version method to dropdown style
if (originalAdditionalSettings['supportFixedAPKURL'] == true) {
additionalSettings['defaultPseudoVersioningMethod'] = 'partialAPKHash';
} else if (originalAdditionalSettings['supportFixedAPKURL'] == false) {
additionalSettings['defaultPseudoVersioningMethod'] = 'APKLinkHash';
}
// Ensure additionalSettings are correctly typed // Ensure additionalSettings are correctly typed
for (var item in formItems) { for (var item in formItems) {
if (additionalSettings[item.key] != null) { if (additionalSettings[item.key] != null) {
@ -163,6 +179,24 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
return e['customLinkFilterRegex']?.isNotEmpty == true; return e['customLinkFilterRegex']?.isNotEmpty == true;
}).toList(); }).toList();
} }
// Steam source apps should be converted to HTML (#1244)
var legacySteamSourceApps = SteamMobile().apks.keys;
if (legacySteamSourceApps.contains(additionalSettings['app'] ?? '')) {
json['url'] = '${json['url']}/mobile';
var replacementAdditionalSettings = getDefaultValuesFromFormItems(
HTML().combinedAppSpecificSettingFormItems);
for (var s in replacementAdditionalSettings.keys) {
if (additionalSettings.containsKey(s)) {
replacementAdditionalSettings[s] = additionalSettings[s];
}
}
replacementAdditionalSettings['customLinkFilterRegex'] =
'/${additionalSettings['app']}-(([0-9]+\\.?){1,})\\.apk';
replacementAdditionalSettings['versionExtractionRegEx'] =
replacementAdditionalSettings['customLinkFilterRegex'];
replacementAdditionalSettings['matchGroupToUse'] = '\$1';
additionalSettings = replacementAdditionalSettings;
}
} }
json['additionalSettings'] = jsonEncode(additionalSettings); json['additionalSettings'] = jsonEncode(additionalSettings);
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately // F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
@ -190,6 +224,7 @@ class App {
String? installedVersion; String? installedVersion;
late String latestVersion; late String latestVersion;
List<MapEntry<String, String>> apkUrls = []; List<MapEntry<String, String>> apkUrls = [];
List<MapEntry<String, String>> otherAssetUrls = [];
late int preferredApkIndex; late int preferredApkIndex;
late Map<String, dynamic> additionalSettings; late Map<String, dynamic> additionalSettings;
late DateTime? lastUpdateCheck; late DateTime? lastUpdateCheck;
@ -215,7 +250,8 @@ class App {
this.releaseDate, this.releaseDate,
this.changeLog, this.changeLog,
this.overrideSource, this.overrideSource,
this.allowIdChange = false}); this.allowIdChange = false,
this.otherAssetUrls = const []});
@override @override
String toString() { String toString() {
@ -247,40 +283,44 @@ class App {
changeLog: changeLog, changeLog: changeLog,
releaseDate: releaseDate, releaseDate: releaseDate,
overrideSource: overrideSource, overrideSource: overrideSource,
allowIdChange: allowIdChange); allowIdChange: allowIdChange,
otherAssetUrls: otherAssetUrls);
factory App.fromJson(Map<String, dynamic> json) { factory App.fromJson(Map<String, dynamic> json) {
json = appJSONCompatibilityModifiers(json); json = appJSONCompatibilityModifiers(json);
return App( return App(
json['id'] as String, json['id'] as String,
json['url'] as String, json['url'] as String,
json['author'] as String, json['author'] as String,
json['name'] as String, json['name'] as String,
json['installedVersion'] == null json['installedVersion'] == null
? null ? null
: json['installedVersion'] as String, : json['installedVersion'] as String,
json['latestVersion'] as String, (json['latestVersion'] ?? tr('unknown')) as String,
assumed2DlistToStringMapList(jsonDecode(json['apkUrls'])), assumed2DlistToStringMapList(
json['preferredApkIndex'] as int, jsonDecode((json['apkUrls'] ?? '[["placeholder", "placeholder"]]'))),
jsonDecode(json['additionalSettings']) as Map<String, dynamic>, (json['preferredApkIndex'] ?? -1) as int,
json['lastUpdateCheck'] == null jsonDecode(json['additionalSettings']) as Map<String, dynamic>,
? null json['lastUpdateCheck'] == null
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), ? null
json['pinned'] ?? false, : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
categories: json['categories'] != null json['pinned'] ?? false,
? (json['categories'] as List<dynamic>) categories: json['categories'] != null
.map((e) => e.toString()) ? (json['categories'] as List<dynamic>)
.toList() .map((e) => e.toString())
: json['category'] != null .toList()
? [json['category'] as String] : json['category'] != null
: [], ? [json['category'] as String]
releaseDate: json['releaseDate'] == null : [],
? null releaseDate: json['releaseDate'] == null
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']), ? null
changeLog: : DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
json['changeLog'] == null ? null : json['changeLog'] as String, changeLog: json['changeLog'] == null ? null : json['changeLog'] as String,
overrideSource: json['overrideSource'], overrideSource: json['overrideSource'],
allowIdChange: json['allowIdChange'] ?? false); allowIdChange: json['allowIdChange'] ?? false,
otherAssetUrls: assumed2DlistToStringMapList(
jsonDecode((json['otherAssetUrls'] ?? '[]'))),
);
} }
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -291,6 +331,7 @@ class App {
'installedVersion': installedVersion, 'installedVersion': installedVersion,
'latestVersion': latestVersion, 'latestVersion': latestVersion,
'apkUrls': jsonEncode(stringMapListTo2DList(apkUrls)), 'apkUrls': jsonEncode(stringMapListTo2DList(apkUrls)),
'otherAssetUrls': jsonEncode(stringMapListTo2DList(otherAssetUrls)),
'preferredApkIndex': preferredApkIndex, 'preferredApkIndex': preferredApkIndex,
'additionalSettings': jsonEncode(additionalSettings), 'additionalSettings': jsonEncode(additionalSettings),
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
@ -348,8 +389,12 @@ List<MapEntry<String, String>> getApkUrlsFromUrls(List<String> urls) =>
return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e); return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e);
}).toList(); }).toList();
getSourceRegex(List<String> hosts) {
return '(${hosts.join('|').replaceAll('.', '\\.')})';
}
abstract class AppSource { abstract class AppSource {
String? host; List<String> hosts = [];
bool hostChanged = false; bool hostChanged = false;
late String name; late String name;
bool enforceTrackOnly = false; bool enforceTrackOnly = false;
@ -358,28 +403,24 @@ abstract class AppSource {
bool allowSubDomains = false; bool allowSubDomains = false;
bool naiveStandardVersionDetection = false; bool naiveStandardVersionDetection = false;
bool neverAutoSelect = false; bool neverAutoSelect = false;
bool showReleaseDateAsVersionToggle = false;
bool versionDetectionDisallowed = false;
List<String> excludeCommonSettingKeys = [];
AppSource() { AppSource() {
name = runtimeType.toString(); name = runtimeType.toString();
} }
overrideVersionDetectionFormDefault(String vd, overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(String key,
{bool disableStandard = false, bool disableRelDate = false}) { {bool disabled = true, bool defaultValue = true}) {
additionalAppSpecificSourceAgnosticSettingFormItems = additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly =
additionalAppSpecificSourceAgnosticSettingFormItems.map((e) { additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
.map((e) {
return e.map((e2) { return e.map((e2) {
if (e2.key == 'versionDetection') { if (e2.key == key) {
var item = e2 as GeneratedFormDropdown; var item = e2 as GeneratedFormSwitch;
item.defaultValue = vd; item.disabled = disabled;
item.disabledOptKeys = []; item.defaultValue = defaultValue;
if (disableStandard) {
item.disabledOptKeys?.add('standardVersionDetection');
}
if (disableRelDate) {
item.disabledOptKeys?.add('releaseDateAsVersion');
}
item.disabledOptKeys =
item.disabledOptKeys?.where((element) => element != vd).toList();
} }
return e2; return e2;
}).toList(); }).toList();
@ -395,8 +436,8 @@ abstract class AppSource {
} }
Future<Map<String, String>?> getRequestHeaders( Future<Map<String, String>?> getRequestHeaders(
{Map<String, dynamic> additionalSettings = const <String, dynamic>{}, Map<String, dynamic> additionalSettings,
bool forAPKDownload = false}) async { {bool forAPKDownload = false}) async {
return null; return null;
} }
@ -404,12 +445,10 @@ abstract class AppSource {
return app; return app;
} }
Future<Response> sourceRequest(String url, Future<Response> sourceRequest(
{bool followRedirects = true, String url, Map<String, dynamic> additionalSettings,
Map<String, dynamic> additionalSettings = {bool followRedirects = true}) async {
const <String, dynamic>{}}) async { var requestHeaders = await getRequestHeaders(additionalSettings);
var requestHeaders =
await getRequestHeaders(additionalSettings: additionalSettings);
if (requestHeaders != null || followRedirects == false) { if (requestHeaders != null || followRedirects == false) {
var req = Request('GET', Uri.parse(url)); var req = Request('GET', Uri.parse(url));
req.followRedirects = followRedirects; req.followRedirects = followRedirects;
@ -437,7 +476,7 @@ abstract class AppSource {
// Some additional data may be needed for Apps regardless of Source // Some additional data may be needed for Apps regardless of Source
List<List<GeneratedFormItem>> List<List<GeneratedFormItem>>
additionalAppSpecificSourceAgnosticSettingFormItems = [ additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly = [
[ [
GeneratedFormSwitch( GeneratedFormSwitch(
'trackOnly', 'trackOnly',
@ -455,16 +494,12 @@ abstract class AppSource {
label: tr('matchGroupToUse'), required: false, hint: '\$0') label: tr('matchGroupToUse'), required: false, hint: '\$0')
], ],
[ [
GeneratedFormDropdown( GeneratedFormSwitch('versionDetection',
'versionDetection', label: tr('versionDetectionExplanation'), defaultValue: true)
[ ],
MapEntry( [
'standardVersionDetection', tr('standardVersionDetection')), GeneratedFormSwitch('useVersionCodeAsOSVersion',
MapEntry('releaseDateAsVersion', tr('releaseDateAsVersion')), label: tr('useVersionCodeAsOSVersion'), defaultValue: false)
MapEntry('noVersionDetection', tr('noVersionDetection'))
],
label: tr('versionDetection'),
defaultValue: 'standardVersionDetection')
], ],
[ [
GeneratedFormTextField('apkFilterRegEx', GeneratedFormTextField('apkFilterRegEx',
@ -476,11 +511,21 @@ abstract class AppSource {
} }
]) ])
], ],
[
GeneratedFormSwitch('invertAPKFilter',
label: '${tr('invertRegEx')} (${tr('filterAPKsByRegEx')})',
defaultValue: false)
],
[ [
GeneratedFormSwitch('autoApkFilterByArch', GeneratedFormSwitch('autoApkFilterByArch',
label: tr('autoApkFilterByArch'), defaultValue: true) label: tr('autoApkFilterByArch'), defaultValue: true)
], ],
[GeneratedFormTextField('appName', label: tr('appName'), required: false)], [GeneratedFormTextField('appName', label: tr('appName'), required: false)],
[
GeneratedFormSwitch('shizukuPretendToBeGooglePlay',
label: tr('shizukuPretendToBeGooglePlay'),
defaultValue: false)
],
[ [
GeneratedFormSwitch('exemptFromBackgroundUpdates', GeneratedFormSwitch('exemptFromBackgroundUpdates',
label: tr('exemptFromBackgroundUpdates')) label: tr('exemptFromBackgroundUpdates'))
@ -494,9 +539,48 @@ abstract class AppSource {
// Previous 2 variables combined into one at runtime for convenient usage // Previous 2 variables combined into one at runtime for convenient usage
List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems { List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems {
if (showReleaseDateAsVersionToggle == true) {
if (additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
.indexWhere((List<GeneratedFormItem> e) =>
e.indexWhere((GeneratedFormItem i) =>
i.key == 'releaseDateAsVersion') >=
0) <
0) {
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly.insert(
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
.indexWhere((List<GeneratedFormItem> e) =>
e.indexWhere((GeneratedFormItem i) =>
i.key == 'versionDetection') >=
0) +
1,
[
GeneratedFormSwitch('releaseDateAsVersion',
label:
'${tr('releaseDateAsVersion')} (${tr('pseudoVersion')})',
defaultValue: false)
]);
}
}
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly =
additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
.map((e) => e
.where((ee) => !excludeCommonSettingKeys.contains(ee.key))
.toList())
.where((e) => e.isNotEmpty)
.toList();
if (versionDetectionDisallowed) {
overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(
'versionDetection',
disabled: true,
defaultValue: false);
overrideAdditionalAppSpecificSourceAgnosticSettingSwitch(
'useVersionCodeAsOSVersion',
disabled: true,
defaultValue: false);
}
return [ return [
...additionalSourceAppSpecificSettingFormItems, ...additionalSourceAppSpecificSettingFormItems,
...additionalAppSpecificSourceAgnosticSettingFormItems ...additionalAppSpecificSourceAgnosticSettingFormItemsNeverUseDirectly
]; ];
} }
@ -526,8 +610,8 @@ abstract class AppSource {
return null; return null;
} }
Future<String> apkUrlPrefetchModifier( Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
String apkUrl, String standardUrl) async { Map<String, dynamic> additionalSettings) async {
return apkUrl; return apkUrl;
} }
@ -641,6 +725,20 @@ String? extractVersion(String? versionExtractionRegEx, String? matchGroupString,
} }
} }
List<MapEntry<String, String>> filterApks(
List<MapEntry<String, String>> apkUrls,
String? apkFilterRegEx,
bool? invert) {
if (apkFilterRegEx?.isNotEmpty == true) {
var reg = RegExp(apkFilterRegEx!);
apkUrls = apkUrls.where((element) {
var hasMatch = reg.hasMatch(element.key);
return invert == true ? !hasMatch : hasMatch;
}).toList();
}
return apkUrls;
}
class SourceProvider { class SourceProvider {
// Add more source classes here so they are available via the service // Add more source classes here so they are available via the service
List<AppSource> get sources => [ List<AppSource> get sources => [
@ -650,7 +748,6 @@ class SourceProvider {
FDroid(), FDroid(),
FDroidRepo(), FDroidRepo(),
IzzyOnDroid(), IzzyOnDroid(),
SourceForge(),
SourceHut(), SourceHut(),
APKPure(), APKPure(),
Aptoide(), Aptoide(),
@ -658,14 +755,12 @@ class SourceProvider {
APKMirror(), APKMirror(),
HuaweiAppGallery(), HuaweiAppGallery(),
Jenkins(), Jenkins(),
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
Mullvad(),
Signal(), Signal(),
VLC(), VLC(),
WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date WhatsApp(),
TelegramApp(), TelegramApp(),
SteamMobile(),
NeutronCode(), NeutronCode(),
DirectAPKLink(),
HTML() // This should ALWAYS be the last option as they are tried in order HTML() // This should ALWAYS be the last option as they are tried in order
]; ];
@ -681,22 +776,26 @@ class SourceProvider {
throw UnsupportedURLError(); throw UnsupportedURLError();
} }
var res = srcs.first; var res = srcs.first;
res.host = Uri.parse(url).host; res.hosts = [Uri.parse(url).host];
res.hostChanged = true; res.hostChanged = true;
return srcs.first; return srcs.first;
} }
AppSource? source; AppSource? source;
for (var s in sources.where((element) => element.host != null)) { for (var s in sources.where((element) => element.hosts.isNotEmpty)) {
if (RegExp( try {
'://${s.allowSubDomains ? '([^\\.]+\\.)*' : '(www\\.)?'}${s.host}(/|\\z)?') if (RegExp(
.hasMatch(url)) { '^${s.allowSubDomains ? '([^\\.]+\\.)*' : '(www\\.)?'}(${getSourceRegex(s.hosts)})\$')
source = s; .hasMatch(Uri.parse(url).host)) {
break; source = s;
break;
}
} catch (e) {
// Ignore
} }
} }
if (source == null) { if (source == null) {
for (var s in sources.where( for (var s in sources.where(
(element) => element.host == null && !element.neverAutoSelect)) { (element) => element.hosts.isEmpty && !element.neverAutoSelect)) {
try { try {
s.sourceSpecificStandardizeURL(url); s.sourceSpecificStandardizeURL(url);
source = s; source = s;
@ -731,7 +830,7 @@ class SourceProvider {
AppSource source, String url, Map<String, dynamic> additionalSettings, AppSource source, String url, Map<String, dynamic> additionalSettings,
{App? currentApp, {App? currentApp,
bool trackOnlyOverride = false, bool trackOnlyOverride = false,
String? overrideSource, bool sourceIsOverriden = false,
bool inferAppIdIfOptional = false}) async { bool inferAppIdIfOptional = false}) async {
if (trackOnlyOverride || source.enforceTrackOnly) { if (trackOnlyOverride || source.enforceTrackOnly) {
additionalSettings['trackOnly'] = true; additionalSettings['trackOnly'] = true;
@ -741,8 +840,9 @@ class SourceProvider {
APKDetails apk = APKDetails apk =
await source.getLatestAPKDetails(standardUrl, additionalSettings); await source.getLatestAPKDetails(standardUrl, additionalSettings);
if (source.runtimeType != HTML().runtimeType) { if (source.runtimeType !=
// HTML does it separately HTML().runtimeType && // Some sources do it separately
source.runtimeType != SourceForge().runtimeType) {
String? extractedVersion = extractVersion( String? extractedVersion = extractVersion(
additionalSettings['versionExtractionRegEx'] as String?, additionalSettings['versionExtractionRegEx'] as String?,
additionalSettings['matchGroupToUse'] as String?, additionalSettings['matchGroupToUse'] as String?,
@ -752,15 +852,12 @@ class SourceProvider {
} }
} }
if (additionalSettings['versionDetection'] == 'releaseDateAsVersion' && if (additionalSettings['releaseDateAsVersion'] == true &&
apk.releaseDate != null) { apk.releaseDate != null) {
apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString(); apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString();
} }
if (additionalSettings['apkFilterRegEx'] != null) { apk.apkUrls = filterApks(apk.apkUrls, additionalSettings['apkFilterRegEx'],
var reg = RegExp(additionalSettings['apkFilterRegEx']); additionalSettings['invertAPKFilter']);
apk.apkUrls =
apk.apkUrls.where((element) => reg.hasMatch(element.key)).toList();
}
if (apk.apkUrls.isEmpty && !trackOnly) { if (apk.apkUrls.isEmpty && !trackOnly) {
throw NoAPKError(); throw NoAPKError();
} }
@ -801,12 +898,16 @@ class SourceProvider {
categories: currentApp?.categories ?? const [], categories: currentApp?.categories ?? const [],
releaseDate: apk.releaseDate, releaseDate: apk.releaseDate,
changeLog: apk.changeLog, changeLog: apk.changeLog,
overrideSource: overrideSource ?? currentApp?.overrideSource, overrideSource: sourceIsOverriden
? source.runtimeType.toString()
: currentApp?.overrideSource,
allowIdChange: currentApp?.allowIdChange ?? allowIdChange: currentApp?.allowIdChange ??
trackOnly || trackOnly ||
(source.appIdInferIsOptional && (source.appIdInferIsOptional &&
inferAppIdIfOptional) // Optional ID inferring may be incorrect - allow correction on first install inferAppIdIfOptional), // Optional ID inferring may be incorrect - allow correction on first install
); otherAssetUrls: apk.allAssetUrls
.where((a) => apk.apkUrls.indexWhere((p) => a.key == p.key) < 0)
.toList());
return source.endOfGetAppChanges(finalApp); return source.endOfGetAppChanges(finalApp);
} }
@ -825,6 +926,7 @@ class SourceProvider {
apps.add(await getApp( apps.add(await getApp(
source, source,
url, url,
sourceIsOverriden: sourceOverride != null,
getDefaultValuesFromFormItems( getDefaultValuesFromFormItems(
source.combinedAppSpecificSettingFormItems))); source.combinedAppSpecificSettingFormItems)));
} catch (e) { } catch (e) {

View File

@ -5,10 +5,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: android_intent_plus name: android_intent_plus
sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9 sha256: "2bfdbee8d65e7c26f88b66f0a91f2863da4d3596d8a658b4162c8de5cf04b074"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.3" version: "5.0.2"
android_package_installer: android_package_installer:
dependency: "direct main" dependency: "direct main"
description: description:
@ -22,26 +22,35 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: android_package_manager name: android_package_manager
sha256: b873fe5856f7c442aca9751dac05d117285be9e4de08eb15d1ffb811fd1b688d sha256: "2de859fae7226a7de1c1ff9a2308f1967599408800330501a1ce97927c051153"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.7.1"
android_system_font:
dependency: "direct main"
description:
path: "."
ref: master
resolved-ref: "355f897e92a58a803f91d9270d389d9ec40ba550"
url: "https://github.com/re7gog/android_system_font"
source: git
version: "1.0.0"
animations: animations:
dependency: "direct main" dependency: "direct main"
description: description:
name: animations name: animations
sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5" sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.10" version: "2.0.11"
app_links: app_links:
dependency: "direct main" dependency: "direct main"
description: description:
name: app_links name: app_links
sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb" sha256: "42dc15aecf2618ace4ffb74a2e58a50e45cd1b9f2c17c8f0cafe4c297f08c815"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.5.0" version: "4.0.1"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@ -54,10 +63,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: args name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.2" version: "2.5.0"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -70,10 +79,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: background_fetch name: background_fetch
sha256: f70b28a0f7a3156195e9742229696f004ea3bf10f74039b7bf4c78a74fbda8a4 sha256: "81d0d4eeecd17c971335438a5a55554c8302f479f92c7f7bc7f147f75d3f6074"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.3.3"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -126,18 +135,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" sha256: db7a4e143dc72cc3cb2044ef9b052a7ebfe729513e6a82943bc3526f784365b8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "6.0.3"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a sha256: b6a56efe1e6675be240de39107281d4034b64ac23438026355b4234042a35adb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.4" version: "2.0.0"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -150,10 +159,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+8" version: "0.3.4+1"
crypto: crypto:
dependency: "direct main" dependency: "direct main"
description: description:
@ -174,10 +183,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.6" version: "1.0.8"
dbus: dbus:
dependency: transitive dependency: transitive
description: description:
@ -190,10 +199,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: device_info_plus name: device_info_plus
sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.1.1" version: "10.1.0"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -206,18 +215,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: dynamic_color name: dynamic_color
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.6.9" version: "1.7.0"
easy_localization: easy_localization:
dependency: "direct main" dependency: "direct main"
description: description:
name: easy_localization name: easy_localization
sha256: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5 sha256: "432698c31a488dd64c56d4759f20d04844baba5e9e4f2cb1abb9676257918b17"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.6"
easy_logger: easy_logger:
dependency: transitive dependency: transitive
description: description:
@ -226,6 +235,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.2" version: "0.0.2"
equations:
dependency: "direct main"
description:
name: equations
sha256: ae30e977d601e19aa1fc3409736c5eac01559d1d653a4c30141fbc4e86aa605c
url: "https://pub.dev"
source: hosted
version: "5.0.2"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -238,10 +255,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -254,10 +271,34 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6" sha256: "45c70b43df893027e441a6fa0aacc8f484fb9f9c60c746dc8f1dc4f774cf55cd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.1" version: "8.0.2"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flex_color_picker:
dependency: "direct main"
description:
name: flex_color_picker
sha256: "5c846437069fb7afdd7ade6bf37e628a71d2ab0787095ddcb1253bf9345d5f3a"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flex_seed_scheme:
dependency: transitive
description:
name: flex_seed_scheme
sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -267,10 +308,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_archive name: flutter_archive
sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae" sha256: "5ca235f304c12bf468979235f400f79846d204169d715939e39197106f5fc970"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.0.3"
flutter_fgbg: flutter_fgbg:
dependency: "direct main" dependency: "direct main"
description: description:
@ -291,18 +332,18 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
flutter_local_notifications: flutter_local_notifications:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_local_notifications name: flutter_local_notifications
sha256: "892ada16046d641263f30c72e7432397088810a84f34479f6677494802a2b535" sha256: "8cdc719114ab1c86c64bb7a86d3a679674c3637edd229e3a994797d4a1504ce4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "16.3.0" version: "17.1.0"
flutter_local_notifications_linux: flutter_local_notifications_linux:
dependency: transitive dependency: transitive
description: description:
@ -315,10 +356,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_local_notifications_platform_interface name: flutter_local_notifications_platform_interface
sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.0+1" version: "7.1.0"
flutter_localizations: flutter_localizations:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -328,18 +369,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_markdown name: flutter_markdown
sha256: "35108526a233cc0755664d445f8a6b4b61e6f8fe993b3658b80b4a26827fc196" sha256: "9921f9deda326f8a885e202b1e35237eadfc1345239a0f6f0f1ff287e047547f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.18+2" version: "0.7.1"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.17" version: "2.0.19"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -354,10 +395,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: fluttertoast name: fluttertoast
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 sha256: "81b68579e23fcbcada2db3d50302813d2371664afe6165bc78148050ab94bf66"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.4" version: "8.2.5"
fraction:
dependency: transitive
description:
name: fraction
sha256: "09e9504c9177bbd77df56e5d147abfbb3b43360e64bf61510059c14d6a82d524"
url: "https://pub.dev"
source: hosted
version: "5.0.2"
gtk: gtk:
dependency: transitive dependency: transitive
description: description:
@ -386,10 +435,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.2.1"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -402,10 +451,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image name: image
sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.3" version: "4.1.7"
intl: intl:
dependency: transitive dependency: transitive
description: description:
@ -418,10 +467,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: js name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.7" version: "0.7.1"
json_annotation: json_annotation:
dependency: transitive dependency: transitive
description: description:
@ -430,6 +479,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.8.1" version: "4.8.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "2.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -442,42 +515,42 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: markdown name: markdown
sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.1.1" version: "7.2.2"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.16+1"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.8.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.11.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
name: mime name: mime
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "1.0.5"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@ -498,34 +571,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.3" version: "1.9.0"
path_provider: path_provider:
dependency: "direct main" dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.3"
path_provider_android: path_provider_android:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.4"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.2"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -538,10 +611,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_platform_interface name: path_provider_platform_interface
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
path_provider_windows: path_provider_windows:
dependency: transitive dependency: transitive
description: description:
@ -554,130 +627,130 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: permission_handler name: permission_handler
sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.1.0" version: "11.3.1"
permission_handler_android: permission_handler_android:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_android name: permission_handler_android
sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "12.0.1" version: "12.0.5"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.2.0" version: "9.4.4"
permission_handler_html: permission_handler_html:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_html name: permission_handler_html
sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df" sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.0+2" version: "0.1.1"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.2.1"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_windows name: permission_handler_windows
sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.0" version: "0.2.1"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.2" version: "5.4.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.3" version: "3.1.4"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.7" version: "2.1.8"
pointycastle: pointycastle:
dependency: transitive dependency: transitive
description: description:
name: pointycastle name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.7.3" version: "3.9.0"
provider: provider:
dependency: "direct main" dependency: "direct main"
description: description:
name: provider name: provider
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.1" version: "6.1.2"
share_plus: share_plus:
dependency: "direct main" dependency: "direct main"
description: description:
name: share_plus name: share_plus
sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.2.1" version: "9.0.0"
share_plus_platform_interface: share_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: share_plus_platform_interface name: share_plus_platform_interface
sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.1" version: "4.0.0"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.3"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.2"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_foundation name: shared_preferences_foundation
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.4" version: "2.3.5"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@ -690,18 +763,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_platform_interface name: shared_preferences_platform_interface
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.2"
shared_preferences_web: shared_preferences_web:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_web name: shared_preferences_web
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.3.0"
shared_preferences_windows: shared_preferences_windows:
dependency: transitive dependency: transitive
description: description:
@ -714,10 +787,19 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_storage name: shared_storage
sha256: "7c65a9d64f0f5521256be974cfd74010af12196657cec9f9fb7b03b2f11bcaf6" sha256: cf20428d06af065311b71e09cbfbbfe431e979a3bf9180001c1952129b7c708f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.0" version: "0.8.1"
shizuku_apk_installer:
dependency: "direct main"
description:
path: "."
ref: master
resolved-ref: "25acc02612c2e0fcae40d312e047ac48106f8f6b"
url: "https://github.com/re7gog/shizuku_apk_installer"
source: git
version: "0.0.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -743,18 +825,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqflite name: sqflite
sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" sha256: "5ce2e1a15e822c3b4bfb5400455775e421da7098eed8adc8f26298ada7c9308c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "2.3.3"
sqflite_common: sqflite_common:
dependency: transitive dependency: transitive
description: description:
name: sqflite_common name: sqflite_common
sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0+2" version: "2.5.4"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -823,26 +905,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.2" version: "6.2.6"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.0" version: "6.3.1"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.1" version: "6.2.5"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@ -863,18 +945,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_platform_interface name: url_launcher_platform_interface
sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.3.2"
url_launcher_web: url_launcher_web:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.3.1"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@ -887,10 +969,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: uuid name: uuid
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f" sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.2" version: "4.4.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -899,78 +981,86 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "13.0.0"
web: web:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "0.5.1"
webview_flutter: webview_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: "42393b4492e629aa3a88618530a4a00de8bb46e50e7b3993fedbfdc5352f0dbf" sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.4.2" version: "4.7.0"
webview_flutter_android: webview_flutter_android:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: b54c89fe14a6d26a2a46e24880da0441cdd2bf1f6d01a5b3e1d39558feb1de0b sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.13.1" version: "3.16.0"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_platform_interface name: webview_flutter_platform_interface
sha256: dbe745ee459a16b6fec296f7565a8ef430d0d681001d8ae521898b9361854943 sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.9.0" version: "2.10.0"
webview_flutter_wkwebview: webview_flutter_wkwebview:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: eebfabfa8a115b535b52031b8b26f7a4b58ceceab378bc9db8762b0fb46f7b5d sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.10.0" version: "3.13.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "5.4.0"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
name: win32_registry name: win32_registry
sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
name: xdg_directories name: xdg_directories
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.3" version: "1.0.4"
xml: xml:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.5.0" version: "6.3.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@ -980,5 +1070,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.2.0 <4.0.0" dart: ">=3.3.3 <4.0.0"
flutter: ">=3.16.0" flutter: ">=3.19.0"

View File

@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.15.4+240 # When changing this, update the tag in main() accordingly version: 1.1.6+2263
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
@ -38,7 +38,7 @@ dependencies:
cupertino_icons: ^1.0.5 cupertino_icons: ^1.0.5
path_provider: ^2.0.11 path_provider: ^2.0.11
flutter_fgbg: ^0.3.0 # Try removing reliance on this flutter_fgbg: ^0.3.0 # Try removing reliance on this
flutter_local_notifications: ^16.1.0 flutter_local_notifications: ^17.0.0
provider: ^6.0.3 provider: ^6.0.3
http: ^1.0.0 http: ^1.0.0
webview_flutter: ^4.0.0 webview_flutter: ^4.0.0
@ -48,26 +48,36 @@ dependencies:
url_launcher: ^6.1.5 url_launcher: ^6.1.5
permission_handler: ^11.0.0 permission_handler: ^11.0.0
fluttertoast: ^8.0.9 fluttertoast: ^8.0.9
device_info_plus: ^9.0.0 device_info_plus: ^10.0.1
file_picker: ^6.0.0 file_picker: ^8.0.0+1
animations: ^2.0.4 animations: ^2.0.4
android_package_installer: android_package_installer:
git: git:
url: https://github.com/ImranR98/android_package_installer url: https://github.com/ImranR98/android_package_installer
ref: main ref: main
android_package_manager: ^0.6.0 android_package_manager: ^0.7.0
share_plus: ^7.0.0 share_plus: ^9.0.0
sqflite: ^2.2.0+3 sqflite: ^2.2.0+3
easy_localization: ^3.0.1 easy_localization: ^3.0.1
android_intent_plus: ^4.0.0 android_intent_plus: ^5.0.1
flutter_markdown: ^0.6.14 flutter_markdown: ^0.7.1
flutter_archive: ^6.0.0 flutter_archive: ^6.0.0
hsluv: ^1.1.3 hsluv: ^1.1.3
connectivity_plus: ^5.0.0 connectivity_plus: ^6.0.1
shared_storage: ^0.8.0 shared_storage: ^0.8.0
crypto: ^3.0.3 crypto: ^3.0.3
app_links: ^3.5.0 app_links: ^4.0.0
background_fetch: ^1.2.1 background_fetch: ^1.2.1
equations: ^5.0.2
flex_color_picker: ^3.4.1
android_system_font:
git:
url: https://github.com/re7gog/android_system_font
ref: master
shizuku_apk_installer:
git:
url: https://github.com/re7gog/shizuku_apk_installer
ref: master
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: