Compare commits

...

352 Commits

Author SHA1 Message Date
33c60c9d4f Merge pull request #1801 from ImranR98/dev
Switch back to old Shizuku plugin version (new one broke build)
2024-08-19 23:03:40 -04:00
151e085655 Switch back to old Shizuku plugin version (new one broke build) 2024-08-19 23:03:20 -04:00
b1d641ae24 Merge pull request #1783 from Mr-Update/patch-1
Update de.json
2024-08-19 22:56:51 -04:00
7ddb35f933 Update de.json 2024-08-19 22:56:38 -04:00
b05a2ed14f Merge pull request #1800 from ImranR98/dev
- GitLab bugfix for some APK URLs (#1790)
- Fix Uptodown - was completely broken (#1796)
- Slight XAPK support improvement + BG install bugfix
2024-08-19 22:55:37 -04:00
b22a82d11b Upgrade packages, increment version 2024-08-19 22:54:24 -04:00
ee051153ca Merge remote-tracking branch 'origin/main' into dev 2024-08-19 22:53:44 -04:00
46871191c4 Merge pull request #1787 from Kevinr99089/main
French corrections
2024-08-19 22:48:45 -04:00
0cb518d434 Merge pull request #1785 from PeterDaveHelloKitchen/Improve-zh-TW-translation
Improve zh-TW traditional Chinese translation
2024-08-19 22:48:36 -04:00
03778fd743 Fix Uptodown - was completely broken (#1796) 2024-08-19 22:37:57 -04:00
514ca3e28b Slight XAPK support improvement + BG install bugfix 2024-08-19 21:29:19 -04:00
37e90dabe7 GitLab bugfix for some APK URLs (#1790) 2024-08-19 20:55:37 -04:00
ee99f2b71f Update de.json 2024-08-17 21:39:56 +02:00
ca9289f104 Some words corrected #5
- Correction of some words suggested by [AntoninDelFabbro](https://github.com/AntoninDelFabbro).
2024-08-14 16:10:30 +02:00
aefe7eaa5f Improve zh-TW traditional Chinese translation 2024-08-11 04:19:53 +08:00
e979f7c6f3 Update de.json 2024-08-09 00:13:02 +02:00
e2118a3b53 Some words corrected #4
- Correction of some words suggested by [AntoninDelFabbro](https://github.com/AntoninDelFabbro).
- Additional modification to be made later (awaiting reply)
2024-08-08 02:07:15 +02:00
d3a5d3b0fa Some words corrected #3
- Correction of some words suggested by [AntoninDelFabbro](https://github.com/AntoninDelFabbro).
- Additional modification to be made later (awaiting reply)
2024-08-07 13:49:07 +02:00
bc7c70aca6 Correcting some words #2
- I made half of the modifications previously
- Correction of some words suggested by [AntoninDelFabbro](https://github.com/AntoninDelFabbro).
- Additional modification to be made later (awaiting reply)
2024-08-07 13:39:57 +02:00
6cde454bd9 Some words corrected #01
- Correction of some words suggested by [AntoninDelFabbro](https://github.com/AntoninDelFabbro).
- Additional modification to be made later (awaiting reply)
2024-08-07 12:42:06 +02:00
78e20984ed Merge pull request #1778 from ImranR98/dev
Yet another APKPure bugfix (last one)
2024-08-06 16:48:37 -04:00
f963996a19 Version increment 2024-08-06 16:48:02 -04:00
1ddf2869a6 Yet another APKPure bugfix (last one) 2024-08-06 16:47:38 -04:00
f9b97b4469 Merge pull request #1777 from ImranR98/dev
APKPure bugfix: Correctly extract APKs that are multi-arch but not universal
2024-08-06 15:52:31 -04:00
6f706ab493 Update Flutter version + update packages, increment version 2024-08-06 15:51:54 -04:00
ed2e6e2e9e APKPure bugfix: Correctly extract APKs that are multi-arch but not universal 2024-08-06 15:49:44 -04:00
f00758cd83 Merge pull request #1774 from ImranR98/dev
- Fix bug introduced in v1.1.16: APKPure fails to find universal APKs
- Fix bugs found in APKPure URL matching
2024-08-06 11:47:56 -04:00
25bd61f289 Merge pull request #1773 from Kevinr99089/main
Updating certain words (French File)
2024-08-06 11:46:46 -04:00
25d19d22cf Increment version 2024-08-06 11:46:25 -04:00
9ffb91266f Re-order some sources 2024-08-06 11:45:49 -04:00
8d921cfbf1 Fix bugs in APKPure URL matching 2024-08-06 11:45:01 -04:00
3ed6b168e1 Fix bug from previous version: APKPure fails to find universal APKs 2024-08-06 11:36:39 -04:00
4a45c900c3 Some minor corrections
- A job well done is the key (of I don't know what, but it's the key...)
2024-08-06 01:50:03 +02:00
7e9e6958a3 Merge pull request #1771 from Kevinr99089/main
French translation completely revised
2024-08-05 18:01:02 -04:00
9de082f684 Last modification before PR 2024-08-05 23:38:41 +02:00
b40afc7329 Add files via upload 2024-08-05 23:32:32 +02:00
4fb3da45e9 Merge pull request #1770 from ImranR98/dev
- APKPure: Filter releases by available architecture (#598)
- Use a more obvious icon (magnifying glass) for the "filter apps" button (#1762)
2024-08-05 16:33:29 -04:00
1f8e051ed6 Merge remote-tracking branch 'origin/main' into dev 2024-08-05 16:31:15 -04:00
adc4e7c2b4 Fix error after fgbg package update in previous commit 2024-08-05 16:30:12 -04:00
325d2f48dc More consistent tagline 2024-08-05 16:25:02 -04:00
aa00f42a23 Update packages, increment version 2024-08-05 16:24:30 -04:00
62dbffab52 Use a more obvious icon (magnifying glass) for the "filter apps" button (#1762) 2024-08-05 16:21:42 -04:00
fd38444836 Improvement on previous commit 2024-08-05 16:08:12 -04:00
71cc49a30f APKPure: Filter releases by available architecture (#598) 2024-08-05 15:53:44 -04:00
e4187c8e17 Merge pull request #1755 from ImranR98/dev
Update GitLab for new uploads URL scheme (#1742), Ability to share selective exports (#1752)
2024-07-28 17:28:53 -04:00
15ae98d426 Update Flutter + packages, increment version 2024-07-28 17:27:18 -04:00
7b4fa2269e Merge remote-tracking branch 'origin/main' into dev 2024-07-28 17:25:02 -04:00
656e14793d Merge pull request #1748 from asandikci/main
translate: update turkish
2024-07-28 17:24:41 -04:00
d23381147b Merge pull request #1746 from bluefly000/japanese-translation
Update ja.json
2024-07-28 17:24:33 -04:00
cfe184c6d5 Merge pull request #1749 from VladWinner/main
Change the font (support for more languages)
2024-07-28 17:20:21 -04:00
9c16f24a08 Ability to share selective exports (#1752) 2024-07-28 17:18:36 -04:00
ce200403e0 Update GitLab for new uploads URL scheme (#1742) 2024-07-28 16:41:01 -04:00
caca84f84d Change the font (support for more languages) 2024-07-26 01:18:14 +03:00
ac2d7b9639 translate: update turkish 2024-07-24 20:02:57 +03:00
78069a9b26 Update ja.json 2024-07-24 14:35:05 +09:00
3e23fffaea Merge pull request #1725 from ImranR98/dev
- Attempt bugfix for error when on VPN (#1120)
- Translation fixes (#1699)
- GitHub search bugfix (#1705)
- Fix missing trailing slash for path-less URLs (#1715)
- Rename Codeberg to Forgejo (#1722)
- Rename "Version String Extraction" to "Trim Version String" for non-HTML Sources (#1723)
2024-07-14 20:56:22 -04:00
624b9fb6dc Update packages, increment version 2024-07-14 20:50:48 -04:00
fbd6189721 GitHub search bugfix (#1705) 2024-07-14 20:42:20 -04:00
6a44fe227c Fix missing trailing slash for path-less URLs (#1715) 2024-07-14 20:27:42 -04:00
d1cb2688c6 Rename "Version String Extraction" to "Trim Version String" for non-HTML Sources (#1723)
Rename Codeberg to Forgejo (#1722)
2024-07-14 20:20:41 -04:00
b43b2f9740 Merge pull request #1720 from Erudaro/main
Update bs.json
2024-07-14 02:08:43 -04:00
6288a9cb8d Merge pull request #1710 from DwainZwerg/for-eXense-auth-Update-ru.json-see-#1703](https/github.com/ImranR98/Obtainium/issues/1703)
for eXense-auth: Update ru.json (issue#1703)
2024-07-14 02:08:31 -04:00
18c6f75054 Update bs.json 2024-07-13 23:56:33 +00:00
1f8d187b84 Update ru.json
see [#1703](https://github.com/ImranR98/Obtainium/issues/1703)
2024-07-07 12:13:07 +02:00
77618ad1ff Merge remote-tracking branch 'origin/main' into dev 2024-06-30 13:02:43 -04:00
75efd335e9 Update release.yml 2024-06-30 12:56:28 -04:00
c4438de200 Update release.yml 2024-06-30 12:55:38 -04:00
e3c9a227d3 Translation fixes 2024-06-29 17:04:34 -04:00
548f859349 Merge remote-tracking branch 'origin/main' into dev 2024-06-29 16:49:16 -04:00
db413badec Attempt bugfix for error when on VPN (#1120) 2024-06-29 16:48:59 -04:00
dfac3af3f5 Merge pull request #1698 from ImranR98/dev
Bugfix for files with vague 'content-disposition' header (#1697)
2024-06-29 03:03:36 -04:00
5eceaeecde Increment version, run dart fix 2024-06-29 03:02:56 -04:00
62c23004f7 Bugfix for files with vague 'content-disposition' header (#1697) 2024-06-29 03:01:48 -04:00
cd153e7d11 Merge pull request #1696 from ImranR98/dev
- Bugfix: Pull to refresh not working with few apps (#1680)
- Add third-party F-Droid repo search to main search menu (#1681)
- Added autocomplete for F-Droid repos (#1681)
- Bugfix: Missing request headers for direct APK link apps (#1688)
- Release asset download confirmation even for single choice (#1694)
- Add a less obvious touch target to highlights (#1694)
2024-06-28 23:03:14 -04:00
3b494511d7 Merge remote-tracking branch 'origin/main' into dev 2024-06-28 22:54:15 -04:00
6c806a44d4 Merge pull request #1676 from abc0922001/patch-1
add Traditional Chinese translation
2024-06-28 22:54:01 -04:00
c5bac43bfd Merge pull request #1673 from AntoninDelFabbro/patch-1
(following d8c805a + #1670) Undone a mistake
2024-06-28 22:53:52 -04:00
1636281d6d Merge pull request #1674 from catsnote/main
Improved Danish Translation
2024-06-28 22:53:46 -04:00
0f4feb2da6 Merge pull request #1682 from CertainBot/patch-4
Update es.json
2024-06-28 22:53:23 -04:00
c32f34c116 Update packages, increment version 2024-06-28 22:52:24 -04:00
d391c5cfc2 Bugfix: Missing request headers for direct APK link apps (#1688) 2024-06-28 22:50:43 -04:00
bb45a157b3 Add a less obvious touch target to highlights (#1694) 2024-06-28 22:29:34 -04:00
c90a571f89 Release asset download confirmation even for single choice (#1694) 2024-06-28 22:12:45 -04:00
1278407c90 Slight spacing change 2024-06-28 22:04:21 -04:00
dff1b4cf39 Bugfixes for F-Droid repo search (#1681) 2024-06-28 21:55:13 -04:00
105e70a814 F-Droid repo search bugfix (#1681) 2024-06-28 21:28:02 -04:00
2938cea419 Added autocomplete for F-Droid repos (#1681) 2024-06-28 21:03:53 -04:00
9b6b7780d8 Fix F-Droid repo search bugs (#1681) 2024-06-28 20:11:43 -04:00
f53a4f3827 Add third-party F-Droid repo search to main search menu (#1681) 2024-06-28 19:10:45 -04:00
9b0d672553 Bugfix: Pull to refresh not working with few apps (#1680) 2024-06-28 18:51:02 -04:00
9d14145ac2 Update es.json
- English: corrected a few typos and wrong translations
- Español: corregidas faltas y errores de traducción
2024-06-19 19:31:58 +00:00
9948797b25 Create zh-TW.json
add Traditional Chinese translation
2024-06-17 13:08:26 +08:00
cat
a80d9e3623 Improved assets/translations/da.json 2024-06-16 20:22:03 +02:00
37ecb057f9 Undone a mistake 2024-06-15 22:19:42 +02:00
06cbe74c6c Merge pull request #1672 from ImranR98/dev
- Add release date extraction for track-only GitLab and Codeberg apps (#1664)
- Add a scrollbar to the apps page
2024-06-15 02:00:30 -04:00
f5769b85fe Add release date extraction for track-only GitLab and Codeberg apps (#1664) 2024-06-15 01:58:07 -04:00
875868af47 Added DA to the language menu 2024-06-15 01:21:31 -04:00
24ea15d600 Merge remote-tracking branch 'origin/main' into dev 2024-06-15 01:20:23 -04:00
87cdc3dcef Merge pull request #1657 from Grooty12/main
Add da.json
2024-06-15 01:20:01 -04:00
c2f976d7f4 Merge pull request #1670 from AntoninDelFabbro/patch-1
Reworked french translation
2024-06-15 01:19:50 -04:00
ebc46bfd3f Update Flutter submodule 2024-06-15 01:19:33 -04:00
e674f7e89d Merge remote-tracking branch 'origin/main' into dev 2024-06-15 01:19:13 -04:00
86d29b163c Update packages, increment version 2024-06-15 01:19:06 -04:00
a849919799 Add a scrollbar to the apps page 2024-06-15 01:18:50 -04:00
d8c805a6b3 Reworked french translation 2024-06-12 23:07:57 +02:00
8acbd3ef78 "Material You" shouldn't be translated 2024-06-12 20:43:50 +02:00
b81088d767 Add danish translation 2024-06-05 14:33:22 +02:00
7071e34a74 Merge pull request #1643 from ImranR98/dev
- Bugfix: Include GitLab token in APK request (#1622)
- Bugfix: Don't trim trailing slashes when parsing links (#1625)
- Improve contrast of placeholder icon in dark mode (#1637) 
- Improve app loading times
- Revert a previous change to background downloads
- Add a "clear logs" button
2024-05-24 16:27:18 -04:00
6a73ade359 Add a "clear logs" button 2024-05-23 21:18:13 -04:00
6c5e5043a4 Improve icon loading after last commit 2024-05-23 21:02:50 -04:00
5edaf1306d Update packages + Flutter, increment version 2024-05-23 20:17:44 -04:00
5bf7fdb94e Revert a previous change related to BG downloads (did not work as intended) 2024-05-23 20:15:25 -04:00
7808bc5ccb Improve loading time/stability (at the cost of icon flickering) 2024-05-23 20:02:43 -04:00
06a079e452 Attempt to improve load times again + link parsing bugfix (#1625) 2024-05-23 19:35:52 -04:00
de509737e6 Include GitLab token in APK request (#1622) 2024-05-22 19:51:35 -04:00
08a3ba8d13 Merge remote-tracking branch 'origin/main' into dev 2024-05-22 19:37:30 -04:00
2b27902d5f Merge pull request #1633 from teaminh/main
Update vi.json
2024-05-22 19:37:16 -04:00
62185127c2 Merge remote-tracking branch 'origin/main' into dev 2024-05-22 19:36:53 -04:00
9c46e3f88c Attempt to improve app load time
Slight icon opacity tweak (#1637)
2024-05-22 19:35:51 -04:00
daa4de921d Update vi.json 2024-05-19 12:20:28 +07:00
bc977e2a5a Update vi.json 2024-05-19 12:09:36 +07:00
1e3815ca20 Merge pull request #1631 from ImranR98/dev
- Don't use partial downloads for BG tasks (more reliable)
- More accurate error reports for Huawei AppGallery fails
- Various bugfixes
- Update Flutter
2024-05-17 22:35:12 -04:00
0e2fa96b9f More accurate error reports for Huawei AppGallery fails 2024-05-17 16:17:51 -04:00
389aebe54e Various bugfixes 2024-05-17 15:56:41 -04:00
fbfeaf2a91 Update Flutter, packages, increment version 2024-05-16 22:31:21 -04:00
485812d076 Merge remote-tracking branch 'origin/main' into dev 2024-05-16 22:21:58 -04:00
68e98ec719 Don't use partial downloads for BG tasks (more reliable) 2024-05-16 22:21:52 -04:00
cbe41de734 Merge pull request #1620 from mxhdee/main
Update fa.json
2024-05-16 22:11:23 -04:00
abb8641105 Update fa.json 2024-05-13 18:46:01 -04:00
dbcb4b3c09 Merge pull request #1619 from ImranR98/dev
- Bugfix: Get initial data on config import
- Bugfix: Include overrideSource in exported config
2024-05-12 20:47:51 -04:00
b231c756e6 Increment version, update packages 2024-05-12 20:47:06 -04:00
3cb3f7fdd4 Merge remote-tracking branch 'origin/main' into dev 2024-05-12 20:45:34 -04:00
9837e8e325 Merge pull request #1608 from ArcticFoxPro/main
Update zh.json
2024-05-12 20:45:21 -04:00
73d4814f18 Merge pull request #1616 from KoolTechTricks/patch-1
Update ru.json
2024-05-12 20:45:04 -04:00
0a9219c314 Merge pull request #1618 from GitGitro/main
Enable per-app language, closes #1430
2024-05-12 20:44:55 -04:00
56c5a73d9a Bugfix: Get initial data on config import 2024-05-12 20:41:53 -04:00
a30e063246 Bugfix: Include overrideSource in exported config 2024-05-12 20:12:02 -04:00
bd26b6514a Enable per-app language, closes #1430 2024-05-12 18:07:35 +02:00
3ea8c7e888 Update ru.json
Fix incorrect machine translated strings
2024-05-11 21:26:42 +05:00
5f2ec5ce6f Update zh.json 2024-05-07 08:12:17 +08:00
783ce9d555 Update zh.json
`trackOnlyInBrackets`、`searchableInBrackets`:更正半角标点为全角
`enableBackgroundUpdates`:改为“启用全局后台更新”突出全局性
`exemptFromBackgroundUpdates`:“仅此应用生效,即使已启用全局后台更新”更明确了选项条件
`shizukuPretendToBeGooglePlay`:Google Play 不是确定的字符串,在这里指代了 Play Store 实体,无需双引号。
`useLatestAssetDateAsReleaseDate`:英文 Latest 通常在简体中文对应“最新”而非“最近”。
2024-05-06 19:12:38 +08:00
a719b2475b Update zh.json 2024-05-06 13:21:08 +08:00
84df499ea6 Merge pull request #1606 from ImranR98/dev
- Add more links to the settings page (#1554)
- Support for more Huawei AppGallery URL schemes (#1570)
- Fix HTML release asset download (#1578)
- Fix non-APK GitHub release asset downloads (#1581)
- Fix asset date UI (#1590)
2024-05-05 22:07:51 -04:00
0d25b74050 Increment version 2024-05-05 22:05:59 -04:00
0effbc3841 Merge remote-tracking branch 'origin/main' into dev 2024-05-05 22:05:07 -04:00
7478a7af22 Add more links to the settings page (#1554) 2024-05-05 22:03:04 -04:00
0838a6d30b Support for more Huawei AppGallery URL schemes (#1570) 2024-05-05 21:51:05 -04:00
8cee268d13 Fix HTML release asset download (#1578) 2024-05-05 21:43:05 -04:00
a3fddc5400 Fix non-APK GitHub release asset downloads (#1581) 2024-05-05 21:35:16 -04:00
c0a2e372e5 Fix asset date UI (#1590) + update packages 2024-05-05 21:11:59 -04:00
c633963203 Merge pull request #1598 from LilligantMatsuri/main
Update Chinese translation
2024-05-02 09:11:40 -04:00
299f457938 Update zh.json
- Add a missing string and its translation (#L116)
- Correct inaccurate translations
- Minor improvements

Signed-off-by: Matsuri <matsuri@vmoe.info>
2024-05-02 16:00:00 +08:00
37e62c922b Merge pull request #1591 from Hamster45105/main
Fix README link
2024-05-01 09:05:15 -04:00
95722ce47b Fix README link 2024-05-01 16:42:56 +10:00
8b806b3ef1 Merge pull request #1585 from Hamster45105/patch-1
Update README
2024-04-30 16:35:41 -04:00
09221b3526 Update README 2024-04-29 11:56:28 +10:00
31b6250082 Merge pull request #1568 from DwainZwerg/patch-16
Update de.json
2024-04-28 13:32:12 -04:00
07372da91b Merge pull request #1569 from bluefly000/japanese-translation
Update ja.json
2024-04-28 13:32:01 -04:00
5c36bcfb4b Merge pull request #1575 from Stephan-P/patch-1
Update nl.json
2024-04-28 13:31:52 -04:00
e5012b1fcb Merge pull request #1582 from CertainBot/patch-3
Spanish Update
2024-04-28 13:31:43 -04:00
6f951175a4 Spanish Update
English:
- texts homogenized, few corrections
Español:
- homogeneizar textos, algunas correcciones
2024-04-27 17:31:38 +02:00
be52ec372f Update nl.json
Significant overhaul of the Dutch translation including addition of a line that was missing compared to en.json
2024-04-24 20:02:50 +02:00
eb7126afc3 Update ja.json 2024-04-24 13:14:41 +09:00
26fc63a02a Update de.json 2024-04-23 22:15:22 +02:00
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
8dd8f471a2 Update vi.json 2023-11-20 10:09:45 +07:00
657d1cd042 Update vi.json 2023-11-20 10:00:08 +07:00
75 changed files with 5221 additions and 2639 deletions

View File

@ -10,6 +10,7 @@ assignees: ''
**Prerequisites**
<!-- Please ensure your request is not part of an existing issue. -->
<!-- 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**
<!-- A clear and concise description of what the bug is. -->

View File

@ -14,11 +14,14 @@ assignees: ''
**Describe the feature**
<!-- 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
- One or more APK URL(s) for the latest release
- 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. -->
**Describe alternatives you've considered (if applicable)**

View File

@ -6,7 +6,10 @@ on:
beta:
type: boolean
description: Is beta?
draft:
type: boolean
description: Is draft?
jobs:
build:
runs-on: ubuntu-latest
@ -15,7 +18,16 @@ jobs:
- uses: actions/checkout@v3
- 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
id: import_pgp_key
uses: crazy-max/ghaction-import-gpg@v6
@ -26,13 +38,7 @@ jobs:
- 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
git checkout ${{ inputs.checkout }}
- name: Extract Version
id: extract_version
@ -42,7 +48,6 @@ jobs:
if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi
echo "beta=$BETA" >> $GITHUB_OUTPUT
TAG="v$VERSION"
if [ $BETA == true ]; then TAG="$TAG"-beta; fi
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Build APKs
@ -84,5 +89,6 @@ jobs:
token: ${{ secrets.GH_ACCESS_TOKEN }}
tag: "${{ steps.extract_version.outputs.tag }}"
prerelease: "${{ steps.extract_version.outputs.beta }}"
draft: "${{ inputs.draft }}"
artifacts: ./build/app/outputs/flutter-apk/*-release*.apk*
generateReleaseNotes: true

View File

@ -2,47 +2,62 @@
[![Ceasefire Now](https://badge.techforpalestine.org/default)](https://techforpalestine.org/learn-more)
Get Android App Updates Directly From the Source.
Get Android app updates straight 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)
Read the Wiki: [https://github.com/ImranR98/Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki)
More info:
- [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:
- Open Source - General:
- [GitHub](https://github.com/)
- [GitLab](https://gitlab.com/)
- [Codeberg](https://codeberg.org/)
- [Forgejo](https://forgejo.org/) ([Codeberg](https://codeberg.org/))
- [F-Droid](https://f-droid.org/)
- Third Party F-Droid Repos
- [IzzyOnDroid](https://android.izzysoft.de/)
- [SourceForge](https://sourceforge.net/)
- [SourceHut](https://git.sr.ht/)
- Other - General:
- [APKPure](https://apkpure.net/)
- [Aptoide](https://aptoide.com/)
- [Uptodown](https://uptodown.com/)
- [APKMirror](https://apkmirror.com/) (Track-Only)
- [Huawei AppGallery](https://appgallery.huawei.com/)
- Jenkins Jobs
- [APKMirror](https://apkmirror.com/) (Track-Only)
- Open Source - App-Specific:
- [Mullvad](https://mullvad.net/en/)
- [Signal](https://signal.org/)
- [VLC](https://videolan.org/)
- Other - App-Specific:
- [WhatsApp](https://whatsapp.com)
- [Telegram App](https://telegram.org)
- [Neutron Code](https://neutroncode.com)
- Direct APK Link
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files
## Finding App Configurations
You can find crowdsourced app configurations at [apps.obtainium.imranr.dev](https://apps.obtainium.imranr.dev).
If you can't find the configuration for an app you want, feel free to leave a request on the [discussions page](https://github.com/ImranR98/apps.obtainium.imranr.dev/discussions/new?category=app-requests).
Or, contribute some configurations to the website by creating a PR at [this repo](https://github.com/ImranR98/apps.obtainium.imranr.dev).
## Installation
[<img src="https://github.com/machiav3lli/oandbackupx/blob/034b226cea5c1b30eb4f6a6f313e4dadcbb0ece4/badge_github.png"
alt="Get it on GitHub"
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)
## 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 localPropertiesFile = rootProject.file('local.properties')
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')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -21,11 +22,6 @@ if (flutterVersionName == null) {
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 keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@ -33,7 +29,8 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion rootProject.ext.compileSdkVersion
namespace "dev.imranr.obtainium"
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
@ -54,7 +51,7 @@ android {
// 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.
minSdkVersion 24
targetSdkVersion rootProject.ext.targetSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
@ -95,16 +92,15 @@ repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
def shizuku_version = '13.1.5'
implementation "dev.rikka.shizuku:api:$shizuku_version"
implementation "dev.rikka.shizuku:provider:$shizuku_version"
def hidden_api_version = '4.3.1'
implementation "dev.rikka.tools.refine:runtime:$hidden_api_version"
implementation "dev.rikka.hidden:compat:$hidden_api_version"
compileOnly "dev.rikka.hidden:stub:$hidden_api_version"
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
implementation "com.github.topjohnwu.libsu:core:5.2.2"
}
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
if (abiVersionCode != null) {
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
} else {
output.versionCodeOverride = variant.versionCode * 10
}
}
}

View File

@ -6,7 +6,8 @@
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
android:usesCleartextTraffic="true"
android:localeConfig="@xml/locales_config">
<activity
android:name=".MainActivity"
android:exported="true"
@ -47,7 +48,7 @@
android:value="2" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="dev.imranr.obtainium"
android:authorities="${applicationId}"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
@ -70,6 +71,6 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
android:maxSdkVersion="29" />\
<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
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.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() {
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)
}
}
class MainActivity: FlutterActivity()

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

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="bs"/>
<locale android:name="cs"/>
<locale android:name="de"/>
<locale android:name="en"/>
<locale android:name="es"/>
<locale android:name="fa"/>
<locale android:name="fr"/>
<locale android:name="hu"/>
<locale android:name="it"/>
<locale android:name="ja"/>
<locale android:name="nl"/>
<locale android:name="pl"/>
<locale android:name="pt"/>
<locale android:name="ru"/>
<locale android:name="sv"/>
<locale android:name="tr"/>
<locale android:name="uk"/>
<locale android:name="vi"/>
<locale android:name="zh"/>
</locale-config>

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 {
repositories {
google()

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx2048M
android.useAndroidX=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")
def properties = new Properties()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
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.

Binary file not shown.

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(obavezno)",
"dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU",
"colour": "Boja",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Koristi Material You temu",
"githubStarredRepos": "GitHub repo-i sa zvjezdicom",
"uname": "Korisničko ime",
"wrongArgNum": "Naveden je pogrešan broj argumenata",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Uklonite zastarjeli filter aplikacija",
"showOutdatedOnly": "Prikaži samo zastarjele aplikacije",
"filter": "Filtriranje",
"filterActive": "Filtriranje",
"filterApps": "Filtriraj aplikacije",
"appName": "Naziv aplikacije",
"author": "Autor",
@ -111,6 +113,7 @@
"dark": "Tamna",
"light": "Svijetla",
"followSystem": "Pratite sistem",
"followSystemThemeExplanation": "Praćenje sistemske teme je moguće jedino koristeći aplikacije treće strane",
"useBlackTheme": "Koristite čisto crnu tamnu temu",
"appSortBy": "Aplikacije sortirane po",
"authorName": "Autor/Ime",
@ -144,8 +147,10 @@
"noNewUpdates": "Nema novih ažuriranja.",
"xHasAnUpdate": "{} ima ažuriranje.",
"appsUpdated": "Aplikacije su ažurirane",
"appsNotUpdated": "Neuspješno ažuriranje aplikacija",
"appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija",
"xWasUpdatedToY": "{} je ažuriran na {}.",
"xWasNotUpdatedToY": "Neuspješno ažuriranje {} na {}.",
"errorCheckingUpdates": "Greška pri provjeri ažuriranja",
"errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije",
"appsRemoved": "Aplikacije su uklonjene",
@ -184,6 +189,9 @@
"disableVersionDetection": "Onemogući detekciju verzije",
"noVersionDetectionExplanation": "Ova opcija bi se trebala koristiti samo za aplikacije gdje detekcija verzije ne radi ispravno.",
"downloadingX": "Preuzimanje {}",
"downloadX": "Download {}",
"downloadedX": "Downloaded {}",
"releaseAsset": "Fajlovi verzije",
"downloadNotifDescription": "Obavještava korisnika o napretku u preuzimanju aplikacije",
"noAPKFound": "APK nije pronađen",
"noVersionDetection": "Nema detekcije verzije",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Ne prikazuj upozorenja „Samo za praćenje”",
"dontShowAPKOriginWarnings": "Ne prikazuj upozorenja o porijeklu APK-a",
"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",
"requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)",
"checkOnStart": "Provjerite ima li novosti pri pokretanju",
@ -233,7 +241,6 @@
"addInfoBelow": "Dodajte ove informacije ispod.",
"addInfoInSettings": "Dodajte ove informacije u Postavkama.",
"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",
"filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu",
"customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')",
@ -246,12 +253,14 @@
"verifyLatestTag": "Provjerite 'posljednu' ('latest') oznaku",
"intermediateLinkRegex": "Filter za 'srednju' vezu za posjetu",
"filterByLinkText": "Filtriraj linkove prema tekstu linka",
"intermediateLinkNotFound": "Intermediate link nije nađen",
"intermediateLink": "srednja karika",
"intermediateLinkNotFound": "Intermediate veza nije nađena",
"intermediateLink": "Intermediate veza",
"exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)",
"bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na WiFi-ju",
"autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a",
"versionExtractionRegEx": "RegEx ekstrakcija verzije",
"trimVersionString": "Trim Version String With RegEx",
"matchGroupToUseForX": "Match Group to Use for \"{}\"",
"matchGroupToUse": "Podjesite grupu za upotebu",
"highlightTouchTargets": "Istaknite manje vidljive touch mete",
"pickExportDir": "Izaberite datoteku za izvoz",
@ -281,12 +290,12 @@
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
"selectX": "Izaberite {}",
"parallelDownloads": "Dozvoli paralelna preuzimanja",
"installMethod": "Način instalacije",
"normal": "normalno",
"root": "korijen",
"shizukuBinderNotFound": "Shizuku is not running",
"useShizuku": "Koristi Shizuku ili Sui za instaliranje",
"shizukuBinderNotFound": "Shizuku nije pokrenut",
"shizukuOld": "Stara Shizuku verzija (<11) - ažurirajte je",
"shizukuOldAndroidWithADB": "Shizuku pokrenut na Android-u < 8.1 pomoću ADB-a - ažurirajte Android ili koristite Sui",
"shizukuPretendToBeGooglePlay": "Postavi Google Play kao izvor instalacije (samo ako je Shizuku u upotrebi)",
"useSystemFont": "Koristite sistemski font",
"systemFontError": "Greška pri učitavanju sistemskog fonta: {}",
"useVersionCodeAsOSVersion": "Koristite kod verzije aplikacije kao verziju koju je otkrio OS",
"requestHeader": "Zaglavlje zahtjeva",
"useLatestAssetDateAsReleaseDate": "Koristite najnovije otpremanje materijala kao datum izdavanja",
@ -298,6 +307,13 @@
"installed": "Instalirano",
"latest": "Najnoviji",
"invertRegEx": "Obrni regularni izraz",
"note": "Zabelješke",
"selfHostedNote": "\"{}\" padajući meni se može koristiti da dosegnete vlastite/prilagođene instance bilo kojeg izvora.",
"badDownload": "APK ne može biti raščlanjen (nekomaptibilno ili delimično preuzimanje)",
"beforeNewInstallsShareToAppVerifier": "Dijeli nove aplikacije sa AppVerifier-om (ako je dostupno)",
"appVerifierInstructionToast": "Dijeli sa AppVerifier-om, zatim se vratite kada ste spremni.",
"wiki": "Pomoć/Wiki",
"crowdsourcedConfigsLabel": "Konfiguracije aplikacije obezbeđene pomoću velikog broja ljudi (crowdsourcing) (koristite na svoju odgovornost)",
"removeAppQuestion": {
"one": "Želite li ukloniti aplikaciju?",
"other": "Želite li ukloniti aplikacije?"
@ -346,8 +362,16 @@
"one": "{} i još 1 aplikacija je ažurirana.",
"other": "{} i još {} aplikacija je ažurirano."
},
"xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.",
"other": "Failed to update {} and {} more apps."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} i još jedna aplikacija je vjerovatno ažurirana.",
"other": "{} i još {} aplikacija su vjerovatno ažurirane."
},
"apk": {
"one": "{} APK",
"other": "{} APK-a"
}
}

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(Požadované)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
"colour": "Barva",
"standard": "Standardní",
"custom": "Vlastní",
"useMaterialYou": "Použijte materiál, který jste",
"githubStarredRepos": "GitHub označená hvězdičkou",
"uname": "Uživatelské jméno",
"wrongArgNum": "Nesprávný počet zadaných argumentů",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Odstranit filtr Neaktuální",
"showOutdatedOnly": "Zobrazovat pouze zastaralé aplikace",
"filter": "Filtr",
"filterActive": "Filtr *",
"filterApps": "Filtrovat aplikace",
"appName": "Název aplikace",
"author": "Autor",
@ -111,6 +113,7 @@
"dark": "Tmavé",
"light": "Světlé",
"followSystem": "Jako systém",
"followSystemThemeExplanation": "Sledování motivu systému je možné pouze pomocí aplikací třetích stran.",
"useBlackTheme": "Použít čistě černé tmavé téma",
"appSortBy": "Seřadit podle",
"authorName": "Autor/Jméno",
@ -144,8 +147,10 @@
"noNewUpdates": "Žádné nové aktualizace.",
"xHasAnUpdate": "{} má aktualizaci.",
"appsUpdated": "Aplikace aktualizovány",
"appsNotUpdated": "Nepodařilo se aktualizovat aplikace",
"appsUpdatedNotifDescription": "Upozornit, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
"xWasUpdatedToY": "{} byla aktualizována na {}",
"xWasNotUpdatedToY": "Nepodařilo se aktualizovat {} na {}.",
"errorCheckingUpdates": "Chyba kontroly aktualizací",
"errorCheckingUpdatesNotifDescription": "Zobrazit oznámení při neúspěšné kontrole aktualizací na pozadí",
"appsRemoved": "Odstraněné aplikace",
@ -184,6 +189,9 @@
"disableVersionDetection": "Deaktivovat detekci verze",
"noVersionDetectionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně.",
"downloadingX": "Stáhnout {}",
"downloadX": "Stáhnout {}",
"downloadedX": "Staženo {}",
"releaseAsset": "Vydání aktiva",
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
"noAPKFound": "Žádná APK nebyla nalezena",
"noVersionDetection": "Žádná detekce verze",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'",
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
"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",
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
"checkOnStart": "Zkontrolovat jednou při spuštění",
@ -233,7 +241,6 @@
"addInfoBelow": "Přidat tuto informaci na konec stránky.",
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
"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",
"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$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "Deaktivovat aktualizace na pozadí, pokud není k dispozici Wi-Fi",
"autoSelectHighestVersionCode": "Automaticky vybrat nejvyšší verzi APK",
"versionExtractionRegEx": "Extrakce verze pomocí RegEx",
"trimVersionString": "Oříznutí řetězce verze pomocí příkazu RegEx",
"matchGroupToUseForX": "Skupina shody, která se použije pro \"{}\"",
"matchGroupToUse": "Odpovídá použité skupině",
"highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
"pickExportDir": "Vybrat adresář pro export",
@ -281,23 +290,30 @@
"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 {}",
"parallelDownloads": "Povolit souběžné stahování",
"installMethod": "Metoda instalace",
"normal": "Normální",
"root": "Správce",
"useShizuku": "K instalaci použijte Shizuku nebo Sui",
"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",
"systemFontError": "Chyba při načítání systémového písma: {}",
"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": "APK Link Hash",
"APKLinkHash": "Odkaz APK Hash",
"directAPKLink": "Přímý odkaz APK",
"pseudoVersionInUse": "Pseudoverze se používá",
"installed": "Instalováno",
"latest": "Nejnovější",
"invertRegEx": "Invertovat regulární výraz",
"note": "Poznámka",
"selfHostedNote": "Rozbalovací seznam \"{}\" lze použít k dosažení vlastních/obvyklých instancí libovolného zdroje.",
"badDownload": "APK nelze analyzovat (nekompatibilní nebo částečné stažení)",
"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.",
"wiki": "Nápověda/Wiki",
"crowdsourcedConfigsLabel": "Konfigurace aplikací s využitím crowdsourcingu (použití na vlastní nebezpečí)",
"removeAppQuestion": {
"one": "Odstranit Apku?",
"other": "Odstranit Apky?"
@ -346,8 +362,16 @@
"one": "{} a 1 další aplikace mají aktualizace.",
"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": {
"one": "{} a 1 další aplikace možno aktualizovat",
"other": "{} a {} další aplikace mohou být aktualizovány."
},
"apk": {
"one": "{} APK",
"other": "{} APK"
}
}

377
assets/translations/da.json Normal file
View File

@ -0,0 +1,377 @@
{
"invalidURLForSource": "Ikke et gyldigt {} App-URL",
"noReleaseFound": "Kunne ikke finde en passende udgivelse",
"noVersionFound": "Kunne ikke afgøre udgivelsesversion",
"urlMatchesNoSource": "URL'en matcher ikke en kendt kilde",
"cantInstallOlderVersion": "Kan ikke installere en ældre version af en app",
"appIdMismatch": "Hentet pakke-ID matcher ikke eksisterende app-ID",
"functionNotImplemented": "Denne klasse har ikke implementeret denne funktion",
"placeholder": "Pladsholder",
"someErrors": "Nogle fejl opstod",
"unexpectedError": "Uventet fejl",
"ok": "Okay",
"and": "og",
"githubPATLabel": "GitHub Personlig Adgangstoken (øger hastighedsgrænse)",
"includePrereleases": "Inkluder forudgivelser",
"fallbackToOlderReleases": "Fallback til ældre udgivelser",
"filterReleaseTitlesByRegEx": "Filtrer udgivelsestitler efter regulært udtryk",
"invalidRegEx": "Ugyldigt regulært udtryk",
"noDescription": "Ingen beskrivelse",
"cancel": "Annuller",
"continue": "Fortsæt",
"requiredInBrackets": "(Påkrævet)",
"dropdownNoOptsError": "FEJL: RULLEMENU SKAL HAVE MINDST ÉT TILVALG",
"colour": "Farve",
"standard": "Standard",
"custom": "Brugerdefineret",
"useMaterialYou": "Brug Material You",
"githubStarredRepos": "Stjernemarkeret GitHub-repos",
"uname": "Brugernavn",
"wrongArgNum": "Forkert antal argumenter angivet",
"xIsTrackOnly": "{} kan kun følges",
"source": "Kilde",
"app": "App",
"appsFromSourceAreTrackOnly": "Apps fra denne kilde er 'Følg Kun'.",
"youPickedTrackOnly": "Du har valgt 'Følg Kun'-indstillingen.",
"trackOnlyAppDescription": "Appen tjekkes for opdateringer, men Obtainium kan ikke hente eller installere den.",
"cancelled": "Annulleret",
"appAlreadyAdded": "Appen er allerede tilføjet",
"alreadyUpToDateQuestion": "Appen er allerede opdateret?",
"addApp": "Tilføj app",
"appSourceURL": "URL til app-kilde",
"error": "Fejl",
"add": "Tilføj",
"searchSomeSourcesLabel": "Søg (kun visse kilder)",
"search": "Søg",
"additionalOptsFor": "Yderligere indstillinger for {}",
"supportedSources": "Understøttede kilder",
"trackOnlyInBrackets": "(Følg Kun)",
"searchableInBrackets": "(Kan Søges)",
"appsString": "Apps",
"noApps": "Ingen apps",
"noAppsForFilter": "Ingen apps til filter",
"byX": "Af {}",
"percentProgress": "Fremskridt: {}%",
"pleaseWait": "Vent venligst",
"updateAvailable": "Opdatering tilgængelig",
"notInstalled": "Ikke installeret",
"pseudoVersion": "pseudo-version",
"selectAll": "Vælg alle",
"deselectX": "Fravælg {}",
"xWillBeRemovedButRemainInstalled": "{} fjernes fra Obtainium, men forbliver installeret på enheden.",
"removeSelectedAppsQuestion": "Fjern valgte apps?",
"removeSelectedApps": "Fjern valgte apps",
"updateX": "Opdater {}",
"installX": "Installer {}",
"markXTrackOnlyAsUpdated": "Markér {}\n(Følg Kun)\nsom opdateret",
"changeX": "Skift {}",
"installUpdateApps": "Installer/Opdater apps",
"installUpdateSelectedApps": "Installer/Opdater valgte apps",
"markXSelectedAppsAsUpdated": "Markér {} valgte apps som opdateret?",
"no": "Nej",
"yes": "Ja",
"markSelectedAppsUpdated": "Markér valgte apps som opdateret",
"pinToTop": "Fastgør til toppen",
"unpinFromTop": "Frigør fra toppen",
"resetInstallStatusForSelectedAppsQuestion": "Nulstil installationsstatus for valgte apps?",
"installStatusOfXWillBeResetExplanation": "Installationsstatus for alle valgte apps nulstilles.\n\nDette kan hjælpe, når den app-version, der vises i Obtainium, er forkert grundet mislykkede opdateringer eller andre problemer.",
"customLinkMessage": "Disse links virker på enheder med Obtainium installeret",
"shareAppConfigLinks": "Del app-konfiguration som HTML-link",
"shareSelectedAppURLs": "Del valgte app-URL'er",
"resetInstallStatus": "Nulstil installationsstatus",
"more": "Mere",
"removeOutdatedFilter": "Fjern forældet app-filter",
"showOutdatedOnly": "Vis kun forældet apps",
"filter": "Filtrer",
"filterApps": "Filtrer Apps",
"appName": "Appnavn",
"author": "Udvikler",
"upToDateApps": "Opdaterede apps",
"nonInstalledApps": "Ikke-installerede apps",
"importExport": "Import/Eksport",
"settings": "Indstillinger",
"exportedTo": "Eksportér til {}",
"obtainiumExport": "Obtainium-eksport",
"invalidInput": "Ugyldigt input",
"importedX": "Importerede {}",
"obtainiumImport": "Obtainium-import",
"importFromURLList": "Importér fra URL-liste",
"searchQuery": "Søgning",
"appURLList": "Liste over app-URL'er",
"line": "Linje",
"searchX": "Søg {}",
"noResults": "Ingen resultater fundet",
"importX": "Importér {}",
"importedAppsIdDisclaimer": "Importerede apps vises muligvis forkert som \"Ikke installeret\".\nFor at løse dette, geninstaller dem via Obtainium.\nDette bør ikke påvirke app-data.\n\nPåvirker kun URL- og tredjepartsimportmetoder.",
"importErrors": "Importfejl",
"importedXOfYApps": "{} af {} app importeret.",
"followingURLsHadErrors": "Følgende URL'er havde fejl:",
"selectURL": "Vælg URL",
"selectURLs": "Vælg URL'er",
"pick": "Vælg",
"theme": "Tema",
"dark": "Mørk",
"light": "Lys",
"followSystem": "Følg system",
"followSystemThemeExplanation": "Det er kun muligt at følge systemtemaet ved brug af tredjepartsapplikationer",
"useBlackTheme": "Brug rent sort, mørkt tema",
"appSortBy": "Sortér apps efter:",
"authorName": "Udvikler/Navn",
"nameAuthor": "Navn/Udvikler",
"asAdded": "Som tilføjet",
"appSortOrder": "Sorteringsrækkefølge for apps",
"ascending": "Stigende",
"descending": "Faldende",
"bgUpdateCheckInterval": "Kontrolinterval for baggrundsopdatering",
"neverManualOnly": "Aldrig - Kun manuelt",
"appearance": "Udseende",
"showWebInAppView": "Vis kildewebsiden i appvisning",
"pinUpdates": "Fastgør opdateringer til toppen af appvisning",
"updates": "Opdateringer",
"sourceSpecific": "Kildespecifik",
"appSource": "App-kilde",
"noLogs": "Ingen logs",
"appLogs": "App-logs",
"close": "Luk",
"share": "Del",
"appNotFound": "App ikke fundet",
"obtainiumExportHyphenatedLowercase": "obtainium-eksport",
"pickAnAPK": "Vælg en APK",
"appHasMoreThanOnePackage": "{} har mere end én pakke:",
"deviceSupportsXArch": "Din enhed understøtter {} CPU-arkitekturen.",
"deviceSupportsFollowingArchs": "Din enhed understøtter følgende CPU-arkitekturer:",
"warning": "Advarsel",
"sourceIsXButPackageFromYPrompt": "App-kilden er '{}', men udgivelsespakken kommer fra '{}'. Fortsæt?",
"updatesAvailable": "Opdateringer tilgængelige",
"updatesAvailableNotifDescription": "Underretter brugeren om, at opdateringer er tilgængelige for en eller flere apps, der spores af Obtainium",
"noNewUpdates": "Ingen nye opdateringer.",
"xHasAnUpdate": "{} har en opdatering.",
"appsUpdated": "Apps opdateret",
"appsNotUpdated": "Kunne ikke opdatere applikationerne",
"appsUpdatedNotifDescription": "Underretter brugeren om, at opdateringer til en eller flere apps blev udført i baggrunden",
"xWasUpdatedToY": "{} blev opdateret til {}.",
"xWasNotUpdatedToY": "Kunne ikke opdatere {} til {}.",
"errorCheckingUpdates": "Fejl ved tjek for opdateringer",
"errorCheckingUpdatesNotifDescription": "En meddelelse, der vises, når opdateringstjek i baggrunden mislykkes",
"appsRemoved": "Apps fjernet",
"appsRemovedNotifDescription": "Underretter brugeren om, at en eller flere apps blev fjernet grundet fejl under indlæsning af dem",
"xWasRemovedDueToErrorY": "{} blev fjernet grundet denne fejl: {}",
"completeAppInstallation": "Færdiggør app-installation",
"obtainiumMustBeOpenToInstallApps": "Obtainium skal være åben for at installere apps",
"completeAppInstallationNotifDescription": "Beder brugeren om at vende tilbage til Obtainium for at afslutte installationen af en app",
"checkingForUpdates": "Tjekker for opdateringer",
"checkingForUpdatesNotifDescription": "Kortvarig meddelelse, der vises ved tjek for opdateringer",
"pleaseAllowInstallPerm": "Tillad venligst Obtainium at installere apps",
"trackOnly": "Følg Kun",
"errorWithHttpStatusCode": "Fejl {}",
"versionCorrectionDisabled": "Versionskorrigering deaktiveret (plugin ser ikke ud til at virke)",
"unknown": "Ukendt",
"none": "Ingen",
"never": "Aldrig",
"latestVersionX": "Seneste: {}",
"installedVersionX": "Installeret: {}",
"lastUpdateCheckX": "Sidste opdateringstjek: {}",
"remove": "Fjern",
"yesMarkUpdated": "Ja, markér som opdateret",
"fdroid": "F-Droid Officiel",
"appIdOrName": "App-ID eller navn",
"appId": "App-ID",
"appWithIdOrNameNotFound": "Ingen app med det ID eller navn blev fundet",
"reposHaveMultipleApps": "Repos kan indeholde flere apps",
"fdroidThirdPartyRepo": "F-Droid Tredjeparts-repo",
"steamMobile": "Steam Mobil",
"steamChat": "Steam Chat",
"install": "Installer",
"markInstalled": "Markér som installeret",
"update": "Opdater",
"markUpdated": "Markér som opdateret",
"additionalOptions": "Yderligere indstillinger",
"disableVersionDetection": "Deaktivér versionsregistrering",
"noVersionDetectionExplanation": "Denne indstilling bør kun bruges til apps, hvor versionsregistrering ikke virker korrekt.",
"downloadingX": "Henter {}",
"downloadX": "Hent {}",
"downloadedX": "Hentede {}",
"releaseAsset": "Udgivelsesressource",
"downloadNotifDescription": "Underretter brugeren om fremskridt i hentning af en app",
"noAPKFound": "Ingen APK fundet",
"noVersionDetection": "Ingen versionsregistrering",
"categorize": "Kategoriser",
"categories": "Kategorier",
"category": "Kategori",
"noCategory": "Ingen kategori",
"noCategories": "Ingen kategorier",
"deleteCategoriesQuestion": "Slet kategorier?",
"categoryDeleteWarning": "Alle apps i slettede kategorier indstilles til ukategoriseret.",
"addCategory": "Tilføj kategori",
"label": "Etiket",
"language": "Sprog",
"copiedToClipboard": "Kopieret til udklipsholder",
"storagePermissionDenied": "Lagringstilladelse nægtet",
"selectedCategorizeWarning": "Dette erstatter alle eksisterende kategoriindstillinger for de valgte apps.",
"filterAPKsByRegEx": "Filtrer APK'er efter regulært udtryk",
"removeFromObtainium": "Fjern fra Obtainium",
"uninstallFromDevice": "Afinstaller fra enhed",
"onlyWorksWithNonVersionDetectApps": "Virker kun for apps med versionsregistrering deaktiveret.",
"releaseDateAsVersion": "Brug udgivelsesdato som versionsstreng",
"releaseDateAsVersionExplanation": "Denne indstilling bør kun bruges til apps, hvor versionsregistrering ikke virker korrekt, men hvor en udgivelsesdato er tilgængelig.",
"changes": "Ændringer",
"releaseDate": "Udgivelsesdato",
"importFromURLsInFile": "Importér fra URL'er i fil (som OPML)",
"versionDetectionExplanation": "Afstem versionsstreng med versionen registreret fra OS",
"versionDetection": "Versionsregistrering",
"standardVersionDetection": "Standard versionsregistrering",
"groupByCategory": "Gruppér efter kategori",
"autoApkFilterByArch": "Forsøg at filtrere APK'er efter CPU-arkitektur, hvis muligt",
"overrideSource": "Tilsidesæt kilde",
"dontShowAgain": "Vis ikke denne igen",
"dontShowTrackOnlyWarnings": "Vis ikke 'Følg Kun'-advarsler",
"dontShowAPKOriginWarnings": "Vis ikke advarsler om APK-oprindelse",
"moveNonInstalledAppsToBottom": "Flyt ikke-installerede apps til bunden af appvisning",
"gitlabPATLabel": "GitLab Personlig Adgangstoken",
"about": "Om",
"requiresCredentialsInSettings": "{} kræver yderligere legitimation (i Indstillinger)",
"checkOnStart": "Tjek for opdateringer ved opstart",
"tryInferAppIdFromCode": "Forsøg at udlede app-ID fra kildekode",
"removeOnExternalUninstall": "Fjern automatisk eksternt afinstallerede apps",
"pickHighestVersionCode": "Auto-vælg højeste versionKode af APK",
"checkUpdateOnDetailPage": "Tjek for opdateringer ved åbning af appens detaljeside",
"disablePageTransitions": "Deaktivér sideovergangsanimationer",
"reversePageTransitions": "Omvendte sideovergangsanimationer",
"minStarCount": "Minimum antal stjerner",
"addInfoBelow": "Tilføj denne info nedenfor.",
"addInfoInSettings": "Tilføj denne info i indstillingerne.",
"githubSourceNote": "GitHubs hastighedsbegrænsning kan undgås med en API-nøgle.",
"sortByLastLinkSegment": "Sortér kun efter det sidste segment af linket",
"filterReleaseNotesByRegEx": "Filtrer udgivelsesnoter efter regulært udtryk",
"customLinkFilterRegex": "Brugerdefineret APK-linkfilter efter regulært udtryk (standard '.apk$')",
"appsPossiblyUpdated": "App-opdateringer forsøgt",
"appsPossiblyUpdatedNotifDescription": "Underretter brugeren om, at opdateringer til en eller flere apps potentielt blev udført i baggrunden",
"xWasPossiblyUpdatedToY": "{} er muligvis blevet opdateret til {}.",
"enableBackgroundUpdates": "Aktivér baggrundsopdateringer",
"backgroundUpdateReqsExplanation": "Baggrundsopdateringer er muligvis ikke mulige for alle apps.",
"backgroundUpdateLimitsExplanation": "Om en baggrundsinstallation er vellykket, kan kun afgøres, når Obtainium åbnes.",
"verifyLatestTag": "Verificer 'seneste'-tagget",
"intermediateLinkRegex": "Filtrer efter et 'mellemliggende' link at besøge",
"filterByLinkText": "Filtrer links efter linktekst",
"intermediateLinkNotFound": "Mellemliggende link ikke fundet",
"intermediateLink": "Mellemliggende link",
"exemptFromBackgroundUpdates": "Undtag fra baggrundsopdateringer (hvis aktiveret)",
"bgUpdatesOnWiFiOnly": "Deaktivér baggrundsopdateringer, når du ikke er på WiFi",
"autoSelectHighestVersionCode": "Auto-vælg højeste versionKode af APK",
"versionExtractionRegEx": "RegEx for versionsstrengsudtrækning",
"trimVersionString": "Trim versionsstrengen med RegEx",
"matchGroupToUseForX": "Matchgruppe til brug for \"{}\"",
"matchGroupToUse": "Match gruppe til brug til RegEx for versionsstrengsudtrækning",
"highlightTouchTargets": "Fremhæv mindre åbenlyse berøringsmål",
"pickExportDir": "Vælg eksportmappe",
"autoExportOnChanges": "Auto-eksport ved ændringer",
"includeSettings": "Inkluder indstillinger",
"filterVersionsByRegEx": "Filtrer versioner efter regulært udtryk",
"trySelectingSuggestedVersionCode": "Forsøg at vælge den foreslåede versionKode af APK",
"dontSortReleasesList": "Behold udgivelsesordre fra API",
"reverseSort": "Omvendt sortering",
"takeFirstLink": "Tag første link",
"skipSort": "Spring sortering over",
"debugMenu": "Fejlfindingsmenu",
"bgTaskStarted": "Baggrundsopgave startet - tjek logfiler.",
"runBgCheckNow": "Kør baggrundsopdateringstjek nu",
"versionExtractWholePage": "Anvend RegEx for versionsstrengsudtrækning for hele siden",
"installing": "Installerer",
"skipUpdateNotifications": "Spring opdateringsmeddelelser over",
"updatesAvailableNotifChannel": "Opdateringer tilgængelige",
"appsUpdatedNotifChannel": "Apps opdateret",
"appsPossiblyUpdatedNotifChannel": "App-opdateringer forsøgt",
"errorCheckingUpdatesNotifChannel": "Fejl ved opdateringstjek",
"appsRemovedNotifChannel": "Apps fjernet",
"downloadingXNotifChannel": "Henter {}",
"completeAppInstallationNotifChannel": "Færdiggør app-installation",
"checkingForUpdatesNotifChannel": "Tjekker for opdateringer",
"onlyCheckInstalledOrTrackOnlyApps": "Tjek kun installeret og 'Følg Kun'-apps for opdateringer",
"supportFixedAPKURL": "Understøt fikserede APK-URL'er",
"selectX": "Vælg {}",
"parallelDownloads": "Tillad samtidige overførsler",
"useShizuku": "Brug Shizuku eller Sui til at installere",
"shizukuBinderNotFound": "Shizuku-tjeneste kører ikke",
"shizukuOld": "Forældet Shizuku-version (<11). Opdater den",
"shizukuOldAndroidWithADB": "Shizuku kører på Android <8.1 med ADB. Opdater Android eller brug Sui i stedet",
"shizukuPretendToBeGooglePlay": "Indstil Google Play som installationskilde (hvis Shizuku bruges)",
"useSystemFont": "Brug systemskrifttype",
"useVersionCodeAsOSVersion": "Brug app versionKode som OS-registreret version",
"requestHeader": "Anmod overskrift",
"useLatestAssetDateAsReleaseDate": "Brug seneste ressourceupload som udgivelsesdato",
"defaultPseudoVersioningMethod": "Standard pseudo-versioneringsmetode",
"partialAPKHash": "Delvis APK-hash",
"APKLinkHash": "Hash for APK-link",
"directAPKLink": "Direkte APK-link",
"pseudoVersionInUse": "En pseudo-version er i brug",
"installed": "Installeret",
"latest": "Seneste",
"invertRegEx": "Inverter regulært udtryk",
"note": "Bemærk",
"selfHostedNote": "Rullemenuen \"{}\" kan bruges til at nå selvhostede/brugerdefinerede instanser af enhver kilde.",
"badDownload": "APK'en kunne ikke analyseres (inkompatibel eller delvis hentning)",
"beforeNewInstallsShareToAppVerifier": "Del nye apps med AppVerifier (hvis tilgængelig)",
"appVerifierInstructionToast": "Del til AppVerifier, og vend tilbage hertil, når du er klar.",
"wiki": "Hjælp/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourcede app-konfigurationer (brug på egen risiko)",
"removeAppQuestion": {
"one": "Fjern app?",
"other": "Fjern apps?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "For mange anmodninger (begrænset hastighed). Prøv igen om {} minut",
"other": "For mange anmodninger (begrænset hastighed). Prøv igen om {} minutter"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "Baggrundsopdateringstjek stødte på en {}. Planlægger et nyt tjek om {} minut",
"other": "Baggrundsopdateringstjek stødte på en {}. Planlægger et nyt tjek om {} minutter"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "Baggrundsopdateringstjek fandt {} opdatering. Underretter brugeren, hvis nødvendigt",
"other": "Baggrundsopdateringstjek fandt {} opdateringer. Underretter brugeren, hvis nødvendigt"
},
"apps": {
"one": "{} App",
"other": "{} Apps"
},
"url": {
"one": "{} URL",
"other": "{} URL'er"
},
"minute": {
"one": "{} Minut",
"other": "{} Minutter"
},
"hour": {
"one": "{} Time",
"other": "{} Timer"
},
"day": {
"one": "{} Dag",
"other": "{} Dage"
},
"clearedNLogsBeforeXAfterY": {
"one": "Ryddet {n} log (før = {before}, efter = {after})",
"other": "Ryddet {n} logs (før = {before}, efter = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} og 1 anden app har opdateringer.",
"other": "{} og {} andre apps har opdateringer."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} og 1 anden app blev opdateret.",
"other": "{} og {} andre apps blev opdateret."
},
"xAndNMoreUpdatesFailed": {
"one": "Kunne ikke opdatere {} og 1 anden app.",
"other": "Kunne ikke opdatere {} og {} andre apps."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} og 1 anden app blev muligvis opdateret.",
"other": "{} og {} andre apps blev muligvis opdateret."
},
"apk": {
"one": "{} APK",
"other": "{} APK'er"
}
}

View File

@ -9,9 +9,9 @@
"placeholder": "Platzhalter",
"someErrors": "Es traten einige Fehler auf",
"unexpectedError": "Unerwarteter Fehler",
"ok": "Okay",
"ok": "OK",
"and": "und",
"githubPATLabel": "GitHub Personal Access Token (Erhöht das Ratenlimit)",
"githubPATLabel": "Persönlicher Zugangstoken für GitHub (erhöht das Ratenlimit)",
"includePrereleases": "Vorabversionen einbeziehen",
"fallbackToOlderReleases": "Fallback auf ältere Versionen",
"filterReleaseTitlesByRegEx": "Release-Titel nach regulärem Ausdruck\nfiltern",
@ -19,18 +19,21 @@
"noDescription": "Keine Beschreibung",
"cancel": "Abbrechen",
"continue": "Weiter",
"requiredInBrackets": "(Benötigt)",
"requiredInBrackets": "(wird benötigt)",
"dropdownNoOptsError": "FEHLER: DROPDOWN MUSS MINDESTENS EINE OPTION HABEN",
"colour": "Farbe",
"standard": "Standard",
"custom": "Benutzerdefiniert",
"useMaterialYou": "Material You verwenden",
"githubStarredRepos": "GitHub Starred Repos",
"uname": "Benutzername",
"wrongArgNum": "Falsche Anzahl von Argumenten (Parametern) übermittelt",
"xIsTrackOnly": "{} ist nur zur Nachverfolgung",
"source": "Quelle",
"app": "App",
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zum Nachverfolgen.",
"youPickedTrackOnly": "Sie haben die Option „Nur Nachverfolgen“ gewählt.",
"trackOnlyAppDescription": "Die App wird auf Updates überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
"appsFromSourceAreTrackOnly": "Apps aus dieser Quelle sind nur zur Nachverfolgung.",
"youPickedTrackOnly": "Sie haben die Option „Nur nachverfolgen“ gewählt.",
"trackOnlyAppDescription": "Die App wird auf Aktualisierungen überwacht, aber Obtainium wird sie nicht herunterladen oder installieren.",
"cancelled": "Abgebrochen",
"appAlreadyAdded": "App bereits hinzugefügt",
"alreadyUpToDateQuestion": "App bereits auf dem neuesten Stand?",
@ -42,7 +45,7 @@
"search": "Suchen",
"additionalOptsFor": "Zusatzoptionen für {}",
"supportedSources": "Unterstützte Quellen",
"trackOnlyInBrackets": "(Nur Nachverfolgen)",
"trackOnlyInBrackets": "(Nur nachverfolgen)",
"searchableInBrackets": "(Durchsuchbar)",
"appsString": "Apps",
"noApps": "Keine Apps",
@ -58,49 +61,48 @@
"xWillBeRemovedButRemainInstalled": "{} wird aus Obtainium entfernt, bleibt aber auf dem Gerät installiert.",
"removeSelectedAppsQuestion": "Ausgewählte Apps entfernen?",
"removeSelectedApps": "Ausgewählte Apps entfernen",
"updateX": "Aktualisiere {}",
"installX": "Installiere {}",
"markXTrackOnlyAsUpdated": "Markiere {}\n(Nur Nachverfolgen)\nals aktualisiert",
"changeX": "Ändere {}",
"updateX": "{} aktualisieren",
"installX": "{} installieren",
"markXTrackOnlyAsUpdated": "Markiere {}\n(Nur nachverfolgen)\nals aktualisiert",
"changeX": "{} ändern",
"installUpdateApps": "Apps installieren/aktualisieren",
"installUpdateSelectedApps": "Ausgewählte Apps installieren/aktualisieren",
"markXSelectedAppsAsUpdated": "Markiere {} ausgewählte Apps als aktuell?",
"markXSelectedAppsAsUpdated": "{} ausgewählte Apps als aktuell markieren?",
"no": "Nein",
"yes": "Ja",
"markSelectedAppsUpdated": "Markiere ausgewählte Apps als aktuell",
"markSelectedAppsUpdated": "Ausgewählte Apps als aktuell markieren",
"pinToTop": "Oben anheften",
"unpinFromTop": "„Oben anheften“ aufheben",
"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.",
"customLinkMessage": "Diese Links funktionieren auf Geräten, wo Obtainium installiert ist",
"shareAppConfigLinks": "Teile die Appkonfiguration als HTML-Link",
"shareAppConfigLinks": "App-Konfiguration als HTML-Link teilen",
"shareSelectedAppURLs": "Ausgewählte App-URLs teilen",
"resetInstallStatus": "Installationsstatus zurücksetzen",
"more": "Mehr",
"removeOutdatedFilter": "App-Filter Nicht aktuell entfernen",
"showOutdatedOnly": "Nur nicht aktuelle Apps anzeigen",
"filter": "Filter",
"filterActive": "Filter *",
"filterApps": "Apps filtern",
"appName": "App Name",
"appName": "App-Name",
"author": "Autor",
"upToDateApps": "Apps mit aktueller Version",
"nonInstalledApps": "Nicht installierte Apps",
"importExport": "Import/Export",
"settings": "Einstellungen",
"exportedTo": "Exportiert zu {}",
"exportedTo": "Exportiert nach {}",
"obtainiumExport": "Obtainium-Export",
"invalidInput": "Ungültige Eingabe",
"importedX": "Importiert {}",
"importedX": "{} importiert",
"obtainiumImport": "Obtainium-Import",
"importFromURLList": "Importieren aus URL-Liste",
"importFromURLList": "Aus URL-Liste importieren",
"searchQuery": "Suchanfrage",
"appURLList": "App URL-Liste",
"appURLList": "App-URL-Liste",
"line": "Linie",
"searchX": "Suche {}",
"searchX": "{} suchen",
"noResults": "Keine Ergebnisse gefunden",
"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.",
"importX": "{} importieren",
"importedAppsIdDisclaimer": "Importierte Apps werden manchmal fälschlicherweise als Nicht installiert angezeigt. Um dies zu beheben, installieren Sie sie erneut über Obtainium. Dies hat keine Auswirkungen auf App-Daten. Es betrifft nur URL- und Drittanbieter-Importmethoden.",
"importErrors": "Importfehler",
"importedXOfYApps": "{} von {} Apps importiert.",
"followingURLsHadErrors": "Bei folgenden URLs traten Fehler auf:",
@ -111,7 +113,8 @@
"dark": "Dunkel",
"light": "Hell",
"followSystem": "System folgen",
"useBlackTheme": "Verwende Pure Black Dark Theme",
"followSystemThemeExplanation": "Das Folgen des Systemthemes ist unter Android < 10 nur mit Hilfe von Drittanbieterapps möglich",
"useBlackTheme": "Pure Black Dark Theme verwenden",
"appSortBy": "App sortieren nach",
"authorName": "Autor/Name",
"nameAuthor": "Name/Autor",
@ -143,21 +146,23 @@
"updatesAvailableNotifDescription": "Benachrichtigt den Nutzer, dass Aktualisierungen für eine oder mehrere von Obtainium verfolgte Apps verfügbar sind",
"noNewUpdates": "Keine neuen Aktualisierungen.",
"xHasAnUpdate": "{} hat eine Aktualisierung.",
"appsUpdated": "Apps aktualisiert",
"appsUpdated": "App wurde aktualisiert",
"appsNotUpdated": "Aktualisierung der Apps fehlgeschlagen",
"appsUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps im Hintergrund durchgeführt wurden",
"xWasUpdatedToY": "{} wurde auf {} aktualisiert.",
"xWasNotUpdatedToY": "Die Aktualisierung von {} auf {} ist fehlgeschlagen.",
"errorCheckingUpdates": "Fehler beim Prüfen auf Aktualisierungen",
"errorCheckingUpdatesNotifDescription": "Eine Benachrichtigung, die angezeigt wird, wenn die Prüfung der Hintergrundaktualisierung fehlschlägt",
"appsRemoved": "Apps entfernt",
"appsRemovedNotifDescription": "Benachrichtigt den Benutzer, dass eine oder mehrere Apps aufgrund von Fehlern beim Laden entfernt wurden",
"xWasRemovedDueToErrorY": "{} wurde aufgrund des folgenden Fehlers entfernt: {}",
"completeAppInstallation": "App Installation abschließen",
"completeAppInstallation": "App-Installation abschließen",
"obtainiumMustBeOpenToInstallApps": "Obtainium muss geöffnet sein, um Apps zu installieren",
"completeAppInstallationNotifDescription": "Aufforderung an den Benutzer, zu Obtainium zurückzukehren, um die Installation einer App abzuschließen",
"checkingForUpdates": "Nach Aktualisierungen suchen",
"checkingForUpdatesNotifDescription": "Vorübergehende Benachrichtigung, die bei der Suche nach Aktualisierungen angezeigt wird",
"pleaseAllowInstallPerm": "Bitte erlauben Sie Obtainium die Installation von Apps",
"trackOnly": "Nur Nachverfolgen",
"trackOnly": "Nur nachverfolgen",
"errorWithHttpStatusCode": "Fehler {}",
"versionCorrectionDisabled": "Versionskorrektur deaktiviert (Plugin scheint nicht zu funktionieren)",
"unknown": "Unbekannt",
@ -168,22 +173,25 @@
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
"remove": "Entfernen",
"yesMarkUpdated": "Ja, als aktualisiert markieren",
"fdroid": "offizielles F-Droid-Repo",
"appIdOrName": "App ID oder Name",
"appId": "App ID",
"fdroid": "Offizielles F-Droid-Repo",
"appIdOrName": "App-ID oder Name",
"appId": "App-ID",
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
"fdroidThirdPartyRepo": "F-Droid Drittparteienrepo",
"fdroidThirdPartyRepo": "F-Droid-Drittanbieter-Repo",
"steamMobile": "Steam Mobile",
"steamChat": "Steam-Chat",
"install": "Installieren",
"markInstalled": "Als Installiert markieren",
"markInstalled": "Als installiert markieren",
"update": "Aktualisieren",
"markUpdated": "Als Aktuell markieren",
"markUpdated": "Als aktuell markieren",
"additionalOptions": "Zusätzliche Optionen",
"disableVersionDetection": "Versionsermittlung deaktivieren",
"disableVersionDetection": "Versionserkennung deaktivieren",
"noVersionDetectionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert.",
"downloadingX": "Lade {} herunter",
"downloadingX": "{} wird heruntergeladen",
"downloadX": "{} herunterladen",
"downloadedX": "{} heruntergeladen",
"releaseAsset": "Release-Asset",
"downloadNotifDescription": "Benachrichtigt den Nutzer über den Fortschritt beim Herunterladen einer App",
"noAPKFound": "Keine APK gefunden",
"noVersionDetection": "Keine Versionserkennung",
@ -208,51 +216,52 @@
"releaseDateAsVersionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert, aber ein Veröffentlichungsdatum verfügbar ist.",
"changes": "Änderungen",
"releaseDate": "Veröffentlichungsdatum",
"importFromURLsInFile": "Importieren von URLs aus Datei (z. B. OPML)",
"importFromURLsInFile": "URLs aus Datei importieren (z. B. OPML)",
"versionDetectionExplanation": "Abgleich der Versionsnummer mit der vom Betriebssystem erkannten Version",
"versionDetection": "Versionserkennung",
"standardVersionDetection": "Standardversionserkennung",
"groupByCategory": "Nach Kategorie gruppieren",
"autoApkFilterByArch": "Nach Möglichkeit versuchen, APKs nach CPU-Architektur zu filtern",
"overrideSource": "Quelle überschreiben",
"dontShowAgain": "Nicht noch einmal zeigen",
"dontShowTrackOnlyWarnings": "Warnung für 'Nur Nachverfolgen' nicht anzeigen",
"dontShowAgain": "Nicht noch einmal anzeigen",
"dontShowTrackOnlyWarnings": "Warnung für 'Nur nachverfolgen' nicht anzeigen",
"dontShowAPKOriginWarnings": "Warnung für APK-Herkunft nicht anzeigen",
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben",
"gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)",
"moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der App-Ansicht verschieben",
"gitlabPATLabel": "Persönlicher Zugangstoken für GitLab",
"about": "Über",
"requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
"checkOnStart": "Überprüfe einmalig beim Start",
"tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln",
"checkOnStart": "Einmalig beim Start überprüfen",
"tryInferAppIdFromCode": "Versuchen, die App-ID aus dem Quellcode zu ermitteln",
"removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps",
"pickHighestVersionCode": "Automatische Auswahl des APK mit höchstem Versionscode",
"checkUpdateOnDetailPage": "Nach Updates suchen, wenn eine App-Detailseite geöffnet wird",
"checkUpdateOnDetailPage": "Nach Aktualisierungen suchen, wenn eine App-Detailseite geöffnet wird",
"disablePageTransitions": "Animationen für Seitenübergänge deaktivieren",
"reversePageTransitions": "Umgekehrte Animationen für Seitenübergänge",
"minStarCount": "Minimale Anzahl von Sternen",
"addInfoBelow": "Fügen Sie diese Informationen unten hinzu.",
"addInfoInSettings": "Fügen Sie diese Info in den Einstellungen hinzu.",
"addInfoBelow": "Diese Information unten hinzufügen.",
"addInfoInSettings": "Diese Information in den Einstellungen hinzufügen.",
"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": "Nur nach dem letzten Teil des Links sortieren",
"filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern",
"customLinkFilterRegex": "Benutzerdefinierter APK Link Filter nach Regulärem Ausdruck (Standard '.apk$')",
"appsPossiblyUpdated": "App Aktualisierungen wurden versucht",
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Updates für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
"customLinkFilterRegex": "Benutzerdefinierter APK-Linkfilter durch regulären Ausdruck (Standard '.apk$')",
"appsPossiblyUpdated": "App-Aktualisierungen wurden versucht",
"appsPossiblyUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps möglicherweise im Hintergrund durchgeführt wurden",
"xWasPossiblyUpdatedToY": "{} wurde möglicherweise aktualisiert auf {}.",
"enableBackgroundUpdates": "Aktiviere Hintergrundaktualisierungen",
"enableBackgroundUpdates": "Hintergrundaktualisierungen aktivieren",
"backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
"backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
"verifyLatestTag": "Überprüfe das „latest“ Tag",
"intermediateLinkRegex": "Filter für einen „Zwischen“-Link, der zuerst besucht werden soll",
"filterByLinkText": "Filtere Links durch Linktext",
"filterByLinkText": "Links durch Linktext filtern",
"intermediateLinkNotFound": "„Zwischen“-Link nicht gefunden",
"intermediateLink": "„Zwischen“-Link",
"exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
"bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
"autoSelectHighestVersionCode": "Automatisch höchste APK-Version auswählen",
"versionExtractionRegEx": "Versions-Extraktion per RegEx",
"matchGroupToUse": "zu verwendende Gruppe abgleichen",
"versionExtractionRegEx": "Versionsextraktion per RegEx",
"trimVersionString": "Versionszeichenfolge mit RegEx kürzen",
"matchGroupToUseForX": "Zu verwendende Abgleichsgruppe für „{}“",
"matchGroupToUse": "Zu verwendende Gruppe abgleichen",
"highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
"pickExportDir": "Export-Verzeichnis wählen",
"autoExportOnChanges": "Automatischer Export bei Änderung(en)",
@ -261,50 +270,57 @@
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
"dontSortReleasesList": "Freigaberelease von der API ordern",
"reverseSort": "Umgekehrtes Sortieren",
"takeFirstLink": "Verwende den ersten Link",
"skipSort": "Überspringe Sortieren",
"takeFirstLink": "Ersten Link verwenden",
"skipSort": "Sortieren überspringen",
"debugMenu": "Debug-Menü",
"bgTaskStarted": "Hintergrundaufgabe gestartet Logs prüfen.",
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
"versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden",
"installing": "Installiere",
"skipUpdateNotifications": "Keine Benachrichtigung zu App-Updates geben",
"versionExtractWholePage": "Versionsextraktion per RegEx auf die gesamte Seite anwenden",
"installing": "Installieren",
"skipUpdateNotifications": "Update-Benachrichtigungen überspringen",
"updatesAvailableNotifChannel": "Aktualisierungen verfügbar",
"appsUpdatedNotifChannel": "Apps aktualisiert",
"appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht",
"appsPossiblyUpdatedNotifChannel": "App-Aktualisierungen wurden versucht",
"errorCheckingUpdatesNotifChannel": "Fehler beim Prüfen auf Aktualisierungen",
"appsRemovedNotifChannel": "Apps entfernt",
"downloadingXNotifChannel": "Lade {} herunter",
"completeAppInstallationNotifChannel": "App Installation abschließen",
"downloadingXNotifChannel": "{} herunterladen",
"completeAppInstallationNotifChannel": "App-Installation abschließen",
"checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
"supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
"selectX": "Wähle {}",
"parallelDownloads": "Erlaube parallele Downloads",
"installMethod": "Installationsmethode",
"normal": "Normal",
"root": "Root",
"shizukuBinderNotFound": "Kompatibler Shizukudienst wurde nicht gefunden",
"useSystemFont": "Verwende die Systemschriftart",
"systemFontError": "Fehler beim Laden der Systemschriftart: {}",
"useVersionCodeAsOSVersion": "Verwende die Appversion als erkannte Version vom Betriebssystem",
"requestHeader": "Request Header",
"useLatestAssetDateAsReleaseDate": "Den letzten Asset-Upload als Veröffentlichungsdatum verwenden",
"onlyCheckInstalledOrTrackOnlyApps": "Nur installierte und mit „Nur nachverfolgen“ markierte Apps auf Aktualisierungen prüfen",
"supportFixedAPKURL": "Unterstützung von festen APK-URLs",
"selectX": "{} wählen",
"parallelDownloads": "Parallele Downloads erlauben",
"useShizuku": "Shizuku oder Sui zur Installation verwenden",
"shizukuBinderNotFound": "Kompatibler Shizuku-Dienst wurde nicht gefunden",
"shizukuOld": "Alte Shizuku-Version (< 11) - aktualisieren Sie sie",
"shizukuOldAndroidWithADB": "Shizuku läuft auf Android < 8.1 mit ADB - aktualisieren Sie Android oder verwenden Sie stattdessen Sui",
"shizukuPretendToBeGooglePlay": "Google Play als Installationsquelle festlegen (wenn Shizuku verwendet wird)",
"useSystemFont": "Systemschriftart verwenden",
"useVersionCodeAsOSVersion": "App-Version als erkannte Version vom Betriebssystem verwenden",
"requestHeader": "Kopfzeile anfordern",
"useLatestAssetDateAsReleaseDate": "Letzten Asset-Upload als Veröffentlichungsdatum verwenden",
"defaultPseudoVersioningMethod": "Standardmäßiges Verfahren zur Pseudo-Versionierung",
"partialAPKHash": "partieller APK-Hash",
"partialAPKHash": "Partieller APK-Hash",
"APKLinkHash": "APK-Link-Hash",
"directAPKLink": "Direkter APK-Link",
"pseudoVersionInUse": "Pseudoversionen sind in Benutzung",
"pseudoVersionInUse": "Es werden Pseudoversionen verwendet",
"installed": "Installiert",
"latest": "Neueste(r)",
"invertRegEx": "Regulären Ausdruck invertieren",
"latest": "Neueste Version",
"invertRegEx": "Regulären Ausdruck invertieren",
"note": "Hinweis",
"selfHostedNote": "Das „{}“-Drop-down-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.",
"wiki": "Hilfe/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App-Konfigurationen (Verwendung auf eigene Gefahr)",
"removeAppQuestion": {
"one": "App entfernen?",
"other": "Apps entfernen?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Zu viele Anfragen (Rate begrenzt) versuche es in {} Minute erneut",
"other": "Zu viele Anfragen (Rate begrenzt) versuche es in {} Minuten erneut"
"one": "Zu viele Anfragen (Rate begrenzt) versuchen Sie es in {} Minute erneut",
"other": "Zu viele Anfragen (Rate begrenzt) versuchen Sie es in {} Minuten erneut"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "Bei der Aktualisierungsprüfung im Hintergrund wurde ein {} festgestellt, eine erneute Prüfung wird in {} Minute geplant",
@ -315,12 +331,12 @@
"other": "Die Hintergrundaktualisierungsprüfung fand {} Aktualisierungen benachrichtigt den Benutzer, falls erforderlich"
},
"apps": {
"eine": "{} App",
"andere": "{} Apps"
"one": "{} App",
"other": "{} Apps"
},
"url": {
"eine": "{} URL",
"andere": "{} URLs"
"one": "{} URL",
"other": "{} URLs"
},
"minute": {
"one": "{} Minute",
@ -343,11 +359,19 @@
"other": "{} und {} weitere Apps haben Aktualisierungen."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} und 1 weitere Anwendung wurden aktualisiert.",
"other": "{} und {} weitere Anwendungen wurden aktualisiert."
"one": "{} und 1 weitere App wurden aktualisiert.",
"other": "{} und {} weitere Apps wurden aktualisiert."
},
"xAndNMoreUpdatesFailed": {
"one": "Die Aktualisierung von {} und 1 weiteren App ist fehlgeschlagen.",
"other": "Die Aktualisierung von {} und {} weiteren Apps ist fehlgeschlagen."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
"other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
"one": "{} und 1 weitere App wurden möglicherweise aktualisiert.",
"other": "{} und {} weitere Apps wurden möglicherweise aktualisiert."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(Required)",
"dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT",
"colour": "Colour",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Use Material You",
"githubStarredRepos": "GitHub Starred Repos",
"uname": "Username",
"wrongArgNum": "Wrong number of arguments provided",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Remove Out-of-Date App Filter",
"showOutdatedOnly": "Show Out-of-Date Apps Only",
"filter": "Filter",
"filterActive": "Filter *",
"filterApps": "Filter Apps",
"appName": "App Name",
"author": "Author",
@ -111,6 +113,7 @@
"dark": "Dark",
"light": "Light",
"followSystem": "Follow System",
"followSystemThemeExplanation": "Following system theme is possible only by using third-party applications",
"useBlackTheme": "Use pure black dark theme",
"appSortBy": "App Sort By",
"authorName": "Author/Name",
@ -144,8 +147,10 @@
"noNewUpdates": "No new updates.",
"xHasAnUpdate": "{} has an update.",
"appsUpdated": "Apps Updated",
"appsNotUpdated": "Failed to update applications",
"appsUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were applied in the background",
"xWasUpdatedToY": "{} was updated to {}.",
"xWasNotUpdatedToY": "Failed to update {} to {}.",
"errorCheckingUpdates": "Error Checking for Updates",
"errorCheckingUpdatesNotifDescription": "A notification that shows when background update checking fails",
"appsRemoved": "Apps Removed",
@ -184,6 +189,9 @@
"disableVersionDetection": "Disable Version Detection",
"noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.",
"downloadingX": "Downloading {}",
"downloadX": "Download {}",
"downloadedX": "Downloaded {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "Notifies the user of the progress in downloading an App",
"noAPKFound": "No APK found",
"noVersionDetection": "No version detection",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Don't show 'Track-Only' warnings",
"dontShowAPKOriginWarnings": "Don't show APK origin warnings",
"moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
"gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
"gitlabPATLabel": "GitLab Personal Access Token",
"about": "About",
"requiresCredentialsInSettings": "{} needs additional credentials (in Settings)",
"checkOnStart": "Check for updates on startup",
@ -233,7 +241,6 @@
"addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.",
"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",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
@ -252,7 +259,9 @@
"bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
"autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
"versionExtractionRegEx": "Version String Extraction RegEx",
"matchGroupToUse": "Match Group to Use for Version String Extraction Regex",
"trimVersionString": "Trim Version String With RegEx",
"matchGroupToUseForX": "Match Group to Use for \"{}\"",
"matchGroupToUse": "Match Group to Use for Version String Extraction RegEx",
"highlightTouchTargets": "Highlight less obvious touch targets",
"pickExportDir": "Pick Export Directory",
"autoExportOnChanges": "Auto-export on changes",
@ -281,12 +290,12 @@
"supportFixedAPKURL": "Support fixed APK URLs",
"selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads",
"installMethod": "Installation method",
"normal": "Normal",
"root": "Root",
"shizukuBinderNotFound": "Сompatible Shizuku service wasn't found",
"useShizuku": "Use Shizuku or Sui to install",
"shizukuBinderNotFound": "Shizuku service 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": "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",
@ -298,6 +307,13 @@
"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.",
"wiki": "Help/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
"removeAppQuestion": {
"one": "Remove App?",
"other": "Remove Apps?"
@ -346,8 +362,16 @@
"one": "{} and 1 more app was updated.",
"other": "{} and {} more apps were updated."
},
"xAndNMoreUpdatesFailed": {
"one": "Failed to update {} and 1 more app.",
"other": "Failed to update {} and {} more apps."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} and 1 more app may have been updated.",
"other": "{} and {} more apps may have been updated."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -1,15 +1,15 @@
{
"invalidURLForSource": "URL de la aplicación {} no es válida",
"noReleaseFound": "No se ha podido encontrar una versión válida",
"invalidURLForSource": "El URL de la aplicación {} no es válido",
"noReleaseFound": "No se ha encontrado una versión válida",
"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",
"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",
"placeholder": "Espacio reservado",
"someErrors": "Han ocurrido algunos errores",
"unexpectedError": "Error inesperado",
"ok": "OK",
"ok": "Aceptar",
"and": "y",
"githubPATLabel": "Token de acceso personal a GitHub\n(reduce tiempos de espera)",
"includePrereleases": "Incluir versiones preliminares",
@ -21,89 +21,91 @@
"continue": "Continuar",
"requiredInBrackets": "(Requerido)",
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN",
"colour": "Color",
"githubStarredRepos": "Repositorios favoritos en GitHub",
"colour": "color",
"standard": "Estándar",
"custom": "A medida",
"useMaterialYou": "Aplicar 'Material You'",
"githubStarredRepos": "repositorios favoritos en GitHub",
"uname": "Nombre de usuario",
"wrongArgNum": "Número de argumentos provistos inválido",
"xIsTrackOnly": "{} es de 'sólo seguimiento'",
"source": "Origen",
"source": "origen",
"app": "Aplicación",
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'solo seguimiento'.",
"youPickedTrackOnly": "Debe seleccionar la opción de 'solo seguimiento'.",
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son solo para seguimiento.",
"youPickedTrackOnly": "Debe seleccionar la opción de 'solo para seguimiento'.",
"trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargar o actualizarla.",
"cancelled": "Cancelado",
"appAlreadyAdded": "Aplicación añadida anteriormente",
"alreadyUpToDateQuestion": "¿Aplicación actualizada previamente?",
"addApp": "Añadir Aplicación",
"addApp": "Añadir aplicación",
"appSourceURL": "URL de origen de la aplicación",
"error": "Error",
"add": "Añadir",
"searchSomeSourcesLabel": "Buscar (solo algunas fuentes)",
"search": "Buscar",
"additionalOptsFor": "Opciones Adicionales para {}",
"supportedSources": "Fuentes Soportadas",
"trackOnlyInBrackets": "(Solo seguimiento)",
"searchableInBrackets": "(soporta búsqueda)",
"additionalOptsFor": "Opciones adicionales para {}",
"supportedSources": "Fuentes admitidas",
"trackOnlyInBrackets": "(Solo para seguimiento)",
"searchableInBrackets": "(permite búsqueda)",
"appsString": "Aplicaciones",
"noApps": "Sin Aplicaciones",
"noAppsForFilter": "Sin aplicaciones para filtrar",
"byX": "por: {}",
"percentProgress": "Progreso: {}%",
"pleaseWait": "Por favor, espere",
"updateAvailable": "Actualización Disponible",
"notInstalled": "No Instalado",
"percentProgress": "Progreso: {} %",
"pleaseWait": "Espere...",
"updateAvailable": "Actualización disponible",
"notInstalled": "No instalado",
"pseudoVersion": "pseudoversión",
"selectAll": "Seleccionar Todo",
"selectAll": "Seleccionar todo",
"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?",
"removeSelectedApps": "Eliminar Aplicaciones Seleccionadas",
"removeSelectedApps": "Eliminar aplicaciones seleccionadas",
"updateX": "Actualizar {}",
"installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo seguimiento)\ncomo actualizada",
"markXTrackOnlyAsUpdated": "Marcar {}\n(solo para seguimiento)\ncomo actualizada",
"changeX": "Cambiar {}",
"installUpdateApps": "Instalar/Actualizar aplicaciones",
"installUpdateSelectedApps": "Instalar/Actualizar aplicaciones seleccionadas",
"installUpdateApps": "Instalar/actualizar aplicaciones",
"installUpdateSelectedApps": "Instalar/actualizar aplicaciones seleccionadas",
"markXSelectedAppsAsUpdated": "¿Marcar {} aplicaciones seleccionadas como actualizadas?",
"no": "No",
"yes": "Sí",
"markSelectedAppsUpdated": "Marcar aplicaciones seleccionadas como actualizadas",
"pinToTop": "Fijar arriba",
"unpinFromTop": "Desfijar de arriba",
"pinToTop": "Anclar al principio",
"unpinFromTop": "Desanclar del principio",
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?",
"installStatusOfXWillBeResetExplanation": "Se restaurará el estado de instalación de las aplicaciones seleccionadas.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
"customLinkMessage": "Estos enlaces funcionan en dispositivos con Obtainium instalado",
"shareAppConfigLinks": "Compartir la configuración de la aplicación como enlace HTML",
"shareSelectedAppURLs": "Compartir URLs de las aplicaciones seleccionadas",
"shareSelectedAppURLs": "Compartir URL de las aplicaciones seleccionadas",
"resetInstallStatus": "Restaurar estado de instalación",
"more": "Más",
"removeOutdatedFilter": "Elimiar filtro de aplicaciones desactualizado",
"removeOutdatedFilter": "Eliminar filtro de aplicaciones desactualizado",
"showOutdatedOnly": "Mostrar solo aplicaciones desactualizadas",
"filter": "Filtrar",
"filterActive": "Filtrar *",
"filterApps": "Filtrar Actualizaciones",
"filterApps": "Filtrar actualizaciones",
"appName": "Nombre de la aplicación",
"author": "Autor",
"upToDateApps": "Aplicaciones Actualizadas",
"nonInstalledApps": "Aplicaciones No Instaladas",
"importExport": "Importar/Exportar",
"upToDateApps": "Aplicaciones actualizadas",
"nonInstalledApps": "Aplicaciones no instaladas",
"importExport": "Importar/exportar",
"settings": "Ajustes",
"exportedTo": "Exportado a {}",
"obtainiumExport": "Exportar Obtainium",
"invalidInput": "Input incorrecto",
"invalidInput": "Entrada no válida",
"importedX": "Importado {}",
"obtainiumImport": "Importar Obtainium",
"importFromURLList": "Importar desde lista de URLs",
"searchQuery": "Consulta de Búsqueda",
"appURLList": "Lista de URLs de Aplicaciones",
"importFromURLList": "Importar desde lista de URL",
"searchQuery": "Término de búsqueda",
"appURLList": "Lista de URL de aplicaciones",
"line": "Línea",
"searchX": "Buscar {}",
"noResults": "Resultados no encontrados",
"noResults": "No se ha encontrado ningún resultado",
"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",
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
"followingURLsHadErrors": "Las siguientes URLs han tenido problemas:",
"importedXOfYApps": "{} de {} aplicaciones importadas.",
"followingURLsHadErrors": "Los URL siguientes han tenido problemas:",
"selectURL": "Seleccionar URL",
"selectURLs": "Seleccionar URLs",
"pick": "Escoger",
@ -111,52 +113,55 @@
"dark": "Oscuro",
"light": "Claro",
"followSystem": "Seguir al sistema",
"useBlackTheme": "Negro puro en tema Oscuro",
"appSortBy": "Ordenar Apps Por",
"authorName": "Autor/Nombre",
"followSystemThemeExplanation": "Seguir el tema del sistema sólo es posible utilizando aplicaciones de terceros",
"useBlackTheme": "Negro puro en tema oscuro",
"appSortBy": "Ordenar aplicaciones por",
"authorName": "Autor/nombre",
"nameAuthor": "Nombre/Autor",
"asAdded": "Según se Añadieron",
"appSortOrder": "Orden de Clasificación",
"ascending": "Ascendente",
"descending": "Descendente",
"bgUpdateCheckInterval": "Comprobación actualizaciones en segundo plano",
"bgUpdateCheckInterval": "Comprobar actualizaciones en segundo plano",
"neverManualOnly": "Nunca, solo manual",
"appearance": "Apariencia",
"showWebInAppView": "Mostrar vista de la web de origen",
"pinUpdates": "Fijar actualizaciones al principio",
"pinUpdates": "Anclar actualizaciones al principio",
"updates": "Actualizaciones",
"sourceSpecific": "Fuente Específica",
"appSource": "Obtainium en GitHub",
"noLogs": "Sin Logs",
"appLogs": "Logs",
"sourceSpecific": "Fuente específica",
"appSource": "Filtrar por fuente",
"noLogs": "Ningún registro",
"appLogs": "Registros",
"close": "Cerrar",
"share": "Compartir",
"appNotFound": "Aplicación no encontrada",
"obtainiumExportHyphenatedLowercase": "obtainium-exportación",
"pickAnAPK": "Seleccione una APK",
"appHasMoreThanOnePackage": "{} tiene más de un paquete:",
"deviceSupportsXArch": "Su dispositivo soporta las siguientes arquitecturas de procesador: {}.",
"deviceSupportsFollowingArchs": "Su dispositivo soporta las siguientes arquitecturas de procesador:",
"deviceSupportsXArch": "Su dispositivo admite las siguientes arquitecturas de procesador: {}.",
"deviceSupportsFollowingArchs": "Su dispositivo admite las siguientes arquitecturas de procesador:",
"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",
"updatesAvailableNotifDescription": "Notifica al usuario de que hay actualizaciones para una o más aplicaciones monitoreadas por Obtainium",
"noNewUpdates": "No hay nuevas actualizaciones.",
"xHasAnUpdate": "{} tiene una actualización.",
"appsUpdated": "Aplicaciones actualizadas",
"appsNotUpdated": "Error al actualizar las aplicaciones",
"appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano",
"xWasUpdatedToY": "{} ha sido actualizada a {}.",
"errorCheckingUpdates": "Error buscando ectualizaciones",
"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",
"appsRemoved": "Aplicaciones eliminadas",
"appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
"xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
"completeAppInstallation": "Instalación completa de la aplicación",
"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...",
"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 para seguimiento",
"errorWithHttpStatusCode": "Error {}",
"versionCorrectionDisabled": "Corrección de versiones desactivada (el plugin parece no funcionar)",
@ -171,11 +176,11 @@
"fdroid": "Repositorio oficial F-Droid",
"appIdOrName": "ID o Nombre de la Aplicación",
"appId": "ID de la Aplicación",
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre",
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con ese ID o nombre",
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
"fdroidThirdPartyRepo": "Repositorio de tercera parte F-Droid",
"steamMobile": "Móvil de vapor",
"steamChat": "Chat de vapor",
"fdroidThirdPartyRepo": "Repositorio de terceros F-Droid",
"steamMobile": "Steam para móviles",
"steamChat": "Chat de Steam",
"install": "Instalar",
"markInstalled": "Marcar como instalada",
"update": "Actualizar",
@ -184,8 +189,11 @@
"disableVersionDetection": "Desactivar la detección de versiones",
"noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda que no funcionar correctamente.",
"downloadingX": "Descargando {}",
"downloadX": "Descargar {}",
"downloadedX": "Descargado {}",
"releaseAsset": "Recurso publicado",
"downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación",
"noAPKFound": "No se encontró el paquete de instalación APK",
"noAPKFound": "No se ha encontrado el paquete de instalación APK",
"noVersionDetection": "Sin detección de versiones",
"categorize": "Catogorizar",
"categories": "Categorías",
@ -193,14 +201,14 @@
"noCategory": "Sin categoría",
"noCategories": "Sin categorías",
"deleteCategoriesQuestion": "¿Eliminar categorías?",
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin categoría'.",
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas se marcarán como «Sin categoría».",
"addCategory": "Añadir categoría",
"label": "Nombre",
"language": "Idioma",
"copiedToClipboard": "Copiado al portapapeles",
"copiedToClipboard": "Copiado en el portapapeles",
"storagePermissionDenied": "Permiso de almacenamiento rechazado",
"selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.",
"filterAPKsByRegEx": "Filtrar por APKs",
"filterAPKsByRegEx": "Filtrar por APK",
"removeFromObtainium": "Eliminar de Obtainium",
"uninstallFromDevice": "Desinstalar del dispositivo",
"onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
@ -212,18 +220,18 @@
"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",
"groupByCategory": "Agrupar por categoría",
"autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador (si es posible)",
"overrideSource": "Sobrescribir Fuente",
"groupByCategory": "Agrupar por categorías",
"autoApkFilterByArch": "Filtrar APK por arquitectura del procesador (si es posible)",
"overrideSource": "Forzar desde la fuente",
"dontShowAgain": "No mostrar de nuevo",
"dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps en 'solo seguimiento'",
"dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps 'solo para seguimiento'",
"dontShowAPKOriginWarnings": "No mostrar avisos sobre las fuentes de las APKs",
"moveNonInstalledAppsToBottom": "Mover Apps no instaladas al final",
"gitlabPATLabel": "Token de acceso personal a GitLab\n(habilita la búsqueda y mejor detección de APKs)",
"moveNonInstalledAppsToBottom": "Mover apps no instaladas al final",
"gitlabPATLabel": "Token de acceso personal a GitLab",
"about": "Acerca",
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en ajustes)",
"checkOnStart": "Comprobar actualizaciones al inicio",
"tryInferAppIdFromCode": "Intentar deducir la ID de la app por el código fuente",
"tryInferAppIdFromCode": "Intentar deducir el ID de la app por el código fuente",
"removeOnExternalUninstall": "Auto eliminar apps desinstaladas externamente",
"pickHighestVersionCode": "Auto selección de versión superior del paquete APK",
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la app",
@ -233,7 +241,6 @@
"addInfoBelow": "Añadir esta información debajo.",
"addInfoInSettings": "Puede añadir esta información en Ajustes.",
"githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con un 'token de acceso personal'.",
"gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin un 'token de acceso personal'.",
"sortByLastLinkSegment": "Ordenar sólo por el último segmento del enlace",
"filterReleaseNotesByRegEx": "Filtrar por notas de versión (release notes)",
"customLinkFilterRegex": "Filtro personalizado de enlace APK (por defecto '.apk$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
"autoSelectHighestVersionCode": "Auto selección del paquete APK con versión más reciente",
"versionExtractionRegEx": "Versión de extracción regex",
"trimVersionString": "Recortar cadena de versión con RegEx",
"matchGroupToUseForX": "Grupo de coincidencia a utilizar para \"{}\"",
"matchGroupToUse": "Grupo a usar para versión de extracción regex",
"highlightTouchTargets": "Resaltar objetivos menos obvios",
"pickExportDir": "Directorio para exportar",
@ -264,7 +273,7 @@
"takeFirstLink": "Usar primer enlace",
"skipSort": "Omitir orden",
"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",
"versionExtractWholePage": "Aplicar la versión de extracción regex a la página entera",
"installing": "Instalando",
@ -279,48 +288,55 @@
"checkingForUpdatesNotifChannel": "Buscando actualizaciones",
"onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas o en seguimiento",
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
"selectX": "Selecciona {}",
"selectX": "Elija {}",
"parallelDownloads": "Permitir descargas paralelas",
"installMethod": "Método de instalación",
"normal": "Normal",
"root": "Raíz",
"useShizuku": "Usar Shizuku o Sui para instalar",
"shizukuBinderNotFound": "Shizuku no funciona",
"useSystemFont": "Usar la fuente de impresión del sistema",
"systemFontError": "Error al cargar la fuente de impresión del sistema: {}",
"shizukuOld": "Versión antigua de Shizuku (<11) - actualícela",
"shizukuOldAndroidWithADB": "Shizuku corriendo en Android < 8.1 con ADB - actualiza Android o usa Sui en su lugar",
"shizukuPretendToBeGooglePlay": "Establecer Google Play como fuente de instalación (si se usa 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",
"useLatestAssetDateAsReleaseDate": "Usar la última carga del recurso 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",
"pseudoVersionInUse": "Se está usando una pseudoversión",
"installed": "Instalado",
"latest": "Versión más reciente",
"invertRegEx": "Invertir expresión regular",
"note": "Nota",
"selfHostedNote": "El desplegable «{}» puede usarse para acceder a instancias autoalojadas/personalizadas de cualquier fuente.",
"badDownload": "No se ha podido analizar el APK (incompatible o descarga parcial)",
"beforeNewInstallsShareToAppVerifier": "Compartir aplicaciones nuevas con AppVerifier (si está disponible)",
"appVerifierInstructionToast": "Comparta con AppVerifier y vuelva aquí cuando esté listo.",
"wiki": "Ayuda/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (uso bajo su propia responsabilidad)",
"removeAppQuestion": {
"one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?"
"one": "¿Eliminar aplicación?",
"other": "¿Eliminar aplicaciones?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Muchas peticiones (limitado) - prueba de nuevo en {} minuto",
"other": "Muchas peticiones (limitado) - prueba de nuevo en {} minutos"
"one": "Muchas peticiones (limitado); pruebe de nuevo en {} minuto",
"other": "Muchas peticiones (limitado); pruebe de nuevo en {} minutos"
},
"bgUpdateGotErrorRetryInMinutes": {
"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"
"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"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualización - se notificará al usuario si es necesario",
"other": "La comprobación de actualizaciones en segundo plano ha encontrado {} actualizaciones - 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 le notificará si es necesario"
},
"apps": {
"one": "{} Aplicación",
"other": "{} Aplicaciones"
},
"url": {
"uno": "{} URL",
"otro": "{} URL"
"one": "{} URL",
"other": "{} URL"
},
"minute": {
"one": "{} minuto",
@ -335,19 +351,27 @@
"other": "{} días"
},
"clearedNLogsBeforeXAfterY": {
"one": "Eliminado {n} log (previo a = {before}, posterior a = {after})",
"other": "Eliminados {n} logs (previos a = {before}, posteriores a = {after})"
"one": "Eliminado {n} registro (previo a = {before}, posterior a = {after})",
"other": "Eliminados {n} registros (previos a = {before}, posteriores a = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} y 1 aplicación más tiene actualizaciones.",
"other": "{} y {} aplicaciones más tienen actualizaciones."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} y 1 aplicación más han sido actualizadas.",
"other": "{} y {} aplicaciones más han sido actualizadas."
"one": "{} y 1 aplicación más se han actualizado.",
"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": {
"one": "{} y 1 aplicación más podría haber sido actualizada.",
"other": "{} y {} aplicaciones más podrían haber sido actualizadas."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(ضروری)",
"dropdownNoOptsError": "خطا: کشویی باید حداقل یک گزینه داشته باشد",
"colour": "رنگ",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Use Material You",
"githubStarredRepos": "مخازن ستاره دار گیتهاب",
"uname": "نام کاربری",
"wrongArgNum": "تعداد آرگومان های ارائه شده اشتباه است",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "فیلتر برنامه قدیمی را حذف کنید",
"showOutdatedOnly": "فقط برنامه های قدیمی را نشان دهید",
"filter": "فیلتر",
"filterActive": "فیلتر *",
"filterApps": "فیلتر کردن برنامه ها",
"appName": "نام برنامه",
"author": "سازنده",
@ -111,6 +113,7 @@
"dark": "تاریک",
"light": "روشن",
"followSystem": "هماهنگ با سیستم",
"followSystemThemeExplanation": "دنبال کردن تم سیستم فقط با استفاده از برنامه های شخص ثالث امکان پذیر است",
"useBlackTheme": "استفاده از تم تیره سیاه خالص",
"appSortBy": "مرتب سازی برنامه بر اساس",
"authorName": "سازنده/اسم",
@ -144,8 +147,10 @@
"noNewUpdates": "به روز رسانی جدیدی وجود ندارد.",
"xHasAnUpdate": "{} یک به روز رسانی دارد.",
"appsUpdated": "برنامه ها به روز شدند",
"appsNotUpdated": "به روز رسانی برنامه ها ناموفق بود",
"appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است",
"xWasUpdatedToY": "{} به {} به روز شد.",
"xWasNotUpdatedToY": "به روز رسانی {} به {} انجام نشد.",
"errorCheckingUpdates": "خطا در بررسی به‌روزرسانی‌ها",
"errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی به‌روزرسانی پس‌زمینه ناموفق است نشان می‌دهد",
"appsRemoved": "برنامه ها حذف شدند",
@ -184,6 +189,9 @@
"disableVersionDetection": "غیرفعال کردن تشخیص نسخه",
"noVersionDetectionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند.",
"downloadingX": "در حال دانلود {}",
"downloadX": "دانلود {}",
"downloadedX": "دانلود شده {}",
"releaseAsset": "انتشار دارایی",
"downloadNotifDescription": "کاربر را از پیشرفت دانلود یک برنامه مطلع می کند",
"noAPKFound": "APK پیدا نشد فایل",
"noVersionDetection": "بدون تشخیص نسخه",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "هشدار 'فقط ردیابی' را نشان ندهید",
"dontShowAPKOriginWarnings": "هشدارهای منبع APK را نشان ندهید",
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
"gitlabPATLabel": "رمز دسترسی شخصی GitLab",
"about": "درباره",
"requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
"checkOnStart": "بررسی در شروع",
@ -233,7 +241,6 @@
"addInfoBelow": "این اطلاعات را در زیر اضافه کنید",
"addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.",
"githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.",
"gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.",
"sortByLastLinkSegment": "فقط بر اساس آخرین بخش پیوند مرتب کنید",
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیش‌فرض '.apk$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "به‌روزرسانی‌های پس‌زمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
"autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK",
"versionExtractionRegEx": "نسخه استخراج RegEx",
"trimVersionString": "Trim Version String With RegEx",
"matchGroupToUseForX": "Match Group to Use for \"{}\"",
"matchGroupToUse": "گروه مورد استفاده را مطابقت دهید",
"highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
"pickExportDir": "فهرست برون ریزی را انتخاب کنید",
@ -281,12 +290,12 @@
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
"selectX": "انتخاب کنید {}",
"parallelDownloads": "اجازه دانلود موازی",
"installMethod": "روش نصب",
"normal": "طبیعی",
"root": "ریشه",
"useShizuku": "Use Shizuku or Sui to install",
"shizukuBinderNotFound": "Shizuku در حال اجرا نیست",
"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": "استفاده از فونت سیستم",
"systemFontError": "خطا در بارگیری فونت سیستم: {}",
"useVersionCodeAsOSVersion": "استفاده کد نسخه برنامه به جای نسخه شناسایی شده توسط سیستم عامل استفاده کنید",
"requestHeader": "درخواست سطر بالایی",
"useLatestAssetDateAsReleaseDate": "استفاده از آخرین بارگذاری دارایی به عنوان تاریخ انتشار",
@ -298,6 +307,13 @@
"installed": "نصب شده است",
"latest": "آخرین",
"invertRegEx": "معکوس کردن عبارت منظم",
"note": "یادداشت",
"selfHostedNote": "از منوی کرکره ای \"{}\" می توان برای دسترسی به نمونه های خود میزبانی/سفارشی از هر منبعی استفاده کرد.",
"badDownload": "APK قابل تجزیه نیست (دانلود ناسازگار یا جزئی)",
"beforeNewInstallsShareToAppVerifier": "اشتراک‌گذاری برنامه‌های جدید با AppVerifier (در صورت وجود)",
"appVerifierInstructionToast": "در AppVerifier به اشتراک بگذارید، سپس پس از آماده شدن به اینجا برگردید.",
"wiki": "راهنما/ویکی",
"crowdsourcedConfigsLabel": "تنظیمات برنامه Crowdsourced (با مسئولیت خود استفاده کنید)",
"removeAppQuestion": {
"one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟"
@ -346,8 +362,16 @@
"one": "{} و 1 برنامه دیگر به روز شدند.",
"other": "{} و {} برنامه دیگر به روز شدند."
},
"xAndNMoreUpdatesFailed": {
"one": "{} و 1 برنامه دیگر به روز نشد.",
"other": "{} و {} برنامه دیگر به روز نشد."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
"other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -1,31 +1,34 @@
{
"invalidURLForSource": "URL d'application {} invalide",
"noReleaseFound": "Impossible de trouver une version appropriée",
"noVersionFound": "Impossible de déterminer la version de la version",
"invalidURLForSource": "URL de l'application {} invalide",
"noReleaseFound": "Impossible de trouver une publication correspondante",
"noVersionFound": "Impossible de déterminer la version de la publication",
"urlMatchesNoSource": "L'URL ne correspond pas à une source connue",
"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",
"functionNotImplemented": "Cette classe n'a pas implémenté cette fonction",
"cantInstallOlderVersion": "Impossible d'installer une ancienne version de l'application",
"appIdMismatch": "L'ID du paquet téléchargé ne correspond pas à l'ID de l'application existante",
"functionNotImplemented": "Cette classe n'implémente pas cette fonction",
"placeholder": "Espace réservé",
"someErrors": "Des erreurs se sont produites",
"someErrors": "Des erreurs sont survenues",
"unexpectedError": "Erreur inattendue",
"ok": "D'accord",
"ok": "Ok",
"and": "et",
"githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)",
"includePrereleases": "Inclure les avant-premières",
"githubPATLabel": "Jeton d'accès personnel GitHub (augmente la limite de débit)",
"includePrereleases": "Inclure les versions préliminaires",
"fallbackToOlderReleases": "Retour aux anciennes versions",
"filterReleaseTitlesByRegEx": "Filtrer les titres de version par expression régulière",
"invalidRegEx": "Expression régulière invalide",
"noDescription": "Pas de description",
"noDescription": "Aucune description",
"cancel": "Annuler",
"continue": "Continuer",
"requiredInBrackets": "(Requis)",
"dropdownNoOptsError": "ERREUR : LE DÉROULEMENT DOIT AVOIR AU MOINS UNE OPT",
"dropdownNoOptsError": "ERREUR: LA LISTE DÉROULANTE DOIT AVOIR AU MOINS UNE OPTION",
"colour": "Couleur",
"standard": "Standard",
"custom": "Personnalisé",
"useMaterialYou": "Utiliser Material You",
"githubStarredRepos": "Dépôts étoilés GitHub",
"uname": "Nom d'utilisateur",
"wrongArgNum": "Mauvais nombre d'arguments fournis",
"xIsTrackOnly": "{} est en 'Suivi uniquement'",
"wrongArgNum": "Nombre incorrect des arguments fournis",
"xIsTrackOnly": "{} en Suivi uniquement",
"source": "Source",
"app": "Application",
"appsFromSourceAreTrackOnly": "Les applications de cette source sont en 'Suivi uniquement'.",
@ -33,97 +36,97 @@
"trackOnlyAppDescription": "L'application sera suivie pour les mises à jour, mais Obtainium ne pourra pas la télécharger ou l'installer.",
"cancelled": "Annulé",
"appAlreadyAdded": "Application déjà ajoutée",
"alreadyUpToDateQuestion": "Application déjà à jour ?",
"addApp": "Ajouter une application",
"appSourceURL": "URL de la source de l'application",
"alreadyUpToDateQuestion": "L'application est à jour?",
"addApp": "Ajouter Appli",
"appSourceURL": "URL source de l'application",
"error": "Erreur",
"add": "Ajouter",
"searchSomeSourcesLabel": "Rechercher (certaines sources uniquement)",
"search": "Rechercher",
"additionalOptsFor": "Options supplémentaires pour {}",
"supportedSources": "Sources prises en charge ",
"supportedSources": "Sources prises en charge",
"trackOnlyInBrackets": "(Suivi uniquement)",
"searchableInBrackets": "(Recherchable)",
"searchableInBrackets": "(Interrogeable)",
"appsString": "Applications",
"noApps": "Aucune application",
"noAppsForFilter": "Aucune application pour le filtre",
"noAppsForFilter": "Aucune application à filtrer",
"byX": "Par {}",
"percentProgress": "Progrès: {}%",
"percentProgress": "Progression : {}%",
"pleaseWait": "Veuillez patienter",
"updateAvailable": "Mise à jour disponible",
"notInstalled": "Pas installé",
"pseudoVersion": "pseudo-version",
"notInstalled": "Non installé",
"pseudoVersion": "Version fictive",
"selectAll": "Tout sélectionner",
"deselectX": "Déselectionner {}",
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
"removeSelectedApps": "Supprimer les applications sélectionnées",
"updateX": "Mise à jour {}",
"xWillBeRemovedButRemainInstalled": "{} sera supprimée d'Obtainium mais restera installée sur l'appareil.",
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
"removeSelectedApps": "Les applications sélectionnées ont été supprimées",
"updateX": "Mettre à jour {}",
"installX": "Installer {}",
"markXTrackOnlyAsUpdated": "Marquer {}\n(Suivi uniquement)\nas mis à jour",
"markXTrackOnlyAsUpdated": "Marquer {}\n(Suivi uniquement)\ncomme étant à jour",
"changeX": "Changer {}",
"installUpdateApps": "Installer/Mettre à jour les applications",
"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",
"yes": "Oui",
"markSelectedAppsUpdated": "Marquer les applications sélectionnées comme mises à jour",
"markSelectedAppsUpdated": "Marquer les application sélectionnées comme étant à jour",
"pinToTop": "Épingler en haut",
"unpinFromTop": "Détacher du haut",
"unpinFromTop": "Désépingler du haut",
"resetInstallStatusForSelectedAppsQuestion": "Réinitialiser l'état d'installation des applications sélectionnées ?",
"installStatusOfXWillBeResetExplanation": "L'état 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 Obtenirium est installé",
"installStatusOfXWillBeResetExplanation": "L'état d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut être utile lorsque la version de l'application affichée dans Obtainium est incorrecte en raison de l'échec des 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",
"resetInstallStatus": "Réinitialiser le statut d'installation",
"shareSelectedAppURLs": "Partager les URL des applications sélectionnées",
"resetInstallStatus": "L'état d'installation des applications a été réinitialisé",
"more": "Plus",
"removeOutdatedFilter": "Supprimer le filtre d'application obsolète",
"removeOutdatedFilter": "Supprimer le filtre des applications obsolètes",
"showOutdatedOnly": "Afficher uniquement les applications obsolètes",
"filter": "Filtre",
"filterActive": "Filtre *",
"filterApps": "Filtrer les applications",
"appName": "Nom de l'application",
"author": "Auteur",
"upToDateApps": "Applications à jour",
"nonInstalledApps": "Applications non installées",
"importExport": "Importer/Exporter",
"importExport": "Sauvegarder/Restaurer",
"settings": "Paramètres",
"exportedTo": "Exporté vers {}",
"obtainiumExport": "Exportation d'Obtainium",
"exportedTo": "Sauvegardé vers {}",
"obtainiumExport": "Sauvegarder Obtainium",
"invalidInput": "Entrée invalide",
"importedX": "Importé {}",
"obtainiumImport": "Importation d'Obtainium",
"importFromURLList": "Importer à partir de la liste d'URL",
"importedX": "Restauré {}",
"obtainiumImport": "Restaurer Obtainium",
"importFromURLList": "Restaurer depuis une liste d'URL",
"searchQuery": "Requête de recherche",
"appURLList": "Liste d'URL d'application",
"line": "Queue",
"appURLList": "Liste d'URL de l'application",
"line": "Ligne",
"searchX": "Rechercher {}",
"noResults": "Aucun résultat trouvé",
"importX": "Importer {}",
"importedAppsIdDisclaimer": "Les applications importées peuvent s'afficher à tort comme \"Non installées\".\nPour résoudre ce problème, réinstallez-les via Obtainium.\nCela ne devrait pas affecter les données de l'application.\n\nN'affecte que les URL et les méthodes d'importation tierces.",
"importErrors": "Erreurs d'importation",
"importedXOfYApps": "{} sur {} applications importées.",
"followingURLsHadErrors": "Les URL suivantes comportaient des erreurs :",
"selectURL": "Sélectionnez l'URL",
"selectURLs": "Sélectionnez les URLs",
"pick": "Prendre",
"noResults": "Aucun résultat",
"importX": "Restauré {}",
"importedAppsIdDisclaimer": "Les applications importées peuvent s'afficher de manière incorrecte comme étant \"Non installées\".\nPour résoudre ce problème, réinstallez-les via Obtainium.\nCela n'affectera pas les données des applications.\n\nN'affecte que les méthodes d'importation d'URL et par des tiers.",
"importErrors": "Erreurs lors de la restauration",
"importedXOfYApps": "{} applications sur {} ont été restaurées.",
"followingURLsHadErrors": "Les URL suivants comportent des erreurs :",
"selectURL": "Sélectionner l'URL",
"selectURLs": "Sélectionner les URL",
"pick": "Choisir",
"theme": "Thème",
"dark": "Sombre",
"light": "Clair",
"followSystem": "Suivre le système",
"useBlackTheme": "Utilisez le thème noir pur et sombre",
"appSortBy": "Applications triées par",
"followSystem": "Correspondre au système",
"followSystemThemeExplanation": "Correspondre au thème du système est possible en utilisant des applications tierces.",
"useBlackTheme": "Utiliser un thème Noir",
"appSortBy": "Trier les applications par",
"authorName": "Auteur/Nom",
"nameAuthor": "Nom/Auteur",
"asAdded": "Comme ajouté",
"asAdded": "Date d'ajout",
"appSortOrder": "Ordre de tri des applications",
"ascending": "Ascendant",
"descending": "Descendanr",
"bgUpdateCheckInterval": "Intervalle de vérification des mises à jour en arrière-plan",
"neverManualOnly": "Jamais - Manuel uniquement",
"descending": "Descendant",
"bgUpdateCheckInterval": "Intervalle de recherche de mises à jour en arrière-plan",
"neverManualOnly": "Jamais - Manuellement uniquement",
"appearance": "Apparence",
"showWebInAppView": "Afficher la page Web source dans la vue de l'application",
"pinUpdates": "Épingler les mises à jour dans la vue Top des applications",
"showWebInAppView": "Afficher la page Web source dans l'onglet 'Applications'",
"pinUpdates": "Épingler les mises à jour en haut de l'onglet 'Applications'",
"updates": "Mises à jour",
"sourceSpecific": "Spécifique à la source",
"appSource": "Source de l'application",
@ -132,30 +135,32 @@
"close": "Fermer",
"share": "Partager",
"appNotFound": "Application introuvable",
"obtainiumExportHyphenatedLowercase": "exportation d'obtainium",
"pickAnAPK": "Choisissez un APK",
"appHasMoreThanOnePackage": "{} a plus d'un paquet :",
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture de processeur {}.",
"deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivantes :",
"obtainiumExportHyphenatedLowercase": "export-obtainium",
"pickAnAPK": "Selectionner un APK",
"appHasMoreThanOnePackage": "{} a plus d'un paquet:",
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture CPU {}.",
"deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivants: ",
"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 le paquet de mise à jour provient de '{}'. Continuer ?",
"updatesAvailable": "Mises à jour disponibles",
"updatesAvailableNotifDescription": "Avertit l'utilisateur que des mises à jour sont disponibles pour une ou plusieurs applications suivies par Obtainium",
"updatesAvailableNotifDescription": "Notifie à l'utilisateur que des mises à jour sont disponibles pour une ou plusieurs applications suivies par Obtainium.",
"noNewUpdates": "Aucune nouvelle mise à jour.",
"xHasAnUpdate": "{} a une mise à jour.",
"appsUpdated": "Applications mises à jour",
"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 {}.",
"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",
"appsNotUpdated": "Échec de la mise à jour des applications",
"appsUpdatedNotifDescription": "Notifie à l'utilisateur que des mises à jour d'une ou plusieurs applications ont été installées en arrière-plan.",
"xWasUpdatedToY": "{} a été mis à jour en {}.",
"xWasNotUpdatedToY": "Échec de la mise à jour de {} vers {}.",
"errorCheckingUpdates": "Erreur lors de la recherche de mises à jour",
"errorCheckingUpdatesNotifDescription": "Notifie l'utilisateur lorsque la recherche de mises à jour en arrière-plan échoue.",
"appsRemoved": "Applications supprimées",
"appsRemovedNotifDescription": "Avertit l'utilisateur qu'une ou plusieurs applications ont été supprimées en raison d'erreurs lors de leur chargement",
"xWasRemovedDueToErrorY": "{} a été supprimé en raison de cette erreur : {}",
"appsRemovedNotifDescription": "Notifie à l'utilisateur qu'une ou plusieurs applications ont été supprimées en raison d'erreurs lors de leur chargement.",
"xWasRemovedDueToErrorY": "{} a été supprimée en raison de cette erreur : {}",
"completeAppInstallation": "Installation complète de l'application",
"obtainiumMustBeOpenToInstallApps": "Obtainium doit être ouvert pour installer des applications",
"obtainiumMustBeOpenToInstallApps": "Obtainium doit être ouvert pour installer les applications",
"completeAppInstallationNotifDescription": "Demande à l'utilisateur de retourner sur Obtainium pour terminer l'installation d'une application",
"checkingForUpdates": "Vérification des mises à jour",
"checkingForUpdatesNotifDescription": "Notification transitoire qui apparaît lors de la recherche de mises à jour",
"checkingForUpdates": "Recherche de mises à jour",
"checkingForUpdatesNotifDescription": "Notification temporaire qui apparaît lors de la recherche de mises à jour",
"pleaseAllowInstallPerm": "Veuillez autoriser Obtainium à installer des applications",
"trackOnly": "Suivi uniquement",
"errorWithHttpStatusCode": "Erreur {}",
@ -163,156 +168,167 @@
"unknown": "Inconnu",
"none": "Aucun",
"never": "Jamais",
"latestVersionX": "Dernière version: {}",
"installedVersionX": "Version installée : {}",
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
"remove": "Retirer",
"yesMarkUpdated": "Oui, marquer comme mis à jour",
"fdroid": "F-Droïde Officiel",
"latestVersionX": "Dernière version : {}",
"installedVersionX": "Version installée : {}",
"lastUpdateCheckX": "Dernière recherche de mises à jour : {}",
"remove": "Supprimer",
"yesMarkUpdated": "Oui, marquer comme étant à jour",
"fdroid": "F-Droid Officiel",
"appIdOrName": "ID ou nom de l'application",
"appId": "ID de l'application",
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
"fdroidThirdPartyRepo": "Dépôt tiers F-Droid",
"steamMobile": "Vapeur Mobile",
"steamChat": "Chat sur Steam",
"steamMobile": "Application mobile Steam",
"steamChat": "Steam Chat",
"install": "Installer",
"markInstalled": "Marquer installée",
"markInstalled": "Marquer comme étant installé",
"update": "Mettre à jour",
"markUpdated": "Marquer à jour",
"additionalOptions": "Options additionelles",
"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.",
"markUpdated": "Marquer comme étant à jour",
"additionalOptions": "Options supplémentaires",
"disableVersionDetection": "Désactiver la détection de la version",
"noVersionDetectionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de la version ne fonctionne pas correctement.",
"downloadingX": "Téléchargement {}",
"downloadNotifDescription": "Avertit l'utilisateur de la progression du téléchargement d'une application",
"downloadX": "Télécharger {}",
"downloadedX": "Téléchargé {}",
"releaseAsset": "Version active",
"downloadNotifDescription": "Notifie l'utilisateur sur l'avancement du téléchargement d'une application",
"noAPKFound": "Aucun APK trouvé",
"noVersionDetection": "Pas de détection de version",
"noVersionDetection": "Aucune version trouvée",
"categorize": "Catégoriser",
"categories": "Catégories",
"category": "Catégorie",
"noCategory": "Aucune catégorie",
"noCategories": "Aucune catégorie",
"deleteCategoriesQuestion": "Supprimer les catégories ?",
"categoryDeleteWarning": "Toutes les applications dans les catégories supprimées seront définies sur non catégorisées.",
"noCategories": "Aucune catégories",
"deleteCategoriesQuestion": "Supprimer les catégories?",
"categoryDeleteWarning": "Toutes les applications des catégories supprimées seront définies comme non catégorisées .",
"addCategory": "Ajouter une catégorie",
"label": "Étiquette",
"label": "Nom",
"language": "Langue",
"copiedToClipboard": "Copié dans le presse-papier",
"storagePermissionDenied": "Autorisation de stockage refusée",
"selectedCategorizeWarning": "Cela remplacera tous les paramètres de catégorie existants pour les applications sélectionnées.",
"storagePermissionDenied": "Permission de stockage refusée",
"selectedCategorizeWarning": "Cela va remplacer toutes les catégories définies des applications sélectionnées.",
"filterAPKsByRegEx": "Filtrer les APK par expression régulière",
"removeFromObtainium": "Supprimer d'Obtainium",
"uninstallFromDevice": "Désinstaller de l'appareil",
"onlyWorksWithNonVersionDetectApps": "Fonctionne uniquement pour les applications avec la détection de version désactivée.",
"onlyWorksWithNonVersionDetectApps": "Ne fonctionne que pour les applications dont la détection de la version est désactivée.",
"releaseDateAsVersion": "Utiliser la date de sortie comme version",
"releaseDateAsVersionExplanation": "Cette option ne doit être utilisée que pour les applications la détection de version ne fonctionne pas correctement, mais une date de sortie est disponible.",
"changes": "Changements",
"releaseDateAsVersionExplanation": "Cette option ne doit être utilisée que pour les applications pour lesquelles la détection de la version ne fonctionne pas correctement, mais dont une date de sortie est disponible.",
"changes": "Modifications",
"releaseDate": "Date de sortie",
"importFromURLsInFile": "Importer à partir 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",
"standardVersionDetection": "Détection de version standard",
"groupByCategory": "Regrouper par catégorie",
"autoApkFilterByArch": "Essayez de filtrer les APK par architecture CPU si possible",
"importFromURLsInFile": "Importer à partir des URLs d'un fichier (comme OPML)",
"versionDetectionExplanation": "Reporter la chaîne de version selon la version détectée par le système d'exploitation",
"versionDetection": "Détection de la version",
"standardVersionDetection": "Détection de la version standard",
"groupByCategory": "Grouper par catégorie",
"autoApkFilterByArch": "Essayer de filtrer les APKs par architecture CPU si possible",
"overrideSource": "Remplacer la source",
"dontShowAgain": "Ne montre plus ça",
"dontShowTrackOnlyWarnings": "Don't Show the 'Track-Only' Warning",
"dontShowAPKOriginWarnings": "Ne pas afficher les avertissements sur l'origine de l'APK",
"dontShowAgain": "Ne plus afficher",
"dontShowTrackOnlyWarnings": "Ne plus afficher les erreurs 'Suivi uniquement'",
"dontShowAPKOriginWarnings": "Ne plus afficher les erreurs sur l'origine de l'APK",
"moveNonInstalledAppsToBottom": "Déplacer les applications non installées vers le bas de la vue Applications",
"gitlabPATLabel": "Jeton d'accès personnel GitLab\\n (permet la recherche et une meilleure découverte d'APK)",
"about": "À propos de",
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
"checkOnStart": "Vérifier les mises à jour au démarrage",
"tryInferAppIdFromCode": "Essayez de déduire l'ID de l'application à partir du code source",
"gitlabPATLabel": "Jeton d'accès personnel GitLab",
"about": "À propos",
"requiresCredentialsInSettings": "{} a besoin d'un complément d'information (dans les Paramètres)",
"checkOnStart": "Rechercher les mises à jour au démarrage",
"tryInferAppIdFromCode": "Essayer de déduire l'identifiant de l'application à partir du code source",
"removeOnExternalUninstall": "Supprimer automatiquement les applications désinstallées en externe",
"pickHighestVersionCode": "Sélectionner automatiquement le code APK de la version la plus élevée",
"checkUpdateOnDetailPage": "Vérifier les mises à jour lors de l'ouverture d'une page de détails d'application",
"pickHighestVersionCode": "Sélectionner automatiquement la version la plus récente du code APK",
"checkUpdateOnDetailPage": "Rechercher les mises à jour lors de l'ouverture de la page détaillée d'une application",
"disablePageTransitions": "Désactiver les animations de transition de page",
"reversePageTransitions": "Animations de transition de page inversée",
"reversePageTransitions": "Inverser les animations de transition de page",
"minStarCount": "Nombre minimum d'étoiles",
"addInfoBelow": "Ajoutez ces informations ci-dessous.",
"addInfoInSettings": "Ajoutez ces informations dans les paramètres.",
"githubSourceNote": "La limitation du débit GitHub peut être évitée à l'aide d'une clé API.",
"gitlabSourceNote": "L'extraction d'APK GitLab peut ne pas fonctionner sans clé API.",
"sortByLastLinkSegment": "Trier uniquement sur le dernier segment du lien",
"addInfoBelow": "Ajoutez cette information ci-dessous.",
"addInfoInSettings": "Ajoutez cette information dans les paramètres.",
"githubSourceNote": "La limitation du débit de GitHub peut être évitée à l'aide d'une clé d'API.",
"sortByLastLinkSegment": "Trier par le dernier segment du lien",
"filterReleaseNotesByRegEx": "Filtrer les notes de version par expression régulière",
"customLinkFilterRegex": "Filtre de lien APK personnalisé par expression régulière (par défaut '.apk$')",
"appsPossiblyUpdated": "Tentative de mise à jour de l'application",
"appsPossiblyUpdatedNotifDescription": "Avertit l'utilisateur que des mises à jour d'une ou plusieurs applications ont été potentiellement appliquées en arrière-plan",
"xWasPossiblyUpdatedToY": "{} a peut-être été mis à jour vers {}.",
"appsPossiblyUpdated": "Tentative de mise à jour des applications",
"appsPossiblyUpdatedNotifDescription": "Notifie à l'utilisateur que des mises à jour d'une ou plusieurs applications ont potentiellement été appliquées en arrière-plan",
"xWasPossiblyUpdatedToY": "{} peut être mis à jour en {}.",
"enableBackgroundUpdates": "Activer les mises à jour en arrière-plan",
"backgroundUpdateReqsExplanation": "Les mises à jour en arrière-plan peuvent ne pas être possibles pour toutes les applications.",
"backgroundUpdateLimitsExplanation": "Le succès d'une installation en arrière-plan ne peut être déterminé qu'à l'ouverture d'Obetium.",
"verifyLatestTag": "Vérifiez la balise 'dernière'",
"intermediateLinkRegex": " Filtrer un lien \" intermédiaire \" à visiter ",
"filterByLinkText": "Filtrer les liens par texte de lien",
"backgroundUpdateLimitsExplanation": "Le résultat d'une installation en arrière-plan ne peut être déterminé qu'à l'ouverture d'Obtainium.",
"verifyLatestTag": "Vérifier la balise 'latest'",
"intermediateLinkRegex": "Filtrer un lien 'intermédiaire' à visiter",
"filterByLinkText": "Filtrer les liens par texte du lien",
"intermediateLinkNotFound": "Lien intermédiaire introuvable",
"intermediateLink": "Lien intermédiaire",
"exemptFromBackgroundUpdates": "Exempt des mises à jour en arrière-plan (si activé)",
"bgUpdatesOnWiFiOnly": "Désactiver les mises à jour en arrière-plan lorsque vous n'êtes pas connecté au WiFi",
"autoSelectHighestVersionCode": "Sélection automatique de la version la plus élevéeCode APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Mettez en évidence les cibles tactiles moins évidentes",
"pickExportDir": "Choisir le répertoire d'exportation",
"autoExportOnChanges": "Exportation automatique sur modifications",
"exemptFromBackgroundUpdates": "Exclure de la mise à jour en arrière-plan (si activé)",
"bgUpdatesOnWiFiOnly": "Désactiver les mises à jour en arrière-plan lorsque vous n'êtes pas en WiFi",
"autoSelectHighestVersionCode": "Sélectionner automatiquement la version la plus récente du code APK",
"versionExtractionRegEx": "Extraire la version par Expression régulière",
"trimVersionString": "Découper la version par Expression régulière",
"matchGroupToUseForX": "Groupe de correspondance à utiliser pour \"{}\"",
"matchGroupToUse": "Groupe de correspondance à utiliser pour l'extraction de la version par Expression régulière",
"highlightTouchTargets": "Mettre en évidence les touches moins évidentes",
"pickExportDir": "Selectionner le dossier pour l'exportation",
"autoExportOnChanges": "Exportation automatique lors de modification",
"includeSettings": "Inclure les paramètres",
"filterVersionsByRegEx": "Filtrer les versions par expression régulière",
"trySelectingSuggestedVersionCode": "Essayez de sélectionner la version suggéréeCode APK",
"dontSortReleasesList": "Conserver la commande de version de l'API",
"trySelectingSuggestedVersionCode": "Essayer de sélectionner la version suggérée du code APK",
"dontSortReleasesList": "Conserver l'ordre de la version de l'API",
"reverseSort": "Tri inversé",
"takeFirstLink": "Prendre le premier lien",
"skipSort": "Sauter le tri",
"debugMenu": "Menu de débogage",
"takeFirstLink": "Utiliser le premier lien",
"skipSort": "Ignorer le tri",
"debugMenu": "Menu de déboggage",
"bgTaskStarted": "Tâche en arrière-plan démarrée - vérifier les journaux.",
"runBgCheckNow": "Exécuter la vérification de la mise à jour en arrière-plan maintenant",
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
"runBgCheckNow": "Exécuter la recherche de mise à jour en arrière-plan maintenant",
"versionExtractWholePage": "Appliquer l'extraction de la version par expression régulière à l'ensemble de la page",
"installing": "Installation",
"skipUpdateNotifications": "Ignorer les notifications de mise à jour",
"updatesAvailableNotifChannel": "Mises à jour disponibles",
"appsUpdatedNotifChannel": "Applications mises à jour",
"appsPossiblyUpdatedNotifChannel": "Tentative de mise à jour de l'application",
"errorCheckingUpdatesNotifChannel": "Erreur lors de la vérification des mises à jour",
"appsPossiblyUpdatedNotifChannel": "Essayer de mettre à jour les applications",
"errorCheckingUpdatesNotifChannel": "Erreur lors de la recherche de mises à jour",
"appsRemovedNotifChannel": "Applications supprimées",
"downloadingXNotifChannel": "Téléchargement {}",
"completeAppInstallationNotifChannel": "Installation complète de l'application",
"checkingForUpdatesNotifChannel": "Vérification des mises à jour",
"onlyCheckInstalledOrTrackOnlyApps": "Vérifiez uniquement les mises à jour des applications installées et de suivi uniquement",
"checkingForUpdatesNotifChannel": "Recherche de mises à jour",
"onlyCheckInstalledOrTrackOnlyApps": "Rechercher uniquement les mises à jour des applications installées et des applications 'Suivi uniquement'",
"supportFixedAPKURL": "Prise en charge des URL APK fixes",
"selectX": "Sélectionner {}",
"parallelDownloads": "Autoriser les téléchargements parallèles",
"installMethod": "Méthode d'installation",
"normal": "Normale",
"root": "Racine",
"shizukuBinderNotFound": "Shizuku is not running",
"useSystemFont": "Utiliser la police système",
"systemFontError": "Erreur de chargement de la police 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 téléchargement d'élément 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",
"parallelDownloads": "Autoriser les téléchargements simultanés",
"useShizuku": "Utiliser Shizuku ou Sui pour l'installation",
"shizukuBinderNotFound": "Le service Shizuku est introuvable",
"shizukuOld": "Ancienne version de Shizuku (<11) - veuillez le mettre à jour",
"shizukuOldAndroidWithADB": "Shizuku fonctionne sur Android < 8.1 avec ADB - veuillez mettre à jour Android ou utiliser Sui à la place",
"shizukuPretendToBeGooglePlay": "Définir Google Play comme source d'installation (si Shizuku est utilisé)",
"useSystemFont": "Utiliser la police du système",
"useVersionCodeAsOSVersion": "Utiliser le code de version de l'application détectée par le système d'exploitation",
"requestHeader": "Intitulé de la demande",
"useLatestAssetDateAsReleaseDate": "Utiliser le dernier élément mis en ligne comme date de sortie",
"defaultPseudoVersioningMethod": "Méthode de version fictive par défaut",
"partialAPKHash": "Hash partiel de l'APK",
"APKLinkHash": "Hash du lien APK",
"directAPKLink": "Lien direct de l'APK",
"pseudoVersionInUse": "Version fictive utilisé",
"installed": "Installée",
"latest": "Dernier",
"latest": "Dernière version",
"invertRegEx": "Inverser l'expression régulière",
"note": "Note",
"selfHostedNote": "La liste déroulante \"{}\" peut être utilisée pour accéder à des 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 tout est prêt.",
"wiki": "Aide/Wiki",
"crowdsourcedConfigsLabel": "Configurations d'applications par la communauté (à utiliser à vos risques et périls)",
"removeAppQuestion": {
"one": "Supprimer l'application ?",
"other": "Supprimer les applications ?"
"one": "Supprimer l'application?",
"other": "Supprimer les applications?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Trop de demandes (taux limité) - réessayez dans {} minute",
"other": "Trop de demandes (taux limité) - réessayez dans {} minutes"
"one": "Trop de requêtes (taux limité) - réessayez dans {} minute",
"other": "Trop de requêtes (taux limité) - réessayez dans {} minutes"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "La vérification de la mise à jour en arrière-plan a rencontré un {}, planifiera une nouvelle tentative de vérification 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"
"one": "La recherche de mise à jour en arrière-plan a rencontré un {}, une nouvelle tentative programmée dans {} minute",
"other": "La recherche de mise à jour en arrière-plan a rencontré un {}, une nouvelle tentative programmée dans {} minutes"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "La vérification des mises à jour en arrière-plan trouvée {} mise à jour - avertira l'utilisateur si nécessaire",
"other": "La vérification des mises à jour en arrière-plan a trouvé {} mises à jour - avertira l'utilisateur si nécessaire"
"one": "La recherche de mises à jour en arrière-plan à trouvée {} mise à jour - l'utilisateur sera notifié si nécessaire",
"other": "La recherche de mises à jour en arrière-plan à trouvée {} mises à jour - l'utilisateur sera notifié si nécessaire"
},
"apps": {
"one": "{} Application",
@ -323,7 +339,7 @@
"other": "{} URL"
},
"minute": {
"one": "{} Minutes",
"one": "{} Minute",
"other": "{} Minutes"
},
"hour": {
@ -335,8 +351,8 @@
"other": "{} Jours"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} journal effacé (avant = {before}, après = {after})",
"other": "{n} journaux effacés (avant = {before}, après = {after})"
"one": "Nettoyage du journal {n} (avant = {before}, après = {after})",
"other": "Nettoyage des journaux {n} (avant = {before}, après = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} et 1 autre application ont des mises à jour.",
@ -344,10 +360,18 @@
},
"xAndNMoreUpdatesInstalled": {
"one": "{} et 1 autre application ont été mises à jour.",
"other": "{} et {} autres applications ont été mises à jour."
"other": "{} et {} autres applications ont étés mis à jour."
},
"xAndNMoreUpdatesFailed": {
"one": "Échec de la mise à jour de {} et 1 autre application.",
"other": "Échec de la mise à jour de {} et {} autres applications."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"une": "{} et 1 application supplémentaire ont peut-être été mises à jour.",
"other": "{} et {} autres applications peuvent avoir été mises à jour."
"one": "{} et 1 autre application ont peut-être été mises à jour.",
"other": "{} et {} autres applications ont peut-être étés mis à jour."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(Kötelező)",
"dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI",
"colour": "Szín",
"standard": "Standard",
"custom": "Custom",
"useMaterialYou": "Használja az Ön által használt anyagot",
"githubStarredRepos": "GitHub Csillagos Repo-k",
"uname": "Felh.név",
"wrongArgNum": "Rossz számú argumentumot adott meg",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Távolítsa el az elavult app szűrőt",
"showOutdatedOnly": "Csak az elavult appok megjelenítése",
"filter": "Szűrő",
"filterActive": "Szűrő *",
"filterApps": "Appok szűrése",
"appName": "App név",
"author": "Szerző",
@ -111,6 +113,7 @@
"dark": "Sötét",
"light": "Világos",
"followSystem": "Rendszer szerint",
"followSystemThemeExplanation": "A következő rendszer téma csak harmadik féltől származó alkalmazások használatával lehetséges",
"useBlackTheme": "Használjon teljesen fekete sötét témát",
"appSortBy": "App rendezés...",
"authorName": "Szerző/Név",
@ -144,8 +147,10 @@
"noNewUpdates": "Nincsenek új frissítések.",
"xHasAnUpdate": "A(z) {} frissítést kapott.",
"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",
"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",
"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",
@ -184,6 +189,9 @@
"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.",
"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",
"noAPKFound": "Nem található APK",
"noVersionDetection": "Nincs verzió érzékelés",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
"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",
"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",
"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",
@ -233,7 +241,6 @@
"addInfoBelow": "Adja hozzá ezt az infót alább.",
"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.",
"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",
"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$')",
@ -252,6 +259,8 @@
"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",
"versionExtractionRegEx": "Verzió kibontása reguláris kifejezéssel",
"trimVersionString": "Trim Version String RegEx-szel",
"matchGroupToUseForX": "A \"{}\" csoporthoz használandó csoport egyeztetése",
"matchGroupToUse": "Párosítsa a csoportot a használathoz",
"highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
"pickExportDir": "Válassza az Exportálási könyvtárat",
@ -281,16 +290,16 @@
"supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
"selectX": "Kiválaszt {}",
"parallelDownloads": "Párhuzamos letöltéseket enged",
"installMethod": "Telepítési mód",
"normal": "Normál",
"root": "Gyökér",
"useShizuku": "Használja Shizuku vagy Sui telepítéséhez",
"shizukuBinderNotFound": "A Shizuku nem fut",
"shizukuOld": "Régi Shizuku verzió (<11) - frissítsd!",
"shizukuOldAndroidWithADB": "Shizuku fut Android < 8.1 ADB-vel - frissítse az Androidot vagy használja a Sui-t helyette",
"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",
"systemFontError": "Hiba a rendszer betűtípusának betöltésekor: {}",
"useVersionCodeAsOSVersion": "Az app versionCode használata a rendszer által észlelt verzióként",
"useVersionCodeAsOSVersion": "Az app verziókód használata a rendszer által észlelt verzióként",
"requestHeader": "Kérelem fejléc",
"useLatestAssetDateAsReleaseDate": "Használja a legújabb tartalomfeltöltést megjelenési dátumként",
"defaultPseudoVersioningMethod": "Alapértelmezett álversziós módszer",
"defaultPseudoVersioningMethod": "Alapértelmezett álverziós módszer",
"partialAPKHash": "Részleges APK Hash",
"APKLinkHash": "APK Link Hash",
"directAPKLink": "Közvetlen APK Link",
@ -298,9 +307,16 @@
"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.",
"wiki": "Súgó/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (használat saját felelősségre)",
"removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?"
"other": "Eltávolítja az alkalmazásokat?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva",
@ -346,8 +362,16 @@
"one": "A(z) {} és 1 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": {
"one": "{} és 1 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)",
"dropdownNoOptsError": "ERRORE: LA TENDINA DEVE AVERE ALMENO UN'OPZIONE",
"colour": "Colore",
"standard": "Standard",
"custom": "Personalizzato",
"useMaterialYou": "Utilizzate il materiale che avete a disposizione",
"githubStarredRepos": "repository stellati da GitHub",
"uname": "Nome utente",
"wrongArgNum": "Numero di argomenti forniti errato",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Rimuovi il filtro per le app non aggiornate",
"showOutdatedOnly": "Mostra solo le app non aggiornate",
"filter": "Filtri",
"filterActive": "Filtri *",
"filterApps": "Filtra app",
"appName": "Nome dell'app",
"author": "Autore",
@ -111,7 +113,8 @@
"dark": "Scuro",
"light": "Chiaro",
"followSystem": "Segui il sistema",
"useBlackTheme": "Usa il tema Nero puro",
"followSystemThemeExplanation": "È possibile seguire il tema di sistema solo utilizzando applicazioni di terze parti.",
"useBlackTheme": "Usa il tema nero puro",
"appSortBy": "App ordinate per",
"authorName": "Autore/Nome",
"nameAuthor": "Nome/Autore",
@ -144,8 +147,10 @@
"noNewUpdates": "Nessun nuovo aggiornamento.",
"xHasAnUpdate": "Aggiornamento disponibile per {}",
"appsUpdated": "App aggiornate",
"appsNotUpdated": "Impossibile aggiornare le applicazioni",
"appsUpdatedNotifDescription": "Notifica all'utente che una o più app sono state aggiornate in secondo piano",
"xWasUpdatedToY": "{} è stato aggiornato alla {}.",
"xWasNotUpdatedToY": "Impossibile aggiornare {} a {}.",
"errorCheckingUpdates": "Controllo degli errori per gli aggiornamenti",
"errorCheckingUpdatesNotifDescription": "Una notifica che mostra quando il controllo degli aggiornamenti in secondo piano fallisce",
"appsRemoved": "App rimosse",
@ -184,6 +189,9 @@
"disableVersionDetection": "Disattiva il rilevamento della versione",
"noVersionDetectionExplanation": "Questa opzione dovrebbe essere usata solo per le app la cui versione non viene rilevata correttamente.",
"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",
"noAPKFound": "Nessun APK trovato",
"noVersionDetection": "Disattiva rilevamento di versione",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Non mostrare gli avvisi 'Solo-Monitoraggio'",
"dontShowAPKOriginWarnings": "Non mostrare gli avvisi di origine dell'APK",
"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",
"requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)",
"checkOnStart": "Controlla una volta all'avvio",
@ -233,7 +241,6 @@
"addInfoBelow": "Aggiungi questa info sotto.",
"addInfoInSettings": "Aggiungi questa info nelle impostazioni.",
"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",
"filterReleaseNotesByRegEx": "Filtra le note di rilascio con espressione regolare",
"customLinkFilterRegex": "Filtra link APK personalizzato con espressione regolare (predefinito '.apk$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "Disattiva aggiornamenti in secondo piano quando non si usa il WiFi",
"autoSelectHighestVersionCode": "Auto-seleziona APK con versionCode più alto",
"versionExtractionRegEx": "RegEx di estrazione versione",
"trimVersionString": "Tagliare la stringa della versione con RegEx",
"matchGroupToUseForX": "Gruppo di corrispondenza da utilizzare per \"{}\"",
"matchGroupToUse": "Gruppo da usare",
"highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
"pickExportDir": "Scegli cartella esp.",
@ -281,15 +290,15 @@
"supportFixedAPKURL": "Supporta URL fissi di APK",
"selectX": "Seleziona {}",
"parallelDownloads": "Permetti download paralleli",
"installMethod": "Metodo d'installazione",
"normal": "Normale",
"root": "Radice",
"useShizuku": "Utilizzare Shizuku o Sui per installare",
"shizukuBinderNotFound": "Shizuku non è in esecuzione",
"useSystemFont": "Utilizza il carattere di sistema",
"systemFontError": "Errore durante il caricamento del carattere di sistema: {}",
"useVersionCodeAsOSVersion": "Utilizza il codice versione dell'app come versione rilevata dal sistema operativo",
"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": "Utilizza l'ultimo caricamento della risorsa come data di rilascio",
"useLatestAssetDateAsReleaseDate": "Usa l'ultimo caricamento della risorsa come data di rilascio",
"defaultPseudoVersioningMethod": "Metodo di pseudoversione predefinito",
"partialAPKHash": "Hash APK parziale",
"APKLinkHash": "Hash collegamento APK",
@ -298,6 +307,13 @@
"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.",
"wiki": "Aiuto/Wiki",
"crowdsourcedConfigsLabel": "Configurazioni di app in crowdsourcing (uso a proprio rischio)",
"removeAppQuestion": {
"one": "Rimuovere l'app?",
"other": "Rimuovere le app?"
@ -346,8 +362,16 @@
"one": "{} e un'altra 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": {
"one": "{} e un'altra 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": "(必須)",
"dropdownNoOptsError": "エラー: ドロップダウンには、少なくとも1つのオプションが必要です",
"colour": "カラー",
"standard": "スタンダード",
"custom": "カスタム",
"useMaterialYou": "Material Youを使用する",
"githubStarredRepos": "Githubでスターしたリポジトリ",
"uname": "ユーザー名",
"wrongArgNum": "提供する引数の数が間違っています",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "アップデートが存在するアプリのフィルターを解除",
"showOutdatedOnly": "アップデートが存在するアプリのみ表示する",
"filter": "フィルター",
"filterActive": "フィルター *",
"filterApps": "アプリをフィルタリングする",
"appName": "アプリ名",
"author": "作者",
@ -111,6 +113,7 @@
"dark": "ダーク",
"light": "ライト",
"followSystem": "システムに従う",
"followSystemThemeExplanation": "以下のシステムテーマは、サードパーティのアプリケーションを使用することによってのみ可能です。",
"useBlackTheme": "ピュアブラックダークテーマを使用する",
"appSortBy": "アプリの並び方",
"authorName": "作者名/アプリ名",
@ -144,8 +147,10 @@
"noNewUpdates": "新しいアップデートはありません",
"xHasAnUpdate": "{} のアップデートが利用可能です。",
"appsUpdated": "アプリをアップデートしました",
"appsNotUpdated": "アプリケーションの更新に失敗",
"appsUpdatedNotifDescription": "1つまたは複数のAppのアップデートがバックグラウンドで適用されたことをユーザーに通知する",
"xWasUpdatedToY": "{} が {} にアップデートされました",
"xWasNotUpdatedToY": "への更新に失敗しました。",
"errorCheckingUpdates": "アップデート確認中のエラー",
"errorCheckingUpdatesNotifDescription": "バックグラウンドでのアップデート確認に失敗した際に表示される通知",
"appsRemoved": "削除されたアプリ",
@ -184,6 +189,9 @@
"disableVersionDetection": "バージョン検出を無効にする",
"noVersionDetectionExplanation": "このオプションは、バージョン検出が正しく機能しないアプリにのみ使用する必要があります。",
"downloadingX": "{} をダウンロード中",
"downloadX": "ダウンロード",
"downloadedX": "ダウンロード",
"releaseAsset": "リリース資産",
"downloadNotifDescription": "アプリのダウンロード状況を通知する",
"noAPKFound": "APKが見つかりません",
"noVersionDetection": "バージョン検出を行わない",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "「追跡のみ」の警告を表示しない",
"dontShowAPKOriginWarnings": "APKのダウンロード元の警告を表示しない",
"moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる",
"gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)",
"gitlabPATLabel": "GitLab パーソナルアクセストークン",
"about": "概要",
"requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)",
"checkOnStart": "起動時にアップデートを確認する",
@ -233,7 +241,6 @@
"addInfoBelow": "下部でこの情報を追加してください。",
"addInfoInSettings": "設定でこの情報を追加してください。",
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。",
"sortByLastLinkSegment": "リンクの最後のセグメントのみでソートする",
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合、バックグラウンドアップデートを無効にする",
"autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する",
"versionExtractionRegEx": "バージョン抽出の正規表現",
"trimVersionString": "正規表現でバージョン文字列をトリムする",
"matchGroupToUseForX": "\"{}\"に使用するマッチしたグループ",
"matchGroupToUse": "使用するマッチしたグループ",
"highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
"pickExportDir": "エクスポートディレクトリを選択",
@ -281,12 +290,12 @@
"supportFixedAPKURL": "固定されたAPKのURLをサポートする",
"selectX": "{} 選択",
"parallelDownloads": "並行ダウンロードを許可する",
"installMethod": "インストール方法",
"normal": "通常",
"root": "Root",
"useShizuku": "ShizukuまたはSuiを使用してインストールする",
"shizukuBinderNotFound": "Shizukuが起動していません",
"shizukuOld": "古いShizukuのバージョン (<11) - アップデートしてください",
"shizukuOldAndroidWithADB": "ShizukuがAndroid 8.1未満でADBを使用して動作しています - Androidをアップデートするか、代わりにSuiを使用してください",
"shizukuPretendToBeGooglePlay": "Google Playをインストール元として設定するShizukuを使用する場合",
"useSystemFont": "システムフォントを使用する",
"systemFontError": "システムフォントの読み込みエラー: {}",
"useVersionCodeAsOSVersion": "アプリのバージョンコードをOSで検出されたバージョンとして使用する",
"requestHeader": "リクエストヘッダー",
"useLatestAssetDateAsReleaseDate": "最新のアセットアップロードをリリース日として使用する",
@ -298,6 +307,13 @@
"installed": "インストール済み",
"latest": "最新",
"invertRegEx": "正規表現を反転",
"note": "注",
"selfHostedNote": "ドロップダウン\"{}\"を使用すると、あらゆるソースのセルフホスト/カスタムインスタンスにアクセスできます。",
"badDownload": "APK を解析できませんでした(互換性がないか、部分的にダウンロードされています)。",
"beforeNewInstallsShareToAppVerifier": "AppVerifierで新しいアプリを共有する利用可能な場合",
"appVerifierInstructionToast": "AppVerifierに共有し、準備ができたらここに戻ってください。",
"wiki": "ヘルプ/ウィキ",
"crowdsourcedConfigsLabel": "クラウドソーシングによるアプリの設定(利用は自己責任で)",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"
@ -346,8 +362,16 @@
"one": "{} とさらに {} 個のアプリがアップデートされました。",
"other": "{} とさらに {} 個のアプリがアップデートされました。"
},
"xAndNMoreUpdatesFailed": {
"one": "更新に失敗しました。",
"other": "アプリのアップデートに失敗しました。"
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。",
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。"
},
"apk": {
"one": "{}APK",
"other": "{}APK"
}
}

View File

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

View File

@ -22,6 +22,9 @@
"requiredInBrackets": "(Wymagane)",
"dropdownNoOptsError": "BŁĄD: LISTA ROZWIJANA MUSI MIEĆ CO NAJMNIEJ JEDNĄ OPCJĘ",
"colour": "Kolor",
"standard": "Standard",
"custom": "Niestandardowe",
"useMaterialYou": "Używaj materiałów",
"githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką",
"uname": "Nazwa użytkownika",
"wrongArgNum": "Nieprawidłowa liczba podanych argumentów",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Usuń filtr nieaktualnych aplikacji",
"showOutdatedOnly": "Pokaż tylko nieaktualne aplikacje",
"filter": "FIltr",
"filterActive": "Filtruj *",
"filterApps": "Filtruj aplikacje",
"appName": "Nazwa aplikacji",
"author": "Autor",
@ -111,6 +113,7 @@
"dark": "Ciemny",
"light": "Jasny",
"followSystem": "Zgodny z systemem",
"followSystemThemeExplanation": "Podążanie za motywem systemowym jest możliwe tylko przy użyciu aplikacji firm trzecich",
"useBlackTheme": "Użyj czarnego motywu",
"appSortBy": "Sortuj aplikacje według",
"authorName": "Autor/Nazwa",
@ -144,8 +147,10 @@
"noNewUpdates": "Brak nowych aktualizacji.",
"xHasAnUpdate": "{} ma aktualizację.",
"appsUpdated": "Zaktualizowano aplikacje",
"appsNotUpdated": "Nie udało się zaktualizować aplikacji",
"appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle",
"xWasUpdatedToY": "{} zaktualizowano do {}.",
"xWasNotUpdatedToY": "Nie udało się zaktualizować {} do {}.",
"errorCheckingUpdates": "Błąd sprawdzania aktualizacji",
"errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
"appsRemoved": "Usunięte aplikacje",
@ -184,6 +189,9 @@
"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.",
"downloadingX": "Pobieranie {}",
"downloadX": "Pobierz {}",
"downloadedX": "Pobrano {}",
"releaseAsset": "Release Asset",
"downloadNotifDescription": "Informuje o postępach w pobieraniu aplikacji",
"noAPKFound": "Nie znaleziono pakietu APK",
"noVersionDetection": "Bez wykrywania wersji",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Nie pokazuj ostrzeżeń \"Tylko obserwowana\"",
"dontShowAPKOriginWarnings": "Nie pokazuj ostrzeżeń o pochodzeniu APK",
"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",
"requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)",
"checkOnStart": "Sprawdź aktualizacje przy uruchomieniu",
@ -233,7 +241,6 @@
"addInfoBelow": "Dodaj tę informację poniżej.",
"addInfoInSettings": "Dodaj tę informację w Ustawieniach.",
"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",
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
"customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
"autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
"versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
"trimVersionString": "Przycinanie łańcucha wersji za pomocą RegEx",
"matchGroupToUseForX": "Dopasuj grupę do użycia dla \"{}\"",
"matchGroupToUse": "Dopasuj grupę do użycia dla wyrażenia regularnego wyodrębniania wersji",
"highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
"pickExportDir": "Wybierz katalog eksportu",
@ -281,12 +290,12 @@
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
"selectX": "Wybierz {}",
"parallelDownloads": "Zezwól na pobieranie równoległe",
"installMethod": "Metoda instalacji",
"normal": "Normalna",
"root": "Źródło",
"useShizuku": "Użyj Shizuku lub Sui, aby zainstalować",
"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",
"systemFontError": "Błąd podczas ładowania 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",
@ -298,6 +307,13 @@
"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.",
"wiki": "Pomoc/Wiki",
"crowdsourcedConfigsLabel": "Konfiguracje aplikacji pochodzące z crowdsourcingu (korzystanie na własne ryzyko)",
"removeAppQuestion": {
"one": "Usunąć aplikację?",
"few": "Usunąć aplikacje?",
@ -370,10 +386,18 @@
"many": "{} i {} innych apek zostało zaktualizowanych.",
"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": {
"one": "{} i 1 inna apka mogły zostać zaktualizowane.",
"few": "{} i {} inne apki mogły zostać zaktualizowane.",
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
"other": "{} i {} inne apki mogły zostać zaktualizowane."
},
"apk": {
"one": "{} APK",
"other": "{} APK"
}
}

View File

@ -2,7 +2,7 @@
"invalidURLForSource": "URL {} inválida",
"noReleaseFound": "Não foi possível encontrar uma versão adequada",
"noVersionFound": "Não foi possível encontrar uma versão",
"urlMatchesNoSource": "URL não corresponde a uma fonte conhecida",
"urlMatchesNoSource": "A URL não corresponde a uma fonte conhecida",
"cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de um aplicativo",
"appIdMismatch": "ID do pacote baixado não é igual ao ID do aplicativo instalado",
"functionNotImplemented": "Esta classe não implementou essa função",
@ -11,10 +11,10 @@
"unexpectedError": "Erro inesperado",
"ok": "OK",
"and": "e",
"githubPATLabel": "Token de acesso pessoal do GitHub (Reduz tempos de espera)",
"githubPATLabel": "Token de acesso pessoal do GitHub\n(Reduz tempos de espera)",
"includePrereleases": "Incluir pré-lançamentos",
"fallbackToOlderReleases": "Retornar para versões anteriores",
"filterReleaseTitlesByRegEx": "Filtrar títulos de versões por expressão regular",
"filterReleaseTitlesByRegEx": "Filtrar títulos de versões usando expressão regular",
"invalidRegEx": "Expressão regular inválida",
"noDescription": "Sem descrição",
"cancel": "Cancelar",
@ -22,7 +22,10 @@
"requiredInBrackets": "(Necessário)",
"dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO",
"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",
"wrongArgNum": "Número de argumentos errado",
"xIsTrackOnly": "{} é 'Apenas monitorar'",
@ -38,12 +41,12 @@
"appSourceURL": "URL de origem do aplicativo",
"error": "Erro",
"add": "Adicionar",
"searchSomeSourcesLabel": "Procurar (Apenas algumas fontes)",
"searchSomeSourcesLabel": "Procurar (apenas algumas fontes)",
"search": "Procurar",
"additionalOptsFor": "Opções adicionais para {}",
"supportedSources": "Fontes compatíveis",
"trackOnlyInBrackets": "(Apenas monitorar)",
"searchableInBrackets": "(Pesquisável)",
"trackOnlyInBrackets": "(apenas monitorar)",
"searchableInBrackets": "(pesquisável)",
"appsString": "Aplicativos",
"noApps": "Não há aplicativos",
"noAppsForFilter": "Sem aplicativos para filtrar",
@ -70,34 +73,33 @@
"markSelectedAppsUpdated": "Marcar aplicativos selecionados como Atualizados",
"pinToTop": "Fixar no topo",
"unpinFromTop": "Desafixar do topo",
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação para aplicativos selecionados?",
"installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer aplicativo selecionado será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação nos aplicativos selecionados?",
"installStatusOfXWillBeResetExplanation": "O status de instalação de todos os aplicativos selecionados será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
"customLinkMessage": "Esses links funcionam em dispositivos com o Obtainium instalado",
"shareAppConfigLinks": "Compartilhar configuração do aplicativo como link HTML",
"shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados",
"resetInstallStatus": "Reiniciar status de Iistalação",
"resetInstallStatus": "Reiniciar status de instalação",
"more": "Mais",
"removeOutdatedFilter": "Remover filtro de aplicativos desatualizados",
"showOutdatedOnly": "Mostrar apenas aplicativos desatualizados",
"filter": "Filtro",
"filterActive": "Filtro *",
"filterApps": "Filtrar aplicativos",
"appName": "Nome do aplicativo",
"author": "Autor",
"upToDateApps": "Aplicativos tualizados",
"upToDateApps": "Aplicativos atualizados",
"nonInstalledApps": "Aplicativos não instalados",
"importExport": "Importar/Exportar",
"settings": "Configurações",
"exportedTo": "Exportado para {}",
"obtainiumExport": "Exportar Obtainium",
"invalidInput": "Input Inválido",
"obtainiumExport": "Exportar dados do Obtainium",
"invalidInput": "Entrada inválida",
"importedX": "Importado {}",
"obtainiumImport": "Importar Obtainium",
"obtainiumImport": "Importar dados do Obtainium",
"importFromURLList": "Importar de lista de URLs",
"searchQuery": "Pesquisa",
"appURLList": "Lista de URLs de aplicativos",
"line": "Linha",
"searchX": "Pesquisa {}",
"searchX": "Pesquisar na/o {}",
"noResults": "Nenhum resultado encontrado",
"importX": "Importar {}",
"importedAppsIdDisclaimer": "Aplicativos Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do aplicativo.\n\nAfeta apenas métodos de importação de URL e de terceiros.",
@ -110,8 +112,9 @@
"theme": "Tema",
"dark": "Escuro",
"light": "Claro",
"followSystem": "Seguir o sistema",
"useBlackTheme": "Usar tema preto completamente escuro",
"followSystem": "Padrão do sistema",
"followSystemThemeExplanation": "O tema do sistema seguinte só é possível através da utilização de aplicações de terceiros",
"useBlackTheme": "Usar tema preto AMOLED",
"appSortBy": "Classificar aplicativo por",
"authorName": "Autor/Nome",
"nameAuthor": "Nome/Autor",
@ -122,10 +125,10 @@
"bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo-plano",
"neverManualOnly": "Nunca - apenas manual",
"appearance": "Aparência",
"showWebInAppView": "Mostrar página da internet em informações do aplicativo",
"showWebInAppView": "Mostrar página web do aplicativo em informações do aplicativo",
"pinUpdates": "Fixar atualizações no topo da janela de aplicativos",
"updates": "Atualizações",
"sourceSpecific": "Específico a fonte",
"sourceSpecific": "Token de acesso",
"appSource": "Fonte do aplicativo",
"noLogs": "Sem logs",
"appLogs": "Logs do aplicativo",
@ -144,14 +147,16 @@
"noNewUpdates": "Sem novas atualizações.",
"xHasAnUpdate": "{} tem uma atualização.",
"appsUpdated": "Aplicativos atualizados",
"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 {}.",
"xWasNotUpdatedToY": "Falha ao atualizar {} para {}.",
"errorCheckingUpdates": "Erro ao procurar por atualizações",
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo-plano falha",
"appsRemoved": "Aplicativos removidos",
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais aplicativos foram removidos devido a erros de carregamento",
"xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}",
"completeAppInstallation": "Instalação do aplicativo completa",
"completeAppInstallation": "Instalação do aplicativo concluída",
"obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar os aplicativos",
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um aplicativo",
"checkingForUpdates": "Verificando atualizações",
@ -167,15 +172,15 @@
"installedVersionX": "Versão instalada: {}",
"lastUpdateCheckX": "Última verificação de atualizações: {}",
"remove": "Remover",
"yesMarkUpdated": "Sim, marcar como Atualizado",
"yesMarkUpdated": "Sim, marcar como atualizado",
"fdroid": "Oficial F-Droid",
"appIdOrName": "ID do aplicativo ou nome",
"appId": "ID do aplicativo",
"appWithIdOrNameNotFound": "Nenhum aplicativo foi encontrado com esse ID ou nome",
"reposHaveMultipleApps": "Repositórios podem conter multiplos aplicativos",
"reposHaveMultipleApps": "Repositórios podem conter múltiplos aplicativos",
"fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid",
"steamMobile": "Vapor Móvel",
"steamChat": "Bate-papo Steam",
"steamMobile": "Steam para celular",
"steamChat": "Chat do Steam",
"install": "Instalar",
"markInstalled": "Marcar instalado",
"update": "Atualizar",
@ -184,6 +189,9 @@
"disableVersionDetection": "Desativar detecção de versão",
"noVersionDetectionExplanation": "Essa opção deve apenas ser usada por aplicativos onde a detecção de versão não funciona corretamente.",
"downloadingX": "Baixando {}",
"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",
"noVersionDetection": "Sem detecção de versão",
@ -191,7 +199,7 @@
"categories": "Categorias",
"category": "Categoria",
"noCategory": "Sem categoria",
"noCategories": "Sem categoria",
"noCategories": "Sem categorias",
"deleteCategoriesQuestion": "Deletar categorias?",
"categoryDeleteWarning": "Todos os aplicativos em categorias removidas serão descategorizados.",
"addCategory": "Adicionar categoria",
@ -200,15 +208,15 @@
"copiedToClipboard": "Copiado para a área de transferência",
"storagePermissionDenied": "Permissão de armazenamento negada",
"selectedCategorizeWarning": "Isso vai substituir qualquer configuração de categoria para os aplicativos selecionados.",
"filterAPKsByRegEx": "Filtrar APKs por expressão regular",
"filterAPKsByRegEx": "Filtrar APKs usando expressão regular",
"removeFromObtainium": "Remover do Obtainium",
"uninstallFromDevice": "Desinstalar do dispositivo",
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicativos com detecção de versão desativada.",
"releaseDateAsVersion": "Usar data de lançamento como versão",
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.",
"changes": "Mudanças",
"changes": "Alterações",
"releaseDate": "Data de lançamento",
"importFromURLsInFile": "Importar de URLs em arquivo (como OPML)",
"importFromURLsInFile": "Importar de URLs em arquivo (formato OPML)",
"versionDetectionExplanation": "Reconciliar string de versão com versão detectada no sistema operacional",
"versionDetection": "Detecção de versão",
"standardVersionDetection": "Detecção de versão padrão",
@ -216,10 +224,10 @@
"autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível",
"overrideSource": "Substituir fonte",
"dontShowAgain": "Não mostrar isso novamente",
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas Monitorar'",
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas monitorar'",
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
"moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos",
"gitlabPATLabel": "Token de Acesso Pessoal do Gitlab\n(Ativa pesquisa e melhora a descoberta de APKs)",
"gitlabPATLabel": "Token de acesso pessoal do Gitlab",
"about": "Sobre",
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
"checkOnStart": "Verificar se há atualizações ao iniciar",
@ -228,12 +236,11 @@
"pickHighestVersionCode": "Auto-selecionar o maior número de versão do APK",
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de um aplicativo",
"disablePageTransitions": "Desativar animações de transição de página",
"reversePageTransitions": "Reverter animações de transição de página",
"reversePageTransitions": "Animações de transição de página invertidas",
"minStarCount": "Contagem mínima de estrelas",
"addInfoBelow": "Adicionar essa informação abaixo.",
"addInfoInSettings": "Adicionar essa informação nas configurações.",
"githubSourceNote": "A limitação de taxa do GitHub pode ser evitada usando uma chave de API.",
"gitlabSourceNote": "A extração de endereço de download do APK no GitLab provavelmente não funcione sem que seja fornecido uma chave de API.",
"sortByLastLinkSegment": "Ordenar apenas usando o último segmento do link",
"filterReleaseNotesByRegEx": "Filtrar notas de versão usando Regex",
"customLinkFilterRegex": "Filtro de link personalizado usando expressão regular (Padrão '.apk$')",
@ -252,9 +259,11 @@
"bgUpdatesOnWiFiOnly": "Desative as atualizações em segundo-plano quando não estiver conectado no Wi-Fi",
"autoSelectHighestVersionCode": "Auto-selecionar a versão mais recente",
"versionExtractionRegEx": "Regex de extração de versão",
"trimVersionString": "Cortar a cadeia de caracteres da versão com RegEx",
"matchGroupToUseForX": "Grupo de correspondência a utilizar para \"{}\"",
"matchGroupToUse": "Grupo correspondente a ser usado no Regex de extração de versão",
"highlightTouchTargets": "Realçar áreas sensíveis ao toque que são menos óbvias",
"pickExportDir": "Escolher diretório para a exportação",
"pickExportDir": "Escolher diretório para exportação",
"autoExportOnChanges": "Auto-exportar em mudanças",
"includeSettings": "Incluir configurações",
"filterVersionsByRegEx": "Filtrar versões por expressão regular",
@ -272,21 +281,21 @@
"updatesAvailableNotifChannel": "Atualizações disponíveis",
"appsUpdatedNotifChannel": "Aplicativos atualizados",
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de aplicativos",
"errorCheckingUpdatesNotifChannel": "Erro ao Procurar por Atualizações",
"errorCheckingUpdatesNotifChannel": "Erro ao procurar por atualizações",
"appsRemovedNotifChannel": "Aplicativos removidos",
"downloadingXNotifChannel": "Baixando {}",
"completeAppInstallationNotifChannel": "Instalação completa do aplicativo",
"checkingForUpdatesNotifChannel": "Checando por Atualizações",
"onlyCheckInstalledOrTrackOnlyApps": "Apenas checar aplicativos instalados e 'Apenas Seguir' por updates",
"checkingForUpdatesNotifChannel": "Checando por atualizações",
"onlyCheckInstalledOrTrackOnlyApps": "Apenas verificar atualizações de aplicativos instalados e 'Apenas monitorar'",
"supportFixedAPKURL": "Suporte a APK com URLs fixas",
"selectX": "Selecionar {}",
"parallelDownloads": "Permitir downloads paralelos",
"installMethod": "Método de instalação",
"normal": "Normal",
"root": "Raiz",
"useShizuku": "Utilizar Shizuku ou Sui para instalar",
"shizukuBinderNotFound": "O Shizuku não está rodando",
"shizukuOld": "Versão antiga do Shizuku (<11) - atualizar",
"shizukuOldAndroidWithADB": "Shizuku a funcionar no Android < 8.1 com ADB - atualizar o Android ou utilizar o Sui",
"shizukuPretendToBeGooglePlay": "Definir o Google Play como fonte de instalação (se for utilizado o Shizuku)",
"useSystemFont": "Usar fonte padrão do sistema",
"systemFontError": "Erro ao carregar a fonte 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",
@ -298,6 +307,13 @@
"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.",
"wiki": "Ajuda/Wiki",
"crowdsourcedConfigsLabel": "Configurações de aplicações de crowdsourcing (utilização por sua conta e risco)",
"removeAppQuestion": {
"one": "Remover aplicativo?",
"other": "Remover aplicativos?"
@ -339,15 +355,23 @@
"other": "Foram limpos {n} logs (antes = {antes}, depois = {depois})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} e 1 outro aplicativo possui atualizações.",
"one": "{} e um outro aplicativo possui atualizações.",
"other": "{} e {} outros aplicativo possuem atualizações."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} e um outro aplicativo foi atualizado.",
"one": "{} e um outro aplicativo foram atualizado.",
"other": "{} e {} outros aplicativos foram atualizados."
},
"xAndNMoreUpdatesFailed": {
"one": "Falha ao atualizar {} e mais 1 aplicação.",
"other": "Falha ao atualizar {} e {} mais aplicações."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} e 1 outro aplicativo pode ter sido atualizado.",
"one": "{} e um outro aplicativo podem ter sido atualizados.",
"other": "{} e {} outros aplicativos podem ter sido atualizados."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -9,7 +9,7 @@
"placeholder": "Заполнитель",
"someErrors": "Возникли некоторые ошибки",
"unexpectedError": "Неожиданная ошибка",
"ok": "Ok",
"ok": "Ок",
"and": "и",
"githubPATLabel": "Персональный токен доступа GitHub\n(увеличивает лимит запросов)",
"includePrereleases": "Включить предварительные релизы",
@ -22,6 +22,9 @@
"requiredInBrackets": "(обязательно)",
"dropdownNoOptsError": "Ошибка: в выпадающем списке должна быть выбрана хотя бы одна настройка",
"colour": "Цвет",
"standard": "Стандартный",
"custom": "Индивидуальный",
"useMaterialYou": "Использовать Material You",
"githubStarredRepos": "Избранные репозитории GitHub",
"uname": "Имя пользователя",
"wrongArgNum": "Неправильное количество предоставленных аргументов",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Удалить фильтр для устаревших приложений",
"showOutdatedOnly": "Показывать только устаревшие приложения",
"filter": "Фильтр",
"filterActive": "Фильтр *",
"filterApps": "Фильтровать приложения",
"appName": "Название приложения",
"author": "Автор",
@ -111,6 +113,7 @@
"dark": "Тёмная",
"light": "Светлая",
"followSystem": "Системная",
"followSystemThemeExplanation": "Следование системной теме возможно только при использовании сторонних приложений",
"useBlackTheme": "Использовать чёрную тему",
"appSortBy": "Сортировка приложений",
"authorName": "Автор/Название",
@ -126,13 +129,13 @@
"pinUpdates": "Отображать обновления приложений сверху списка",
"updates": "Обновления",
"sourceSpecific": "Настройки источников",
"appSource": "Исходный код",
"appSource": "Источник",
"noLogs": "Нет журналов",
"appLogs": "Логи",
"close": "Закрыть",
"share": "Поделиться",
"appNotFound": "Приложение не найдено",
"obtainiumExportHyphenatedLowercase": "получение-экспорт",
"obtainiumExportHyphenatedLowercase": "экспорт-obtainium",
"pickAnAPK": "Выберите APK-файл",
"appHasMoreThanOnePackage": "{} имеет более одного пакета:",
"deviceSupportsXArch": "Ваше устройство поддерживает архитектуру процессора {}",
@ -144,8 +147,10 @@
"noNewUpdates": "Нет новых обновлений",
"xHasAnUpdate": "{} есть обновление",
"appsUpdated": "Приложения обновлены",
"appsNotUpdated": "Не удалось обновить приложения",
"appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме",
"xWasUpdatedToY": "{} была обновлена до версии {}",
"xWasNotUpdatedToY": "Не удалось обновить {} до версии {}",
"errorCheckingUpdates": "Ошибка при проверке обновлений",
"errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой",
"appsRemoved": "Приложение удалено",
@ -174,8 +179,8 @@
"appWithIdOrNameNotFound": "Приложение с таким ID или названием не было найдено",
"reposHaveMultipleApps": "В хранилище несколько приложений",
"fdroidThirdPartyRepo": "Сторонние репозитории F-Droid",
"steamMobile": "Стим Мобайл",
"steamChat": "Стим-чат",
"steamMobile": "Приложение Steam",
"steamChat": "Steam Chat",
"install": "Установить",
"markInstalled": "Пометить как установленное",
"update": "Обновить",
@ -184,6 +189,9 @@
"disableVersionDetection": "Отключить обнаружение версии",
"noVersionDetectionExplanation": "Эта настройка должна использоваться только для приложений, где обнаружение версии не работает корректно",
"downloadingX": "Загрузка {}",
"downloadX": "Скачать {}",
"downloadedX": "Загружено {}",
"releaseAsset": "Релизный объект",
"downloadNotifDescription": "Уведомляет пользователя о прогрессе загрузки приложения",
"noAPKFound": "APK не найден",
"noVersionDetection": "Обнаружение версий отключено",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Не показывать предупреждения о только отслеживаемых приложениях",
"dontShowAPKOriginWarnings": "Не показывать предупреждения об отличающемся источнике APK-файлов",
"moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка",
"gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)",
"gitlabPATLabel": "Персональный токен доступа GitLab",
"about": "Описание",
"requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)",
"checkOnStart": "Проверять наличие обновлений при запуске",
@ -233,7 +241,6 @@
"addInfoBelow": "Добавьте эту информацию ниже",
"addInfoInSettings": "Добавьте эту информацию в Настройки",
"githubSourceNote": "Используя ключ API можно обойти лимит запросов GitHub",
"gitlabSourceNote": "Без ключа API может не работать извлечение APK с GitLab",
"sortByLastLinkSegment": "Сортировать только по последнему сегменту ссылки",
"filterReleaseNotesByRegEx": "Фильтровать примечания к выпуску\n(регулярное выражение)",
"customLinkFilterRegex": "Пользовательский фильтр ссылок APK\n(регулярное выражение, по умолчанию: '.apk$')",
@ -247,11 +254,13 @@
"intermediateLinkRegex": "Фильтр для \"промежуточной\" ссылки для посещения",
"filterByLinkText": "Фильтрация ссылок по тексту ссылки",
"intermediateLinkNotFound": "Промежуточная ссылка не найдена",
"intermediateLink": "Промежуточное звено",
"intermediateLink": "Промежуточная ссылка",
"exemptFromBackgroundUpdates": "Исключить из фоновых обновлений (если включено)",
"bgUpdatesOnWiFiOnly": "Отключить фоновые обновления, если нет соединения с Wi-Fi",
"autoSelectHighestVersionCode": "Автоматически выбирать APK с актуальной версией кода",
"versionExtractionRegEx": "Регулярное выражение для извлечения версии",
"trimVersionString": "Обрезка строки версии с помощью RegEx",
"matchGroupToUseForX": "Группа соответствия, которую следует использовать для \"{}\"",
"matchGroupToUse": "Выберите группу для использования",
"highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
"pickExportDir": "Выбрать каталог для экспорта",
@ -281,12 +290,12 @@
"supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK",
"selectX": "Выбрать {}",
"parallelDownloads": "Разрешить параллельные загрузки",
"installMethod": "Метод установки",
"normal": "Нормальный",
"root": "Суперпользователь",
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден",
"useShizuku": "Использовать Shizuku или Sui для установки",
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден, возможно он не запущен",
"shizukuOld": "Устаревшая версия Shizuku (<11), обновите",
"shizukuOldAndroidWithADB": "Shizuku работает на Android < 8.1 с ADB, обновите Android или используйте Sui",
"shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки (если используется Shizuku)",
"useSystemFont": "Использовать системный шрифт",
"systemFontError": "Ошибка загрузки системного шрифта: {}",
"useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС",
"requestHeader": "Заголовок запроса",
"useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска",
@ -298,6 +307,13 @@
"installed": "Установлен",
"latest": "Последний",
"invertRegEx": "Инвертировать регулярное выражение",
"note": "Примечание",
"selfHostedNote": "Выпадающий список \"{}\" можно использовать для доступа к самостоятельно размещенным/настроенным экземплярам любого источника.",
"badDownload": "APK не удалось разобрать (несовместимая или неполная загрузка)",
"beforeNewInstallsShareToAppVerifier": "Поделитесь новыми приложениями с AppVerifier (если доступно)",
"appVerifierInstructionToast": "Поделитесь с AppVerifier, а затем вернитесь сюда, когда будете готовы.",
"wiki": "Помощь/Вики",
"crowdsourcedConfigsLabel": "Конфигурации приложений на основе краудсорсинга (используйте на свой страх и риск)",
"removeAppQuestion": {
"one": "Удалить приложение?",
"other": "Удалить приложения?"
@ -346,8 +362,16 @@
"one": "{} и ещё 1 приложение были обновлены",
"other": "{} и ещё {} приложений были обновлены"
},
"xAndNMoreUpdatesFailed": {
"one": "Не удалось обновить {} и ещё 1 приложение",
"other": "Не удалось обновить {} и ещё {} приложений"
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} и ещё 1 приложение могли быть обновлены",
"other": "{} и ещё {} приложений могли быть обновлены"
},
"apk": {
"one": "{} APK",
"other": "{} APKи"
}
}

View File

@ -1,30 +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
// Then report which other translation files have identical items (or auto-translate them if a DeepL API key is provided)
const fs = require('fs')
const https = require('https')
const translationsDir = __dirname
const templateFile = `${translationsDir}/en.json`
const otherFiles = fs.readdirSync(translationsDir).map(f => {
return `${translationsDir}/${f}`
}).filter(f => f.endsWith('.json') && f != templateFile)
const deeplAPIKey = process.argv[2]
const templateTranslation = require(templateFile)
const neverAutoTranslate = {
steamMobile: ['*'],
steamChat: ['*'],
root: ['*'],
obtainiumExportHyphenatedLowercase: ['*'],
theme: ['de'],
appId: ['de']
}
otherFiles.forEach(file => {
const thisTranslationOriginal = require(file)
const thisTranslationNew = {}
Object.keys(templateTranslation).forEach(k => {
thisTranslationNew[k] = thisTranslationOriginal[k] || templateTranslation[k]
})
fs.writeFileSync(file, `${JSON.stringify(thisTranslationNew, null, ' ')}\n`)
});
otherFiles.forEach(file => {
const thisTranslation = require(file)
Object.keys(templateTranslation).forEach(k => {
if (JSON.stringify(thisTranslation[k]) == JSON.stringify(templateTranslation[k])) {
console.log(`${file} :::: ${k} :::: ${JSON.stringify(thisTranslation[k])}`)
const translateText = async (text, targetLang, authKey) => {
return new Promise((resolve, reject) => {
const postData = `text=${encodeURIComponent(text)}&target_lang=${encodeURIComponent(targetLang)}&source_lang=EN`
const options = {
hostname: 'api-free.deepl.com',
port: 443,
path: '/v2/translate',
method: 'POST',
headers: {
'Authorization': `DeepL-Auth-Key ${authKey}`,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
}
const req = https.request(options, (res) => {
let responseData = ''
res.on('data', (chunk) => {
responseData += chunk
})
res.on('end', () => {
try {
const jsonResponse = JSON.parse(responseData)
resolve(jsonResponse)
} catch (error) {
reject(error)
}
})
})
req.on('error', (error) => {
reject(error)
})
req.write(postData)
req.end()
})
});
}
const main = async () => {
const translationsDir = __dirname
const templateFile = `${translationsDir}/en.json`
const otherFiles = fs.readdirSync(translationsDir).map(f => {
return `${translationsDir}/${f}`
}).filter(f => f.endsWith('.json') && f != templateFile)
const templateTranslation = 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

@ -22,6 +22,9 @@
"requiredInBrackets": "(Kräver)",
"dropdownNoOptsError": "FEL: DROPDOWN MÅSTE HA MINST ETT OPT",
"colour": "Färg",
"standard": "Standard",
"custom": "Anpassad",
"useMaterialYou": "Använd material Du",
"githubStarredRepos": "GitHub Stjärnmärkta Förråd",
"uname": "Användarnamn",
"wrongArgNum": "Fel antal argument har angetts",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Ta bort Utgånga App-filtret",
"showOutdatedOnly": "Visa Endast Utgånga Appar",
"filter": "Filtrera",
"filterActive": "Filter *",
"filterApps": "Filtrera Appar",
"appName": "Appnamn",
"author": "Utvecklare",
@ -111,6 +113,7 @@
"dark": "Mörkt",
"light": "Ljust",
"followSystem": "Följ System",
"followSystemThemeExplanation": "Följande systemtema är endast möjligt med hjälp av tredjepartsapplikationer",
"useBlackTheme": "Använd svart tema",
"appSortBy": "Sortera Appar via",
"authorName": "Utvecklare/Namn",
@ -144,8 +147,10 @@
"noNewUpdates": "Inga nya uppdateringar.",
"xHasAnUpdate": "{} har en uppdatering.",
"appsUpdated": "Appar Uppdaterade",
"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 {}.",
"xWasNotUpdatedToY": "Det gick inte att uppdatera {} till {}.",
"errorCheckingUpdates": "Fel vid uppdateringskoll",
"errorCheckingUpdatesNotifDescription": "En aviserings som visar när bakgrundsuppdateringarkollar misslyckas",
"appsRemoved": "Appar borttagna",
@ -184,6 +189,9 @@
"disableVersionDetection": "Inaktivera versionsdetektering",
"noVersionDetectionExplanation": "Det här alternativet bör endast användas för appar där versionsidentifiering inte fungerar korrekt.",
"downloadingX": "Laddar ner {}",
"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",
"noVersionDetection": "Ingen versiondetektering",
@ -219,7 +227,7 @@
"dontShowTrackOnlyWarnings": "Visa inte 'Följ-Endast' varningar",
"dontShowAPKOriginWarnings": "Visa inte APK-ursprung varningar",
"moveNonInstalledAppsToBottom": "Flytta icke-installerade appar till botten av appvyn",
"gitlabPATLabel": "GitLab Personal Access Token\\n(Möjliggör sökning och bättre APK-upptäckt)",
"gitlabPATLabel": "Personligt åtkomsttoken för GitLab",
"about": "Om",
"requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
"checkOnStart": "Kolla efter uppdateringar vid start",
@ -233,7 +241,6 @@
"addInfoBelow": "Lägg till denna information nedanför.",
"addInfoInSettings": "Lägg till denna information i Inställningar.",
"githubSourceNote": "GitHub-hastighetsbegränsning kan undvikas med en API-nyckel.",
"gitlabSourceNote": "GitLab APK-extraktion kanske inte fungerar utan en API-nyckel.",
"sortByLastLinkSegment": "Sortera endast efter det sista segmentet av länken",
"filterReleaseNotesByRegEx": "Filtrera versionskommentarer efter reguljärt uttryck",
"customLinkFilterRegex": "Anpassad APK-länkfiltrera efter reguljärt uttryck (standard '.apk$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "Inaktivera Bakgrundsuppdateringar utan WiFi",
"autoSelectHighestVersionCode": "Välj automatiskt högsta versionskod APK",
"versionExtractionRegEx": "Version Extraction RegEx",
"trimVersionString": "Trimma versionssträng med RegEx",
"matchGroupToUseForX": "Matchningsgrupp att använda för \"{}\"",
"matchGroupToUse": "Match Group to Use",
"highlightTouchTargets": "Markera mindre uppenbara beröringsobjekt",
"pickExportDir": "Välj Exportsökväg",
@ -281,23 +290,30 @@
"supportFixedAPKURL": "Stöd fasta APK-webbadresser",
"selectX": "Välj {}",
"parallelDownloads": "Tillåt parallella nedladdningar",
"installMethod": "Installationsmetod",
"normal": "Vanligt",
"root": "Rot",
"useShizuku": "Använd Shizuku eller Sui för att installera",
"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",
"systemFontError": "Fel vid laddning av systemteckensnittet: {}",
"useVersionCodeAsOSVersion": "Använd appversionskoden som OS-upptäckt version",
"requestHeader": "Request header",
"requestHeader": "Rubrik för begäran",
"useLatestAssetDateAsReleaseDate": "Använd senaste tillgångsuppladdning som releasedatum",
"defaultPseudoVersioningMethod": "Standard pseudoversionsmetod",
"partialAPKHash": "Delvis APK-hash",
"APKLinkHash": "APK Link Hash",
"APKLinkHash": "APK-länk Hash",
"directAPKLink": "Direkt APK-länk",
"pseudoVersionInUse": "En pseudoversion används",
"installed": "Installerad",
"latest": "Senast",
"invertRegEx": "Invertera reguljärt uttryck",
"note": "Anmärkning",
"selfHostedNote": "Rullgardinsmenyn \"{}\" kan användas för att nå självhostade/anpassade instanser av valfri källa.",
"badDownload": "APK kunde inte analyseras (inkompatibel eller partiell nedladdning)",
"beforeNewInstallsShareToAppVerifier": "Dela nya appar med AppVerifier (om tillgängligt)",
"appVerifierInstructionToast": "Dela till AppVerifier och återvänd sedan hit när du är klar.",
"wiki": "Hjälp/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourcade appkonfigurationer (använd på egen risk)",
"removeAppQuestion": {
"one": "Ta Bort App?",
"other": "Ta Bort Appar?"
@ -346,8 +362,16 @@
"one": "{} och 1 till app 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": {
"one": "{} och 1 till app kan ha uppdaterats.",
"other": "{} och {} appar till kan ha uppdaterats."
},
"apk": {
"one": "{} APK",
"other": "{} APK:er"
}
}

View File

@ -3,7 +3,7 @@
"noReleaseFound": "Uygun bir sürüm bulunamadı",
"noVersionFound": "Sürüm bulunamadı",
"urlMatchesNoSource": "URL, bilinen bir kaynağa uymuyor",
"cantInstallOlderVersion": "Eski bir sürümü yükleyemem",
"cantInstallOlderVersion": "Eski bir sürüm yüklenemez",
"appIdMismatch": "İndirilen paket kimliği mevcut Uygulama kimliği ile eşleşmiyor",
"functionNotImplemented": "Bu sınıf bu işlevi uygulamamıştır",
"placeholder": "Yer Tutucu",
@ -13,19 +13,22 @@
"and": "ve",
"githubPATLabel": "GitHub Kişisel Erişim Anahtarı (Sınırlamayı Artırır)",
"includePrereleases": "Ön sürümleri dahil et",
"fallbackToOlderReleases": "Daha eski sürümlere geri dön",
"fallbackToOlderReleases": "Daha eski sürümleri alternatif olarak tut",
"filterReleaseTitlesByRegEx": "Düzenli İfadelerle Sürüm Başlıklarını Filtrele",
"invalidRegEx": "Geçersiz düzenli ifade",
"noDescription": "Açıklama yok",
"cancel": "İptal",
"continue": "Devam Et",
"requiredInBrackets": "(Gerekli)",
"dropdownNoOptsError": "HATA: DİPLOMADA EN AZ BİR SEÇENEK OLMALI",
"dropdownNoOptsError": "HATA: AÇILIR MENÜDE EN AZ BİR SEÇENEK OLMALI",
"colour": "Renk",
"githubStarredRepos": "GitHub'a Yıldızlı Depolar",
"standard": "Standart",
"custom": "Özel",
"useMaterialYou": "MaterialYou Kullanın",
"githubStarredRepos": "GitHub Yıldızlı Depolar",
"uname": "Kullanıcı Adı",
"wrongArgNum": "Hatalı argüman sayısı sağlandı",
"xIsTrackOnly": "{} yalnızca Takip Edilen",
"wrongArgNum": "Hatalı sayıda argüman sağlandı",
"xIsTrackOnly": "{} yalnızca Takip Ediliyor",
"source": "Kaynak",
"app": "Uygulama",
"appsFromSourceAreTrackOnly": "Bu kaynaktan gelen uygulamalar 'Yalnızca Takip Edilen'dir.",
@ -38,9 +41,9 @@
"appSourceURL": "Uygulama Kaynak URL'si",
"error": "Hata",
"add": "Ekle",
"searchSomeSourcesLabel": "Ara (Bazı Kaynaklar Yalnızca)",
"searchSomeSourcesLabel": "Ara (Yalnızca Bazı Kaynaklar)",
"search": "Ara",
"additionalOptsFor": "{} İçin Ek Seçenekler",
"additionalOptsFor": "{} için Ek Seçenekler",
"supportedSources": "Desteklenen Kaynaklar",
"trackOnlyInBrackets": "(Yalnızca Takip)",
"searchableInBrackets": "(Aranabilir)",
@ -80,7 +83,6 @@
"removeOutdatedFilter": "Güncel Olmayan Uygulama Filtresini Kaldır",
"showOutdatedOnly": "Yalnızca Güncel Olmayan Uygulamaları Göster",
"filter": "Filtre",
"filterActive": "Filtre *",
"filterApps": "Uygulamaları Filtrele",
"appName": "Uygulama Adı",
"author": "Yazar",
@ -111,6 +113,7 @@
"dark": "Koyu",
"light": "Aydınlık",
"followSystem": "Sistemi Takip Et",
"followSystemThemeExplanation": "Sistem temasını takip etmek yalnızca üçüncü taraf uygulamaları kullanarak mümkündür",
"useBlackTheme": "Saf siyah koyu temasını kullan",
"appSortBy": "Uygulama Sıralama Ölçütü",
"authorName": "Yazar/Ad",
@ -144,8 +147,10 @@
"noNewUpdates": "Yeni güncelleme yok.",
"xHasAnUpdate": "{} güncelleme alıyor.",
"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",
"xWasUpdatedToY": "{} şu sürüme güncellendi: {}.",
"xWasNotUpdatedToY": "{} öğesi {} olarak güncellenemedi.",
"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",
"appsRemoved": "Uygulamalar Kaldırıldı",
@ -168,13 +173,13 @@
"lastUpdateCheckX": "Son Güncelleme Kontrolü: {}",
"remove": "Kaldır",
"yesMarkUpdated": "Evet, Güncellendi olarak İşaretle",
"fdroid": "F-Droid Resmi",
"fdroid": "Resmi F-Droid",
"appIdOrName": "Uygulama Kimliği veya Adı",
"appId": "Uygulama Kimliği",
"appWithIdOrNameNotFound": "Bu kimlik veya ada sahip bir uygulama bulunamadı",
"reposHaveMultipleApps": "Depolar birden fazla uygulama içerebilir",
"fdroidThirdPartyRepo": "F-Droid Üçüncü Taraf Depo",
"steamMobile": "Buhar Mobil",
"fdroidThirdPartyRepo": "F-Droid Üçüncü Parti Depo",
"steamMobile": "Steam Mobil",
"steamChat": "Steam Sohbet",
"install": "Yükle",
"markInstalled": "Yüklendi olarak İşaretle",
@ -184,6 +189,9 @@
"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.",
"downloadingX": "{} İndiriliyor",
"downloadX": "İndir {}",
"downloadedX": "İndirildi {}",
"releaseAsset": "Varlık Serbest Bırakma",
"downloadNotifDescription": "Bir uygulamanın indirme sürecinde ilerlemeyi bildiren bir bildirim",
"noAPKFound": "APK bulunamadı",
"noVersionDetection": "Sürüm Algılanamıyor",
@ -214,12 +222,12 @@
"standardVersionDetection": "Standart sürüm tespiti",
"groupByCategory": "Kategoriye Göre Grupla",
"autoApkFilterByArch": "Mümkünse APK'leri CPU mimarisi ile filtreleme girişimi",
"overrideSource": "Kaynağı Geçersiz Kıl",
"overrideSource": "Öncelenecek Kaynak",
"dontShowAgain": "Bunu tekrar gösterme",
"dontShowTrackOnlyWarnings": "'Yalnızca Takip Edilen' 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şı",
"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",
"requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)",
"checkOnStart": "Başlangıçta güncellemeleri kontrol et",
@ -233,7 +241,6 @@
"addInfoBelow": "Bu bilgiyi aşağıya ekle.",
"addInfoInSettings": "Bu bilgiyi Ayarlar'da ekleyin.",
"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",
"filterReleaseNotesByRegEx": "Sürüm Notlarını Düzenli İfade ile Filtrele",
"customLinkFilterRegex": "Özel APK Bağlantı Filtresi Düzenli İfade ile (Varsayılan '.apk$')",
@ -252,6 +259,8 @@
"bgUpdatesOnWiFiOnly": "WiFi olmadığında arka plan güncellemelerini devre dışı bırak",
"autoSelectHighestVersionCode": "Otomatik olarak en yüksek sürüm kodunu seç",
"versionExtractionRegEx": "Sürüm Çıkarma Düzenli İfade",
"trimVersionString": "RegEx ile Sürüm Dizesini Kırpma",
"matchGroupToUseForX": "\"{}\" için Kullanılacak Grubu Eşleştirin",
"matchGroupToUse": "Sürüm Çıkarma Regex için Kullanılacak Eşleşme Grubu",
"highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
"pickExportDir": "Dışa Aktarılacak Klasörü Seç",
@ -279,14 +288,14 @@
"checkingForUpdatesNotifChannel": "Güncellemeler Kontrol Ediliyor",
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
"supportFixedAPKURL": "Sabit APK URL'lerini destekleyin",
"selectX": "Seçme {}",
"selectX": "{} Tanesini Seç",
"parallelDownloads": "Paralel indirmelere izin ver",
"installMethod": "Kurulum yöntemi",
"normal": "Normal",
"root": "Kök",
"shizukuBinderNotFound": "Shizuku is not running",
"useShizuku": "Yüklemek için Shizuku veya Sui'yi kullanın",
"shizukuBinderNotFound": "Shizuku servisi çalışmıyor",
"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",
"systemFontError": "Sistem yazı tipi yüklenirken hata oluştu: {}",
"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",
@ -298,6 +307,13 @@
"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.",
"wiki": "Yardım/Wiki",
"crowdsourcedConfigsLabel": "Kitle Kaynaklı Uygulama Yapılandırmaları (riski size ait olmak üzere kullanın)",
"removeAppQuestion": {
"one": "Uygulamayı Kaldır?",
"other": "Uygulamaları Kaldır?"
@ -320,7 +336,7 @@
},
"url": {
"one": "{} URL",
"other": "{} URL'ler"
"other": "{} URL"
},
"minute": {
"one": "{} Dakika",
@ -346,8 +362,16 @@
"one": "{} ve 1 diğer 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": {
"one": "{} ve 1 diğer uygulama muhtemelen güncellendi.",
"other": "{} ve {} daha fazla uygulama muhtemelen güncellendi."
},
"apk": {
"one": "{} APK",
"other": "{} APK"
}
}

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

@ -0,0 +1,377 @@
{
"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": "Дотримуватися системи",
"followSystemThemeExplanation": "Зміна теми системи можлива лише за допомогою сторонніх додатків",
"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": "Регулярний вираз для вилучення рядка версії",
"trimVersionString": "Обрізати рядок версії за допомогою RegEx",
"matchGroupToUseForX": "Група збігів для \"{}\"",
"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, а потім поверніться сюди, коли будете готові.",
"wiki": "Довідка/Вікі",
"crowdsourcedConfigsLabel": "Краудсорсингові конфігурації додатків (використовуйте на свій страх і ризик)",
"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

@ -11,7 +11,7 @@
"unexpectedError": "Lỗi không mong đợi",
"ok": "OK",
"and": "và",
"githubPATLabel": "GitHub Token (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",
"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",
@ -22,14 +22,17 @@
"requiredInBrackets": "(Yêu cầu)",
"dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN",
"colour": "Màu sắc",
"standard": "Mặc định",
"custom": "Tùy chỉnh",
"useMaterialYou": "Sử dụng Material You",
"githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub",
"uname": "Tên người dùng",
"wrongArgNum": "Số lượng đối số được cung cấp sai",
"xIsTrackOnly": "{}là Chỉ-Theo dõi",
"xIsTrackOnly": "{} là Chỉ theo dõi",
"source": "Nguồn",
"app": "Ứng dụng",
"appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ-Theo dõi'.",
"youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ-Theo dõi'.",
"appsFromSourceAreTrackOnly": "Các ứng dụng từ nguồn này là 'Chỉ theo dõi'.",
"youPickedTrackOnly": "Bạn đã chọn tùy chọn 'Chỉ theo dõi'.",
"trackOnlyAppDescription": "Ứng dụng sẽ được theo dõi để cập nhật, nhưng Obtainium sẽ không thể tải xuống hoặc cài đặt nó.",
"cancelled": "Đã hủy",
"appAlreadyAdded": "Ứng dụng được thêm rồi",
@ -42,7 +45,7 @@
"search": "Tìm kiếm",
"additionalOptsFor": "Tùy chọn bổ sung cho {}",
"supportedSources": "Nguồn được hỗ trợ",
"trackOnlyInBrackets": "(Chỉ-Theo dõi)",
"trackOnlyInBrackets": "(Chỉ theo dõi)",
"searchableInBrackets": "(Có thể tìm kiếm)",
"appsString": "Ứng dụng",
"noApps": "Không có ứng dụng",
@ -60,7 +63,7 @@
"removeSelectedApps": "Xóa ứng dụng đã chọn",
"updateX": "Cập nhật {}",
"installX": "Cài đặt {}",
"markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ-Theo dõi)\nnhư là đã cập nhật",
"markXTrackOnlyAsUpdated": "Đánh dấu {}\n(Chỉ theo dõi)\nnhư là đã cập nhật",
"changeX": "Thay đổi {}",
"installUpdateApps": "Cài đặt/Cập nhật ứng dụng",
"installUpdateSelectedApps": "Cài đặt/Cập nhật ứng dụng đã chọn",
@ -72,7 +75,7 @@
"unpinFromTop": "Bỏ ghim khỏi đầu trang",
"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.",
"customLinkMessage": "Các liên kết này hoạt động trên các thiết bị có cài đặt Gainium",
"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",
"resetInstallStatus": "Đặt lại trạng thái cài đặt",
@ -80,14 +83,13 @@
"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",
"filter": "Lọc",
"filterActive": "Lọc *",
"filterApps": "Lọc ứng dụng",
"appName": "Tên ứng dụng",
"author": "Tác giả",
"upToDateApps": "Ứng dụng cập nhật",
"nonInstalledApps": "Ứng dụng chưa được cài đặt",
"importExport": "Nhập/Xuất",
"settings": "Cài đặt",
"settings": "Thiết đặt",
"exportedTo": "Đã xuất sang {}",
"obtainiumExport": "Xuất",
"invalidInput": "Đầu vào không hợp lệ",
@ -111,6 +113,7 @@
"dark": "Tối",
"light": "Sáng",
"followSystem": "Theo hệ thống",
"followSystemThemeExplanation": "Following system theme is possible only by using third-party applications",
"useBlackTheme": "Nền đen",
"appSortBy": "Sắp xếp ứng dụng",
"authorName": "Tác giả",
@ -132,7 +135,7 @@
"close": "Đóng",
"share": "Chia sẻ",
"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",
"appHasMoreThanOnePackage": "{} có nhiều gói:",
"deviceSupportsXArch": "Thiết bị của bạn hỗ trợ kiến trúc CPU {}.",
@ -144,8 +147,10 @@
"noNewUpdates": "Không có bản cập nhật mới.",
"xHasAnUpdate": "{} có bản cập nhật.",
"appsUpdated": "Ứng dụng đã cập nhật ",
"appsNotUpdated": "Ứng dụng đã cập nhật không thành công",
"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 {}.",
"xWasNotUpdatedToY": "{} đã cập nhật thành {} không thành công.",
"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",
"appsRemoved": "Ứng dụng đã loại bỏ",
@ -168,7 +173,7 @@
"lastUpdateCheckX": "Kiểm tra cập nhật lần cuối: {}",
"remove": "Loại bỏ",
"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",
"appId": "ID ứng dụng",
"appWithIdOrNameNotFound": "Không tìm thấy ứng dụng nào có ID hoặc tên đó",
@ -184,22 +189,25 @@
"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.",
"downloadingX": "Đang tải xuống {}",
"downloadX": "Tải xuống {}",
"downloadedX": "Đã tải xuống {}",
"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",
"noAPKFound": "Không tìm thấy APK",
"noVersionDetection": "Không phát hiện phiên bản",
"categorize": "Phân loại",
"categories": "Thể loại",
"category": "Thể loại",
"noCategory": "Không thể loại",
"noCategories": "Không thể loại",
"deleteCategoriesQuestion": "Xóa thể loại?",
"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.",
"categories": "Danh mục",
"category": "Danh mục",
"noCategory": "Không danh mục",
"noCategories": "Không danh mục",
"deleteCategoriesQuestion": "Xóa danh mục?",
"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",
"label": "Nhãn",
"language": "Ngôn ngữ",
"copiedToClipboard": "Sao chép vào clipboard",
"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",
"removeFromObtainium": "Loại khỏi Obtainium",
"uninstallFromDevice": "Gỡ cài đặt khỏi thiết bị",
@ -212,16 +220,16 @@
"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",
"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ể",
"overrideSource": "Ghi đè nguồn",
"dontShowAgain": "Đừng hiển thị thông tin này nữa",
"dontShowTrackOnlyWarnings": "Không hiển thị cảnh báo 'Chỉ theo dõi'",
"dontShowAPKOriginWarnings": "Không hiển thị cảnh báo nguồn gốc APK",
"moveNonInstalledAppsToBottom": "Chuyển Ứng dụng chưa được cài đặt xuống cuối danh sách",
"gitlabPATLabel": "GitLab Token\n(Cho phép tìm kiếm và lọc APK tốt hơn)",
"gitlabPATLabel": "Token truy cập cá nhân GitLab",
"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",
"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",
@ -231,9 +239,8 @@
"reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược",
"minStarCount": "Số lượng sao tối thiểu",
"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.",
"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",
"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$')",
@ -252,11 +259,13 @@
"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",
"versionExtractionRegEx": "Trích xuất phiên bản RegEx",
"trimVersionString": "Trim Version String With RegEx",
"matchGroupToUseForX": "Match Group to Use for \"{}\"",
"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",
"pickExportDir": "Chọn thư mục xuất",
"autoExportOnChanges": "Tự động xuất",
"includeSettings": "Bao gồm cài đặt ứng dụng",
"includeSettings": "Bao gồm thiết đặt",
"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",
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
@ -277,16 +286,16 @@
"downloadingXNotifChannel": "Đang tải xuống {}",
"completeAppInstallationNotifChannel": "Hoàn tất cài đặt ứng dụng",
"checkingForUpdatesNotifChannel": "Đang kiểm tra cập nhật",
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra cập nhật các ứng dụng đã cài đặt và Chỉ-Theo dõi",
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra cập nhật các ứng dụng đã cài đặt và Chỉ theo dõi",
"supportFixedAPKURL": "Hỗ trợ URL APK cố định",
"selectX": "Lựa chọn {}",
"parallelDownloads": "Cho phép tải đa luồng",
"installMethod": "Phương thức cài đặt",
"normal": "Mặc định",
"root": "Root",
"useShizuku": "Sử dụng Shizuku hoặc Sui để cài đặt",
"shizukuBinderNotFound": "Shizuku chưa khởi động",
"shizukuOld": "Phiên bản Shizuku lỗi thời (<11) - hãy cập nhật nó",
"shizukuOldAndroidWithADB": "Shizuku chạy trên Android < 8.1 với ADB - hãy cập nhật Android hoặc thay bằng Sui",
"shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)",
"useSystemFont": "Sử dụng phông chữ hệ thống",
"systemFontError": "Lỗi tải 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",
@ -295,9 +304,16 @@
"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",
"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.",
"wiki": "Trợ giúp/Wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App Configurations (use at your own risk)",
"removeAppQuestion": {
"one": "Gỡ ứng dụng?",
"other": "Gỡ ứng dụng?"
@ -346,8 +362,16 @@
"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."
},
"xAndNMoreUpdatesFailed": {
"one": "{} và 1 ứng dụng khác đã cập nhật không thành công.",
"other": "{} và {} ứng dụng khác đã cập nhật không thảnh công."
},
"xAndNMoreUpdatesPossiblyInstalled": {
"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."
},
"apk": {
"one": "{} APK",
"other": "{} APKs"
}
}

View File

@ -0,0 +1,377 @@
{
"invalidURLForSource": "不是有效的 {} 應用程式 URL",
"noReleaseFound": "找不到合適的版本",
"noVersionFound": "無法確定版本",
"urlMatchesNoSource": "URL 不符合已知來源",
"cantInstallOlderVersion": "無法安裝舊版本的應用程式",
"appIdMismatch": "下載的套件 ID 與現有的應用程式 ID 不相符",
"functionNotImplemented": "此類別尚未實作此功能",
"placeholder": "佔位字串",
"someErrors": "發生了一些錯誤",
"unexpectedError": "意外錯誤",
"ok": "確定",
"and": "和",
"githubPATLabel": "GitHub 個人存取權杖(放寬速率限制)",
"includePrereleases": "包含預先釋出版本",
"fallbackToOlderReleases": "回退到舊版本",
"filterReleaseTitlesByRegEx": "用正則表達式過濾版本發佈標題",
"invalidRegEx": "無效的正則表達式",
"noDescription": "沒有描述",
"cancel": "取消",
"continue": "繼續",
"requiredInBrackets": "(必填)",
"dropdownNoOptsError": "錯誤:下拉選單必須至少有一個選項",
"colour": "顏色",
"standard": "標準",
"custom": "自訂",
"useMaterialYou": "使用 Material You",
"githubStarredRepos": "GitHub 打星星的專案",
"uname": "使用者名稱",
"wrongArgNum": "提供的參數數量錯誤",
"xIsTrackOnly": "{} 是僅追蹤",
"source": "來源",
"app": "應用程式",
"appsFromSourceAreTrackOnly": "來自此來源的應用程式是「僅追蹤」。",
"youPickedTrackOnly": "您已選擇「僅追蹤」選項。",
"trackOnlyAppDescription": "該應用程式將被追蹤更新,但 Obtainium 將無法下載或安裝它。",
"cancelled": "已取消",
"appAlreadyAdded": "應用程式已新增",
"alreadyUpToDateQuestion": "應用程式已經是最新的?",
"addApp": "新增應用程式",
"appSourceURL": "應用程式來源 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": "跟隨系統",
"followSystemThemeExplanation": "僅使用第三方應用程式時才可跟隨系統主題",
"useBlackTheme": "使用純黑色深色主題",
"appSortBy": "應用程式排序依據",
"authorName": "作者/名稱",
"nameAuthor": "名稱/作者",
"asAdded": "新增順序",
"appSortOrder": "應用程式排序順序",
"ascending": "升序",
"descending": "降序",
"bgUpdateCheckInterval": "背景更新檢查間隔",
"neverManualOnly": "從不 - 僅手動",
"appearance": "外觀",
"showWebInAppView": "在應用程式檢視中顯示來源網頁",
"pinUpdates": "將更新釘選至應用程式檢視的頂端",
"updates": "更新",
"sourceSpecific": "特定來源",
"appSource": "應用程式來源",
"noLogs": "無日誌",
"appLogs": "應用程式日誌",
"close": "關閉",
"share": "分享",
"appNotFound": "沒有找到應用程式",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "選擇一個 APK",
"appHasMoreThanOnePackage": "{} 有多個套件:",
"deviceSupportsXArch": "您的裝置支援 {} CPU 架構。",
"deviceSupportsFollowingArchs": "您的裝置支援以下 CPU 架構:",
"warning": "警告",
"sourceIsXButPackageFromYPrompt": "應用程式來源是 「{}」,但發佈套件來自 「{}」。要繼續嗎?",
"updatesAvailable": "有可用的更新",
"updatesAvailableNotifDescription": "通知使用者有一個或多個由 Obtainium 追蹤的應用程式有更新",
"noNewUpdates": "沒有新更新。",
"xHasAnUpdate": "{} 有一個更新。",
"appsUpdated": "應用程式已更新",
"appsNotUpdated": "未能更新應用程式",
"appsUpdatedNotifDescription": "通知使用者一個或多個應用程式的更新已在背景中套用",
"xWasUpdatedToY": "{} 已更新到 {}。",
"xWasNotUpdatedToY": "未能將 {} 更新到 {}。",
"errorCheckingUpdates": "檢查更新時出錯",
"errorCheckingUpdatesNotifDescription": "背景檢查更新失敗時顯示的通知",
"appsRemoved": "應用程式已移除",
"appsRemovedNotifDescription": "通知使用者由於載入時出錯,一個或多個應用程式已被移除",
"xWasRemovedDueToErrorY": "{} 已因以下錯誤被移除:{}",
"completeAppInstallation": "完成應用程式安裝",
"obtainiumMustBeOpenToInstallApps": "Obtainium 必須開啟才能安裝應用程式",
"completeAppInstallationNotifDescription": "請使用者回到 Obtainium 以完成應用程式安裝",
"checkingForUpdates": "正在檢查更新",
"checkingForUpdatesNotifDescription": "檢查更新時顯示的暫時性通知",
"pleaseAllowInstallPerm": "請允許 Obtainium 安裝應用程式",
"trackOnly": "僅追蹤",
"errorWithHttpStatusCode": "錯誤 {}",
"versionCorrectionDisabled": "版本校正已停用(外掛程式似乎無法正常工作)",
"unknown": "未知",
"none": "無",
"never": "從不",
"latestVersionX": "最新版本:{}",
"installedVersionX": "已安裝版本:{}",
"lastUpdateCheckX": "上次檢查更新時間:{}",
"remove": "移除",
"yesMarkUpdated": "是,標記為已更新",
"fdroid": "F-Droid 官方",
"appIdOrName": "應用程式 ID 或名稱",
"appId": "應用程式 ID",
"appWithIdOrNameNotFound": "找不到具有該 ID 或名稱的應用程式",
"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": "如果可能,嘗試按 CPU 架構過濾 APK",
"overrideSource": "覆蓋來源",
"dontShowAgain": "不要再顯示",
"dontShowTrackOnlyWarnings": "不要顯示「僅追蹤」警告",
"dontShowAPKOriginWarnings": "不要顯示 APK 來源警告",
"moveNonInstalledAppsToBottom": "將未安裝的應用程式移到應用程式檢視的末端",
"gitlabPATLabel": "GitLab 個人存取權杖",
"about": "關於",
"requiresCredentialsInSettings": "{} 需要額外的憑證(在設定中)",
"checkOnStart": "啟動時檢查更新",
"tryInferAppIdFromCode": "嘗試從原始碼推斷應用程式 ID",
"removeOnExternalUninstall": "自動移除外部解除安裝的應用程式",
"pickHighestVersionCode": "自動選取最高版本號的 APK",
"checkUpdateOnDetailPage": "在開啟應用程式詳細頁面時檢查更新",
"disablePageTransitions": "停用頁面過渡動畫",
"reversePageTransitions": "反轉頁面過渡動畫",
"minStarCount": "最少星星數",
"addInfoBelow": "在下方新增此資訊。",
"addInfoInSettings": "在設定中增加此資訊。",
"githubSourceNote": "使用 API 金鑰可以避免 GitHub 的速率限制。",
"sortByLastLinkSegment": "僅按連結的最後一段排序",
"filterReleaseNotesByRegEx": "用正則表達式過濾發佈說明",
"customLinkFilterRegex": "自定 APK 連結過濾正則表達式(預設為 '.apk$'",
"appsPossiblyUpdated": "嘗試更新應用程式",
"appsPossiblyUpdatedNotifDescription": "通知使用者一個或多個應用程式的更新可能已在背景中套用",
"xWasPossiblyUpdatedToY": "{} 可能已更新到 {}。",
"enableBackgroundUpdates": "啟用背景更新",
"backgroundUpdateReqsExplanation": "並非所有應用程式都能進行背景更新。",
"backgroundUpdateLimitsExplanation": "背景安裝的成功與否只能在開啟 Obtainium 時確定。",
"verifyLatestTag": "驗證「最新」標籤",
"intermediateLinkRegex": "過濾要存取的「中間」連結",
"filterByLinkText": "按連結文字過濾連結",
"intermediateLinkNotFound": "沒有找到中間連結",
"intermediateLink": "中間連結",
"exemptFromBackgroundUpdates": "免除背景更新(若已啟用)",
"bgUpdatesOnWiFiOnly": "停用非 WiFi 的背景更新",
"autoSelectHighestVersionCode": "自動選擇最高 versionCode 的 APK",
"versionExtractionRegEx": "版本字串提取正則表達式",
"trimVersionString": "用正則表達式修剪版本字串",
"matchGroupToUseForX": "用於「{}」的對應群組",
"matchGroupToUse": "要用於版本字串提取的對應群組",
"highlightTouchTargets": "突出顯示不明顯的觸控目標",
"pickExportDir": "選擇匯出目錄",
"autoExportOnChanges": "更改時自動匯出",
"includeSettings": "包含設定",
"filterVersionsByRegEx": "用正則表達式過濾版本",
"trySelectingSuggestedVersionCode": "嘗試選擇建議的 versionCode APK",
"dontSortReleasesList": "保留 API 的發佈順序",
"reverseSort": "反向排序",
"takeFirstLink": "使用第一個連結",
"skipSort": "跳過排序",
"debugMenu": "除錯選單",
"bgTaskStarted": "背景任務已啟動 - 檢查日誌。",
"runBgCheckNow": "立即執行背景更新檢查",
"versionExtractWholePage": "將版本字串提取正則表達式套用於整個頁面",
"installing": "正在安裝",
"skipUpdateNotifications": "跳過更新通知",
"updatesAvailableNotifChannel": "有可用的更新",
"appsUpdatedNotifChannel": "應用程式已更新",
"appsPossiblyUpdatedNotifChannel": "嘗試更新應用程式",
"errorCheckingUpdatesNotifChannel": "檢查更新錯誤",
"appsRemovedNotifChannel": "應用程式已移除",
"downloadingXNotifChannel": "正在下載 {}",
"completeAppInstallationNotifChannel": "完成應用程式安裝",
"checkingForUpdatesNotifChannel": "正在檢查更新",
"onlyCheckInstalledOrTrackOnlyApps": "僅檢查已安裝和僅追蹤的應用程式更新",
"supportFixedAPKURL": "支援固定的 APK 網址",
"selectX": "選擇 {}",
"parallelDownloads": "允許平行下載",
"useShizuku": "使用 Shizuku 或 Sui 來安裝",
"shizukuBinderNotFound": "Shizuku 服務未運作",
"shizukuOld": "舊版 Shizuku (<11) - 請更新",
"shizukuOldAndroidWithADB": "Shizuku 在 Android 8.1 以下版本使用 ADB 執行 - 請更新 Android 或改用 Sui",
"shizukuPretendToBeGooglePlay": "設定 Google Play 為安裝來源(如果使用 Shizuku",
"useSystemFont": "使用系統字型",
"useVersionCodeAsOSVersion": "使用應用程式 versionCode 作為作業系統偵測的版本",
"requestHeader": "請求標頭",
"useLatestAssetDateAsReleaseDate": "使用最新資源上傳日期作為發佈日期",
"defaultPseudoVersioningMethod": "預設偽版本管理方法",
"partialAPKHash": "部分 APK Hash",
"APKLinkHash": "APK 連結 Hash",
"directAPKLink": "直接 APK 連結",
"pseudoVersionInUse": "正在使用偽版本",
"installed": "已安裝",
"latest": "最新",
"invertRegEx": "反轉正則表達式",
"note": "備註",
"selfHostedNote": "可使用「{}」下拉選單來存取任何來源的自行託管/自訂執行個體。",
"badDownload": "無法解析 APK不相容或下載不完整",
"beforeNewInstallsShareToAppVerifier": "將新應用程式分享到 AppVerifier如果可用",
"appVerifierInstructionToast": "分享至 AppVerifier然後準備好時回到此處。",
"wiki": "幫助/維基",
"crowdsourcedConfigsLabel": "群眾外包的應用程式設定(使用風險自負)",
"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

@ -13,8 +13,8 @@
"and": "和",
"githubPATLabel": "GitHub 个人访问令牌(提升 API 请求限额)",
"includePrereleases": "包含预发行版",
"fallbackToOlderReleases": "将发行版作为备选",
"filterReleaseTitlesByRegEx": "筛选发行标题正则表达式",
"fallbackToOlderReleases": "将过往的发行版作为备选",
"filterReleaseTitlesByRegEx": "筛选发行标题正则表达式",
"invalidRegEx": "无效的正则表达式",
"noDescription": "无描述",
"cancel": "取消",
@ -22,6 +22,9 @@
"requiredInBrackets": "(必填)",
"dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项",
"colour": "配色",
"standard": "标准",
"custom": "定制",
"useMaterialYou": "使用 Material You",
"githubStarredRepos": "已星标的 GitHub 仓库",
"uname": "用户名",
"wrongArgNum": "参数数量错误",
@ -42,8 +45,8 @@
"search": "搜索",
"additionalOptsFor": "{} 的更多选项",
"supportedSources": "支持的来源",
"trackOnlyInBrackets": "(仅追踪)",
"searchableInBrackets": "(可搜索)",
"trackOnlyInBrackets": "仅追踪",
"searchableInBrackets": "可搜索",
"appsString": "应用列表",
"noApps": "无应用",
"noAppsForFilter": "没有符合条件的应用",
@ -52,7 +55,7 @@
"pleaseWait": "请稍候",
"updateAvailable": "更新可用",
"notInstalled": "未安装",
"pseudoVersion": "版本",
"pseudoVersion": "虚拟版本",
"selectAll": "全选",
"deselectX": "取消选择 {}",
"xWillBeRemovedButRemainInstalled": "“{}”将从 Obtainium 中删除,但仍安装在您的设备中。",
@ -72,15 +75,14 @@
"unpinFromTop": "取消置顶",
"resetInstallStatusForSelectedAppsQuestion": "是否重置选中应用的安装状态?",
"installStatusOfXWillBeResetExplanation": "选中应用的安装状态将会被重置。\n\n当更新安装失败或其他问题导致 Obtainium 中的应用版本显示错误时,可以尝试通过此方法解决。",
"customLinkMessage": "这些链接适用于安装了 Gettingium 的设备",
"shareAppConfigLinks": "将应用程序配置共享为 HTML 链接",
"customLinkMessage": "分享链接适用于安装 Obtainium 的设备",
"shareAppConfigLinks": "通过链接分享应用配置",
"shareSelectedAppURLs": "分享选中应用的 URL",
"resetInstallStatus": "重置安装状态",
"more": "更多",
"removeOutdatedFilter": "删除失效的应用筛选",
"showOutdatedOnly": "只显示待更新应用",
"filter": "筛选",
"filterActive": "筛选 *",
"filterApps": "筛选应用",
"appName": "应用名称",
"author": "作者",
@ -111,6 +113,7 @@
"dark": "深色",
"light": "浅色",
"followSystem": "跟随系统",
"followSystemThemeExplanation": "跟随系统主题仅在使用第三方应用时有效",
"useBlackTheme": "使用纯黑深色主题",
"appSortBy": "排序依据",
"authorName": "作者 / 应用名称",
@ -122,7 +125,7 @@
"bgUpdateCheckInterval": "后台更新检查间隔",
"neverManualOnly": "手动",
"appearance": "外观",
"showWebInAppView": "应用详情页显示来源网",
"showWebInAppView": "应用详情页显示来源网站内容",
"pinUpdates": "将待更新应用置顶",
"updates": "更新",
"sourceSpecific": "来源",
@ -132,7 +135,7 @@
"close": "关闭",
"share": "分享",
"appNotFound": "未找到应用",
"obtainiumExportHyphenatedLowercase": "获取出口",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "选择一个 APK 文件",
"appHasMoreThanOnePackage": "“{}”有多个架构可用:",
"deviceSupportsXArch": "您的设备支持 {} 架构。",
@ -144,8 +147,10 @@
"noNewUpdates": "全部应用已是最新。",
"xHasAnUpdate": "“{}”可以更新了。",
"appsUpdated": "应用已更新",
"appsNotUpdated": "更新应用失败",
"appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知",
"xWasUpdatedToY": "“{}”已更新至 {}。",
"xWasNotUpdatedToY": "未能将 {} 更新为 {}。",
"errorCheckingUpdates": "检查更新出错",
"errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知",
"appsRemoved": "应用已删除",
@ -174,8 +179,8 @@
"appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用",
"reposHaveMultipleApps": "存储库中可能包含多个应用",
"fdroidThirdPartyRepo": "F-Droid 第三方存储库",
"steamMobile": "蒸汽手机",
"steamChat": "蒸汽聊天",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "安装",
"markInstalled": "标记为已安装",
"update": "更新",
@ -184,6 +189,9 @@
"disableVersionDetection": "禁用版本检测",
"noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。",
"downloadingX": "正在下载“{}”",
"downloadX": "下载 {}",
"downloadedX": "下载 {}",
"releaseAsset": "发行版附件",
"downloadNotifDescription": "提示应用的下载进度",
"noAPKFound": "未找到 APK 文件",
"noVersionDetection": "禁用版本检测",
@ -193,14 +201,14 @@
"noCategory": "无类别",
"noCategories": "无类别",
"deleteCategoriesQuestion": "是否删除选中的类别?",
"categoryDeleteWarning": "被删除类别的应用将恢复为未分类状态。",
"categoryDeleteWarning": "被删除类别的应用将恢复为未分类状态。",
"addCategory": "添加类别",
"label": "标签",
"language": "语言",
"copiedToClipboard": "已复制至剪贴板",
"storagePermissionDenied": "已拒绝授予存储权限",
"selectedCategorizeWarning": "这将覆盖选中应用当前的类别设置。",
"filterAPKsByRegEx": "筛选 APK 文件正则表达式",
"filterAPKsByRegEx": "筛选 APK 文件正则表达式",
"removeFromObtainium": "从 Obtainium 中删除",
"uninstallFromDevice": "从设备中卸载",
"onlyWorksWithNonVersionDetectApps": "仅适用于禁用版本检测的应用。",
@ -209,7 +217,7 @@
"changes": "更新日志",
"releaseDate": "发行日期",
"importFromURLsInFile": "从文件中的 URL 导入(如 OPML",
"versionDetectionExplanation": "将版本字符串与操作系统检测到的版本进行协调",
"versionDetectionExplanation": "使发行版本号与应用定义的版本号一致",
"versionDetection": "版本检测",
"standardVersionDetection": "常规版本检测",
"groupByCategory": "按类别分组显示",
@ -219,13 +227,13 @@
"dontShowTrackOnlyWarnings": "忽略“仅追踪”模式警告",
"dontShowAPKOriginWarnings": "忽略 APK 文件来源警告",
"moveNonInstalledAppsToBottom": "将未安装应用置底",
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
"gitlabPATLabel": "GitLab 个人访问令牌",
"about": "相关文档",
"requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)",
"checkOnStart": "启动时进行一次检查",
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
"removeOnExternalUninstall": "自动删除列表中已卸载的应用",
"pickHighestVersionCode": "自动选版本号最高的 APK 文件",
"pickHighestVersionCode": "自动选取内部版本号最高的 APK 文件",
"checkUpdateOnDetailPage": "打开应用详情页时进行检查",
"disablePageTransitions": "禁用页面过渡动画效果",
"reversePageTransitions": "反转页面过渡动画效果",
@ -233,31 +241,32 @@
"addInfoBelow": "在下方添加此凭据。",
"addInfoInSettings": "在“设置”中添加此凭据。",
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
"sortByLastLinkSegment": "仅根据链接的末尾部分进行筛选",
"filterReleaseNotesByRegEx": "筛选发行说明正则表达式",
"filterReleaseNotesByRegEx": "筛选发行说明正则表达式",
"customLinkFilterRegex": "筛选自定义来源的 APK 文件链接\n正则表达式默认匹配模式为“.apk$”)",
"appsPossiblyUpdated": "已尝试更新应用",
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
"xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。",
"enableBackgroundUpdates": "启用后台更新",
"enableBackgroundUpdates": "启用全局后台更新",
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
"verifyLatestTag": "验证“Latest”标签",
"intermediateLinkRegex": "筛选中转链接正则表达式",
"intermediateLinkRegex": "筛选中转链接正则表达式",
"filterByLinkText": "根据链接文本进行筛选",
"intermediateLinkNotFound": "未找到中转链接",
"intermediateLink": "中转链接",
"exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用",
"exemptFromBackgroundUpdates": "禁用后台更新(仅此应用生效,即使已启用全局后台更新",
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
"versionExtractionRegEx": "版本号提取规则(正则表达式",
"matchGroupToUse": "引用的捕获组",
"highlightTouchTargets": "突出展示不明显的触摸区域",
"autoSelectHighestVersionCode": "自动选择内部版本号最高的 APK 文件",
"versionExtractionRegEx": "提取版本号正则表达式",
"trimVersionString": "使用 RegEx 修剪版本字符串",
"matchGroupToUseForX": "用于\"{}\"的匹配组",
"matchGroupToUse": "从上述匹配结果中引用的捕获组",
"highlightTouchTargets": "突出展示不明显的可交互区域",
"pickExportDir": "选择导出文件夹",
"autoExportOnChanges": "数据变更时自动导出",
"includeSettings": "同时导出应用设置",
"filterVersionsByRegEx": "筛选版本号正则表达式",
"filterVersionsByRegEx": "筛选版本号正则表达式",
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
"dontSortReleasesList": "保持来自 API 的发行顺序",
"reverseSort": "反转排序",
@ -281,34 +290,41 @@
"supportFixedAPKURL": "支持固定的 APK 文件链接",
"selectX": "选择{}",
"parallelDownloads": "启用并行下载",
"installMethod": "安装方式",
"normal": "常规",
"root": "root",
"useShizuku": "使用 Shizuku 或 Sui 安装",
"shizukuBinderNotFound": "未发现兼容的 Shizuku 服务",
"shizukuOld": "Shizuku 版本过低(<11- 请更新",
"shizukuOldAndroidWithADB": "正在低版本 Android<8.1)系统中以 ADB 模式运行 Shizuku - 请更新 Android 系统版本或使用 Sui 代替",
"shizukuPretendToBeGooglePlay": "将安装来源伪装为 Google Play需要使用 Shizuku",
"useSystemFont": "使用系统字体",
"systemFontError": "加载系统字体出错:{}",
"useVersionCodeAsOSVersion": "使用应用程序版本代码作为操作系统检测到的版本",
"useVersionCodeAsOSVersion": "使用内部版本号代替应用定义的版本号",
"requestHeader": "请求标头",
"useLatestAssetDateAsReleaseDate": "使用最新资产上传作为发日期",
"defaultPseudoVersioningMethod": "默认伪版本控制方法",
"partialAPKHash": "部分 APK 哈希值",
"APKLinkHash": "APK 链接哈希",
"directAPKLink": "直接 APK 链",
"pseudoVersionInUse": "伪版本正在使用",
"installed": "已安装",
"latest": "最新",
"invertRegEx": "反转正则表达式",
"useLatestAssetDateAsReleaseDate": "使用最新文件上传时间作为发日期",
"defaultPseudoVersioningMethod": "默认虚拟版本方案",
"partialAPKHash": "APK 文件散列值片段",
"APKLinkHash": "APK 文件链接散列值",
"directAPKLink": "APK 文件直链",
"pseudoVersionInUse": "正在使用虚拟版本号",
"installed": "当前版本",
"latest": "最新版本",
"invertRegEx": "反转匹配",
"note": "备注",
"selfHostedNote": "可以通过“{}”下拉菜单来指向任意来源的自托管/自定义实例。",
"badDownload": "无法解析 APK 文件(不兼容或文件不完整)",
"beforeNewInstallsShareToAppVerifier": "通过 AppVerifier 校验新应用(如果可用)",
"appVerifierInstructionToast": "分享至 AppVerifier完成后返回此处。",
"wiki": "帮助/Wiki",
"crowdsourcedConfigsLabel": "众包应用程序配置(使用风险自负)",
"removeAppQuestion": {
"one": "是否删除应用?",
"other": "是否删除应用?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试",
"other": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试"
"one": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试",
"other": "API 请求过于频繁(速率限制)- 在 {} 分钟后重试"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "后台更新检查遇到了“{}”问题,预定于 {} 分钟后重试",
"other": "后台更新检查遇到了“{}”问题,预定于 {} 分钟后重试"
"one": "后台更新检查遇到了“{}”问题,于 {} 分钟后重试",
"other": "后台更新检查遇到了“{}”问题,于 {} 分钟后重试"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "后台检查发现 {} 个应用更新 - 如有需要将发送通知",
@ -346,8 +362,16 @@
"one": "{} 和另外 1 个应用已更新。",
"other": "“{}”和另外 {} 个应用已更新。"
},
"xAndNMoreUpdatesFailed": {
"one": "{} 和另外 1 个应用更新失败。",
"other": "{} 和另外 {} 个应用更新失败。"
},
"xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} 和另外 1 个应用已尝试更新。",
"other": "“{}”和另外 {} 个应用已尝试更新。"
},
"apk": {
"one": "{}APK",
"other": "{}APK"
}
}

View File

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

View File

@ -9,7 +9,7 @@
<ul>
<li>GitHub</li>
<li>GitLab</li>
<li>Codeberg</li>
<li>Forgejo (Codeberg)</li>
<li>F-Droid</li>
<li>Third Party F-Droid Repos</li>
<li>IzzyOnDroid</li>

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>Forgejo (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

@ -1,5 +1,7 @@
import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -29,14 +31,15 @@ class APKPure extends AppSource {
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp(
'^https?://m.${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?',
'^https?://m.${getSourceRegex(hosts)}(/+[^/]{2})?/+[^/]+/+[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegExB.firstMatch(url);
if (match != null) {
url = 'https://${getSourceRegex(hosts)}${Uri.parse(url).path}';
var uri = Uri.parse(url);
url = 'https://${uri.host.substring(2)}${uri.path}';
}
RegExp standardUrlRegExA = RegExp(
'^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?',
'^https?://(www\\.)?${getSourceRegex(hosts)}(/+[^/]{2})?/+[^/]+/+[^/]+',
caseSensitive: false);
match = standardUrlRegExA.firstMatch(url);
if (match == null) {
@ -58,40 +61,104 @@ class APKPure extends AppSource {
) async {
String appId = (await tryInferringAppId(standardUrl))!;
String host = Uri.parse(standardUrl).host;
var res = await sourceRequest('$standardUrl/download', additionalSettings);
var resChangelog = await sourceRequest(standardUrl, additionalSettings);
if (res.statusCode == 200 && resChangelog.statusCode == 200) {
var html = parse(res.body);
var htmlChangelog = parse(resChangelog.body);
String? version = html.querySelector('span.info-sdk span')?.text.trim();
if (version == null) {
throw NoVersionError();
}
String? dateString =
html.querySelector('span.info-other span.date')?.text.trim();
DateTime? releaseDate = parseDateTimeMMMddCommayyyy(dateString);
String type = html.querySelector('a.info-tag')?.text.trim() ?? 'APK';
List<MapEntry<String, String>> apkUrls = [
MapEntry('$appId.apk',
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?version=latest')
];
String author = html
.querySelector('span.info-sdk')
?.text
.trim()
.substring(version.length + 4) ??
Uri.parse(standardUrl).pathSegments.reversed.last;
String appName =
html.querySelector('h1.info-title')?.text.trim() ?? appId;
String? changeLog = htmlChangelog
.querySelector("div.whats-new-info p:not(.date)")
?.innerHtml
.trim()
.replaceAll("<br>", " \n");
return APKDetails(version, apkUrls, AppNames(author, appName),
releaseDate: releaseDate, changeLog: changeLog);
} else {
throw getObtainiumHttpError(res);
var res0 = await sourceRequest('$standardUrl/versions', additionalSettings);
var versionLinks = await grabLinksCommon(res0, {
'skipSort': true,
'customLinkFilterRegex': '$standardUrl/download/[^/]+\$'
});
var supportedArchs = (await DeviceInfoPlugin().androidInfo).supportedAbis;
if (additionalSettings['autoApkFilterByArch'] != true) {
// No need to request multiple versions when we're not going to filter them (always pick the top one)
versionLinks = versionLinks.sublist(0, 1);
}
if (versionLinks.isEmpty) {
throw NoReleasesError();
}
for (var i = 0; i < versionLinks.length; i++) {
var link = versionLinks[i];
var res = await sourceRequest(link.key, additionalSettings);
if (res.statusCode == 200) {
var html = parse(res.body);
var apksDiv =
html.querySelector('#version-list div div.show-more-content');
DateTime? topReleaseDate;
var apkUrls = apksDiv
?.querySelectorAll('div.group-title')
.map((e) {
String architectureString = e.text.trim();
if (architectureString.toLowerCase() == 'unlimited' ||
architectureString.toLowerCase() == 'universal') {
architectureString = '';
}
List<String> architectures = architectureString
.split(',')
.map((e) => e.trim())
.where((e) => e.isNotEmpty)
.toList();
// Only take the first APK for each architecture, ignore others for now, for simplicity
// Unclear why there can even be multiple APKs for the same version and arch
var apkInfo = e.nextElementSibling?.querySelector('div.info');
String? versionCode = RegExp('[0-9]+')
.firstMatch(apkInfo
?.querySelector('div.info-top span.code')
?.text ??
'')
?.group(0)
?.trim();
String? type = apkInfo
?.querySelector('div.info-top span.tag')
?.text
.trim() ??
'APK';
String? dateString = apkInfo
?.querySelector('div.info-bottom span.time')
?.text
.trim();
DateTime? releaseDate =
parseDateTimeMMMddCommayyyy(dateString);
if (additionalSettings['autoApkFilterByArch'] == true &&
architectures.isNotEmpty &&
architectures
.where((a) => supportedArchs.contains(a))
.isEmpty) {
return const MapEntry('', '');
}
topReleaseDate ??=
releaseDate; // Just use the release date of the first APK in the list as the release date for this version
return MapEntry(
'$appId-$versionCode-$architectureString.${type.toLowerCase()}',
'https://d.${hosts.contains(host) ? 'cdnpure.com' : host}/b/$type/$appId?versionCode=$versionCode');
})
.where((e) => e.key.isNotEmpty)
.toList() ??
[];
if (apkUrls.isEmpty) {
continue;
}
String version = Uri.parse(link.key).pathSegments.last;
String author = html
.querySelector('span.info-sdk')
?.text
.trim()
.substring(version.length + 4) ??
Uri.parse(standardUrl).pathSegments.reversed.last;
String appName =
html.querySelector('h1.info-title')?.text.trim() ?? appId;
String? changeLog = html
.querySelector('div.module.change-log')
?.innerHtml
.trim()
.replaceAll("<br>", " \n");
return APKDetails(version, apkUrls, AppNames(author, appName),
releaseDate: topReleaseDate, changeLog: changeLog);
} else {
throw getObtainiumHttpError(res);
}
}
throw NoAPKError();
}
}

View File

@ -5,6 +5,7 @@ import 'package:obtainium/providers/source_provider.dart';
class Codeberg extends AppSource {
GitHub gh = GitHub();
Codeberg() {
name = 'Forgejo (Codeberg)';
hosts = ['codeberg.org'];
additionalSourceAppSpecificSettingFormItems =

View File

@ -24,6 +24,14 @@ class DirectAPKLink extends AppSource {
];
}
@override
Future<Map<String, String>?> getRequestHeaders(
Map<String, dynamic> additionalSettings,
{bool forAPKDownload = false}) {
return html.getRequestHeaders(additionalSettings,
forAPKDownload: forAPKDownload);
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,

View File

@ -3,6 +3,8 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/app_sources/gitlab.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -74,16 +76,7 @@ class FDroid extends AppSource {
'https://$host/repo/$appId',
standardUrl,
name,
autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode:
additionalSettings['trySelectingSuggestedVersionCode'] == true,
filterVersionsByRegEx:
(additionalSettings['filterVersionsByRegEx'] as String?)
?.isNotEmpty ==
true
? additionalSettings['filterVersionsByRegEx']
: null);
additionalSettings: additionalSettings);
if (!hostChanged) {
try {
var res = await sourceRequest(
@ -95,17 +88,32 @@ class FDroid extends AppSource {
details.names.author =
authorLines.first.split(': ').sublist(1).join(': ');
}
var changelogUrls = lines.where((l) => l.startsWith('Changelog: '));
var changelogUrls = lines
.where((l) => l.startsWith('Changelog: '))
.map((e) => e.split(' ').sublist(1).join(' '));
if (changelogUrls.isNotEmpty) {
details.changeLog = changelogUrls.first;
details.changeLog = (await sourceRequest(
details.changeLog!
.split(': ')
.sublist(1)
.join(': ')
.replaceFirst('/blob/', '/raw/'),
additionalSettings))
.body;
bool isGitHub = false;
bool isGitLab = false;
try {
GitHub().sourceSpecificStandardizeURL(details.changeLog!);
isGitHub = true;
} catch (e) {
//
}
try {
GitLab().sourceSpecificStandardizeURL(details.changeLog!);
isGitLab = true;
} catch (e) {
//
}
if ((isGitHub || isGitLab) &&
(details.changeLog?.indexOf('/blob/') ?? -1) >= 0) {
details.changeLog = (await sourceRequest(
details.changeLog!.replaceFirst('/blob/', '/raw/'),
additionalSettings))
.body;
}
}
} catch (e) {
// Fail silently
@ -149,12 +157,30 @@ class FDroid extends AppSource {
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
{bool autoSelectHighestVersionCode = false,
bool trySelectingSuggestedVersionCode = false,
String? filterVersionsByRegEx}) {
{Map<String, dynamic> additionalSettings = const {}}) {
var autoSelectHighestVersionCode =
additionalSettings['autoSelectHighestVersionCode'] == true;
var trySelectingSuggestedVersionCode =
additionalSettings['trySelectingSuggestedVersionCode'] == true;
var filterVersionsByRegEx =
(additionalSettings['filterVersionsByRegEx'] as String?)?.isNotEmpty ==
true
? additionalSettings['filterVersionsByRegEx']
: null;
var apkFilterRegEx =
(additionalSettings['apkFilterRegEx'] as String?)?.isNotEmpty == true
? additionalSettings['apkFilterRegEx']
: null;
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
List<dynamic> releases = response['packages'] ?? [];
if (apkFilterRegEx != null) {
releases = releases.where((rel) {
String apk = '${apkUrlPrefix}_${rel['versionCode']}.apk';
return filterApks([MapEntry(apk, apk)], apkFilterRegEx, false)
.isNotEmpty;
}).toList();
}
if (releases.isEmpty) {
throw NoReleasesError();
}

View File

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -8,7 +9,7 @@ class FDroidRepo extends AppSource {
FDroidRepo() {
name = tr('fdroidThirdPartyRepo');
canSearch = true;
excludeFromMassSearch = true;
includeAdditionalOptsInMainSearch = true;
neverAutoSelect = true;
showReleaseDateAsVersionToggle = true;
@ -45,7 +46,7 @@ class FDroidRepo extends AppSource {
String sourceSpecificStandardizeURL(String url) {
var standardUri = Uri.parse(url);
var pathSegments = standardUri.pathSegments;
if (pathSegments.last == 'index.xml') {
if (pathSegments.isNotEmpty && pathSegments.last == 'index.xml') {
pathSegments.removeLast();
standardUri = standardUri.replace(path: pathSegments.join('/'));
}
@ -60,7 +61,7 @@ class FDroidRepo extends AppSource {
throw NoReleasesError();
}
url = removeQueryParamsFromUrl(standardizeUrl(url));
var res = await sourceRequest('$url/index.xml', {});
var res = await sourceRequestWithURLVariants(url, {});
if (res.statusCode == 200) {
var body = parse(res.body);
Map<String, List<String>> results = {};
@ -72,7 +73,11 @@ class FDroidRepo extends AppSource {
appId.contains(query) ||
appName.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;
@ -81,6 +86,27 @@ class FDroidRepo extends AppSource {
}
}
@override
void runOnAddAppInputChange(String userInput) {
additionalSourceAppSpecificSettingFormItems =
additionalSourceAppSpecificSettingFormItems.map((row) {
row = row.map((item) {
if (item.key == 'appIdOrName') {
try {
var appId = Uri.parse(userInput).queryParameters['appId'];
if (appId != null && item is GeneratedFormTextField) {
item.required = false;
}
} catch (e) {
//
}
}
return item;
}).toList();
return row;
}).toList();
}
@override
App endOfGetAppChanges(App app) {
var uri = Uri.parse(app.url);
@ -102,6 +128,26 @@ class FDroidRepo extends AppSource {
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
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
@ -117,9 +163,9 @@ class FDroidRepo extends AppSource {
if (appIdOrName == null) {
throw NoReleasesError();
}
var res = await sourceRequest(
'$standardUrl${standardUrl.endsWith('/index.xml') ? '' : '/index.xml'}',
additionalSettings);
additionalSettings['appIdOrName'] = appIdOrName;
var res =
await sourceRequestWithURLVariants(standardUrl, additionalSettings);
if (res.statusCode == 200) {
var body = parse(res.body);
var foundApps = body.querySelectorAll('application').where((element) {
@ -168,7 +214,8 @@ class FDroidRepo extends AppSource {
latestVersionReleases = [latestVersionReleases[0]];
}
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();
return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls),
AppNames(authorName, appName),

View File

@ -171,7 +171,7 @@ class GitHub extends AppSource {
{bool forAPKDownload = false}) async {
var token = await getTokenIfAny(additionalSettings);
var headers = <String, String>{};
if (token != null) {
if (token != null && token.isNotEmpty) {
headers[HttpHeaders.authorizationHeader] = 'Token $token';
}
if (forAPKDownload == true) {
@ -271,23 +271,23 @@ class GitHub extends AppSource {
}
}
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
(release['assets'] as List<dynamic>?)
?.map((e) {
return (e['name'] != null) &&
((e['url'] ?? e['browser_download_url']) != null)
? MapEntry(e['name'] as String,
(e['url'] ?? e['browser_download_url']) as String)
: const MapEntry('', '');
})
.where((element) => element.key.toLowerCase().endsWith('.apk'))
.toList() ??
List<MapEntry<String, String>> getReleaseAssetUrls(dynamic release) =>
(release['assets'] as List<dynamic>?)?.map((e) {
var url = !e['name'].toString().toLowerCase().endsWith('.apk')
? (e['browser_download_url'] ?? e['url'])
: (e['url'] ?? e['browser_download_url']);
return (e['name'] != null) && (url != null)
? MapEntry(e['name'] as String, url as String)
: const MapEntry('', '');
}).toList() ??
[];
DateTime? getPublishDateFromRelease(dynamic rel) =>
rel?['published_at'] != null
? DateTime.parse(rel['published_at'])
: null;
: rel?['commit']?['created'] != null
? DateTime.parse(rel['commit']['created'])
: null;
DateTime? getNewestAssetDateFromRelease(dynamic rel) {
var t = (rel['assets'] as List<dynamic>?)
?.map((e) {
@ -344,12 +344,14 @@ class GitHub extends AppSource {
});
}
if (latestRelease != null &&
(latestRelease['tag_name'] ?? latestRelease['name']) != null &&
releases.isNotEmpty &&
latestRelease !=
(releases[releases.length - 1]['tag_name'] ??
releases[0]['name'])) {
var ind = releases.indexWhere((element) =>
latestRelease == (element['tag_name'] ?? element['name']));
(latestRelease['tag_name'] ?? latestRelease['name']) ==
(element['tag_name'] ?? element['name']));
if (ind >= 0) {
releases.add(releases.removeAt(ind));
}
@ -381,7 +383,11 @@ class GitHub extends AppSource {
.hasMatch(((releases[i]['body'] as String?) ?? '').trim())) {
continue;
}
var apkUrls = getReleaseAPKUrls(releases[i]);
var allAssetUrls = getReleaseAssetUrls(releases[i]);
List<MapEntry<String, String>> apkUrls = allAssetUrls
.where((element) => element.key.toLowerCase().endsWith('.apk'))
.toList();
apkUrls = filterApks(apkUrls, additionalSettings['apkFilterRegEx'],
additionalSettings['invertAPKFilter']);
if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) {
@ -389,24 +395,39 @@ class GitHub extends AppSource {
}
targetRelease = releases[i];
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;
}
if (targetRelease == null) {
throw NoReleasesError();
}
String? version = targetRelease['tag_name'] ?? targetRelease['name'];
String? version = targetRelease['version'];
DateTime? releaseDate = getReleaseDateFromRelease(
targetRelease, useLatestAssetDateAsReleaseDate);
if (version == null) {
throw NoVersionError();
}
var changeLog = targetRelease['body'].toString();
var changeLog = (targetRelease['body'] ?? '').toString();
return APKDetails(
version,
targetRelease['apkUrls'] as List<MapEntry<String, String>>,
getAppNames(standardUrl),
releaseDate: releaseDate,
changeLog: changeLog.isEmpty ? null : changeLog);
changeLog: changeLog.isEmpty ? null : changeLog,
allAssetUrls:
targetRelease['allAssetUrls'] as List<MapEntry<String, String>>);
} else {
if (onHttpErrorCode != null) {
onHttpErrorCode(res);

View File

@ -1,7 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/custom_errors.dart';
@ -72,14 +72,6 @@ class GitLab extends AppSource {
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
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async {
@ -104,105 +96,121 @@ class GitLab extends AppSource {
String? changeLogPageFromStandardUrl(String standardUrl) =>
'$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
Future<String> apkUrlPrefetchModifier(String apkUrl, String standardUrl,
Map<String, dynamic> additionalSettings) async {
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
String optionalAuth = (PAT != null) ? 'private_token=$PAT' : '';
return '$apkUrl?$optionalAuth';
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) 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;
// Get project ID
Response res0 = await sourceRequest(
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}?$optionalAuth',
additionalSettings);
if (res0.statusCode != 200) {
throw getObtainiumHttpError(res0);
}
int? projectId = jsonDecode(res0.body)['id'];
if (projectId == null) {
throw NoReleasesError();
}
// 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) => 'https://${hosts[0]}/-/project/$projectId$s')
.toList();
var apkUrlsSet = apkUrlsFromAssets.toSet();
apkUrlsSet.addAll(uploadedAPKsFromDescription);
var releaseDateString =
e['released_at'] ?? e['created_at'] ?? e['commit']?['created_at'];
DateTime? releaseDate =
releaseDateString != null ? DateTime.parse(releaseDateString) : null;
return APKDetails(
e['tag_name'] ?? e['name'],
getApkUrlsFromUrls(apkUrlsSet.toList()),
GitHub().getAppNames(standardUrl),
releaseDate: releaseDate);
});
if (apkDetailsList.isEmpty) {
throw NoReleasesError();
}
var finalResult = apkDetailsList.first;
// Fallback procedure
bool fallbackToOlderReleases =
additionalSettings['fallbackToOlderReleases'] == true;
String? PAT = await getPATIfAny(hostChanged ? additionalSettings : {});
Iterable<APKDetails> apkDetailsList = [];
if (PAT != null) {
var names = GitHub().getAppNames(standardUrl);
Response res = await sourceRequest(
'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT',
additionalSettings);
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', additionalSettings);
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 (finalResult.apkUrls.isEmpty && fallbackToOlderReleases && !trackOnly) {
apkDetailsList =
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
finalResult = apkDetailsList.first;
}
if (apkDetailsList.isEmpty) {
throw NoReleasesError(note: tr('gitlabSourceNote'));
if (finalResult.apkUrls.isEmpty && !trackOnly) {
throw NoAPKError();
}
if (fallbackToOlderReleases) {
if (additionalSettings['trackOnly'] != true) {
apkDetailsList =
apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
finalResult.apkUrls = finalResult.apkUrls.map((apkUrl) {
if (RegExp('^$standardUrl/-/jobs/[0-9]+/artifacts/file/[^/]+')
.hasMatch(apkUrl.value)) {
return MapEntry(
apkUrl.key, apkUrl.value.replaceFirst('/file/', '/raw/'));
} else {
return apkUrl;
}
if (apkDetailsList.isEmpty) {
throw NoReleasesError(note: tr('gitlabSourceNote'));
}
}
return apkDetailsList.first;
}).toList();
return finalResult;
}
}

View File

@ -92,7 +92,89 @@ bool _isNumeric(String s) {
return s.codeUnitAt(0) >= 48 && s.codeUnitAt(0) <= 57;
}
// Given an HTTP response, grab some links according to the common additional settings
// (those that apply to intermediate and final steps)
Future<List<MapEntry<String, String>>> grabLinksCommon(
Response res, Map<String, dynamic> additionalSettings) async {
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var html = parse(res.body);
List<MapEntry<String, String>> allLinks = html
.querySelectorAll('a')
.map((element) => MapEntry(
element.attributes['href'] ?? '',
element.text.isNotEmpty
? element.text
: (element.attributes['href'] ?? '').split('/').last))
.where((element) => element.key.isNotEmpty)
.map((e) => MapEntry(ensureAbsoluteUrl(e.key, res.request!.url), e.value))
.toList();
if (allLinks.isEmpty) {
allLinks = RegExp(
r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?')
.allMatches(res.body)
.map((match) =>
MapEntry(match.group(0)!, match.group(0)?.split('/').last ?? ''))
.toList();
}
List<MapEntry<String, String>> links = [];
bool skipSort = additionalSettings['skipSort'] == true;
bool filterLinkByText = additionalSettings['filterByLinkText'] == true;
if ((additionalSettings['customLinkFilterRegex'] as String?)?.isNotEmpty ==
true) {
var reg = RegExp(additionalSettings['customLinkFilterRegex']);
links = allLinks.where((element) {
var link = element.key;
try {
link = Uri.decodeFull(element.key);
} catch (e) {
// Some links may not have valid encoding
}
return reg.hasMatch(filterLinkByText ? element.value : link);
}).toList();
} else {
links = allLinks.where((element) {
var link = element.key;
try {
link = Uri.decodeFull(element.key);
} catch (e) {
// Some links may not have valid encoding
}
return Uri.parse(filterLinkByText ? element.value : link)
.path
.toLowerCase()
.endsWith('.apk');
}).toList();
}
if (!skipSort) {
links.sort((a, b) => additionalSettings['sortByLastLinkSegment'] == true
? compareAlphaNumeric(a.key.split('/').where((e) => e.isNotEmpty).last,
b.key.split('/').where((e) => e.isNotEmpty).last)
: compareAlphaNumeric(a.key, b.key));
}
if (additionalSettings['reverseSort'] == true) {
links = links.reversed.toList();
}
return links;
}
class HTML extends AppSource {
@override
List<List<GeneratedFormItem>> get combinedAppSpecificSettingFormItems {
return super.combinedAppSpecificSettingFormItems.map((r) {
return r.map((e) {
if (e.key == 'versionExtractionRegEx') {
e.label = tr('versionExtractionRegEx');
}
if (e.key == 'matchGroupToUse') {
e.label = tr('matchGroupToUse');
}
return e;
}).toList();
}).toList();
}
var finalStepFormitems = [
[
GeneratedFormTextField('customLinkFilterRegex',
@ -210,65 +292,6 @@ class HTML extends AppSource {
return url;
}
// Given an HTTP response, grab some links according to the common additional settings
// (those that apply to intermediate and final steps)
Future<List<MapEntry<String, String>>> grabLinksCommon(
Response res, Map<String, dynamic> additionalSettings) async {
if (res.statusCode != 200) {
throw getObtainiumHttpError(res);
}
var html = parse(res.body);
List<MapEntry<String, String>> allLinks = html
.querySelectorAll('a')
.map((element) => MapEntry(
element.attributes['href'] ?? '',
element.text.isNotEmpty
? element.text
: (element.attributes['href'] ?? '').split('/').last))
.where((element) => element.key.isNotEmpty)
.map((e) =>
MapEntry(ensureAbsoluteUrl(e.key, res.request!.url), e.value))
.toList();
if (allLinks.isEmpty) {
allLinks = RegExp(
r'(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?')
.allMatches(res.body)
.map((match) =>
MapEntry(match.group(0)!, match.group(0)?.split('/').last ?? ''))
.toList();
}
List<MapEntry<String, String>> links = [];
bool skipSort = additionalSettings['skipSort'] == true;
bool filterLinkByText = additionalSettings['filterByLinkText'] == true;
if ((additionalSettings['customLinkFilterRegex'] as String?)?.isNotEmpty ==
true) {
var reg = RegExp(additionalSettings['customLinkFilterRegex']);
links = allLinks
.where((element) =>
reg.hasMatch(filterLinkByText ? element.value : element.key))
.toList();
} else {
links = allLinks
.where((element) =>
Uri.parse(filterLinkByText ? element.value : element.key)
.path
.toLowerCase()
.endsWith('.apk'))
.toList();
}
if (!skipSort) {
links.sort((a, b) => additionalSettings['sortByLastLinkSegment'] == true
? compareAlphaNumeric(
a.key.split('/').where((e) => e.isNotEmpty).last,
b.key.split('/').where((e) => e.isNotEmpty).last)
: compareAlphaNumeric(a.key, b.key));
}
if (additionalSettings['reverseSort'] == true) {
links = links.reversed.toList();
}
return links;
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
@ -309,17 +332,26 @@ class HTML extends AppSource {
links = [MapEntry(currentUrl, currentUrl)];
}
var rel = links.last.key;
var relDecoded = rel;
try {
relDecoded = Uri.decodeFull(rel);
} catch (e) {
// Some links may not have valid encoding
}
String? version;
version = extractVersion(
additionalSettings['versionExtractionRegEx'] as String?,
additionalSettings['matchGroupToUse'] as String?,
additionalSettings['versionExtractWholePage'] == true
? versionExtractionWholePageString
: rel);
version ??=
additionalSettings['defaultPseudoVersioningMethod'] == 'APKLinkHash'
? rel.hashCode.toString()
: (await checkPartialDownloadHashDynamc(rel)).toString();
: relDecoded);
version ??= additionalSettings['defaultPseudoVersioningMethod'] ==
'APKLinkHash'
? rel.hashCode.toString()
: (await checkPartialDownloadHashDynamic(rel,
headers: await getRequestHeaders(additionalSettings,
forAPKDownload: true)))
.toString();
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
AppNames(uri.host, tr('app')));
}

View File

@ -6,7 +6,7 @@ import 'package:obtainium/providers/source_provider.dart';
class HuaweiAppGallery extends AppSource {
HuaweiAppGallery() {
name = 'Huawei AppGallery';
hosts = ['appgallery.huawei.com'];
hosts = ['appgallery.huawei.com', 'appgallery.cloud.huawei.com'];
versionDetectionDisallowed = true;
showReleaseDateAsVersionToggle = true;
}
@ -14,7 +14,7 @@ class HuaweiAppGallery extends AppSource {
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp(
'^https?://(www\\.)?${getSourceRegex(hosts)}/app/[^/]+',
'^https?://(www\\.)?${getSourceRegex(hosts)}(/#)?/(app|appdl)/[^/]+',
caseSensitive: false);
RegExpMatch? match = standardUrlRegEx.firstMatch(url);
if (match == null) {
@ -24,7 +24,7 @@ class HuaweiAppGallery extends AppSource {
}
getDlUrl(String standardUrl) =>
'https://${hosts[0].replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}';
'https://${hosts[0].replaceAll('appgallery.huawei', 'appgallery.cloud.huawei')}/appdl/${standardUrl.split('/').last}';
requestAppdlRedirect(
String dlUrl, Map<String, dynamic> additionalSettings) async {
@ -73,21 +73,23 @@ class HuaweiAppGallery extends AppSource {
throw NoReleasesError();
}
String appId = appIdFromRedirectDlUrl(res.headers['location']!);
if (appId.isEmpty) {
throw NoReleasesError();
}
var relDateStr =
res.headers['location']?.split('?')[0].split('.').reversed.toList()[1];
var relDateStrAdj = relDateStr?.split('');
var tempLen = relDateStrAdj?.length ?? 0;
var i = 2;
while (i < tempLen) {
relDateStrAdj?.insert((i + i ~/ 2 - 1), '-');
i += 2;
}
var relDate = relDateStrAdj == null
? null
: DateFormat('yy-MM-dd-HH-mm').parse(relDateStrAdj.join(''));
if (relDateStr == null) {
if (relDateStr == null || relDateStr.length != 10) {
throw NoVersionError();
}
var relDateStrAdj = relDateStr.split('');
var tempLen = relDateStrAdj.length;
var i = 2;
while (i < tempLen) {
relDateStrAdj.insert((i + i ~/ 2 - 1), '-');
i += 2;
}
var relDate =
DateFormat('yy-MM-dd-HH-mm', 'en_US').parse(relDateStrAdj.join(''));
return APKDetails(
relDateStr, [MapEntry('$appId.apk', dlUrl)], AppNames(name, appId),
releaseDate: relDate);

View File

@ -50,10 +50,6 @@ class IzzyOnDroid extends AppSource {
'https://android.izzysoft.de/frepo/$appId',
standardUrl,
name,
autoSelectHighestVersionCode:
additionalSettings['autoSelectHighestVersionCode'] == true,
trySelectingSuggestedVersionCode:
additionalSettings['trySelectingSuggestedVersionCode'] == true,
filterVersionsByRegEx: additionalSettings['filterVersionsByRegEx']);
additionalSettings: additionalSettings);
}
}

View File

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

View File

@ -39,6 +39,15 @@ class SourceHut extends AppSource {
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
if (standardUrl.endsWith('/refs')) {
standardUrl = standardUrl
.split('/')
.reversed
.toList()
.sublist(1)
.reversed
.join('/');
}
Uri standardUri = Uri.parse(standardUrl);
String appName = standardUri.pathSegments.last;
bool fallbackToOlderReleases =

View File

@ -10,6 +10,7 @@ class Uptodown extends AppSource {
allowSubDomains = true;
naiveStandardVersionDetection = true;
showReleaseDateAsVersionToggle = true;
urlsAlwaysHaveExtension = true;
}
@override
@ -39,20 +40,29 @@ class Uptodown extends AppSource {
}
var html = parse(res.body);
String? version = html.querySelector('div.version')?.innerHtml;
String? apkUrl =
'${standardUrl.split('/').reversed.toList().sublist(1).reversed.join('/')}/post-download';
String? name = html.querySelector('#detail-app-name')?.innerHtml.trim();
String? author = html.querySelector('#author-link')?.innerHtml.trim();
var detailElements = html.querySelectorAll('#technical-information td');
String? appId = (detailElements.elementAtOrNull(2))?.innerHtml.trim();
String? dateStr = (detailElements.elementAtOrNull(29))?.innerHtml.trim();
String? fileId =
html.querySelector('#detail-app-name')?.attributes['data-file-id'];
String? extension = html
.querySelectorAll('td')
.where((e) => e.text.toLowerCase().trim() == 'file type')
.firstOrNull
?.nextElementSibling
?.text
.toLowerCase()
.trim();
return Map.fromEntries([
MapEntry('version', version),
MapEntry('apkUrl', apkUrl),
MapEntry('appId', appId),
MapEntry('name', name),
MapEntry('author', author),
MapEntry('dateStr', dateStr)
MapEntry('dateStr', dateStr),
MapEntry('fileId', fileId),
MapEntry('extension', extension)
]);
}
@ -64,14 +74,16 @@ class Uptodown extends AppSource {
var appDetails =
await getAppDetailsFromPage(standardUrl, additionalSettings);
var version = appDetails['version'];
var apkUrl = appDetails['apkUrl'];
var appId = appDetails['appId'];
var fileId = appDetails['fileId'];
var extension = appDetails['extension'];
if (version == null) {
throw NoVersionError();
}
if (apkUrl == null) {
if (fileId == null) {
throw NoAPKError();
}
var apkUrl = '$standardUrl/$fileId-x';
if (appId == null) {
throw NoReleasesError();
}
@ -82,8 +94,8 @@ class Uptodown extends AppSource {
if (dateStr != null) {
relDate = parseDateTimeMMMddCommayyyy(dateStr);
}
return APKDetails(
version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName),
return APKDetails(version, [MapEntry('$appId.$extension', apkUrl)],
AppNames(author, appName),
releaseDate: relDate);
}
@ -96,7 +108,7 @@ class Uptodown extends AppSource {
}
var html = parse(res.body);
var finalUrlKey =
html.querySelector('.post-download')?.attributes['data-url'];
html.querySelector('#detail-download-button')?.attributes['data-url'];
if (finalUrlKey == null) {
throw NoAPKError();
}

View File

@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:obtainium/components/generated_form_modal.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
abstract class GeneratedFormItem {
late String key;
@ -28,6 +29,7 @@ class GeneratedFormTextField extends GeneratedFormItem {
late String? hint;
late bool password;
late TextInputType? textInputType;
late List<String>? autoCompleteOptions;
GeneratedFormTextField(super.key,
{super.label,
@ -39,7 +41,8 @@ class GeneratedFormTextField extends GeneratedFormItem {
this.max = 1,
this.hint,
this.password = false,
this.textInputType});
this.textInputType,
this.autoCompleteOptions});
@override
String ensureType(val) {
@ -245,8 +248,8 @@ class _GeneratedFormState extends State<GeneratedForm> {
void someValueChanged({bool isBuilding = false, bool forceInvalid = false}) {
Map<String, dynamic> returnValues = values;
var valid = true;
for (int r = 0; r < widget.items.length; r++) {
for (int i = 0; i < widget.items[r].length; i++) {
for (int r = 0; r < formInputs.length; r++) {
for (int i = 0; i < formInputs[r].length; i++) {
if (formInputs[r][i] is TextFormField) {
valid = valid && validateTextField(formInputs[r][i] as TextFormField);
}
@ -274,38 +277,62 @@ class _GeneratedFormState extends State<GeneratedForm> {
var formItem = e.value;
if (formItem is GeneratedFormTextField) {
final formFieldKey = GlobalKey<FormFieldState>();
return TextFormField(
keyboardType: formItem.textInputType,
obscureText: formItem.password,
autocorrect: !formItem.password,
enableSuggestions: !formItem.password,
key: formFieldKey,
initialValue: values[formItem.key],
autovalidateMode: AutovalidateMode.onUserInteraction,
onChanged: (value) {
var ctrl = TextEditingController(text: values[formItem.key]);
return TypeAheadField<String>(
controller: ctrl,
builder: (context, controller, focusNode) {
return TextFormField(
controller: ctrl,
focusNode: focusNode,
keyboardType: formItem.textInputType,
obscureText: formItem.password,
autocorrect: !formItem.password,
enableSuggestions: !formItem.password,
key: formFieldKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
onChanged: (value) {
setState(() {
values[formItem.key] = value;
someValueChanged();
});
},
decoration: InputDecoration(
helperText:
formItem.label + (formItem.required ? ' *' : ''),
hintText: formItem.hint),
minLines: formItem.max <= 1 ? null : formItem.max,
maxLines: formItem.max <= 1 ? 1 : formItem.max,
validator: (value) {
if (formItem.required &&
(value == null || value.trim().isEmpty)) {
return '${formItem.label} ${tr('requiredInBrackets')}';
}
for (var validator in formItem.additionalValidators) {
String? result = validator(value);
if (result != null) {
return result;
}
}
return null;
},
);
},
itemBuilder: (context, value) {
return ListTile(title: Text(value));
},
onSelected: (value) {
ctrl.text = value;
setState(() {
values[formItem.key] = value;
someValueChanged();
});
},
decoration: InputDecoration(
helperText: formItem.label + (formItem.required ? ' *' : ''),
hintText: formItem.hint),
minLines: formItem.max <= 1 ? null : formItem.max,
maxLines: formItem.max <= 1 ? 1 : formItem.max,
validator: (value) {
if (formItem.required &&
(value == null || value.trim().isEmpty)) {
return '${formItem.label} ${tr('requiredInBrackets')}';
}
for (var validator in formItem.additionalValidators) {
String? result = validator(value);
if (result != null) {
return result;
}
}
return null;
suggestionsCallback: (search) {
return formItem.autoCompleteOptions
?.where((t) => t.toLowerCase().contains(search.toLowerCase()))
.toList();
},
hideOnEmpty: true,
);
} else if (formItem is GeneratedFormDropdown) {
if (formItem.opts!.isEmpty) {

View File

@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:obtainium/pages/home.dart';
import 'package:obtainium/providers/apps_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/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
@ -38,6 +39,8 @@ List<MapEntry<Locale, String>> supportedLocales = const [
MapEntry(Locale('nl'), 'Nederlands'),
MapEntry(Locale('vi'), 'Tiếng Việt'),
MapEntry(Locale('tr'), 'Türkçe'),
MapEntry(Locale('uk'), 'Українська'),
MapEntry(Locale('da'), 'Dansk'),
];
const fallbackLocale = Locale('en');
const localeDir = 'assets/translations';
@ -117,8 +120,6 @@ void main() async {
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
var defaultThemeColour = Colors.deepPurple;
class Obtainium extends StatefulWidget {
const Obtainium({super.key});
@ -214,22 +215,25 @@ class _ObtainiumState extends State<Obtainium> {
ColorScheme darkColorScheme;
if (lightDynamic != null &&
darkDynamic != null &&
settingsProvider.colour == ColourSettings.materialYou) {
settingsProvider.useMaterialYou) {
lightColorScheme = lightDynamic.harmonized();
darkColorScheme = darkDynamic.harmonized();
} else {
lightColorScheme = ColorScheme.fromSeed(seedColor: defaultThemeColour);
lightColorScheme =
ColorScheme.fromSeed(seedColor: settingsProvider.themeColor);
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
if (settingsProvider.useBlackTheme) {
darkColorScheme = darkColorScheme
.copyWith(background: Colors.black, surface: Colors.black)
.harmonized();
darkColorScheme =
darkColorScheme.copyWith(surface: Colors.black).harmonized();
}
if (settingsProvider.useSystemFont) NativeFeatures.loadSystemFont();
return MaterialApp(
title: 'Obtainium',
localizationsDelegates: context.localizationDelegates,
@ -242,14 +246,14 @@ class _ObtainiumState extends State<Obtainium> {
? darkColorScheme
: lightColorScheme,
fontFamily:
settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
settingsProvider.useSystemFont ? 'SystemFont' : 'Wix-Madefor-Display'),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.light
? lightColorScheme
: darkColorScheme,
fontFamily:
settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
settingsProvider.useSystemFont ? 'SystemFont' : 'Wix-Madefor-Display'),
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
}, child: const HomePage()));

View File

@ -1,7 +1,6 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart';
@ -52,28 +51,19 @@ class AddAppPageState extends State<AddAppPage> {
}
changeUserInput(String input, bool valid, bool isBuilding,
{bool updateUrlInput = false}) {
{bool updateUrlInput = false, String? overrideSource}) {
userInput = input;
if (!isBuilding) {
setState(() {
if (overrideSource != null) {
pickedSourceOverride = overrideSource;
}
if (updateUrlInput) {
urlInputKey++;
}
var prevHost = pickedSource?.hosts.isNotEmpty == true
? pickedSource?.hosts[0]
: null;
try {
var naturalSource =
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
? sourceProvider.getSource(userInput,
overrideSource: pickedSourceOverride)
@ -81,6 +71,7 @@ class AddAppPageState extends State<AddAppPage> {
if (pickedSource.runtimeType != source.runtimeType ||
(prevHost != null && prevHost != source?.hosts[0])) {
pickedSource = source;
pickedSource?.runOnAddAppInputChange(userInput);
additionalSettings = source != null
? getDefaultValuesFromFormItems(
source.combinedAppSpecificSettingFormItems)
@ -163,12 +154,13 @@ class AddAppPageState extends State<AddAppPage> {
app = await sourceProvider.getApp(
pickedSource!, userInput.trim(), additionalSettings,
trackOnlyOverride: trackOnly,
overrideSource: pickedSourceOverride,
sourceIsOverriden: pickedSourceOverride != null,
inferAppIdIfOptional: inferAppIdIfOptional);
// Only download the APK here if you need to for the package ID
if (isTempId(app) && app.additionalSettings['trackOnly'] != true) {
// ignore: use_build_context_synchronously
var apkUrl = await appsProvider.confirmApkUrl(app, context);
var apkUrl =
await appsProvider.confirmAppFileUrl(app, context, false);
if (apkUrl == null) {
throw ObtainiumError(tr('cancelled'));
}
@ -271,9 +263,7 @@ class AddAppPageState extends State<AddAppPage> {
searching = true;
});
var sourceStrings = <String, List<String>>{};
sourceProvider.sources
.where((e) => e.canSearch && !e.excludeFromMassSearch)
.forEach((s) {
sourceProvider.sources.where((e) => e.canSearch).forEach((s) {
sourceStrings[s.name] = [s.name];
});
try {
@ -294,32 +284,78 @@ class AddAppPageState extends State<AddAppPage> {
settingsProvider.searchDeselected = sourceStrings.keys
.where((s) => !searchSources.contains(s))
.toList();
var results = await Future.wait(sourceProvider.sources
.where((e) => searchSources.contains(e.name))
.map((e) async {
List<MapEntry<String, Map<String, List<String>>>?> results =
(await Future.wait(sourceProvider.sources
.where((e) => searchSources.contains(e.name))
.map((e) async {
try {
return await e.search(searchQuery);
Map<String, dynamic>? querySettings = {};
if (e.includeAdditionalOptsInMainSearch) {
querySettings = await showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: tr('searchX', args: [e.name]),
items: [
...e.searchQuerySettingFormItems.map((e) => [e]),
[
GeneratedFormTextField('url',
label: e.hosts.isNotEmpty
? tr('overrideSource')
: plural('url', 1).substring(2),
autoCompleteOptions: [
...(e.hosts.isNotEmpty ? [e.hosts[0]] : []),
...appsProvider.apps.values
.where((a) =>
sourceProvider
.getSource(a.app.url,
overrideSource:
a.app.overrideSource)
.runtimeType ==
e.runtimeType)
.map((a) {
var uri = Uri.parse(a.app.url);
return '${uri.origin}${uri.path}';
})
],
defaultValue:
e.hosts.isNotEmpty ? e.hosts[0] : '',
required: true)
],
],
);
});
if (querySettings == null) {
return null;
}
}
return MapEntry(e.runtimeType.toString(),
await e.search(searchQuery, querySettings: querySettings));
} catch (err) {
if (err is! CredsNeededError) {
rethrow;
} else {
err.unexpected = true;
showError(err, context);
return <String, List<String>>{};
return null;
}
}
}));
})))
.where((a) => a != null)
.toList();
// Interleave results instead of simple reduce
Map<String, List<String>> res = {};
Map<String, MapEntry<String, List<String>>> res = {};
var si = 0;
var done = false;
while (!done) {
done = true;
for (var r in results) {
if (r.length > si) {
var sourceName = r!.key;
if (r.value.length > si) {
done = false;
res.addEntries([r.entries.elementAt(si)]);
var singleRes = r.value.entries.elementAt(si);
res[singleRes.key] = MapEntry(sourceName, singleRes.value);
}
}
si++;
@ -334,13 +370,15 @@ class AddAppPageState extends State<AddAppPage> {
context: context,
builder: (BuildContext ctx) {
return SelectionModal(
entries: res,
entries: res.map((k, v) => MapEntry(k, v.value)),
selectedByDefault: false,
onlyOneSelectionAllowed: true,
);
});
if (selectedUrls != null && selectedUrls.isNotEmpty) {
changeUserInput(selectedUrls[0], true, false, updateUrlInput: true);
var sourceName = res[selectedUrls[0]]?.key;
changeUserInput(selectedUrls[0], true, false,
updateUrlInput: true, overrideSource: sourceName);
}
}
} catch (e) {
@ -361,8 +399,9 @@ class AddAppPageState extends State<AddAppPage> {
[
GeneratedFormDropdown(
'overrideSource',
defaultValue: HTML().runtimeType.toString(),
defaultValue: pickedSourceOverride ?? '',
[
MapEntry('', tr('none')),
...sourceProvider.sources.map(
(s) => MapEntry(s.runtimeType.toString(), s.name))
],
@ -530,7 +569,20 @@ class AddAppPageState extends State<AddAppPage> {
? TextDecoration.underline
: TextDecoration.none),
))),
)
),
const SizedBox(
height: 16,
),
Text(
'${tr('note')}:',
style:
const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 4,
),
Text(tr('selfHostedNote',
args: [tr('overrideSource')])),
],
);
},
@ -564,11 +616,7 @@ class AddAppPageState extends State<AddAppPage> {
const SizedBox(
height: 16,
),
if (pickedSourceOverride != null ||
(pickedSource != null &&
pickedSource.runtimeType.toString() ==
HTML().runtimeType.toString()))
getHTMLSourceOverrideDropdown(),
if (pickedSource != null) getHTMLSourceOverrideDropdown(),
if (shouldShowSearchBar()) getSearchBarRow(),
if (pickedSource != null)
FutureBuilder(

View File

@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
import 'package:obtainium/components/generated_form_modal.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/main.dart';
import 'package:obtainium/pages/apps.dart';
import 'package:obtainium/pages/settings.dart';
import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/settings_provider.dart';
@ -104,6 +105,11 @@ class _AppPageState extends State<AppPage> {
if (installedVersionIsEstimate) {
infoLines = '${tr('pseudoVersionInUse')}\n$infoLines';
}
if ((app?.app.apkUrls.length ?? 0) > 0) {
infoLines =
'$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,
@ -121,13 +127,26 @@ class _AppPageState extends State<AppPage> {
.textTheme
.bodyLarge!
.copyWith(fontWeight: FontWeight.bold)),
app?.app.releaseDate == null
? const SizedBox.shrink()
: Text(
app!.app.releaseDate.toString(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall,
),
changeLogFn != null || app?.app.releaseDate != null
? GestureDetector(
onTap: changeLogFn,
child: Text(
app?.app.releaseDate == null
? tr('changes')
: app!.app.releaseDate!.toLocal().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,
),
@ -139,6 +158,49 @@ class _AppPageState extends State<AppPage> {
textAlign: TextAlign.center,
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
),
if (app?.app.apkUrls.isNotEmpty == true ||
app?.app.otherAssetUrls.isNotEmpty == true)
GestureDetector(
onTap: app?.app == null || updating
? null
: () async {
try {
await appsProvider
.downloadAppAssets([app!.app.id], context);
} catch (e) {
showError(e, context);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: settingsProvider.highlightTouchTargets
? (Theme.of(context).brightness ==
Brightness.light
? Theme.of(context).primaryColor
: Theme.of(context).primaryColorLight)
.withAlpha(20)
: null),
padding: settingsProvider.highlightTouchTargets
? const EdgeInsetsDirectional.fromSTEB(12, 6, 12, 6)
: const EdgeInsetsDirectional.fromSTEB(0, 6, 0, 6),
margin:
const EdgeInsetsDirectional.fromSTEB(0, 6, 0, 0),
child: Text(
tr('downloadX',
args: [tr('releaseAsset').toLowerCase()]),
textAlign: TextAlign.center,
style:
Theme.of(context).textTheme.labelSmall!.copyWith(
decoration: TextDecoration.underline,
fontStyle: FontStyle.italic,
),
))
],
)),
const SizedBox(
height: 48,
),
@ -185,18 +247,26 @@ class _AppPageState extends State<AppPage> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 20),
app?.icon != null
? Row(mainAxisAlignment: MainAxisAlignment.center, children: [
GestureDetector(
child: Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
),
onTap: () => pm.openApp(app.app.id),
)
])
: Container(),
FutureBuilder(
future: appsProvider.updateAppIcon(app?.app.id),
builder: (ctx, val) {
return app?.icon != null
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: app == null
? null
: () => pm.openApp(app.app.id),
child: Image.memory(
app!.icon!,
height: 150,
gaplessPlayback: true,
),
)
])
: Container();
}),
const SizedBox(
height: 25,
),
@ -205,6 +275,12 @@ class _AppPageState extends State<AppPage> {
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.displayLarge,
),
Text(tr('byX', args: [app?.app.author ?? tr('unknown')]),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium),
const SizedBox(
height: 24,
),
GestureDetector(
onTap: () {
if (app?.app.url != null) {
@ -219,15 +295,12 @@ class _AppPageState extends State<AppPage> {
));
},
child: Text(
tr('byX', args: [app?.app.author ?? tr('unknown')]),
app?.app.url ?? '',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
style: Theme.of(context).textTheme.labelSmall!.copyWith(
decoration: TextDecoration.underline,
fontStyle: FontStyle.italic),
)),
const SizedBox(
height: 8,
),
Text(
app?.app.id ?? '',
textAlign: TextAlign.center,
@ -242,7 +315,7 @@ class _AppPageState extends State<AppPage> {
? WebViewWidget(
controller: WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(Theme.of(context).colorScheme.background)
..setBackgroundColor(Theme.of(context).colorScheme.surface)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
@ -354,6 +427,9 @@ class _AppPageState extends State<AppPage> {
!areDownloadsRunning
? () async {
try {
var successMessage = app?.app.installedVersion == null
? tr('installed')
: tr('appsUpdated');
HapticFeedback.heavyImpact();
var res = await appsProvider.downloadAndInstallLatestApps(
app?.app.id != null ? [app!.app.id] : [],
@ -361,7 +437,7 @@ class _AppPageState extends State<AppPage> {
);
if (res.isNotEmpty && !trackOnly) {
// ignore: use_build_context_synchronously
showMessage(tr('appsUpdated'), context);
showMessage(successMessage, context);
}
if (res.isNotEmpty && mounted) {
Navigator.of(context).pop();

View File

@ -26,6 +26,92 @@ class AppsPage extends StatefulWidget {
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> {
AppsFilter filter = AppsFilter();
final AppsFilter neutralFilter = AppsFilter();
@ -57,11 +143,14 @@ class AppsPageState extends State<AppsPage> {
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
late final ScrollController scrollController = ScrollController();
var sourceProvider = SourceProvider();
@override
Widget build(BuildContext context) {
var appsProvider = context.watch<AppsProvider>();
var settingsProvider = context.watch<SettingsProvider>();
var sourceProvider = SourceProvider();
var listedApps = appsProvider.getAppValues().toList();
refresh() {
@ -262,73 +351,17 @@ class AppsPageState extends State<AppsPage> {
.where((a) => selectedAppIds.contains(a.id))
.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() {
return [
if (listedApps.isEmpty)
SliverFillRemaining(
child: Center(
child: Text(
appsProvider.apps.isEmpty ? tr('noApps') : tr('noAppsForFilter'),
appsProvider.apps.isEmpty
? appsProvider.loadingApps
? tr('pleaseWait')
: tr('noApps')
: tr('noAppsForFilter'),
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
))),
@ -351,25 +384,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) {
return IconButton(
visualDensity: VisualDensity.compact,
@ -395,29 +409,36 @@ class AppsPageState extends State<AppsPage> {
}
getAppIcon(int appIndex) {
return listedApps[appIndex].icon != null
? Image.memory(
listedApps[appIndex].icon!,
gaplessPlayback: true,
)
: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.31),
child: Padding(
padding: const EdgeInsets.all(15),
child: Image(
image: const AssetImage(
'assets/graphics/icon_small.png'),
color: Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate,
gaplessPlayback: true,
),
)),
]);
return FutureBuilder(
future: appsProvider.updateAppIcon(listedApps[appIndex].app.id),
builder: (ctx, val) {
return listedApps[appIndex].icon != null
? Image.memory(
listedApps[appIndex].icon!,
gaplessPlayback: true,
)
: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationZ(0.31),
child: Padding(
padding: const EdgeInsets.all(15),
child: Image(
image: const AssetImage(
'assets/graphics/icon_small.png'),
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.white.withOpacity(0.4)
: Colors.white.withOpacity(0.3),
colorBlendMode: BlendMode.modulate,
gaplessPlayback: true,
),
)),
]);
});
}
getVersionText(int appIndex) {
@ -430,11 +451,11 @@ class AppsPageState extends State<AppsPage> {
? tr('changes')
: ''
: DateFormat('yyyy-MM-dd')
.format(listedApps[appIndex].app.releaseDate!);
.format(listedApps[appIndex].app.releaseDate!.toLocal());
}
getSingleAppHorizTile(int index) {
var showChangesFn = getChangeLogFn(index);
var showChangesFn = getChangeLogFn(context, listedApps[index].app);
var hasUpdate = listedApps[index].app.installedVersion != null &&
listedApps[index].app.installedVersion !=
listedApps[index].app.latestVersion;
@ -445,7 +466,7 @@ class AppsPageState extends State<AppsPage> {
hasUpdate ? getUpdateButton(index) : const SizedBox.shrink(),
hasUpdate
? const SizedBox(
width: 10,
width: 5,
)
: const SizedBox.shrink(),
GestureDetector(
@ -496,7 +517,7 @@ class AppsPageState extends State<AppsPage> {
);
var transparent =
Theme.of(context).colorScheme.background.withAlpha(0).value;
Theme.of(context).colorScheme.surface.withAlpha(0).value;
List<double> stops = [
...listedApps[index].app.categories.asMap().entries.map(
(e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
@ -847,69 +868,97 @@ class AppsPageState extends State<AppsPage> {
scrollable: true,
content: Padding(
padding: const EdgeInsets.only(top: 6),
child: Row(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
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),
'overrideSource': a.overrideSource
}))}\n\n';
}
Share.share(urls,
subject:
'Obtainium - ${tr('appsString')}');
},
child: Text(tr('shareAppConfigLinks'))),
const Divider(),
TextButton(
onPressed: selectedAppIds.isEmpty
? null
: () {
var exportJSON = jsonEncode(
appsProvider.generateExportJSON(
appIds: selectedApps
.map((e) => e.id)
.toList(),
overrideExportSettings: false));
XFile f = XFile.fromData(
Uint8List.fromList(
utf8.encode(exportJSON)),
mimeType: 'application/json',
name:
'${tr('obtainiumExportHyphenatedLowercase')}-${selectedApps.length}-${DateTime.now().millisecondsSinceEpoch}');
Share.shareXFiles([f]);
},
child: Text(
'${tr('share')} - ${tr('obtainiumExport')}')),
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()
? null
: showMassMarkDialog,
tooltip: 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 += '${a.url}\n';
}
urls = urls.substring(0, urls.length - 1);
Share.share(urls,
subject: 'Obtainium - ${tr('appsString')}');
Navigator.of(context).pop();
},
tooltip: tr('shareSelectedAppURLs'),
icon: const Icon(Icons.share_rounded),
),
IconButton(
onPressed: selectedAppIds.isEmpty
? null
: () {
String urls =
'<p>${tr('customLinkMessage')}:</p>\n\n<ul>\n';
for (var a in selectedApps) {
urls +=
' <li><a href="obtainium://app/${Uri.encodeComponent(jsonEncode({
'id': a.id,
'url': a.url,
'author': a.author,
'name': a.name,
'preferredApkIndex':
a.preferredApkIndex,
'additionalSettings':
jsonEncode(a.additionalSettings)
}))}">${a.name}</a></li>\n';
}
urls +=
'</ul>\n\n<p><a href="$obtainiumUrl">${tr('about')}</a></p>';
Share.share(urls,
subject: 'Obtainium - ${tr('appsString')}');
},
tooltip: tr('shareAppConfigLinks'),
icon: const Icon(Icons.ios_share),
),
child: Text(tr('markSelectedAppsUpdated'))),
]),
),
);
@ -1028,7 +1077,9 @@ class AppsPageState extends State<AppsPage> {
IconButton(
color: Theme.of(context).colorScheme.primary,
style: const ButtonStyle(visualDensity: VisualDensity.compact),
tooltip: isFilterOff ? tr('filter') : tr('filterActive'),
tooltip: isFilterOff
? tr('filterApps')
: '${tr('filter')} - ${tr('remove')}',
onPressed: isFilterOff
? showFilterDialog
: () {
@ -1037,8 +1088,8 @@ class AppsPageState extends State<AppsPage> {
});
},
icon: Icon(isFilterOff
? Icons.filter_list_rounded
: Icons.filter_list_off_rounded)),
? Icons.search_rounded
: Icons.search_off_rounded)),
const SizedBox(
width: 10,
),
@ -1073,11 +1124,17 @@ class AppsPageState extends State<AppsPage> {
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: refresh,
child: CustomScrollView(slivers: <Widget>[
CustomAppBar(title: tr('appsString')),
...getLoadingWidgets(),
getDisplayedList()
])),
child: Scrollbar(
interactive: true,
controller: scrollController,
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: scrollController,
slivers: <Widget>[
CustomAppBar(title: tr('appsString')),
...getLoadingWidgets(),
getDisplayedList()
]))),
persistentFooterButtons: appsProvider.apps.isEmpty
? null
: [

View File

@ -13,6 +13,7 @@ import 'package:obtainium/pages/import_export.dart';
import 'package:obtainium/pages/settings.dart';
import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
@ -102,13 +103,22 @@ class _HomePageState extends State<HomePage> {
}) !=
null) {
// ignore: use_build_context_synchronously
var result = await context.read<AppsProvider>().import(
action == 'app'
? '{ "apps": [$dataStr] }'
: '{ "apps": $dataStr }');
var appsProvider = context.read<AppsProvider>();
var result = await appsProvider.import(action == 'app'
? '{ "apps": [$dataStr] }'
: '{ "apps": $dataStr }');
// ignore: use_build_context_synchronously
showMessage(
tr('importedX', args: [plural('apps', result.key)]), context);
tr('importedX', args: [plural('apps', result.key.length)]),
context);
await appsProvider
.checkUpdates(specificIds: result.key.map((e) => e.id).toList())
.catchError((e) {
if (e is Map && e['errors'] is MultiAppMultiError) {
showError(e['errors'].toString(), context);
}
return <App>[];
});
}
} else {
throw ObtainiumError(tr('unknown'));
@ -119,7 +129,7 @@ class _HomePageState extends State<HomePage> {
}
// Check initial link if app was in cold state (terminated)
final appLink = await _appLinks.getInitialAppLink();
final appLink = await _appLinks.getInitialLink();
if (appLink != null) {
await interpretLink(appLink);
}

View File

@ -33,7 +33,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
var settingsProvider = context.watch<SettingsProvider>();
var outlineButtonStyle = ButtonStyle(
shape: MaterialStateProperty.all(
shape: WidgetStateProperty.all(
StadiumBorder(
side: BorderSide(
width: 1,
@ -144,7 +144,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
appsProvider.addMissingCategories(settingsProvider);
showMessage(
'${tr('importedX', args: [
plural('apps', value.key)
plural('apps', value.key.length)
])}${value.value ? ' + ${tr('settings')}' : ''}',
context);
});
@ -213,7 +213,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
setState(() {
importInProgress = true;
});
if (values['url'] != source.hosts[0]) {
if (source.hosts.isEmpty || values['url'] != source.hosts[0]) {
source = sourceProvider.getSource(values['url'],
overrideSource: source.runtimeType.toString());
}

View File

@ -1,8 +1,11 @@
import 'package:device_info_plus/device_info_plus.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:obtainium/components/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/main.dart';
import 'package:obtainium/providers/apps_provider.dart';
@ -12,6 +15,7 @@ import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.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';
class SettingsPage extends StatefulWidget {
@ -22,78 +26,184 @@ class SettingsPage extends StatefulWidget {
}
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
Widget build(BuildContext context) {
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
SourceProvider sourceProvider = SourceProvider();
if (settingsProvider.prefs == null) {
settingsProvider.initializeSettings();
if (settingsProvider.prefs == null) 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(
decoration: InputDecoration(labelText: tr('installMethod')),
value: settingsProvider.installMethod,
items: [
DropdownMenuItem(
value: InstallMethodSettings.normal,
child: Text(tr('normal')),
),
const DropdownMenuItem(
value: InstallMethodSettings.shizuku,
child: Text('Shizuku'),
),
DropdownMenuItem(
value: InstallMethodSettings.root,
child: Text(tr('root')),
)
],
onChanged: (value) {
if (value != null) {
settingsProvider.installMethod = value;
}
});
var colorPicker = ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
title: Text(tr('selectX', args: [tr('colour')])),
subtitle: Text(
"${ColorTools.nameThatColor(settingsProvider.themeColor)} "
"(${ColorTools.materialNameAndCode(settingsProvider.themeColor, colorSwatchNameMap: colorsNameMap)})"),
trailing: ColorIndicator(
width: 40,
height: 40,
borderRadius: 20,
color: settingsProvider.themeColor,
onSelectFocus: false,
onSelect: () async {
final Color colorBeforeDialog = settingsProvider.themeColor;
if (!(await colorPickerDialog())) {
setState(() {
settingsProvider.themeColor = colorBeforeDialog;
});
}
}));
var themeDropdown = DropdownButtonFormField(
decoration: InputDecoration(labelText: tr('theme')),
value: settingsProvider.theme,
items: [
DropdownMenuItem(
value: ThemeSettings.dark,
child: Text(tr('dark')),
),
DropdownMenuItem(
value: ThemeSettings.light,
child: Text(tr('light')),
),
DropdownMenuItem(
value: ThemeSettings.system,
child: Text(tr('followSystem')),
)
],
onChanged: (value) {
if (value != null) {
settingsProvider.theme = value;
}
});
var colourDropdown = DropdownButtonFormField(
decoration: InputDecoration(labelText: tr('colour')),
value: settingsProvider.colour,
items: const [
DropdownMenuItem(
value: ColourSettings.basic,
child: Text('Obtainium'),
),
DropdownMenuItem(
value: ColourSettings.materialYou,
child: Text('Material You'),
)
],
onChanged: (value) {
if (value != null) {
settingsProvider.colour = value;
}
});
var useMaterialThemeSwitch = FutureBuilder(
builder: (ctx, val) {
return ((val.data?.version.sdkInt ?? 0) >= 31)
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(tr('useMaterialYou'))),
Switch(
value: settingsProvider.useMaterialYou,
onChanged: (value) {
settingsProvider.useMaterialYou = value;
})
],
)
: const SizedBox.shrink();
},
future: DeviceInfoPlugin().androidInfo);
var sortDropdown = DropdownButtonFormField(
isExpanded: true,
@ -165,30 +275,29 @@ class _SettingsPageState extends State<SettingsPage> {
}
});
var intervalDropdown = DropdownButtonFormField(
decoration: InputDecoration(labelText: tr('bgUpdateCheckInterval')),
value: settingsProvider.updateInterval,
items: updateIntervals.map((e) {
int displayNum = (e < 60
? e
: e < 1440
? e / 60
: e / 1440)
.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;
}
var intervalSlider = Slider(
value: settingsProvider.updateIntervalSliderVal,
max: updateIntervalNodes.length.toDouble(),
divisions: updateIntervalNodes.length * 20,
label: updateIntervalLabel,
onChanged: (double value) {
setState(() {
settingsProvider.updateIntervalSliderVal = value;
processIntervalSliderValue(value);
});
},
onChangeStart: (double value) {
setState(() {
showIntervalLabel = false;
});
},
onChangeEnd: (double value) {
setState(() {
showIntervalLabel = true;
settingsProvider.updateInterval = updateInterval;
});
},
);
var sourceSpecificFields = sourceProvider.sources.map((e) {
if (e.sourceConfigSettingFormItems.isNotEmpty) {
@ -239,15 +348,26 @@ class _SettingsPageState extends State<SettingsPage> {
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary),
),
intervalDropdown,
//intervalDropdown,
height16,
if (showIntervalLabel)
SizedBox(
child: Text(
"${tr('bgUpdateCheckInterval')}: $updateIntervalLabel"))
else
const SizedBox(height: 16),
intervalSlider,
FutureBuilder(
builder: (ctx, val) {
return (val.data?.version.sdkInt ?? 0) >= 30
return (settingsProvider.updateInterval >
0) &&
(((val.data?.version.sdkInt ?? 0) >=
30) ||
settingsProvider.useShizuku)
? Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
height16,
Row(
mainAxisAlignment:
MainAxisAlignment
@ -351,6 +471,22 @@ class _SettingsPageState extends State<SettingsPage> {
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child:
Text(tr('removeOnExternalUninstall'))),
Switch(
value: settingsProvider
.removeOnExternalUninstall,
onChanged: (value) {
settingsProvider
.removeOnExternalUninstall = value;
})
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -363,7 +499,85 @@ 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,
Text(
tr('sourceSpecific'),
@ -379,21 +593,49 @@ class _SettingsPageState extends State<SettingsPage> {
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary),
),
themeDropdown,
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(tr('useBlackTheme'))),
Switch(
value: settingsProvider.useBlackTheme,
onChanged: (value) {
settingsProvider.useBlackTheme = value;
})
],
),
colourDropdown,
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,
if (settingsProvider.theme != ThemeSettings.light)
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(tr('useBlackTheme'))),
Switch(
value: settingsProvider.useBlackTheme,
onChanged: (value) {
settingsProvider.useBlackTheme =
value;
})
]),
height8,
useMaterialThemeSwitch,
if (!settingsProvider.useMaterialYou) colorPicker,
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
@ -407,34 +649,46 @@ class _SettingsPageState extends State<SettingsPage> {
),
height16,
localeDropdown,
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(tr('useSystemFont'))),
Switch(
value: settingsProvider.useSystemFont,
onChanged: (useSystemFont) {
if (useSystemFont) {
NativeFeatures.loadSystemFont()
.then((fontLoadRes) {
if (fontLoadRes == 'ok') {
settingsProvider.useSystemFont =
true;
} else {
showError(
ObtainiumError(tr(
'systemFontError',
args: [fontLoadRes])),
context);
}
});
} else {
settingsProvider.useSystemFont = false;
}
})
],
),
FutureBuilder(
builder: (ctx, val) {
return (val.data?.version.sdkInt ?? 0) >= 34
? Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
height16,
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Flexible(
child: Text(tr(
'useSystemFont'))),
Switch(
value: settingsProvider
.useSystemFont,
onChanged:
(useSystemFont) {
if (useSystemFont) {
NativeFeatures
.loadSystemFont()
.then((val) {
settingsProvider
.useSystemFont =
true;
});
} else {
settingsProvider
.useSystemFont =
false;
}
})
])
])
: const SizedBox.shrink();
},
future: DeviceInfoPlugin().androidInfo),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -474,22 +728,6 @@ class _SettingsPageState extends State<SettingsPage> {
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child:
Text(tr('removeOnExternalUninstall'))),
Switch(
value: settingsProvider
.removeOnExternalUninstall,
onChanged: (value) {
settingsProvider
.removeOnExternalUninstall = value;
})
],
),
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -603,17 +841,31 @@ class _SettingsPageState extends State<SettingsPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextButton.icon(
IconButton(
onPressed: () {
launchUrlString(settingsProvider.sourceUrl,
mode: LaunchMode.externalApplication);
},
icon: const Icon(Icons.code),
label: Text(
tr('appSource'),
),
tooltip: tr('appSource'),
),
TextButton.icon(
IconButton(
onPressed: () {
launchUrlString('${settingsProvider.sourceUrl}/wiki',
mode: LaunchMode.externalApplication);
},
icon: const Icon(Icons.help_outline_rounded),
tooltip: tr('wiki'),
),
IconButton(
onPressed: () {
launchUrlString('https://apps.obtainium.imranr.dev/',
mode: LaunchMode.externalApplication);
},
icon: const Icon(Icons.apps_rounded),
tooltip: tr('crowdsourcedConfigsLabel'),
),
IconButton(
onPressed: () {
context.read<LogsProvider>().get().then((logs) {
if (logs.isEmpty) {
@ -629,7 +881,7 @@ class _SettingsPageState extends State<SettingsPage> {
});
},
icon: const Icon(Icons.bug_report_outlined),
label: Text(tr('appLogs'))),
tooltip: tr('appLogs'))
],
),
const SizedBox(
@ -694,6 +946,25 @@ class _LogsDialogState extends State<LogsDialog> {
],
),
actions: [
TextButton(
onPressed: () async {
var cont = (await showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: tr('appLogs'),
items: const [],
initValid: true,
message: tr('removeFromObtainium'),
);
})) !=
null;
if (cont) {
logsProvider.clear();
Navigator.of(context).pop();
}
},
child: Text(tr('remove'))),
TextButton(
onPressed: () {
Navigator.of(context).pop();

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +1,22 @@
import 'dart:async';
import 'dart:io';
import 'package:android_system_font/android_system_font.dart';
import 'package:flutter/services.dart';
class NativeFeatures {
static const MethodChannel _channel = MethodChannel('native');
static bool _systemFontLoaded = false;
static bool _callbacksApplied = false;
static int _resPermShizuku = -2; // not set
static Future<ByteData> _readFileBytes(String path) async {
var file = File(path);
var bytes = await file.readAsBytes();
var bytes = await File(path).readAsBytes();
return ByteData.view(bytes.buffer);
}
static Future _handleCalls(MethodCall call) async {
if (call.method == 'resPermShizuku') {
_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
static Future loadSystemFont() async {
if (_systemFontLoaded) return;
var fontLoader = FontLoader('SystemFont');
fontLoader.addFont(_readFileBytes(getFontRes));
await fontLoader.load();
var fontFilePath = await AndroidSystemFont().getFilePath();
fontLoader.addFont(_readFileBytes(fontFilePath!));
fontLoader.load();
_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 {
SilentUpdateNotification(List<App> updates, {int? id})
SilentUpdateNotification(List<App> updates, bool succeeded, {int? id})
: super(
id ?? 3,
tr('appsUpdated'),
succeeded
? tr('appsUpdated')
: tr('appsNotUpdated'),
'',
'APPS_UPDATED',
tr('appsUpdatedNotifChannel'),
tr('appsUpdatedNotifDescription'),
Importance.defaultImportance) {
message = updates.length == 1
? tr('xWasUpdatedToY',
args: [updates[0].finalName, updates[0].latestVersion])
: plural('xAndNMoreUpdatesInstalled', updates.length - 1,
args: [updates[0].finalName, (updates.length - 1).toString()]);
? tr(succeeded
? 'xWasUpdatedToY'
: 'xWasNotUpdatedToY',
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);
}
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(
1,
tr('completeAppInstallation'),

View File

@ -17,27 +17,14 @@ import 'package:shared_storage/shared_storage.dart' as saf;
String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}';
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 ColourSettings { basic, materialYou }
enum SortColumnSettings { added, nameAuthor, authorName, releaseDate }
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 {
SharedPreferences? prefs;
String? defaultAppDir;
@ -61,13 +48,12 @@ class SettingsProvider with ChangeNotifier {
notifyListeners();
}
InstallMethodSettings get installMethod {
return InstallMethodSettings.values[
prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];
bool get useShizuku{
return prefs?.getBool('useShizuku') ?? false;
}
set installMethod(InstallMethodSettings t) {
prefs?.setInt('installMethod', t.index);
set useShizuku(bool useShizuku) {
prefs?.setBool('useShizuku', useShizuku);
notifyListeners();
}
@ -81,13 +67,23 @@ class SettingsProvider with ChangeNotifier {
notifyListeners();
}
ColourSettings get colour {
return ColourSettings
.values[prefs?.getInt('colour') ?? ColourSettings.basic.index];
Color get themeColor {
int? colorCode = prefs?.getInt('themeColor');
return (colorCode != null) ?
Color(colorCode) : obtainiumThemeColor;
}
set colour(ColourSettings t) {
prefs?.setInt('colour', t.index);
set themeColor(Color themeColor) {
prefs?.setInt('themeColor', themeColor.value);
notifyListeners();
}
bool get useMaterialYou {
return prefs?.getBool('useMaterialYou') ?? false;
}
set useMaterialYou(bool useMaterialYou) {
prefs?.setBool('useMaterialYou', useMaterialYou);
notifyListeners();
}
@ -101,21 +97,20 @@ class SettingsProvider with ChangeNotifier {
}
int get updateInterval {
var min = 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;
return prefs?.getInt('updateInterval') ?? 360;
}
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();
}
@ -465,4 +460,13 @@ class SettingsProvider with ChangeNotifier {
prefs?.setStringList('searchDeselected', list);
notifyListeners();
}
bool get beforeNewInstallsShareToAppVerifier {
return prefs?.getBool('beforeNewInstallsShareToAppVerifier') ?? true;
}
set beforeNewInstallsShareToAppVerifier(bool val) {
prefs?.setBool('beforeNewInstallsShareToAppVerifier', val);
notifyListeners();
}
}

View File

@ -47,9 +47,10 @@ class APKDetails {
late AppNames names;
late DateTime? releaseDate;
late String? changeLog;
late List<MapEntry<String, String>> allAssetUrls;
APKDetails(this.version, this.apkUrls, this.names,
{this.releaseDate, this.changeLog});
{this.releaseDate, this.changeLog, this.allAssetUrls = const []});
}
stringMapListTo2DList(List<MapEntry<String, String>> mapList) =>
@ -223,6 +224,7 @@ class App {
String? installedVersion;
late String latestVersion;
List<MapEntry<String, String>> apkUrls = [];
List<MapEntry<String, String>> otherAssetUrls = [];
late int preferredApkIndex;
late Map<String, dynamic> additionalSettings;
late DateTime? lastUpdateCheck;
@ -248,7 +250,8 @@ class App {
this.releaseDate,
this.changeLog,
this.overrideSource,
this.allowIdChange = false});
this.allowIdChange = false,
this.otherAssetUrls = const []});
@override
String toString() {
@ -280,41 +283,44 @@ class App {
changeLog: changeLog,
releaseDate: releaseDate,
overrideSource: overrideSource,
allowIdChange: allowIdChange);
allowIdChange: allowIdChange,
otherAssetUrls: otherAssetUrls);
factory App.fromJson(Map<String, dynamic> json) {
json = appJSONCompatibilityModifiers(json);
return App(
json['id'] as String,
json['url'] as String,
json['author'] as String,
json['name'] as String,
json['installedVersion'] == null
? null
: json['installedVersion'] as String,
(json['latestVersion'] ?? tr('unknown')) as String,
assumed2DlistToStringMapList(jsonDecode(
(json['apkUrls'] ?? '[["placeholder", "placeholder"]]'))),
(json['preferredApkIndex'] ?? -1) as int,
jsonDecode(json['additionalSettings']) as Map<String, dynamic>,
json['lastUpdateCheck'] == null
? null
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
json['pinned'] ?? false,
categories: json['categories'] != null
? (json['categories'] as List<dynamic>)
.map((e) => e.toString())
.toList()
: json['category'] != null
? [json['category'] as String]
: [],
releaseDate: json['releaseDate'] == null
? null
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
changeLog:
json['changeLog'] == null ? null : json['changeLog'] as String,
overrideSource: json['overrideSource'],
allowIdChange: json['allowIdChange'] ?? false);
json['id'] as String,
json['url'] as String,
json['author'] as String,
json['name'] as String,
json['installedVersion'] == null
? null
: json['installedVersion'] as String,
(json['latestVersion'] ?? tr('unknown')) as String,
assumed2DlistToStringMapList(
jsonDecode((json['apkUrls'] ?? '[["placeholder", "placeholder"]]'))),
(json['preferredApkIndex'] ?? -1) as int,
jsonDecode(json['additionalSettings']) as Map<String, dynamic>,
json['lastUpdateCheck'] == null
? null
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
json['pinned'] ?? false,
categories: json['categories'] != null
? (json['categories'] as List<dynamic>)
.map((e) => e.toString())
.toList()
: json['category'] != null
? [json['category'] as String]
: [],
releaseDate: json['releaseDate'] == null
? null
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
changeLog: json['changeLog'] == null ? null : json['changeLog'] as String,
overrideSource: json['overrideSource'],
allowIdChange: json['allowIdChange'] ?? false,
otherAssetUrls: assumed2DlistToStringMapList(
jsonDecode((json['otherAssetUrls'] ?? '[]'))),
);
}
Map<String, dynamic> toJson() => {
@ -325,6 +331,7 @@ class App {
'installedVersion': installedVersion,
'latestVersion': latestVersion,
'apkUrls': jsonEncode(stringMapListTo2DList(apkUrls)),
'otherAssetUrls': jsonEncode(stringMapListTo2DList(otherAssetUrls)),
'preferredApkIndex': preferredApkIndex,
'additionalSettings': jsonEncode(additionalSettings),
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
@ -347,11 +354,17 @@ preStandardizeUrl(String url) {
url.toLowerCase().indexOf('https://') != 0) {
url = 'https://$url';
}
var uri = Uri.tryParse(url);
var trailingSlash = ((uri?.path.endsWith('/') ?? false) ||
((uri?.path.isEmpty ?? false) && url.endsWith('/'))) &&
(uri?.queryParameters.isEmpty ?? false);
url = url
.split('/')
.where((e) => e.isNotEmpty)
.join('/')
.replaceFirst(':/', '://');
.split('/')
.where((e) => e.isNotEmpty)
.join('/')
.replaceFirst(':/', '://') +
(trailingSlash ? '/' : '');
return url;
}
@ -399,6 +412,7 @@ abstract class AppSource {
bool showReleaseDateAsVersionToggle = false;
bool versionDetectionDisallowed = false;
List<String> excludeCommonSettingKeys = [];
bool urlsAlwaysHaveExtension = false;
AppSource() {
name = runtimeType.toString();
@ -454,6 +468,10 @@ abstract class AppSource {
}
}
void runOnAddAppInputChange(String inputUrl) {
//
}
String sourceSpecificStandardizeURL(String url) {
throw NotImplementedError();
}
@ -478,13 +496,15 @@ abstract class AppSource {
],
[
GeneratedFormTextField('versionExtractionRegEx',
label: tr('versionExtractionRegEx'),
label: tr('trimVersionString'),
required: false,
additionalValidators: [(value) => regExValidator(value)]),
],
[
GeneratedFormTextField('matchGroupToUse',
label: tr('matchGroupToUse'), required: false, hint: '\$0')
label: tr('matchGroupToUseForX', args: [tr('trimVersionString')]),
required: false,
hint: '\$0')
],
[
GeneratedFormSwitch('versionDetection',
@ -514,6 +534,10 @@ abstract class AppSource {
label: tr('autoApkFilterByArch'), defaultValue: true)
],
[GeneratedFormTextField('appName', label: tr('appName'), required: false)],
[
GeneratedFormSwitch('shizukuPretendToBeGooglePlay',
label: tr('shizukuPretendToBeGooglePlay'), defaultValue: false)
],
[
GeneratedFormSwitch('exemptFromBackgroundUpdates',
label: tr('exemptFromBackgroundUpdates'))
@ -604,7 +628,7 @@ abstract class AppSource {
}
bool canSearch = false;
bool excludeFromMassSearch = false;
bool includeAdditionalOptsInMainSearch = false;
List<GeneratedFormItem> searchQuerySettingFormItems = [];
Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) {
@ -736,14 +760,13 @@ class SourceProvider {
FDroid(),
FDroidRepo(),
IzzyOnDroid(),
SourceForge(),
SourceHut(),
APKPure(),
Aptoide(),
Uptodown(),
APKMirror(),
HuaweiAppGallery(),
Jenkins(),
APKMirror(),
Signal(),
VLC(),
WhatsApp(),
@ -819,7 +842,7 @@ class SourceProvider {
AppSource source, String url, Map<String, dynamic> additionalSettings,
{App? currentApp,
bool trackOnlyOverride = false,
String? overrideSource,
bool sourceIsOverriden = false,
bool inferAppIdIfOptional = false}) async {
if (trackOnlyOverride || source.enforceTrackOnly) {
additionalSettings['trackOnly'] = true;
@ -829,8 +852,9 @@ class SourceProvider {
APKDetails apk =
await source.getLatestAPKDetails(standardUrl, additionalSettings);
if (source.runtimeType != HTML().runtimeType) {
// HTML does it separately
if (source.runtimeType !=
HTML().runtimeType && // Some sources do it separately
source.runtimeType != SourceForge().runtimeType) {
String? extractedVersion = extractVersion(
additionalSettings['versionExtractionRegEx'] as String?,
additionalSettings['matchGroupToUse'] as String?,
@ -886,12 +910,16 @@ class SourceProvider {
categories: currentApp?.categories ?? const [],
releaseDate: apk.releaseDate,
changeLog: apk.changeLog,
overrideSource: overrideSource ?? currentApp?.overrideSource,
overrideSource: sourceIsOverriden
? source.runtimeType.toString()
: currentApp?.overrideSource,
allowIdChange: currentApp?.allowIdChange ??
trackOnly ||
(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);
}
@ -910,6 +938,7 @@ class SourceProvider {
apps.add(await getApp(
source,
url,
sourceIsOverriden: sourceOverride != null,
getDefaultValuesFromFormItems(
source.combinedAppSpecificSettingFormItems)));
} catch (e) {

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
name: obtainium
description: Get Android App Updates Directly From the Source.
description: Get Android app updates straight from the source.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+250 # When changing this, update the tag in main() accordingly
version: 1.1.20+2277
environment:
sdk: '>=3.0.0 <4.0.0'
@ -37,8 +37,8 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.5
path_provider: ^2.0.11
flutter_fgbg: ^0.3.0 # Try removing reliance on this
flutter_local_notifications: ^16.1.0
flutter_fgbg: ^0.6.0
flutter_local_notifications: ^17.0.0
provider: ^6.0.3
http: ^1.0.0
webview_flutter: ^4.0.0
@ -48,27 +48,39 @@ dependencies:
url_launcher: ^6.1.5
permission_handler: ^11.0.0
fluttertoast: ^8.0.9
device_info_plus: ^9.0.0
file_picker: ^6.0.0
device_info_plus: ^10.0.1
file_picker: ^8.0.0+1
animations: ^2.0.4
android_package_installer:
git:
url: https://github.com/ImranR98/android_package_installer
ref: main
android_package_manager: ^0.7.0
share_plus: ^7.0.0
share_plus: ^10.0.0
sqflite: ^2.2.0+3
easy_localization: ^3.0.1
android_intent_plus: ^4.0.0
flutter_markdown: ^0.6.14
android_intent_plus: ^5.0.1
flutter_markdown: ^0.7.1
flutter_archive: ^6.0.0
hsluv: ^1.1.3
connectivity_plus: ^5.0.0
connectivity_plus: ^6.0.1
shared_storage: ^0.8.0
crypto: ^3.0.3
app_links: ^3.5.0
app_links: ^6.0.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
markdown: any
flutter_typeahead: ^5.2.0
dev_dependencies:
flutter_test:
sdk: flutter
@ -79,7 +91,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
flutter_lints: ^4.0.0
flutter_launcher_icons:
android: "ic_launcher"
@ -133,6 +145,6 @@ flutter:
# see https://flutter.dev/custom-fonts/#from-packages
fonts:
- family: Metropolis
- family: Wix-Madefor-Display
fonts:
- asset: assets/fonts/Metropolis-Regular.otf
- asset: assets/fonts/WixMadeforDisplay-Regular.otf