Compare commits

..

613 Commits
7.0.0 ... 7.3.0

Author SHA1 Message Date
Athou
c7c95efb3a release 7.3.0 2026-08-11 08:17:05 +02:00
Athou
4c1293d357 change the default value of blockLocalAddresses 2026-08-11 08:07:56 +02:00
Athou
76a92ea44c more SSRF protection regarding IPv6 2026-08-11 07:46:02 +02:00
Jérémie Panzer
ba454322db Merge pull request #2263 from Athou/renovate/org.apache.commons-commons-collections4-4.x
fix(deps): update dependency org.apache.commons:commons-collections4 to v4.6.0
2026-08-10 16:55:55 +02:00
renovate[bot]
a3b9f471e1 fix(deps): update dependency org.apache.commons:commons-collections4 to v4.6.0 2026-08-10 13:47:04 +00:00
Athou
d58ddc888e fix flaky tests 2026-08-10 09:45:00 +02:00
Athou
fe9837c45c fix typo 2026-08-10 09:30:31 +02:00
Athou
332d126bd7 reduce log verbosity during tests 2026-08-10 09:08:15 +02:00
Athou
0c66bf2695 speed up tests 2026-08-10 09:05:11 +02:00
Athou
4d6d07c98b remove warning 2026-08-10 09:02:07 +02:00
renovate[bot]
8f7d4bfa10 chore(deps): lock file maintenance 2026-08-10 05:05:39 +00:00
renovate[bot]
7b3f39001f fix(deps): update react-querybuilder to v8.22.3 2026-08-10 03:27:17 +00:00
Jérémie Panzer
b9630809a0 Merge pull request #2262 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.10.0
2026-08-10 05:26:27 +02:00
renovate[bot]
ce3244da5e chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.10.0 2026-08-09 21:51:02 +00:00
renovate[bot]
4067297d2a chore(deps): update dependency vite to v8.2.1 2026-08-09 17:40:42 +00:00
renovate[bot]
03c11fa181 fix(deps): update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.4 2026-08-09 09:42:31 +00:00
renovate[bot]
3676e78eb9 fix(deps): update react-querybuilder to v8.22.2 2026-08-08 16:44:38 +00:00
Athou
1e08c4486f fix google reader api icon url 2026-08-08 16:06:47 +02:00
Athou
fd8bb60daa google reader api support 2026-08-08 10:22:58 +02:00
renovate[bot]
557d56ffc5 fix(deps): update react-querybuilder to v8.22.1 2026-08-08 04:30:28 +08:00
renovate[bot]
30f76a2349 chore(deps): update dependency @biomejs/biome to v2.5.7 2026-08-07 15:07:29 +00:00
renovate[bot]
cf0fa5e22e chore(deps): update dependency @testing-library/user-event to v14.6.3 2026-08-07 04:00:58 +00:00
Jérémie Panzer
f16becbd82 Merge pull request #2259 from Athou/renovate/graalvm-setup-graalvm-1.6.x
chore(deps): update graalvm/setup-graalvm action to v1.6.4
2026-08-07 06:00:37 +02:00
Jérémie Panzer
4855450ddc Merge pull request #2260 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.22.0
2026-08-07 06:00:33 +02:00
Jérémie Panzer
0733aedce5 Merge pull request #2257 from Athou/renovate/lock-file-maintenance
chore(deps): lock file maintenance
2026-08-07 06:00:25 +02:00
renovate[bot]
399ad14d9b chore(deps): lock file maintenance 2026-08-07 02:59:35 +00:00
renovate[bot]
7881ea3c5c fix(deps): update react-querybuilder to v8.22.0 2026-08-07 02:58:54 +00:00
renovate[bot]
7e9de622da chore(deps): update graalvm/setup-graalvm action to v1.6.4 2026-08-07 02:58:46 +00:00
Athou
c6655078f2 fix build 2026-08-07 04:58:06 +02:00
Athou
b86cef9499 package-lock refresh 2026-08-06 21:47:31 +02:00
Athou
5375ab6e97 profile menu now works again on mobile when action buttons are at the bottom of the screen 2026-08-06 21:46:47 +02:00
renovate[bot]
75920a6473 fix(deps): update mantine monorepo to v9.5.1 2026-08-05 08:38:05 +00:00
renovate[bot]
f474e76866 chore(deps): update debian:13.6 docker digest to 34cd9e9 2026-08-05 04:50:23 +00:00
renovate[bot]
d0ae27c4ce fix(deps): update quarkus.version to v3.38.1 2026-08-04 17:07:29 +00:00
renovate[bot]
ab191dded6 chore(deps): update ibm-semeru-runtimes:open-jdk-25.0.4.0-jre docker digest to 02888a2 2026-08-04 10:32:11 +00:00
renovate[bot]
950b571a9f chore(deps): update ibm-semeru-runtimes docker tag to open-jdk-25.0.4.0-jre 2026-08-04 04:59:45 +00:00
Jérémie Panzer
7a153ca69a Merge pull request #2258 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.19.0
2026-08-03 19:08:30 +02:00
renovate[bot]
effd8946de chore(deps): update node.js to v24.19.0 2026-08-03 16:42:20 +00:00
Athou
ae3ad3745c move global System.setProperty calls in CommaFeedApplication 2026-08-03 09:15:23 +02:00
Athou
2e1e5257b4 release 7.2.1 2026-08-03 09:06:32 +02:00
Athou
bc6e767953 prevent XXS in third party apps
CommaFeed React client already strips malicious "javascript:" urls but we might as well not store those in the database
2026-08-03 08:47:01 +02:00
Jérémie Panzer
b9eceaae73 Merge pull request #2255 from Athou/renovate/vite-8.x
chore(deps): update dependency vite to v8.2.0
2026-08-03 08:46:22 +02:00
renovate[bot]
8ad9ba7d74 chore(deps): update react monorepo 2026-08-03 01:41:07 +00:00
renovate[bot]
10bb4b7936 chore(deps): update dependency vite to v8.2.0 2026-08-02 12:46:43 +00:00
renovate[bot]
ab3127cef9 chore(deps): update dependency @vitejs/plugin-react to v6.0.5 2026-08-02 12:46:33 +00:00
Athou
5d64e2f21e correctly handle root favicons for non-default ports 2026-08-02 07:26:44 +02:00
renovate[bot]
e404c9ce3b chore(deps): update dependency npm to v12.0.2 2026-08-01 22:16:06 +00:00
Jérémie Panzer
0c5a71650c Merge pull request #2254 from Athou/renovate/axios-1.x
fix(deps): update dependency axios to v1.19.0
2026-08-02 00:15:20 +02:00
renovate[bot]
8ce9eb8537 fix(deps): update dependency axios to v1.19.0 2026-08-01 20:46:05 +00:00
renovate[bot]
1ce0e4af97 chore(deps): update dependency jsdom to v30.0.1 2026-08-01 10:11:03 +00:00
Jérémie Panzer
44ad42a8a5 Merge pull request #2252 from Athou/renovate/patch-react-router-monorepo
fix(deps): update dependency react-router-dom to v7.18.2
2026-08-01 12:09:31 +02:00
Jérémie Panzer
f213726c40 Merge pull request #2251 from Athou/renovate/react-draggable-4.7.x
fix(deps): update dependency react-draggable to v4.7.1
2026-08-01 12:09:08 +02:00
renovate[bot]
972d424846 fix(deps): update dependency react-router-dom to v7.18.2 2026-08-01 09:19:12 +00:00
renovate[bot]
6049263137 fix(deps): update dependency react-draggable to v4.7.1 2026-08-01 09:19:02 +00:00
Athou
ae2dc7f066 update google java format and let renovate know about it 2026-08-01 11:17:50 +02:00
Athou
503d129f22 increase timeout even more because tests are still flaky 2026-08-01 11:13:41 +02:00
renovate[bot]
543ec3bbb1 fix(deps): update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.3 2026-07-31 17:48:01 +00:00
renovate[bot]
bed45127c4 chore(deps): update dependency @biomejs/biome to v2.5.6 2026-07-31 15:42:20 +00:00
Jérémie Panzer
71f10b0a62 Merge pull request #2250 from Athou/renovate/patch-react-querybuilder
fix(deps): update react-querybuilder to v8.21.2
2026-07-31 07:42:57 +02:00
renovate[bot]
cb235289c4 fix(deps): update react-querybuilder to v8.21.2 2026-07-31 03:05:02 +00:00
renovate[bot]
0b07c65320 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.8 2026-07-30 23:29:02 +00:00
Jérémie Panzer
868f65d7c9 Merge pull request #2249 from Athou/renovate/jsdom-30.x
chore(deps): update dependency jsdom to v30
2026-07-30 11:02:50 +02:00
Jérémie Panzer
07715383f4 Merge pull request #2248 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to v9.5.0
2026-07-30 11:02:48 +02:00
renovate[bot]
29d10debdb chore(deps): update dependency jsdom to v30 2026-07-30 08:36:37 +00:00
renovate[bot]
cd640da960 fix(deps): update mantine monorepo to v9.5.0 2026-07-30 08:36:26 +00:00
Jérémie Panzer
56ecb4a2f4 Merge pull request #2247 from Athou/renovate/org.jsoup-jsoup-1.x
fix(deps): update dependency org.jsoup:jsoup to v1.23.1
2026-07-30 07:23:53 +02:00
renovate[bot]
00161fd549 fix(deps): update dependency org.jsoup:jsoup to v1.23.1 2026-07-30 04:54:33 +00:00
renovate[bot]
a602ce17cd fix(deps): update dependency io.github.hakky54:ayza-for-apache5 to v10.0.6 2026-07-29 23:04:12 +00:00
renovate[bot]
672588d08b chore(deps): update node.js to v24.18.1 2026-07-29 21:06:29 +00:00
renovate[bot]
5fa2579275 chore(deps): update dependency com.github.eirslett:frontend-maven-plugin to v2.0.2 2026-07-29 20:12:39 +00:00
Jérémie Panzer
09bc317797 Merge pull request #2246 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-7.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.5.0
2026-07-29 22:12:10 +02:00
renovate[bot]
440c1ada50 chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.5.0 2026-07-29 18:38:26 +00:00
Athou
911a5ca099 reorder imports using google java format 2026-07-29 20:36:03 +02:00
Jérémie Panzer
65017f6b76 Merge pull request #2244 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.38.0
2026-07-29 20:33:12 +02:00
Athou
64fb5ef3a2 fix warning 2026-07-29 18:24:14 +02:00
Jérémie Panzer
dda389a50f Merge pull request #2245 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.21.0
2026-07-29 17:15:10 +02:00
renovate[bot]
419ad4fb6a fix(deps): update react-querybuilder to v8.21.0 2026-07-29 14:10:05 +00:00
Jérémie Panzer
ebae46f06b Merge pull request #2243 from Athou/renovate/docker-login-action-4.x
chore(deps): update docker/login-action action to v4.6.0
2026-07-29 16:09:58 +02:00
renovate[bot]
ac25accb31 fix(deps): update quarkus.version to v3.38.0 2026-07-29 14:09:50 +00:00
Jérémie Panzer
3c7eb90e50 Merge pull request #2242 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.9.0
2026-07-29 16:09:03 +02:00
renovate[bot]
4b3973f3fa chore(deps): update docker/login-action action to v4.6.0 2026-07-29 12:34:35 +00:00
renovate[bot]
6ddbe40c3c chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.9.0 2026-07-29 12:34:30 +00:00
Jérémie Panzer
d5164750e4 Merge pull request #2241 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to v6.6.0
2026-07-27 17:40:01 +02:00
renovate[bot]
822fbd8add fix(deps): update linguijs monorepo to v6.6.0 2026-07-27 14:46:27 +00:00
Jérémie Panzer
3a341d0707 Merge pull request #2240 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.9.0
2026-07-27 07:42:59 +02:00
renovate[bot]
23884fcbda chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.9.0 2026-07-27 05:15:46 +00:00
renovate[bot]
3b16674f98 chore(deps): lock file maintenance 2026-07-27 00:51:21 +00:00
renovate[bot]
d5eb3e1bb1 chore(deps): update dependency vite-plugin-checker to v0.14.5 2026-07-25 13:00:37 +00:00
renovate[bot]
8d47b38fd4 chore(deps): update dependency @vitejs/plugin-react to v6.0.4 2026-07-25 05:45:24 +00:00
renovate[bot]
04fb3282fa fix(deps): update react monorepo to v19.2.8 2026-07-25 00:34:50 +00:00
renovate[bot]
bcce130b82 chore(deps): update docker/login-action action to v4.5.1 2026-07-24 20:05:24 +00:00
renovate[bot]
e5d336f6ba fix(deps): update mantine monorepo to v9.4.2 2026-07-24 11:08:55 +00:00
renovate[bot]
2d741f6e1e chore(deps): update dependency @biomejs/biome to v2.5.5 2026-07-24 10:03:13 +00:00
Jérémie Panzer
2968dff118 Merge pull request #2237 from Athou/renovate/major-testing-library-monorepo
chore(deps): update dependency @testing-library/jest-dom to v7
2026-07-23 22:25:02 +02:00
renovate[bot]
0c244ec154 chore(deps): update dependency @testing-library/jest-dom to v7 2026-07-23 19:50:05 +00:00
Jérémie Panzer
34cd337992 Merge pull request #2235 from Athou/renovate/monaco-editor-0.x
fix(deps): update dependency monaco-editor to v0.56.0
2026-07-23 21:49:33 +02:00
Jérémie Panzer
1703cb7b93 Merge pull request #2236 from Athou/renovate/docker-login-action-4.x
chore(deps): update docker/login-action action to v4.5.0
2026-07-23 21:00:06 +02:00
Athou
dc89faa4a1 adapt to new paths 2026-07-23 20:58:07 +02:00
renovate[bot]
3eccfe8ccc chore(deps): update docker/login-action action to v4.5.0 2026-07-23 17:29:45 +00:00
renovate[bot]
6e34f6831c fix(deps): update dependency monaco-editor to v0.56.0 2026-07-23 14:01:52 +00:00
renovate[bot]
b7ebcd6a23 fix(deps): update quarkus.version to v3.37.4 2026-07-23 10:00:31 +00:00
Jérémie Panzer
8b7efe056d Merge pull request #2234 from Athou/renovate/fontsource-monorepo
fix(deps): update dependency @fontsource/open-sans to v5.3.0
2026-07-22 08:33:33 +02:00
renovate[bot]
87cb392516 fix(deps): update dependency @fontsource/open-sans to v5.3.0 2026-07-22 05:25:49 +00:00
Jérémie Panzer
aa11cb48f2 Merge pull request #2233 from Athou/renovate/querydsl.version
fix(deps): update querydsl.version to v7.5
2026-07-21 23:31:17 +02:00
renovate[bot]
36a29a393c fix(deps): update querydsl.version to v7.5 2026-07-21 21:08:45 +00:00
renovate[bot]
99f1b45866 chore(deps): update actions/checkout action to v7.0.1 2026-07-21 02:00:56 +00:00
renovate[bot]
9e8d529af3 chore(deps): lock file maintenance 2026-07-20 03:58:17 +00:00
renovate[bot]
bb80641b40 chore(deps): update dependency vite to v8.1.5 2026-07-19 10:03:19 +00:00
renovate[bot]
a2ed16c6fd chore(deps): update dependency @biomejs/biome to v2.5.4 2026-07-18 04:35:49 +00:00
renovate[bot]
4f10f57837 fix(deps): update quarkus.version to v3.37.3 2026-07-15 21:27:37 +00:00
Jérémie Panzer
28f40cd6fe Merge pull request #2230 from Athou/renovate/graalvm-setup-graalvm-1.6.x
chore(deps): update graalvm/setup-graalvm action to v1.6.3
2026-07-14 22:21:04 +02:00
renovate[bot]
89a84a0c83 chore(deps): update graalvm/setup-graalvm action to v1.6.3 2026-07-14 10:01:24 +00:00
Jérémie Panzer
ebcda50a34 Merge pull request #2229 from Athou/renovate/actions-setup-node-7.x
chore(deps): update actions/setup-node action to v7
2026-07-14 09:09:34 +02:00
renovate[bot]
8640e62b77 chore(deps): update actions/setup-node action to v7 2026-07-14 06:21:50 +00:00
Jérémie Panzer
d0418eea89 Merge pull request #2227 from Athou/renovate/actions-setup-node-6.x
chore(deps): update actions/setup-node action to v6.5.0
2026-07-14 08:20:45 +02:00
Jérémie Panzer
41ba067daf Merge pull request #2228 from Athou/renovate/debian-13.x
chore(deps): update debian docker tag to v13.6
2026-07-14 08:19:52 +02:00
renovate[bot]
53346997b6 chore(deps): update debian docker tag to v13.6 2026-07-14 05:14:26 +00:00
renovate[bot]
87a632761e chore(deps): update actions/setup-node action to v6.5.0 2026-07-14 05:14:22 +00:00
renovate[bot]
998ebc43a3 chore(deps): update dependency npm to v12.0.1 2026-07-13 19:32:59 +00:00
renovate[bot]
945e312c41 chore(deps): update dependency vite to v8.1.4 2026-07-12 07:51:07 +00:00
Jérémie Panzer
963a562100 Merge pull request #2224 from Athou/renovate/typescript-7.x
chore(deps): update dependency typescript to v7
2026-07-12 09:49:47 +02:00
Athou
4be961b68b temporary disable checker plugin until TypeScript 7 exposes a stable api 2026-07-12 08:59:24 +02:00
Jérémie Panzer
02a9140c13 Merge pull request #2226 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.8.0
2026-07-12 08:52:40 +02:00
renovate[bot]
5ea60ce6d6 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.8.0 2026-07-12 06:03:35 +00:00
Jérémie Panzer
e9e7be0fc6 Merge pull request #2225 from Athou/renovate/npm-12.x
chore(deps): update dependency npm to v12
2026-07-12 04:39:00 +02:00
renovate[bot]
60c30f2683 chore(deps): update dependency npm to v12 2026-07-11 23:25:34 +00:00
renovate[bot]
a17e69d37d chore(deps): update dependency typescript to v7 2026-07-11 17:37:46 +00:00
renovate[bot]
157f75d33e chore(deps): update dependency @biomejs/biome to v2.5.3 2026-07-11 10:52:37 +00:00
Athou
30a14c73ff specify that docker/podman can be used if graalvm is not found 2026-07-11 09:09:27 +02:00
Athou
27cece3afb release 7.2.0 2026-07-10 10:47:30 +02:00
Jérémie Panzer
8124ecb51f Merge pull request #2222 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to v6.5.0
2026-07-10 08:19:14 +02:00
renovate[bot]
e67c4c8670 fix(deps): update linguijs monorepo to v6.5.0 2026-07-09 16:48:53 +00:00
renovate[bot]
fcc6798d63 fix(deps): update dependency style-to-object to v2.0.2 2026-07-09 16:48:41 +00:00
renovate[bot]
d313ebfc69 chore(deps): update dependency vitest to v4.1.10 2026-07-09 08:48:42 +00:00
renovate[bot]
6edb8f00f6 fix(deps): update quarkus.version to v3.37.2 2026-07-08 20:59:14 +00:00
renovate[bot]
98a261d457 fix(deps): update dependency style-to-object to v2.0.1 2026-07-07 21:44:59 +00:00
renovate[bot]
e97a69f475 chore(deps): update graalvm/setup-graalvm action to v1.6.2 2026-07-07 18:59:06 +00:00
Jérémie Panzer
e9dc284b26 Merge pull request #2221 from Athou/renovate/graalvm-setup-graalvm-1.6.x
chore(deps): update graalvm/setup-graalvm action to v1.6.1
2026-07-07 09:24:17 +02:00
renovate[bot]
856c52c64b chore(deps): update graalvm/setup-graalvm action to v1.6.1 2026-07-06 23:03:42 +00:00
renovate[bot]
386a4a32ef chore(deps): lock file maintenance 2026-07-06 01:33:25 +00:00
renovate[bot]
dbdb40ff01 chore(deps): update dependency vite to v8.1.3 (#2220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-05 12:54:36 +00:00
Athou
1bd9b71215 use google java formatter to avoid downloading eclipse during build 2026-07-05 11:14:17 +02:00
Athou
f4dc279ec9 lombok is required at runtime for sneakyThrow 2026-07-05 09:02:57 +02:00
Athou
ca8438623d prevent access to ULA and CGNAT addresses 2026-07-05 08:48:02 +02:00
Jérémie Panzer
b81aac71f5 Merge pull request #2218 from Athou/renovate/biomejs-biome-2.5.x
chore(deps): update dependency @biomejs/biome to v2.5.2
2026-07-05 08:07:39 +02:00
Jérémie Panzer
a4e0d28fa2 Merge pull request #2219 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-7.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.4.0
2026-07-05 08:07:05 +02:00
renovate[bot]
56d4e4bbb1 chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.4.0 2026-07-04 20:37:01 +00:00
renovate[bot]
af1d10529b chore(deps): update dependency @biomejs/biome to v2.5.2 2026-07-04 16:40:46 +00:00
Athou
a5f84a3f6d search now also works for starred entries (#2217) 2026-07-04 17:57:08 +02:00
renovate[bot]
69dfbc81cc chore(deps): update dependency vite to v8.1.2 2026-07-04 01:29:31 +00:00
Jérémie Panzer
34408ccdb0 Merge pull request #2215 from Athou/renovate/react-icons-5.x
fix(deps): update dependency react-icons to v5.7.0
2026-07-04 03:28:52 +02:00
renovate[bot]
b0522a6e5d fix(deps): update dependency react-icons to v5.7.0 2026-07-03 20:43:38 +00:00
renovate[bot]
2a559a07c0 chore(deps): update dependency org.apache.maven.plugins:maven-help-plugin to v3.5.2 2026-07-03 20:43:16 +00:00
Athou
bdf2a6a915 the Pocket service has been discontinued 2026-07-03 22:40:38 +02:00
Jérémie Panzer
e814f75872 Merge pull request #2216 from Athou/renovate/graalvm-setup-graalvm-1.x
chore(deps): update graalvm/setup-graalvm action to v1.6.0
2026-07-03 20:16:57 +02:00
renovate[bot]
c0e0791854 chore(deps): update graalvm/setup-graalvm action to v1.6.0 2026-07-03 16:40:35 +00:00
renovate[bot]
17dbdb66bf chore(deps): update dependency vite to v8.1.1 2026-07-03 15:25:47 +00:00
Jérémie Panzer
6e3e9add36 Merge pull request #2214 from Athou/renovate/docker-login-action-4.x
chore(deps): update docker/login-action action to v4.4.0
2026-07-03 17:24:52 +02:00
renovate[bot]
5b25011853 chore(deps): update docker/login-action action to v4.4.0 2026-07-03 14:46:14 +00:00
Jérémie Panzer
804b24b2d6 Merge pull request #2213 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.18.0
2026-07-03 07:19:00 +02:00
renovate[bot]
de98b0f7c2 fix(deps): update dependency react-router-dom to v7.18.1 2026-07-02 23:52:17 +00:00
renovate[bot]
709de70fa9 chore(deps): update dependency npm to v11.18.0 2026-07-02 18:37:34 +00:00
renovate[bot]
1b7bc11f9d chore(deps): update ibm-semeru-runtimes:open-jdk-25.0.3.0-jre docker digest to 43ec947 2026-07-02 14:59:22 +00:00
Jérémie Panzer
bbcaacf3dd Merge pull request #2212 from Athou/renovate/docker-setup-buildx-action-4.x
chore(deps): update docker/setup-buildx-action action to v4.2.0
2026-07-02 16:58:33 +02:00
renovate[bot]
e816b1262c chore(deps): update docker/setup-buildx-action action to v4.2.0 2026-07-02 11:32:24 +00:00
Jérémie Panzer
fc019333dc Merge pull request #2211 from Athou/renovate/docker-login-action-4.x
chore(deps): update docker/login-action action to v4.3.0
2026-07-02 13:31:37 +02:00
renovate[bot]
56b0ddbc34 chore(deps): update docker/login-action action to v4.3.0 2026-07-02 09:08:11 +00:00
renovate[bot]
bb74d27d52 fix(deps): update quarkus.version to v3.37.1 2026-07-02 05:24:05 +00:00
Jérémie Panzer
7b1d19ca71 Merge pull request #2210 from Athou/renovate/docker-setup-qemu-action-4.x
chore(deps): update docker/setup-qemu-action action to v4.2.0
2026-07-02 07:23:33 +02:00
Jérémie Panzer
af961678bd Merge pull request #2209 from Athou/renovate/docker-build-push-action-7.x
chore(deps): update docker/build-push-action action to v7.3.0
2026-07-02 07:23:24 +02:00
Jérémie Panzer
1579242644 Merge pull request #2208 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-7.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.3.0
2026-07-02 07:23:18 +02:00
renovate[bot]
2a45f5c596 chore(deps): update docker/setup-qemu-action action to v4.2.0 2026-07-02 01:00:10 +00:00
renovate[bot]
c4560c49b0 chore(deps): update docker/build-push-action action to v7.3.0 2026-07-02 01:00:05 +00:00
Athou
2b7f75d9ef fix tests 2026-07-02 02:56:08 +02:00
renovate[bot]
5f68bcbd46 chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.3.0 2026-07-01 23:54:29 +00:00
renovate[bot]
c93c072259 fix(deps): update mantine monorepo to v9.4.1 2026-07-01 23:54:20 +00:00
Jérémie Panzer
516344a993 Merge pull request #2206 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.8.0
2026-06-30 08:15:35 +02:00
renovate[bot]
4d42fe5a0b chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.8.0 2026-06-30 05:11:59 +00:00
renovate[bot]
47bf132fff fix(deps): update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.2 2026-06-29 23:55:24 +00:00
renovate[bot]
04a9d48505 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.7 2026-06-29 19:44:00 +00:00
renovate[bot]
3716ddcf15 chore(deps): lock file maintenance 2026-06-29 03:37:40 +00:00
Jérémie Panzer
6c3ab40cb6 Merge pull request #2205 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.7.0
2026-06-29 05:35:54 +02:00
renovate[bot]
b7270974e5 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.7.0 2026-06-28 18:05:25 +00:00
renovate[bot]
9bfc53e77a fix(deps): update react-querybuilder to v8.20.2 2026-06-28 18:05:20 +00:00
renovate[bot]
f34fac8d17 chore(deps): update dependency @vitejs/plugin-react to v6.0.3 2026-06-26 13:36:32 +00:00
Jérémie Panzer
555c11649e Merge pull request #2204 from Athou/renovate/vite-8.x
chore(deps): update dependency vite to v8.1.0
2026-06-26 15:35:39 +02:00
Jérémie Panzer
c308c5d606 Merge pull request #2182 from Athou/renovate/biomejs-biome-2.x
chore(deps): update dependency @biomejs/biome to v2.5.1
2026-06-26 15:35:34 +02:00
Athou
54e9615994 add svg titles 2026-06-26 14:10:54 +02:00
renovate[bot]
b0b3b323fd chore(deps): update dependency vite to v8.1.0 2026-06-26 12:01:42 +00:00
renovate[bot]
f740a0b8ba chore(deps): update dependency @biomejs/biome to v2.5.1 2026-06-26 12:01:31 +00:00
renovate[bot]
e154675e72 fix(deps): update dependency axios to v1.18.1 2026-06-25 18:30:55 +00:00
Jérémie Panzer
2b3d3261de Merge pull request #2203 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to v9.4.0
2026-06-25 20:30:14 +02:00
renovate[bot]
fab525ca56 fix(deps): update mantine monorepo to v9.4.0 2026-06-25 17:09:08 +00:00
Jérémie Panzer
1c273d1c1a Merge pull request #2201 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.37.0
2026-06-24 19:04:03 +02:00
renovate[bot]
90d684367c fix(deps): update quarkus.version to v3.37.0 2026-06-24 15:22:44 +00:00
renovate[bot]
1ffc544ab0 chore(deps): update debian:13.5 docker digest to d07d1b5 2026-06-24 07:28:54 +00:00
Jérémie Panzer
cde0cceee1 Merge pull request #2200 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.18.0
2026-06-24 09:28:15 +02:00
renovate[bot]
1604fbc55c chore(deps): update node.js to v24.18.0 2026-06-24 00:45:25 +00:00
Jérémie Panzer
6f586e8866 Merge pull request #2199 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-7.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.2.0
2026-06-23 09:14:52 +02:00
renovate[bot]
c53455ad61 chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.2.0 2026-06-22 22:13:00 +00:00
renovate[bot]
77b3c609f3 chore(deps): lock file maintenance 2026-06-22 05:10:43 +00:00
Jérémie Panzer
3b84598d99 Merge pull request #2197 from Athou/renovate/react-draggable-4.x
fix(deps): update dependency react-draggable to v4.7.0
2026-06-22 07:09:06 +02:00
renovate[bot]
6f536c0981 fix(deps): update dependency react-draggable to v4.7.0 2026-06-21 21:36:40 +00:00
renovate[bot]
1f881ea252 fix(deps): update react-querybuilder to v8.20.1 2026-06-21 21:36:29 +00:00
Jérémie Panzer
b14df1b3cb Merge pull request #2196 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.20.0
2026-06-21 15:23:57 +02:00
renovate[bot]
e13763baa3 fix(deps): update react-querybuilder to v8.20.0 2026-06-21 09:52:35 +00:00
Jérémie Panzer
502ef67a71 Merge pull request #2192 from Athou/renovate/vite-plugin-checker-0.14.x
chore(deps): update dependency vite-plugin-checker to v0.14.4
2026-06-20 21:21:29 +02:00
renovate[bot]
05cab626df chore(deps): update dependency vite-plugin-checker to v0.14.4 2026-06-20 17:53:07 +00:00
renovate[bot]
c7d54af7ad fix(deps): update mantine monorepo to v9.3.2 2026-06-19 22:03:44 +00:00
Jérémie Panzer
ad195fcd80 Merge pull request #2194 from Athou/renovate/react-router-monorepo
fix(deps): update react-router monorepo to v7.18.0
2026-06-20 00:02:36 +02:00
renovate[bot]
b87c68d598 fix(deps): update react-router monorepo to v7.18.0 2026-06-19 19:03:39 +00:00
Jérémie Panzer
33af37ed2e Merge pull request #2191 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to v6.4.0
2026-06-19 12:16:44 +02:00
renovate[bot]
c4113fc92b fix(deps): update linguijs monorepo to v6.4.0 2026-06-19 09:49:58 +00:00
renovate[bot]
ed40025d26 fix(deps): update quarkus.version to v3.36.3 2026-06-19 03:23:19 +00:00
Jérémie Panzer
752008a551 Merge pull request #2190 from Athou/renovate/actions-checkout-7.x
chore(deps): update actions/checkout action to v7
2026-06-19 05:22:13 +02:00
renovate[bot]
a8bf970dca chore(deps): update actions/checkout action to v7 2026-06-18 20:43:45 +00:00
renovate[bot]
5b94bcdfdd chore(deps): update vitest monorepo to v4.1.9 2026-06-18 09:31:04 +00:00
Jérémie Panzer
a66a0e9485 Merge pull request #2187 from Athou/renovate/graalvm-setup-graalvm-1.5.x
chore(deps): update graalvm/setup-graalvm action to v1.5.6
2026-06-18 11:26:32 +02:00
renovate[bot]
80755e15b8 chore(deps): update graalvm/setup-graalvm action to v1.5.6 2026-06-18 06:49:13 +00:00
Jérémie Panzer
8cede0c6e0 Merge pull request #2188 from Athou/renovate/axios-1.x
fix(deps): update dependency axios to v1.18.0
2026-06-18 08:45:44 +02:00
Jérémie Panzer
9556f0f7d2 Merge pull request #2189 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.17.0
2026-06-18 08:45:36 +02:00
renovate[bot]
1b80f3060c chore(deps): update node.js to v24.17.0 2026-06-18 05:41:34 +00:00
renovate[bot]
00811f0be3 fix(deps): update dependency axios to v1.18.0 2026-06-17 22:02:57 +00:00
Jérémie Panzer
0fdfc2c59c Merge pull request #2186 from Athou/renovate/style-to-object-2.x
fix(deps): update dependency style-to-object to v2
2026-06-17 17:46:31 +02:00
renovate[bot]
8d25562a76 fix(deps): update dependency style-to-object to v2 2026-06-17 14:14:50 +00:00
Jérémie Panzer
1d0d9d4be6 Merge pull request #2185 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.7.0
2026-06-16 11:46:39 +02:00
renovate[bot]
9ed4559625 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.7.0 2026-06-16 09:09:09 +00:00
renovate[bot]
2a53e8f15d chore(deps): update graalvm/setup-graalvm action to v1.5.5 2026-06-16 03:56:30 +00:00
Jérémie Panzer
15fbd18f31 Merge pull request #2184 from Athou/renovate/querydsl.version
fix(deps): update querydsl.version to v7.4.0
2026-06-16 05:55:32 +02:00
renovate[bot]
2c248a7ca1 fix(deps): update querydsl.version to v7.4.0 2026-06-15 21:52:53 +00:00
Jérémie Panzer
94d8fdb4ab Merge pull request #2183 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-7.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.1.0
2026-06-15 23:45:05 +02:00
renovate[bot]
3596439ed4 chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7.1.0 2026-06-15 20:48:14 +00:00
Jérémie Panzer
bc732a52ce Merge pull request #2179 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.17.0
2026-06-15 13:21:46 +02:00
renovate[bot]
ef096899a2 chore(deps): update dependency npm to v11.17.0 2026-06-15 09:48:49 +00:00
Jérémie Panzer
bf4bcf740e Merge pull request #2180 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.6.0
2026-06-15 11:39:11 +02:00
renovate[bot]
679281ac26 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.6.0 2026-06-15 06:12:58 +00:00
renovate[bot]
bde3d1408b fix(deps): update react-querybuilder to v8.19.1 2026-06-14 05:09:50 +00:00
Jérémie Panzer
3621a96d3f Merge pull request #2178 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to v6.3.0
2026-06-13 19:01:49 +02:00
renovate[bot]
6583ad114f fix(deps): update linguijs monorepo to v6.3.0 2026-06-13 15:48:28 +00:00
renovate[bot]
f4e42165bf fix(deps): update dependency dev.cel:cel to v0.13.1 2026-06-13 12:21:08 +00:00
Athou
00d7be735a static random fields should not be used with graalvm as they are seeded at compile time and thus not random anymore 2026-06-13 12:03:50 +02:00
Athou
1388fba8e4 apply xml cleaning to opml files 2026-06-13 11:32:57 +02:00
Athou
c43644861f use a secure algorithm to generate image urls so that only server-side generated urls can be used 2026-06-13 11:21:16 +02:00
renovate[bot]
831ca599b8 chore(deps): update dependency com.github.eirslett:frontend-maven-plugin to v2.0.1 2026-06-12 16:36:09 +00:00
Athou
43fbd03292 remove husky and lint-staged 2026-06-12 10:12:47 +02:00
Athou
1efd04f6c5 fix wrapping of long titles 2026-06-12 10:10:02 +02:00
Jérémie Panzer
e89ccfd74b Merge pull request #2177 from Athou/renovate/patch-protobuf-monorepo
fix(deps): update protobuf monorepo to v4.35.1
2026-06-12 09:53:06 +02:00
renovate[bot]
622486160c fix(deps): update protobuf monorepo to v4.35.1 2026-06-12 05:26:54 +00:00
renovate[bot]
6d172cae9e fix(deps): update quarkus.version to v3.36.2 2026-06-12 04:36:06 +00:00
renovate[bot]
91b60fb147 fix(deps): update mantine monorepo to v9.3.1 2026-06-12 01:54:16 +00:00
renovate[bot]
3f9b7f48d5 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.5 2026-06-11 19:27:17 +00:00
renovate[bot]
3599fdec65 chore(deps): update debian:13.5 docker digest to fe7312b 2026-06-11 16:02:59 +00:00
Jérémie Panzer
00e86974b3 Merge pull request #2176 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.19.0
2026-06-11 05:37:46 +02:00
renovate[bot]
2aa897eb80 fix(deps): update react-querybuilder to v8.19.0 2026-06-11 03:11:56 +00:00
renovate[bot]
ce9706b860 chore(deps): update react monorepo to v19.2.17 2026-06-08 22:49:26 +00:00
Jérémie Panzer
e140dfe556 Merge pull request #2174 from Athou/renovate/react-router-monorepo
fix(deps): update react-router monorepo to v7.17.0
2026-06-08 08:01:57 +02:00
renovate[bot]
4a1b845ac2 chore(deps): lock file maintenance 2026-06-08 01:47:00 +00:00
renovate[bot]
573f8af57e fix(deps): update react-router monorepo to v7.17.0 2026-06-07 17:15:57 +00:00
Jérémie Panzer
33445c93e8 Merge pull request #2173 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-7.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7
2026-06-07 08:44:16 +02:00
Athou
f7fcb659f7 fix build 2026-06-07 07:34:00 +02:00
renovate[bot]
b0aa723c2f chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v7 2026-06-06 17:58:01 +00:00
Jérémie Panzer
48dca41738 Merge pull request #2172 from Athou/renovate/axios-1.x
fix(deps): update dependency axios to v1.17.0
2026-06-06 15:55:32 +02:00
renovate[bot]
873d6f487f fix(deps): update dependency axios to v1.17.0 2026-06-06 09:44:37 +00:00
Jérémie Panzer
c8d73b129e Merge pull request #2156 from Athou/renovate/querydsl.version
fix(deps): update querydsl.version to v7.3.0
2026-06-06 06:27:00 +02:00
renovate[bot]
c63e9e1cd2 fix(deps): update querydsl.version to v7.3.0 2026-06-05 21:47:04 +00:00
Jérémie Panzer
def2ad84bb Merge pull request #2171 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to v9.3.0
2026-06-05 15:24:57 +02:00
renovate[bot]
52510e9817 fix(deps): update mantine monorepo to v9.3.0 2026-06-05 10:33:10 +00:00
renovate[bot]
69a95488ce fix(deps): update react monorepo 2026-06-04 22:50:59 +00:00
Jérémie Panzer
636841f647 Merge pull request #2170 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to v6.2.0
2026-06-04 18:13:54 +02:00
renovate[bot]
1ea521236c fix(deps): update linguijs monorepo to v6.2.0 2026-06-04 13:37:49 +00:00
renovate[bot]
6966efeda5 chore(deps): update dependency vite to v8.0.16 2026-06-04 10:09:32 +00:00
Jérémie Panzer
66ad0c3548 Merge pull request #2169 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.18.0
2026-06-04 12:00:32 +02:00
renovate[bot]
e061ccbf79 chore(deps): update vitest monorepo to v4.1.8 2026-06-04 09:08:43 +00:00
renovate[bot]
30cc6a111d chore(deps): update dependency vite to v8.0.15 2026-06-04 04:46:24 +00:00
renovate[bot]
8416ce5124 fix(deps): update react-querybuilder to v8.18.0 2026-06-04 01:33:04 +00:00
renovate[bot]
0cacfe391b fix(deps): update quarkus.version to v3.36.1 (#2168)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-03 18:16:42 +00:00
renovate[bot]
c0eaab48cd chore(deps): update graalvm/setup-graalvm action to v1.5.4 2026-06-03 15:05:10 +00:00
renovate[bot]
c696bc3545 chore(deps): update dependency lint-staged to v17.0.7 2026-06-03 14:05:03 +00:00
renovate[bot]
72505954a9 chore(deps): update dependency lint-staged to v17.0.6 2026-06-02 18:11:51 +00:00
renovate[bot]
49159dba5c chore(deps): update actions/checkout action to v6.0.3 2026-06-02 17:34:01 +00:00
renovate[bot]
41e2d89abc chore(deps): update ibm-semeru-runtimes:open-jdk-25.0.3.0-jre docker digest to 9ff5b13 2026-06-02 16:48:04 +00:00
Jérémie Panzer
6e7de97d93 Merge pull request #2167 from Athou/renovate/react-draggable-4.x
fix(deps): update dependency react-draggable to v4.6.0
2026-06-02 05:57:46 +02:00
renovate[bot]
2f20e6b498 fix(deps): update dependency react-draggable to v4.6.0 2026-06-01 20:10:53 +00:00
Jérémie Panzer
ecf9158fd7 Merge pull request #2166 from Athou/renovate/react-router-monorepo
fix(deps): update dependency react-router-dom to v7.16.0
2026-06-01 07:19:20 +02:00
renovate[bot]
dc7e7321f3 fix(deps): update dependency react-router-dom to v7.16.0 2026-05-31 21:24:56 +00:00
Jérémie Panzer
0f087ad2a0 Merge pull request #2165 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.17.0
2026-05-31 08:06:48 +02:00
renovate[bot]
ff19a6e9eb fix(deps): update react-querybuilder to v8.17.0 2026-05-31 04:53:55 +00:00
renovate[bot]
8cec0c0779 fix(deps): update mantine monorepo to v9.2.2 2026-05-30 21:42:00 +00:00
Jérémie Panzer
314384f8e7 Merge pull request #2164 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.16.0
2026-05-30 23:40:36 +02:00
Jérémie Panzer
6c75ad95ad Merge pull request #2163 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.5.0
2026-05-30 23:40:02 +02:00
renovate[bot]
76e3b3caff chore(deps): update dependency npm to v11.16.0 2026-05-30 20:56:38 +00:00
renovate[bot]
c8f71480c9 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.5.0 2026-05-30 20:56:33 +00:00
renovate[bot]
9172a0cf9c chore(deps): update dependency @biomejs/biome to v2.4.16 2026-05-30 18:07:43 +00:00
renovate[bot]
eb476c6023 fix(deps): update react-querybuilder to v8.16.2 2026-05-30 00:38:03 +00:00
Jérémie Panzer
da4f3bd38e Merge pull request #2162 from Athou/renovate/vite-plugin-checker-0.x
chore(deps): update dependency vite-plugin-checker to v0.14.1
2026-05-29 19:13:53 +02:00
renovate[bot]
c65d628a36 chore(deps): update dependency vite-plugin-checker to v0.14.1 2026-05-29 15:43:11 +00:00
renovate[bot]
589b8c3afc fix(deps): update dependency dayjs to v1.11.21 2026-05-29 05:49:39 +00:00
Jérémie Panzer
2684051da0 Merge pull request #2161 from Athou/renovate/org.apache.maven.plugins-maven-surefire-plugin-3.5.x
chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.6
2026-05-28 13:07:25 +02:00
renovate[bot]
200db68d05 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.6 2026-05-28 02:37:33 +00:00
renovate[bot]
f6d8d32a01 chore(deps): update dependency org.apache.maven.plugins:maven-failsafe-plugin to v3.5.6 2026-05-27 22:55:28 +00:00
Jérémie Panzer
72d5af5bb0 Merge pull request #2160 from Athou/renovate/docker-setup-qemu-action-4.x
chore(deps): update docker/setup-qemu-action action to v4.1.0
2026-05-27 21:58:58 +02:00
Jérémie Panzer
aabbc54cbb Merge pull request #2159 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.6.0
2026-05-27 21:58:50 +02:00
renovate[bot]
09b523bf56 chore(deps): update docker/setup-qemu-action action to v4.1.0 2026-05-27 19:09:41 +00:00
renovate[bot]
5dc7f522bf chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.6.0 2026-05-27 16:16:17 +00:00
Jérémie Panzer
fd58a42824 Merge pull request #2158 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.36.0
2026-05-27 12:58:43 +02:00
Jérémie Panzer
0a6d1c85ed Merge pull request #2157 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-6.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v6.1.0
2026-05-27 12:58:40 +02:00
renovate[bot]
13f84a260c fix(deps): update quarkus.version to v3.36.0 2026-05-27 09:38:52 +00:00
renovate[bot]
4132ad9a97 chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v6.1.0 2026-05-27 09:38:46 +00:00
Athou
1faba92ecc remove unused relationships 2026-05-27 07:28:19 +02:00
renovate[bot]
b93fc86663 fix(deps): update dependency io.dropwizard.metrics:metrics-json to v4.2.39 2026-05-26 00:28:53 +00:00
renovate[bot]
7123036e8d chore(deps): lock file maintenance 2026-05-25 02:01:46 +00:00
Jérémie Panzer
cce4ea4dc6 Merge pull request #2155 from Athou/renovate/linguijs-monorepo
fix(deps): update linguijs monorepo to v6.1.0
2026-05-25 01:49:13 +02:00
renovate[bot]
b8f7df1555 fix(deps): update linguijs monorepo to v6.1.0 2026-05-24 13:45:25 +00:00
renovate[bot]
ba8cc0477c chore(deps): update dependency vite to v8.0.14 2026-05-24 09:26:55 +00:00
Jérémie Panzer
010026bc88 Merge pull request #2154 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.15.0
2026-05-24 07:07:15 +02:00
renovate[bot]
a1ba34caf2 chore(deps): update dependency npm to v11.15.0 2026-05-23 20:45:10 +00:00
renovate[bot]
1214380bee chore(deps): update dependency vitest to v4.1.7 2026-05-23 12:43:21 +00:00
Athou
03eb09c68c limit the number of favicon retries 2026-05-23 11:17:59 +02:00
renovate[bot]
055d503862 chore(deps): update dependency @types/react to v19.2.15 2026-05-22 23:03:14 +00:00
Athou
5755ff762b Trie is thread-safe, no need to recreate one every time 2026-05-22 20:29:48 +02:00
Jérémie Panzer
88e24787e8 Merge pull request #2153 from Athou/renovate/docker-setup-buildx-action-4.x
chore(deps): update docker/setup-buildx-action action to v4.1.0
2026-05-22 20:25:02 +02:00
renovate[bot]
ed32d07ac4 chore(deps): update docker/setup-buildx-action action to v4.1.0 2026-05-22 18:04:22 +00:00
Jérémie Panzer
3a917fcebe Merge pull request #2152 from Athou/renovate/docker-login-action-4.x
chore(deps): update docker/login-action action to v4.2.0
2026-05-22 15:41:58 +02:00
renovate[bot]
3535068ca8 chore(deps): update docker/login-action action to v4.2.0 2026-05-22 13:03:48 +00:00
Jérémie Panzer
4c39d4b96b Merge pull request #2150 from Athou/renovate/docker-build-push-action-7.x
chore(deps): update docker/build-push-action action to v7.2.0
2026-05-21 21:27:31 +02:00
renovate[bot]
8e864c6793 chore(deps): update docker/build-push-action action to v7.2.0 2026-05-21 18:41:32 +00:00
Jérémie Panzer
aeffecced3 Merge pull request #2151 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.16.0
2026-05-21 19:54:18 +02:00
renovate[bot]
4be42dc1e2 chore(deps): update node.js to v24.16.0 2026-05-21 17:03:21 +00:00
Jérémie Panzer
bdf6c62c29 Merge pull request #2149 from Athou/renovate/org.mock-server-mockserver-junit-jupiter-6.x
chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v6
2026-05-21 07:37:36 +02:00
Athou
a4592e5199 fix tests 2026-05-21 04:48:43 +02:00
renovate[bot]
a5268f082d chore(deps): update dependency org.mock-server:mockserver-junit-jupiter to v6 2026-05-20 19:50:45 +00:00
Jérémie Panzer
b556e72922 Merge pull request #2148 from Athou/renovate/patch-quarkus.version
fix(deps): update quarkus.version to v3.35.4
2026-05-20 21:50:23 +02:00
renovate[bot]
3602d2533a fix(deps): update quarkus.version to v3.35.4 2026-05-20 15:46:05 +00:00
Jérémie Panzer
681f2c766a Merge pull request #2147 from Athou/renovate/mindsers-changelog-reader-action-2.x
chore(deps): update mindsers/changelog-reader-action action to v2.4.0
2026-05-20 14:11:52 +02:00
renovate[bot]
6e0a136cd1 chore(deps): update mindsers/changelog-reader-action action to v2.4.0 2026-05-20 10:37:59 +00:00
Jérémie Panzer
9e9945a1cc Merge pull request #2146 from Athou/renovate/debian-13.5
chore(deps): update debian:13.5 docker digest to 4ae6766
2026-05-20 08:46:39 +02:00
renovate[bot]
3368cead8b chore(deps): update debian:13.5 docker digest to 4ae6766 2026-05-20 04:16:03 +00:00
Jérémie Panzer
a7b5126b4a Merge pull request #2145 from Athou/renovate/protobuf-monorepo
fix(deps): update dependency com.google.protobuf:protobuf-java to v4.35.0
2026-05-20 06:15:49 +02:00
Jérémie Panzer
a9ef1a03dc Merge pull request #2144 from Athou/renovate/debian-13.x
chore(deps): update debian docker tag to v13.5
2026-05-20 06:15:22 +02:00
Jérémie Panzer
bc0ec8dbfa Merge pull request #2143 from Athou/renovate/mindsers-changelog-reader-action-2.x
chore(deps): update mindsers/changelog-reader-action action to v2.3.0
2026-05-20 06:15:02 +02:00
renovate[bot]
7bef009922 fix(deps): update dependency com.google.protobuf:protobuf-java to v4.35.0 2026-05-20 01:03:49 +00:00
renovate[bot]
9a467bc27b chore(deps): update debian docker tag to v13.5 2026-05-20 01:03:45 +00:00
renovate[bot]
e3934c9403 chore(deps): update mindsers/changelog-reader-action action to v2.3.0 2026-05-19 22:42:24 +00:00
renovate[bot]
af337aa4d0 chore(deps): update dependency lint-staged to v17.0.5 2026-05-19 13:45:40 +00:00
Jérémie Panzer
313d3a01ec Merge pull request #2141 from Athou/renovate/reduxjs-toolkit-2.x
fix(deps): update dependency @reduxjs/toolkit to v2.12.0
2026-05-19 07:20:37 +02:00
Jérémie Panzer
feb9876cf5 Merge pull request #2142 from Athou/renovate/react-redux-9.x
fix(deps): update dependency react-redux to v9.3.0
2026-05-19 07:20:32 +02:00
renovate[bot]
9c3a5cb820 fix(deps): update dependency react-redux to v9.3.0 2026-05-18 19:12:38 +00:00
renovate[bot]
e219f781bf fix(deps): update dependency @reduxjs/toolkit to v2.12.0 2026-05-18 19:12:29 +00:00
renovate[bot]
21ecd6ae05 chore(deps): lock file maintenance 2026-05-18 00:37:57 +00:00
renovate[bot]
2449902c3d chore(deps): update dependency @vitejs/plugin-react to v6.0.2 2026-05-17 20:56:33 +00:00
renovate[bot]
c1a55037ba chore(deps): update dependency maven to v3.9.16 2026-05-17 19:48:01 +00:00
Jérémie Panzer
646a8122c0 Merge pull request #2140 from Athou/renovate/patch-react-router-monorepo
fix(deps): update dependency react-router-dom to v7.15.1
2026-05-17 21:47:37 +02:00
renovate[bot]
edc4e6c508 fix(deps): update dependency react-router-dom to v7.15.1 2026-05-17 18:40:58 +00:00
renovate[bot]
5ddeb7b156 fix(deps): update mantine monorepo to v9.2.1 2026-05-17 17:25:02 +00:00
renovate[bot]
306624e55f chore(deps): update dependency vite to v8.0.13 2026-05-17 12:41:35 +00:00
renovate[bot]
6a9ca65cbc fix(deps): update react-querybuilder to v8.16.1 2026-05-16 21:39:23 +00:00
renovate[bot]
7db0026a0c fix(deps): update dependency axios to v1.16.1 2026-05-16 16:37:50 +00:00
Jérémie Panzer
7358705248 Merge pull request #2137 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to v9.2.0
2026-05-16 07:58:01 +02:00
Jérémie Panzer
0b9ee2eec9 Merge pull request #2139 from Athou/renovate/dev.cel-cel-0.x
fix(deps): update dependency dev.cel:cel to v0.13.0
2026-05-16 07:57:48 +02:00
renovate[bot]
42eaf7fc39 fix(deps): update dependency dev.cel:cel to v0.13.0 2026-05-16 01:25:48 +00:00
renovate[bot]
6783cac9b4 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.5.1 2026-05-16 01:25:45 +00:00
renovate[bot]
628106ce88 fix(deps): update dependency tss-react to v4.9.21 2026-05-15 16:29:21 +00:00
Jérémie Panzer
1ce69b31a7 Merge pull request #2136 from Athou/renovate/yaml-2.x
chore(deps): update dependency yaml to v2.9.0
2026-05-15 08:01:15 +02:00
Jérémie Panzer
912ac11842 Merge pull request #2138 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.5.0
2026-05-15 07:57:38 +02:00
renovate[bot]
5f2fad560c chore(deps): update dependency yaml to v2.9.0 2026-05-14 21:28:25 +00:00
renovate[bot]
b300f364a7 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.5.0 2026-05-14 21:28:13 +00:00
renovate[bot]
049ec41d13 fix(deps): update mantine monorepo to v9.2.0 2026-05-14 16:49:16 +00:00
renovate[bot]
f0d17bc84e chore(deps): update dependency vitest to v4.1.6 2026-05-14 16:49:07 +00:00
renovate[bot]
cc442d7409 chore(deps): update dependency vite to v8.0.12 2026-05-14 08:51:10 +00:00
renovate[bot]
80bac26f21 fix(deps): update quarkus.version to v3.35.3 2026-05-13 17:00:07 +00:00
renovate[bot]
015c39e6f4 chore(deps): update dependency lint-staged to v17.0.4 2026-05-12 21:11:12 +00:00
renovate[bot]
e0ec3a4ebd chore(deps): update dependency @biomejs/biome to v2.4.15 2026-05-12 17:27:19 +00:00
renovate[bot]
a022de5cc3 chore(deps): update dependency npm to v11.14.1 2026-05-11 20:50:59 +00:00
renovate[bot]
a7b5e1e18c chore(deps): update dependency lint-staged to v17.0.3 2026-05-11 17:57:10 +00:00
renovate[bot]
b8695df1c8 fix(deps): update dependency io.github.hakky54:ayza-for-apache5 to v10.0.5 2026-05-11 11:10:01 +00:00
renovate[bot]
5e4382d1d2 chore(deps): lock file maintenance 2026-05-11 01:26:50 +00:00
Athou
ce983f4906 fix flaky test 2026-05-10 21:19:35 +02:00
Athou
4a8be29616 add support for declared icons in feeds (#2048) 2026-05-10 21:05:33 +02:00
renovate[bot]
1622eb642a chore(deps): update dependency vite to v8.0.11 2026-05-10 10:37:18 +00:00
Jérémie Panzer
422d992484 Merge pull request #2135 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.14.0
2026-05-10 12:36:28 +02:00
renovate[bot]
1065af1233 chore(deps): update dependency npm to v11.14.0 2026-05-09 20:56:38 +00:00
renovate[bot]
dde39d83be fix(deps): update react monorepo to v19.2.6 2026-05-09 16:44:07 +00:00
Jérémie Panzer
94fe7617bf Merge pull request #2134 from Athou/renovate/lint-staged-17.x
chore(deps): update dependency lint-staged to v17
2026-05-09 17:08:52 +02:00
Jérémie Panzer
e781d85b7c Merge pull request #2133 from Athou/renovate/react-router-monorepo
fix(deps): update dependency react-router-dom to v7.15.0
2026-05-09 17:08:35 +02:00
renovate[bot]
c30c9ca380 chore(deps): update dependency lint-staged to v17 2026-05-09 13:42:20 +00:00
renovate[bot]
c395916af9 chore(deps): update debian:13.4 docker digest to e2d08da 2026-05-09 04:41:33 +00:00
renovate[bot]
1a4037e360 fix(deps): update dependency react-router-dom to v7.15.0 2026-05-08 16:51:27 +00:00
Athou
f207bfaddd format empty records and anonymous types as a single line 2026-05-08 17:56:26 +02:00
Jérémie Panzer
3dd298c0c8 Merge pull request #2132 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.16.0 (minor)
2026-05-08 08:29:30 +02:00
renovate[bot]
8e2e4da7c3 fix(deps): update react-querybuilder to v8.16.0 2026-05-08 05:58:29 +00:00
renovate[bot]
418e3c109c chore(deps): update graalvm/setup-graalvm action to v1.5.3 2026-05-06 18:39:48 +00:00
Jérémie Panzer
cfda63a609 Merge pull request #2131 from Athou/renovate/axios-1.x
fix(deps): update dependency axios to v1.16.0
2026-05-06 07:22:28 +02:00
renovate[bot]
ba68276bfb fix(deps): update dependency axios to v1.16.0 2026-05-05 19:30:57 +00:00
renovate[bot]
97c700024a chore(deps): update dependency yaml to v2.8.4 2026-05-05 10:10:38 +00:00
renovate[bot]
d4f67d726c chore(deps): update dependency @biomejs/biome to v2.4.14 2026-05-04 20:37:34 +00:00
Jérémie Panzer
ea87fa3e5a Merge pull request #2130 from Athou/renovate/io.quarkiverse.playwright-quarkus-playwright-2.3.x
chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.4
2026-05-04 22:36:37 +02:00
renovate[bot]
d12d247b3e chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.4 2026-05-04 20:07:06 +00:00
renovate[bot]
c4a23915bd fix(deps): update quarkus.version to v3.35.2 2026-05-04 19:06:39 +00:00
Athou
8645c57d13 simplify settings thunks 2026-05-04 08:03:57 +02:00
Jérémie Panzer
b170e0bc17 Merge pull request #2129 from Athou/renovate/patch-linguijs-monorepo
fix(deps): update linguijs monorepo to v6.0.1 (patch)
2026-05-04 08:03:53 +02:00
renovate[bot]
4199f514f9 fix(deps): update linguijs monorepo to v6.0.1 2026-05-04 05:28:51 +00:00
renovate[bot]
c96efb3bb4 chore(deps): lock file maintenance 2026-05-04 00:57:15 +00:00
renovate[bot]
d5571e0196 chore(deps): update dependency jsdom to v29.1.1 2026-05-03 09:41:32 +00:00
Athou
9f7f87b4ae move handlers in Layout because they actually only apply to the reader pages 2026-05-02 15:04:33 +02:00
renovate[bot]
4b03fce85d chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.4.2 2026-05-02 06:12:23 +00:00
renovate[bot]
4b28d1c635 chore(deps): update ibm-semeru-runtimes docker tag to open-jdk-25.0.3.0-jre 2026-05-01 11:59:50 +00:00
Jérémie Panzer
a5e803177f Merge pull request #2128 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.35.1 (minor)
2026-05-01 13:59:25 +02:00
renovate[bot]
9462f284a4 fix(deps): update quarkus.version to v3.35.1 2026-05-01 11:33:13 +00:00
renovate[bot]
4b0f6bd61b fix(deps): update mantine monorepo to v9.1.1 2026-04-30 21:05:02 +00:00
Jérémie Panzer
d6c85214ef Merge pull request #2127 from Athou/renovate/jsdom-29.x
chore(deps): update dependency jsdom to v29.1.0
2026-04-30 07:18:31 +02:00
renovate[bot]
de770cf801 chore(deps): update dependency jsdom to v29.1.0 2026-04-30 04:39:54 +00:00
renovate[bot]
dae0f740c2 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.4.1 2026-04-27 17:36:06 +00:00
Athou
9452c3e74c remove warning 2026-04-27 18:01:02 +02:00
Athou
f826f1d543 create comparator only once 2026-04-27 18:01:02 +02:00
Athou
fd13c80e5a allow reuse of TLS connections 2026-04-27 17:52:40 +02:00
Athou
75871348d5 compile pattern once 2026-04-27 17:46:50 +02:00
renovate[bot]
0140e88595 chore(deps): lock file maintenance 2026-04-27 00:53:02 +00:00
renovate[bot]
e620b1254b chore(deps): update dependency @biomejs/biome to v2.4.13 2026-04-26 17:49:57 +00:00
Jérémie Panzer
77cfcf6c58 Merge pull request #2123 from Athou/renovate/major-linguijs-monorepo
fix(deps): update linguijs monorepo to v6 (major)
2026-04-26 16:07:22 +02:00
Athou
04e9df3d0a the format and the formatOptions fields have been removed 2026-04-26 15:43:42 +02:00
renovate[bot]
a953134089 chore(deps): update dependency vite to v8.0.10 2026-04-26 06:27:27 +00:00
Jérémie Panzer
88b9c0d783 Merge pull request #2125 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.13.0
2026-04-26 08:26:50 +02:00
renovate[bot]
ff7e0ba95b chore(deps): update dependency npm to v11.13.0 2026-04-25 20:53:09 +00:00
renovate[bot]
53dfbcaf00 fix(deps): update linguijs monorepo to v6 2026-04-25 13:00:15 +00:00
renovate[bot]
d8fca22c00 fix(deps): update dependency react-router-dom to v7.14.2 (#2122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-25 05:15:30 +00:00
renovate[bot]
4fb5963bd9 fix(deps): update dependency axios to v1.15.2 2026-04-24 19:10:13 +00:00
Jérémie Panzer
862d81f446 Merge pull request #2121 from Athou/renovate/mantine-monorepo
fix(deps): update mantine monorepo to v9.1.0 (minor)
2026-04-24 21:09:22 +02:00
renovate[bot]
8ae7943b94 fix(deps): update mantine monorepo to v9.1.0 2026-04-24 17:36:15 +00:00
renovate[bot]
46e947d779 chore(deps): update dependency vitest to v4.1.5 2026-04-24 14:58:06 +00:00
renovate[bot]
7780d6020f chore(deps): update dependency vite to v8.0.9 2026-04-23 07:05:57 +00:00
renovate[bot]
e5803b46f2 fix(deps): update quarkus.version to v3.34.6 2026-04-22 22:04:58 +00:00
renovate[bot]
d776090298 fix(deps): update dependency axios to v1.15.1 2026-04-22 18:11:13 +00:00
renovate[bot]
f2f3111d49 chore(deps): update debian:13.4 docker digest to 35b8ff7 2026-04-22 06:50:35 +00:00
renovate[bot]
dff1b957cd fix(deps): update dependency org.projectlombok:lombok to v1.18.46 2026-04-22 01:21:16 +00:00
Athou
b1ec50c3cb increase timeout to prevent flaky tests 2026-04-20 11:06:03 +02:00
renovate[bot]
85aa188287 chore(deps): lock file maintenance 2026-04-20 08:11:06 +00:00
renovate[bot]
e7d6526da9 fix(deps): update dependency org.jsoup:jsoup to v1.22.2 2026-04-20 07:28:37 +00:00
Jérémie Panzer
d67c6fcc00 Merge pull request #2119 from Athou/renovate/actions-setup-node-6.x
chore(deps): update actions/setup-node action to v6.4.0
2026-04-20 09:25:22 +02:00
renovate[bot]
d9bf084300 chore(deps): update actions/setup-node action to v6.4.0 2026-04-20 06:58:42 +00:00
Athou
da3977b0e2 fix error message not correctly returned 2026-04-20 08:39:24 +02:00
renovate[bot]
7df930bccc chore(deps): update dependency typescript to v6.0.3 2026-04-20 00:42:50 +00:00
renovate[bot]
11f73a1a53 fix(deps): update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.1 2026-04-19 12:22:35 +00:00
Jérémie Panzer
2bbcd55e48 Merge pull request #2118 from Athou/renovate/react-querybuilder
fix(deps): update react-querybuilder to v8.15.0 (minor)
2026-04-19 14:21:37 +02:00
renovate[bot]
43890b8d5c fix(deps): update react-querybuilder to v8.15.0 2026-04-19 11:54:36 +00:00
Athou
b6b8e3741b group react-querybuilder and @react-querybuilder/mantine together 2026-04-19 13:53:45 +02:00
Jérémie Panzer
c39f00c336 Merge pull request #2114 from Athou/renovate/vite-plugin-checker-0.x
chore(deps): update dependency vite-plugin-checker to v0.13.0
2026-04-19 08:04:06 +02:00
renovate[bot]
e48eaacfef chore(deps): update dependency vite-plugin-checker to v0.13.0 2026-04-18 13:49:45 +00:00
renovate[bot]
6d97691388 chore(deps): update dependency @biomejs/biome to v2.4.12 2026-04-17 21:33:53 +00:00
renovate[bot]
d64cc996bc chore(deps): update dependency maven to v3.9.15 2026-04-17 14:05:43 +00:00
renovate[bot]
617d66c9b9 fix(deps): update quarkus.version to v3.34.5 2026-04-17 05:06:55 +00:00
Jérémie Panzer
0fc50d2105 Merge pull request #2113 from Athou/renovate/rolldown-plugin-babel-0.2.x
fix(deps): update dependency @rolldown/plugin-babel to v0.2.3
2026-04-17 07:06:29 +02:00
renovate[bot]
2bb9f28569 fix(deps): update dependency @rolldown/plugin-babel to v0.2.3 2026-04-17 01:09:52 +00:00
renovate[bot]
c1580b8359 fix(deps): update dependency react-router-dom to v7.14.1 2026-04-16 21:56:46 +00:00
Jérémie Panzer
dde235825d Merge pull request #2111 from Athou/renovate/patch-mantine-monorepo
fix(deps): update mantine monorepo to v9.0.2 (patch)
2026-04-16 19:07:20 +02:00
renovate[bot]
eac13b4025 fix(deps): update mantine monorepo to v9.0.2 2026-04-16 15:40:07 +00:00
Athou
59cc9c2706 add explicit dependency 2026-04-16 17:38:54 +02:00
Athou
40e20a0d33 use exact versions 2026-04-16 16:25:54 +02:00
Jérémie Panzer
a42679acaa Merge pull request #2112 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.15.0
2026-04-16 13:28:55 +02:00
renovate[bot]
fd2e8fcdb8 chore(deps): update node.js to v24.15.0 2026-04-16 10:57:14 +00:00
Athou
ebd5a15152 cache npm dependencies during CI build 2026-04-15 09:22:04 +02:00
Athou
6cdd110304 release 7.1.0 2026-04-15 07:45:15 +02:00
Jérémie Panzer
3181cda2e0 Merge pull request #2110 from Athou/renovate/actions-upload-pages-artifact-5.x
chore(deps): update actions/upload-pages-artifact action to v5
2026-04-14 09:21:31 +02:00
renovate[bot]
2659c7fc02 chore(deps): update actions/upload-pages-artifact action to v5 2026-04-14 06:47:52 +00:00
renovate[bot]
5747ae3429 fix(deps): update dependency axios to ^1.15.0 2026-04-13 05:02:56 +00:00
renovate[bot]
8d60be0e3f chore(deps): lock file maintenance 2026-04-13 01:02:19 +00:00
renovate[bot]
7e3f801a5a chore(deps): update dependency @biomejs/biome to v2.4.11 2026-04-12 16:59:32 +00:00
renovate[bot]
687ef53c65 chore(deps): update dependency vitest to ^4.1.4 2026-04-12 08:58:19 +00:00
renovate[bot]
a1fe9e727e chore(deps): update dependency vite to ^8.0.8 2026-04-12 04:30:28 +00:00
renovate[bot]
6d625c88d3 fix(deps): update react monorepo to ^19.2.5 2026-04-11 20:26:25 +00:00
renovate[bot]
c314e2de6b chore(deps): update dependency vitest to ^4.1.3 2026-04-11 01:40:15 +00:00
renovate[bot]
bd08b95390 chore(deps): update actions/upload-artifact digest to 043fb46 2026-04-10 21:11:12 +00:00
renovate[bot]
aed0cca7e1 chore(deps): update dependency vite to ^8.0.7 2026-04-10 16:53:01 +00:00
renovate[bot]
7d7761fb8d chore(deps): update docker/build-push-action digest to bcafcac 2026-04-10 13:24:27 +00:00
Athou
6d2fb5acc2 refactor 2026-04-10 15:17:27 +02:00
Athou
ec35d846cf Merge branch 'perf/batch-guid-check-on-feed-refresh' of https://github.com/ingokegel/commafeed into ingokegel-perf/batch-guid-check-on-feed-refresh 2026-04-10 14:22:32 +02:00
Ingo Kegel
1d23907652 Use batched IN clause for GUID existence check during feed refresh
Instead of fetching all existing GUID hashes for a feed from the database only check the GUIDs present in the current feed XML using a SQL IN clause. GUIDs are partitioned into batches of 1000.

Also adds an integration test verifying that refreshing an up-to-date feed does not create duplicate entries.
2026-04-10 10:13:37 +02:00
renovate[bot]
efc6c10ace fix(deps): update quarkus.version to v3.34.3 2026-04-08 19:39:10 +00:00
renovate[bot]
47a8ad5244 chore(deps): update debian:13.4 docker digest to 3352c2e 2026-04-07 06:43:23 +00:00
Athou
5d879c4619 update all dependencies 2026-04-07 08:42:07 +02:00
Jérémie Panzer
6e0dd3d064 Merge pull request #2094 from Athou/renovate/typescript-6.x
chore(deps): update dependency typescript to v6
2026-04-07 03:23:40 +02:00
Athou
4657a403d0 adapt to typescript 6.0 api 2026-04-07 02:42:44 +02:00
Jérémie Panzer
bf09a07fe2 Merge pull request #2108 from Athou/renovate/org.passay-passay-2.x
fix(deps): update dependency org.passay:passay to v2
2026-04-07 02:28:34 +02:00
Jérémie Panzer
927802ca88 Merge pull request #2096 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.4.0
2026-04-07 02:13:55 +02:00
Athou
3d5bc9ef0e adapt to passay 2.0 api 2026-04-07 02:11:37 +02:00
Jérémie Panzer
706ad7e44d Merge pull request #2107 from Athou/renovate/react-router-monorepo
fix(deps): update dependency react-router-dom to ^7.14.0
2026-04-07 02:06:30 +02:00
Jérémie Panzer
88206f521b Merge pull request #2102 from Athou/renovate/io.github.git-commit-id-git-commit-id-maven-plugin-10.x
chore(deps): update dependency io.github.git-commit-id:git-commit-id-maven-plugin to v10
2026-04-06 20:27:07 +02:00
renovate[bot]
af786b4c11 fix(deps): update dependency org.passay:passay to v2 2026-04-06 18:26:35 +00:00
renovate[bot]
d4c749c997 fix(deps): update dependency react-router-dom to ^7.14.0 2026-04-06 18:26:28 +00:00
Jérémie Panzer
b5aa25cd0b Merge pull request #2093 from Athou/renovate/actions-deploy-pages-5.x
chore(deps): update actions/deploy-pages action to v5
2026-04-06 20:25:27 +02:00
Jérémie Panzer
ae49fb6dad Merge pull request #2092 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.34.2 (minor)
2026-04-06 20:25:17 +02:00
Jérémie Panzer
3e24d17951 Merge pull request #2090 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.12.1
2026-04-06 20:25:06 +02:00
renovate[bot]
22030e6907 chore(deps): update dependency io.github.git-commit-id:git-commit-id-maven-plugin to v10 2026-04-03 13:24:55 +00:00
renovate[bot]
39fdcb9af3 chore(deps): update dependency @biomejs/biome to v2.4.10 2026-04-02 20:55:13 +00:00
renovate[bot]
866998f98b chore(deps): update docker/login-action digest to 4907a6d 2026-04-02 18:53:38 +00:00
renovate[bot]
0fa71f76e8 fix(deps): update quarkus.version to v3.34.2 2026-04-02 13:57:27 +00:00
renovate[bot]
317f655128 chore(deps): update graalvm/setup-graalvm digest to 60c2672 2026-04-02 09:27:39 +00:00
renovate[bot]
a871426f31 chore(deps): update dependency typescript to v6 2026-04-01 17:58:10 +00:00
renovate[bot]
2caf9ff38a chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.4.0 2026-04-01 17:57:52 +00:00
renovate[bot]
1de3a57d50 chore(deps): update graalvm/setup-graalvm digest to 2149f39 2026-03-31 14:12:33 +00:00
renovate[bot]
bed1355d63 fix(deps): update linguijs monorepo to ^5.9.4 (#2101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-31 05:49:21 +00:00
renovate[bot]
189f9b4f26 fix(deps): update dependency axios to ^1.14.0 2026-03-30 20:50:54 +00:00
renovate[bot]
931b5a22e6 chore(deps): lock file maintenance 2026-03-30 05:50:15 +00:00
renovate[bot]
4c9e0a8b74 fix(deps): update dependency @react-querybuilder/mantine to ^8.14.3 (#2098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-30 05:48:48 +00:00
renovate[bot]
589472314b chore(deps): update dependency npm to v11.12.1 2026-03-29 21:36:53 +00:00
renovate[bot]
f11657fdee fix(deps): update dependency react-querybuilder to ^8.14.3 2026-03-29 21:36:40 +00:00
renovate[bot]
e4ec081357 chore(deps): update dependency vitest to ^4.1.2 2026-03-29 16:49:20 +00:00
renovate[bot]
9e55527a2b chore(deps): update dependency vite to ^8.0.3 2026-03-29 09:18:01 +00:00
renovate[bot]
cca70ffcec chore(deps): update dependency @biomejs/biome to v2.4.9 2026-03-28 12:57:11 +00:00
renovate[bot]
2a6a717ef2 fix(deps): update dependency react-querybuilder to ^8.14.2 (#2095)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-28 04:57:16 +00:00
renovate[bot]
11043554c2 fix(deps): update dependency @react-querybuilder/mantine to ^8.14.2 2026-03-27 21:36:47 +00:00
renovate[bot]
cdbef468d7 fix(deps): update dependency react-router-dom to ^7.13.2 2026-03-27 01:19:23 +00:00
renovate[bot]
4705b75580 chore(deps): update dependency vitest to ^4.1.1 2026-03-26 21:24:23 +00:00
renovate[bot]
245ccf889e chore(deps): update dependency vite to ^8.0.2 2026-03-26 18:20:14 +00:00
renovate[bot]
2ad0a3d622 chore(deps): update actions/deploy-pages action to v5 2026-03-25 20:26:46 +00:00
renovate[bot]
e775f774b5 chore(deps): update node.js to v24.14.1 2026-03-25 01:42:38 +00:00
renovate[bot]
ad2aec44e5 chore(deps): update dependency yaml to ^2.8.3 2026-03-24 12:58:01 +00:00
renovate[bot]
11d3effc28 chore(deps): update dependency jsdom to ^29.0.1 2026-03-23 04:57:54 +00:00
renovate[bot]
2ef12e2868 fix(deps): update mantine monorepo to ^8.3.18 (#2084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-23 04:57:27 +00:00
renovate[bot]
ed8c5ce1ea chore(deps): lock file maintenance 2026-03-23 00:39:56 +00:00
renovate[bot]
33e507efe5 chore(deps): update dependency vite to ^8.0.1 2026-03-22 13:45:17 +00:00
renovate[bot]
c425f5cf6a chore(deps): update dependency @biomejs/biome to v2.4.8 2026-03-21 16:56:21 +00:00
renovate[bot]
fc76d7e609 fix(deps): update dependency @rolldown/plugin-babel to ^0.2.2 2026-03-21 08:51:10 +00:00
renovate[bot]
1b24bf33ed fix(deps): update dependency com.google.protobuf:protobuf-java to v4.34.1 2026-03-20 18:02:04 +00:00
Ingo Kegel
16036897cb Merge branch 'Athou:master' into perf/batch-guid-check-on-feed-refresh 2026-03-19 19:25:09 +01:00
renovate[bot]
58ff378735 fix(deps): update dependency io.github.hakky54:ayza-for-apache5 to v10.0.4 2026-03-19 13:44:43 +00:00
Jérémie Panzer
1cee04a233 Merge pull request #2088 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.4.0
2026-03-19 00:03:44 +01:00
renovate[bot]
ac11a0efb8 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.4.0 2026-03-18 21:29:01 +00:00
renovate[bot]
f2ea1e3f7a fix(deps): update dependency io.quarkus.platform:quarkus-bom to v3.32.4 2026-03-18 18:07:29 +00:00
Ingo Kegel
76dbbdfbf9 batch guid existence check on feed refresh
Instead of querying the database once per entry to check if it already
exists, fetch all existing guids for a feed in a single query upfront
and filter new entries in memory before the per-entry loop.

The per-entry findExisting call inside addEntry is kept as a safety net
for concurrent inserts, but is now only reached for new entries.

Profiling in real usage showed a ~50% reduction in guidHash queries,
with the check falling from the most expensive query to sixth in the ranking.
2026-03-18 18:56:40 +01:00
Jérémie Panzer
153970c146 Merge pull request #2087 from Athou/renovate/jsdom-29.x
chore(deps): update dependency jsdom to v29
2026-03-18 13:46:24 +01:00
renovate[bot]
c21287e642 chore(deps): update dependency jsdom to v29 2026-03-18 12:13:28 +00:00
Athou
284942a82e add feature-request to the list of exempted labels 2026-03-18 13:00:44 +01:00
Jérémie Panzer
e796916e73 Merge pull request #2085 from Athou/renovate/com.ibm.icu-icu4j-78.x
fix(deps): update dependency com.ibm.icu:icu4j to v78.3
2026-03-18 12:42:41 +01:00
renovate[bot]
004db1762c fix(deps): update dependency com.ibm.icu:icu4j to v78.3 2026-03-17 22:35:16 +00:00
renovate[bot]
8e05ba8820 chore(deps): update dependency lint-staged to ^16.4.0 (#2083)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-17 20:33:50 +00:00
renovate[bot]
f4f386b5e5 chore(deps): update dependency lint-staged to ^16.3.4 2026-03-17 09:01:31 +00:00
Jérémie Panzer
ecbf8bec23 Merge pull request #2082 from Athou/renovate/debian-13.x
chore(deps): update debian docker tag to v13.4
2026-03-17 07:29:17 +01:00
renovate[bot]
5d8c09ccda chore(deps): update debian docker tag to v13.4 2026-03-17 00:35:37 +00:00
renovate[bot]
47c5c3d8a0 chore(deps): update dependency io.quarkiverse.playwright:quarkus-playwright to v2.3.3 2026-03-16 21:27:52 +00:00
renovate[bot]
3ddce16d5b chore(deps): update graalvm/setup-graalvm digest to 03e8abf 2026-03-16 18:35:24 +00:00
renovate[bot]
acefdc44d9 chore(deps): lock file maintenance 2026-03-16 01:07:15 +00:00
renovate[bot]
d05c5b9d7f chore(deps): update dependency npm to v11.11.1 2026-03-15 04:53:39 +00:00
renovate[bot]
5d1237d1f4 chore(deps): update ncipollo/release-action digest to 339a818 2026-03-15 01:34:06 +00:00
Athou
6bac8631ac output mantine as its own chunk because it is quite large 2026-03-14 20:20:51 +01:00
Athou
2c803327ad upgrade to vite 8 2026-03-13 22:35:20 +01:00
renovate[bot]
451ae5bc51 chore(deps): update dependency lint-staged to ^16.3.3 2026-03-13 05:46:24 +00:00
renovate[bot]
f41ce8c878 chore(deps): update graalvm/setup-graalvm digest to 6e327d2 2026-03-12 19:12:27 +00:00
renovate[bot]
177c54c813 chore(deps): update dependency maven to v3.9.14 2026-03-12 14:46:09 +00:00
Jérémie Panzer
3765e32fd4 Merge pull request #2079 from Athou/renovate/protobuf-monorepo
fix(deps): update dependency com.google.protobuf:protobuf-java to v4.34.0
2026-03-12 08:19:25 +01:00
renovate[bot]
aab7a16d18 fix(deps): update dependency com.google.protobuf:protobuf-java to v4.34.0 2026-03-12 06:26:40 +00:00
Athou
f2463af63c cel update 2026-03-12 07:25:36 +01:00
renovate[bot]
212c2c3b56 fix(deps): update quarkus.version to v3.32.3 2026-03-11 22:48:05 +00:00
renovate[bot]
3ab00b0cdd chore(deps): update actions/download-artifact digest to 3e5f45b 2026-03-11 17:53:34 +00:00
renovate[bot]
fad85e9299 fix(deps): update dependency org.projectlombok:lombok to v1.18.44 2026-03-11 02:48:12 +00:00
renovate[bot]
3cd5e203e2 fix(deps): update mantine monorepo to ^8.3.16 (#2076)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-09 08:40:30 +00:00
Jérémie Panzer
7b081fa870 Merge pull request #2077 from Athou/renovate/biomejs-biome-2.4.x
chore(deps): update dependency @biomejs/biome to v2.4.6
2026-03-09 09:08:37 +01:00
renovate[bot]
5ce22051d4 chore(deps): update dependency @biomejs/biome to v2.4.6 2026-03-09 07:19:20 +00:00
Jérémie Panzer
1e59489fa5 Merge pull request #2078 from Athou/renovate/lock-file-maintenance
chore(deps): lock file maintenance
2026-03-09 08:18:42 +01:00
renovate[bot]
1e691b1255 chore(deps): lock file maintenance 2026-03-09 01:10:36 +00:00
Jérémie Panzer
61e1bef63f Merge pull request #2074 from Athou/renovate/maven-3.9.x
chore(deps): update dependency maven to v3.9.13
2026-03-07 03:45:01 +01:00
renovate[bot]
46dbb78fbf chore(deps): update dependency maven to v3.9.13 2026-03-07 01:16:46 +00:00
renovate[bot]
99890707b3 chore(deps): update dependency lint-staged to ^16.3.2 2026-03-06 21:20:05 +00:00
Jérémie Panzer
f54c841fdc Merge pull request #2073 from Athou/renovate/react-icons-5.x
fix(deps): update dependency react-icons to ^5.6.0
2026-03-06 17:37:19 +01:00
renovate[bot]
e2a6009ee9 fix(deps): update dependency react-icons to ^5.6.0 2026-03-06 10:39:30 +00:00
Jérémie Panzer
a34dd15040 Merge pull request #2072 from Athou/renovate/docker-build-push-action-7.x
chore(deps): update docker/build-push-action action to v7
2026-03-06 07:20:22 +01:00
renovate[bot]
24c934003d chore(deps): update docker/build-push-action action to v7 2026-03-05 21:51:18 +00:00
renovate[bot]
5300e1a245 chore(deps): update dependency @biomejs/biome to v2.4.5 2026-03-05 18:43:33 +00:00
Jérémie Panzer
90381c670b Merge pull request #2071 from Athou/renovate/org.apache.maven.plugins-maven-resources-plugin-3.x
chore(deps): update dependency org.apache.maven.plugins:maven-resources-plugin to v3.5.0
2026-03-05 14:08:00 +01:00
renovate[bot]
23edea93db chore(deps): update dependency org.apache.maven.plugins:maven-resources-plugin to v3.5.0 2026-03-05 12:25:30 +00:00
Jérémie Panzer
a51712e363 Merge pull request #2070 from Athou/renovate/docker-setup-buildx-action-4.x
chore(deps): update docker/setup-buildx-action action to v4
2026-03-05 13:25:03 +01:00
renovate[bot]
4310e979e1 chore(deps): update docker/setup-buildx-action action to v4 2026-03-05 09:47:18 +00:00
renovate[bot]
f690b76d87 fix(deps): update quarkus.version to v3.32.2 2026-03-05 01:25:51 +00:00
Jérémie Panzer
ac29594b67 Merge pull request #2069 from Athou/renovate/docker-setup-qemu-action-4.x
chore(deps): update docker/setup-qemu-action action to v4
2026-03-04 15:29:50 +01:00
renovate[bot]
93f535bb87 chore(deps): update docker/setup-qemu-action action to v4 2026-03-04 13:38:20 +00:00
Jérémie Panzer
076eb3cf42 Merge pull request #2068 from Athou/renovate/docker-login-action-4.x
chore(deps): update docker/login-action action to v4
2026-03-04 14:35:47 +01:00
Athou
7b2e0fffbd reduce timeout for tests to speed up shutdown 2026-03-04 14:19:56 +01:00
renovate[bot]
8eaab0dbc3 chore(deps): update docker/login-action action to v4 2026-03-04 12:59:09 +00:00
Athou
eaa5bc896e stop the application between tests to make sure that there are no active transactions when we truncate the tables 2026-03-04 13:58:06 +01:00
renovate[bot]
42d1db5fc3 chore(deps): update dependency lint-staged to ^16.3.1 2026-03-04 12:09:08 +00:00
Athou
78c017ddaf wait for tasks to complete when shutting down 2026-03-04 12:49:06 +01:00
renovate[bot]
231551d743 chore(deps): update dependency lint-staged to ^16.3.0 (#2067)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 21:56:21 +00:00
Jérémie Panzer
d0984eaba7 Merge pull request #2066 from Athou/renovate/com.diffplug.spotless-spotless-maven-plugin-3.x
chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.3.0
2026-03-03 06:05:51 +01:00
renovate[bot]
15854a72d1 chore(deps): update dependency com.diffplug.spotless:spotless-maven-plugin to v3.3.0 2026-03-03 01:56:14 +00:00
renovate[bot]
7fd2bf0eda fix(deps): update dependency axios to ^1.13.6 2026-03-02 18:15:22 +00:00
renovate[bot]
6a10a2167e chore(deps): lock file maintenance 2026-03-02 01:25:14 +00:00
Jérémie Panzer
1ba99d255c Merge pull request #2063 from Athou/renovate/npm-11.x
chore(deps): update dependency npm to v11.11.0
2026-03-01 00:43:13 +01:00
renovate[bot]
ff1c2947b6 chore(deps): update dependency npm to v11.11.0 2026-02-28 23:17:20 +00:00
Jérémie Panzer
a690d2e0db Merge pull request #2062 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.3.0
2026-03-01 00:16:55 +01:00
renovate[bot]
a3df327396 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13.3.0 2026-02-28 20:52:06 +00:00
Jérémie Panzer
ede0016d8e Merge pull request #2060 from Athou/renovate/major-github-artifact-actions
chore(deps): update github artifact actions (major)
2026-02-26 22:57:55 +01:00
renovate[bot]
c19b091795 chore(deps): update github artifact actions 2026-02-26 20:39:27 +00:00
renovate[bot]
9e62c8b9f3 fix(deps): update linguijs monorepo to ^5.9.2 2026-02-26 20:39:23 +00:00
renovate[bot]
9f30dc181c fix(deps): update dependency react-router-dom to ^7.13.1 2026-02-26 18:02:38 +00:00
Jérémie Panzer
37fe44f860 Merge pull request #2059 from Athou/renovate/quarkus.version
fix(deps): update quarkus.version to v3.32.1 (minor)
2026-02-25 20:23:16 +01:00
renovate[bot]
68aaab8467 fix(deps): update quarkus.version to v3.32.1 2026-02-25 16:48:25 +00:00
renovate[bot]
b951ed1fcd chore(deps): update debian:13.3 docker digest to 3615a74 2026-02-24 21:53:17 +00:00
Jérémie Panzer
9bd9dc568a Merge pull request #2058 from Athou/renovate/node-24.x
chore(deps): update node.js to v24.14.0
2026-02-24 21:18:33 +01:00
renovate[bot]
29e4356fee chore(deps): update node.js to v24.14.0 2026-02-24 17:22:57 +00:00
renovate[bot]
0ed31eaa99 chore(deps): update dependency @biomejs/biome to v2.4.4 2026-02-23 21:56:45 +00:00
Jérémie Panzer
1e9869b217 Merge pull request #2057 from Athou/renovate/npm-11.10.x
chore(deps): update dependency npm to v11.10.1
2026-02-23 07:21:35 +01:00
renovate[bot]
78cfc2c827 chore(deps): update dependency npm to v11.10.1 2026-02-23 05:33:44 +00:00
renovate[bot]
a6e5a0d125 chore(deps): lock file maintenance 2026-02-23 00:55:55 +00:00
renovate[bot]
ea13aecd27 chore(deps): update dependency @biomejs/biome to v2.4.3 2026-02-22 21:47:30 +00:00
Athou
d838e8f28f fix occasional flicker 2026-02-22 14:36:25 +01:00
Athou
c9a7b9e17c ensure the indicator is not shown above the app header 2026-02-22 11:47:13 +01:00
Athou
8fe2d0bc0e fix typo 2026-02-21 23:58:49 +01:00
Athou
71e2f1e1e6 kill biome on build to prevent unlink errors on Windows 2026-02-21 23:55:08 +01:00
Athou
1ce9d1b9b2 prevent title line wrapping 2026-02-21 23:39:38 +01:00
Athou
b3d6ae467f reduce code duplication 2026-02-21 23:24:03 +01:00
Athou
da8d720dc4 don't show a pointer on hover 2026-02-21 22:58:49 +01:00
Athou
824c38f8ce show unread count on mobile only 2026-02-21 22:52:32 +01:00
Athou
b0579a70d8 Merge branch 'master' of https://github.com/lpoirothattermann/commafeed into lpoirothattermann-master 2026-02-21 22:28:03 +01:00
Louis POIROT--HATTERMANN
f9fe2d0976 feat: add unread count badge next to title 2026-02-21 18:54:23 +01:00
285 changed files with 20314 additions and 18476 deletions

1
.github/stale.yml vendored
View File

@@ -7,6 +7,7 @@ exemptLabels:
- pinned
- security
- enhancement
- feature-request
- bug
# Label to use when marking an issue as stale
staleLabel: wontfix

View File

@@ -23,18 +23,24 @@ jobs:
steps:
# Checkout
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
# Setup
- name: Set up GraalVM
uses: graalvm/setup-graalvm@54b4f5a65c1a84b2fdfdc2078fe43df32819e4b1 # v1
uses: graalvm/setup-graalvm@5298d94fb55a4f185c602eeac5de1b553882abe2 # v1.6.4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: "graalvm"
cache: "maven"
- name: Set up NodeJS cache
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: "npm"
cache-dependency-path: 'commafeed-client/package-lock.json'
- name: Install Playwright dependencies
run: sudo apt-get install -y libgbm1
if: matrix.os != 'windows-latest'
@@ -67,20 +73,20 @@ jobs:
# Upload artifacts
- name: Upload cross-platform app
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.os == 'ubuntu-latest' # we only need to upload the cross-platform artifact once per database
with:
name: commafeed-${{ matrix.database }}-jvm
path: commafeed-server/target/commafeed-*.zip
- name: Upload native executable
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: commafeed-${{ matrix.database }}-${{ runner.os }}-${{ runner.arch }}
path: commafeed-server/target/commafeed-*-runner*
- name: Upload pages
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
if: matrix.os == 'ubuntu-latest' && matrix.database == 'h2' # we only need to upload the pages once
with:
path: target/pages
@@ -98,23 +104,23 @@ jobs:
steps:
# Checkout
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
# Setup
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Install required packages
run: sudo apt-get install -y rename unzip
# Prepare artifacts
- name: Download artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: commafeed-${{ matrix.database }}-*
path: ./artifacts
@@ -135,7 +141,7 @@ jobs:
# Docker
- name: Login to Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
if: ${{ env.DOCKERHUB_USERNAME != '' }}
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -143,7 +149,7 @@ jobs:
## build but don't push for PRs and renovate
- name: Docker build - native
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
file: commafeed-server/src/main/docker/Dockerfile.native
@@ -151,7 +157,7 @@ jobs:
platforms: linux/amd64,linux/arm64/v8
- name: Docker build - jvm
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
file: commafeed-server/src/main/docker/Dockerfile.jvm
@@ -160,7 +166,7 @@ jobs:
## build and push tag
- name: Docker build and push tag - native
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
if: ${{ github.ref_type == 'tag' }}
with:
context: .
@@ -172,7 +178,7 @@ jobs:
athou/commafeed:${{ github.ref_name }}-${{ matrix.database }}
- name: Docker build and push tag - jvm
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
if: ${{ github.ref_type == 'tag' }}
with:
context: .
@@ -185,7 +191,7 @@ jobs:
## build and push master
- name: Docker build and push master - native
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
if: ${{ github.ref_name == 'master' }}
with:
context: .
@@ -195,7 +201,7 @@ jobs:
tags: athou/commafeed:master-${{ matrix.database }}
- name: Docker build and push master - jvm
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
if: ${{ github.ref_name == 'master' }}
with:
context: .
@@ -215,12 +221,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: commafeed-*
path: ./artifacts
@@ -230,13 +236,13 @@ jobs:
run: chmod +x artifacts/*-runner
- name: Extract Changelog Entry
uses: mindsers/changelog-reader-action@32aa5b4c155d76c94e4ec883a223c947b2f02656 # v2
uses: mindsers/changelog-reader-action@1faaf50aa09d5793d9a100819973df801febfb31 # v2.4.0
id: changelog_reader
with:
version: ${{ github.ref_name }}
- name: Create GitHub release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
with:
name: CommaFeed ${{ github.ref_name }}
body: ${{ steps.changelog_reader.outputs.changes }}
@@ -249,12 +255,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Update Docker Hub Description
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -274,5 +280,5 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
- uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
id: deployment

View File

@@ -1,3 +1,3 @@
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip

View File

@@ -1,5 +1,35 @@
# Changelog
## [7.3.0]
- Add support for the Google Reader API for native mobile apps that don't support the Fever API
- The profile menu now works again on mobile when action buttons are at the bottom of the screen
- More SSRF protection regarding IPv6
- Changed the default value of `blockLocalAddresses` to make CommaFeed more secure out-of-the-box. See the "Access to
local address blocked" section of the README if you subscribe to feeds that are only available on your local network.
## [7.2.1]
- correctly handle favicons hosted at the root of the domain for feeds with non-default ports
- prevent XXS injections in third party apps. The CommaFeed React client was already stripping malicious "javascript:" URLs, they are now filtered during feed parsing and no longer stored in the database/served through the REST API.
## [7.2.0]
- Add support for icons declared in feeds. This is useful for feeds exposed by RSS bridges (#2048)
- Search now also works for starred entries (#2217)
- The error message when subscribing to a feed is now properly displayed again
- Small performance improvements to the feed refresh engine
- Removed the Pocket sharing feature because the Pocket service has been discontinued
- Prevent the image proxy feature to access any URL to avoid SSRF attacks
- Strengthened the SSRF protection by also blocking ULA and CGNAT ranges
- Prevent DDOS attacks by filtering OPML files during imports
## [7.1.0]
- Display the unread count in the header on mobile, since the tree is hidden by default and the unread count is not visible otherwise (#2055)
- The feed refresh engine is now a little bit more performant (#2089)
- On shutdown, give the feed refresh engine some time to finish refreshing feeds before killing it. This is controlled by the new `commafeed.shutdown-timeout` setting, which defaults to 2 seconds
## [7.0.0]
- Replaced the JEXL filter expression for marking feed entries as read automatically with a user-friendly visual query builder. Expressions are now evaluated with Common Expression Language, which is safer than JEXL and sanboxed by default.

View File

@@ -15,7 +15,7 @@ Google Reader inspired self-hosted RSS reader, based on Quarkus and React/TypeSc
- Supports thousands of users and millions of feeds
- OPML import/export
- REST API
- Fever-compatible API for native mobile apps
- Fever and Google Reader API for native mobile apps
- Can automatically mark articles as read based on user-defined rules
- Push notifications when new articles are published
- Highly customizable with [custom CSS](https://athou.github.io/commafeed/documentation/custom-css) and JavaScript
@@ -70,8 +70,8 @@ memory usage.
./mvnw clean package [-P<database> [-Pnative]] [-DskipTests]
- `<database>` can be one of `h2`, `postgresql`, `mysql` or `mariadb`. The default is `h2`.
- `-Pnative` compiles the application to native code. This requires GraalVM to be installed (`GRAALVM_HOME` environment
variable pointing to a GraalVM installation).
- `-Pnative` compiles the application to native code. This requires either GraalVM to be installed (`GRAALVM_HOME` environment
variable pointing to a GraalVM installation) or a container environment to be available (docker/podman/...).
- `-DskipTests` to speed up the build process by skipping tests.
When the build is complete:
@@ -159,6 +159,14 @@ named [Semeru](https://developer.ibm.com/languages/java/semeru-runtimes/download
This is the JVM used in
the [Docker image](https://github.com/Athou/commafeed/blob/master/commafeed-server/src/main/docker/Dockerfile.jvm).
## FAQ
### Getting "Access to local address blocked" when adding a feed
CommaFeed blocks access to local resources by default to prevent [SSRF](https://en.wikipedia.org/wiki/Server-side_request_forgery) attacks.
If you want to subscribe to feeds that are only available on your local network, you can disable this security measure by setting the `commafeed.http-client.block-local-addresses` variable to `false`.
Do this only if you trust all users of your CommaFeed instance not to access private resources.
## Translation
Files for internationalization are

View File

@@ -1 +0,0 @@
cd commafeed-client && npx lint-staged

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.2/schema.json",
"$schema": "https://biomejs.dev/schemas/2.5.7/schema.json",
"formatter": {
"indentStyle": "space",
"indentWidth": 4,

View File

@@ -1,4 +1,5 @@
import type { LinguiConfig } from "@lingui/conf"
import { formatter } from "@lingui/format-po"
const config: LinguiConfig = {
locales: [
@@ -38,11 +39,9 @@ const config: LinguiConfig = {
exclude: ["src/locales/**"],
},
],
format: "po",
formatOptions: {
origins: true,
format: formatter({
lineNumbers: false,
},
}),
sourceLocale: "en",
fallbackLocales: {
default: "en",

View File

@@ -1,3 +0,0 @@
export default {
"src/**/*.{js,jsx,ts,tsx}": () => ["npm run i18n:extract", "git diff --exit-code commafeed-client/src/locales/en/messages.po"],
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,75 +12,74 @@
"test:ci": "vitest run",
"lint": "biome check",
"lint:fix": "biome check --write",
"i18n:extract": "lingui extract --clean",
"prepare": "cd .. && husky ./commafeed-client/.husky"
"i18n:extract": "lingui extract --clean"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@fontsource/open-sans": "^5.2.7",
"@lingui/core": "^5.9.1",
"@lingui/react": "^5.9.1",
"@mantine/core": "^8.3.15",
"@mantine/form": "^8.3.15",
"@mantine/hooks": "^8.3.15",
"@mantine/modals": "^8.3.15",
"@mantine/notifications": "^8.3.15",
"@mantine/spotlight": "^8.3.15",
"@monaco-editor/react": "^4.7.0",
"@react-querybuilder/mantine": "^8.14.0",
"@reduxjs/toolkit": "^2.11.2",
"axios": "^1.13.5",
"dayjs": "^1.11.19",
"escape-string-regexp": "^5.0.0",
"interweave": "^13.1.1",
"monaco-editor": "^0.55.1",
"mousetrap": "^1.6.5",
"react": "^19.2.4",
"react-async-hook": "^4.0.0",
"react-contexify": "^6.0.0",
"react-dom": "^19.2.4",
"react-draggable": "^4.5.0",
"react-icons": "^5.5.0",
"react-infinite-scroller": "^1.2.6",
"react-querybuilder": "^8.14.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.13.0",
"react-swipeable": "^7.0.2",
"style-to-object": "^1.0.14",
"throttle-debounce": "^5.0.2",
"tinycon": "^0.6.8",
"tss-react": "^4.9.20",
"websocket-heartbeat-js": "^1.1.3"
"@emotion/react": "11.14.0",
"@fontsource/open-sans": "5.3.0",
"@lingui/core": "6.6.0",
"@lingui/react": "6.6.0",
"@mantine/core": "9.5.1",
"@mantine/dates": "9.5.1",
"@mantine/form": "9.5.1",
"@mantine/hooks": "9.5.1",
"@mantine/modals": "9.5.1",
"@mantine/notifications": "9.5.1",
"@mantine/spotlight": "9.5.1",
"@monaco-editor/react": "4.7.0",
"@react-querybuilder/mantine": "8.22.3",
"@reduxjs/toolkit": "2.12.0",
"@rolldown/plugin-babel": "0.2.3",
"axios": "1.19.0",
"dayjs": "1.11.21",
"escape-string-regexp": "5.0.0",
"interweave": "13.1.1",
"monaco-editor": "0.56.0",
"mousetrap": "1.6.5",
"react": "19.2.8",
"react-async-hook": "4.0.0",
"react-contexify": "6.0.0",
"react-dom": "19.2.8",
"react-draggable": "4.7.1",
"react-icons": "5.7.0",
"react-infinite-scroller": "1.2.6",
"react-querybuilder": "8.22.3",
"react-redux": "9.3.0",
"react-router-dom": "7.18.2",
"react-swipeable": "7.0.2",
"style-to-object": "2.0.2",
"throttle-debounce": "5.0.2",
"tinycon": "0.6.8",
"tss-react": "4.9.21",
"websocket-heartbeat-js": "1.1.3"
},
"devDependencies": {
"@biomejs/biome": "^2.4.2",
"@lingui/babel-plugin-lingui-macro": "^5.9.1",
"@lingui/cli": "^5.9.1",
"@lingui/vite-plugin": "^5.9.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/mousetrap": "^1.6.15",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/react-infinite-scroller": "^1.2.5",
"@types/throttle-debounce": "^5.0.2",
"@types/tinycon": "^0.6.7",
"@vitejs/plugin-react": "^5.1.4",
"@babel/types": "^8.0.4",
"@biomejs/biome": "2.5.7",
"@lingui/babel-plugin-lingui-macro": "6.6.0",
"@lingui/cli": "6.6.0",
"@lingui/vite-plugin": "6.6.0",
"@testing-library/jest-dom": "7.0.0",
"@testing-library/react": "16.3.2",
"@testing-library/user-event": "14.6.3",
"@types/mousetrap": "1.6.15",
"@types/react": "19.2.18",
"@types/react-dom": "19.2.4",
"@types/react-infinite-scroller": "1.2.5",
"@types/throttle-debounce": "5.0.2",
"@types/tinycon": "0.6.7",
"@vitejs/plugin-react": "6.0.5",
"babel-plugin-react-compiler": "1.0.0",
"husky": "^9.1.7",
"jsdom": "^28.1.0",
"lint-staged": "^16.2.7",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vite-plugin-checker": "^0.12.0",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.0.18",
"yaml": "^2.8.2"
"jsdom": "30.0.1",
"typescript": "7.0.2",
"vite": "8.2.1",
"vite-plugin-checker": "0.14.5",
"vitest": "4.1.10",
"yaml": "2.9.0"
},
"overrides": {
"react-infinite-scroller": {
"react": "^19.2.4"
"react": "19.2.8"
}
}
}

View File

@@ -6,16 +6,16 @@
<parent>
<groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId>
<version>7.0.0</version>
<version>7.3.0</version>
</parent>
<artifactId>commafeed-client</artifactId>
<name>CommaFeed Client</name>
<properties>
<!-- renovate: datasource=node-version depName=node -->
<node.version>v24.13.1</node.version>
<node.version>v24.19.0</node.version>
<!-- renovate: datasource=npm depName=npm -->
<npm.version>11.10.0</npm.version>
<npm.version>12.0.2</npm.version>
</properties>
<build>
@@ -23,7 +23,7 @@
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>2.0.0</version>
<version>2.0.2</version>
<?m2e ignore?>
<executions>
<execution>
@@ -72,7 +72,7 @@
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
<executions>
<execution>
<id>copy web interface to resources</id>
@@ -94,4 +94,49 @@
</plugin>
</plugins>
</build>
<profiles>
<!-- This profile is used to kill the Biome process on Windows -->
<!-- npm ci can fail if Biome is running (e.g., in the IDE) because it locks some files -->
<profile>
<id>kill-biome</id>
<activation>
<os>
<family>Windows</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.3</version>
<executions>
<execution>
<id>kill-biome</id>
<phase>initialize</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>taskkill</executable>
<arguments>
<argument>/F</argument>
<argument>/IM</argument>
<argument>biome.exe</argument>
</arguments>
<successCodes>
<successCode>0</successCode>
<!-- taskkill returns 128 if the process is not found, which is fine in this case -->
<successCode>128</successCode>
</successCodes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -1,62 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="393.84613"
width="393.84613"
viewBox="0 0 5.0480766 5.0480766"
version="1.1"
id="svg3"
sodipodi:docname="favicon.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs3" />
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.21875"
inkscape:cx="207.17949"
inkscape:cy="187.07692"
inkscape:window-width="1440"
inkscape:window-height="855"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3" />
<rect
fill="#f88a14"
rx="0.53846151"
ry="0.53846151"
height="5.0480766"
width="5.0480766"
id="rect1"
x="0"
y="0"
style="stroke-width:0.769231" />
<path
d="m 1.3450904,0.64548657 c 2.9002,0 2.9002,2.91010003 2.9002,2.91010003"
fill="none"
stroke="#ffffff"
stroke-linecap="round"
stroke-width="0.78125"
id="path1" />
<path
d="m 1.3377904,1.9915866 c 1.5705,-0.00908 1.5705,1.5639 1.5705,1.5639"
fill="none"
stroke="#ffffff"
stroke-linecap="round"
stroke-width="0.78125"
id="path2" />
<path
d="m 2.0192904,3.5227866 c 0,0.23366 -0.10712,0.47418 -0.24663,0.6537 -0.1814,0.2333 -0.5705,0.5618 -0.6913,0.5653 0.0402,-0.0662 0.263,-0.5654 0.2563,-0.5654 -0.36423004,0 -0.65950004,-0.29265 -0.65950004,-0.65365 0,-0.361 0.29527,-0.65365 0.65950004,-0.65365 0.36423,0 0.68159,0.29265 0.68159,0.65365 z"
fill="#ffffff"
id="path3" />
height="393.84613"
width="393.84613"
viewBox="0 0 5.0480766 5.0480766"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg">
<title>CommaFeed favicon</title>
<defs
id="defs3" />
<rect
fill="#f88a14"
rx="0.53846151"
ry="0.53846151"
height="5.0480766"
width="5.0480766"
id="rect1"
x="0"
y="0"
style="stroke-width:0.769231" />
<path
d="m 1.3450904,0.64548657 c 2.9002,0 2.9002,2.91010003 2.9002,2.91010003"
fill="none"
stroke="#ffffff"
stroke-linecap="round"
stroke-width="0.78125"
id="path1" />
<path
d="m 1.3377904,1.9915866 c 1.5705,-0.00908 1.5705,1.5639 1.5705,1.5639"
fill="none"
stroke="#ffffff"
stroke-linecap="round"
stroke-width="0.78125"
id="path2" />
<path
d="m 2.0192904,3.5227866 c 0,0.23366 -0.10712,0.47418 -0.24663,0.6537 -0.1814,0.2333 -0.5705,0.5618 -0.6913,0.5653 0.0402,-0.0662 0.263,-0.5654 0.2563,-0.5654 -0.36423004,0 -0.65950004,-0.29265 -0.65950004,-0.65365 0,-0.361 0.29527,-0.65365 0.65950004,-0.65365 0.36423,0 0.68159,0.29265 0.68159,0.65365 z"
fill="#ffffff"
id="path3" />
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,24 +1,19 @@
import { i18n } from "@lingui/core"
import { I18nProvider } from "@lingui/react"
import { MantineProvider } from "@mantine/core"
import { MantineProvider, v8CssVariablesResolver } from "@mantine/core"
import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications"
import type React from "react"
import { useEffect, useState } from "react"
import { useEffect } from "react"
import { HashRouter, Navigate, Route, Routes, useNavigate } from "react-router-dom"
import Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectTo } from "@/app/redirect/slice"
import { redirectToInitialSetup } from "@/app/redirect/thunks"
import { reloadServerInfos } from "@/app/server/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { categoryUnreadCount } from "@/app/utils"
import { DisablePullToRefresh } from "@/components/DisablePullToRefresh"
import { ErrorBoundary } from "@/components/ErrorBoundary"
import { Header } from "@/components/header/Header"
import { Tree } from "@/components/sidebar/Tree"
import { useAppLoading } from "@/hooks/useAppLoading"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useI18n } from "@/i18n"
import { AdminUsersPage } from "@/pages/admin/AdminUsersPage"
import { MetricsPage } from "@/pages/admin/MetricsPage"
@@ -48,6 +43,8 @@ function Providers(
<I18nProvider i18n={i18n}>
<MantineProvider
defaultColorScheme="auto"
// keep using css variables from mantine v8
cssVariablesResolver={v8CssVariablesResolver}
theme={{
primaryColor: primaryColor,
fontFamily: "Open Sans",
@@ -144,83 +141,8 @@ function RedirectHandler() {
return null
}
function UnreadCountTitleHandler({
enabled,
}: Readonly<{
enabled?: boolean
}>) {
const root = useAppSelector(state => state.tree.rootCategory)
const unreadCount = categoryUnreadCount(root)
return <title>{enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}</title>
}
function UnreadCountFaviconHandler({ enabled }: { enabled?: boolean }) {
const root = useAppSelector(state => state.tree.rootCategory)
const unreadCount = categoryUnreadCount(root)
useEffect(() => {
if (enabled && unreadCount > 0) {
Tinycon.setBubble(unreadCount)
} else {
Tinycon.reset()
}
}, [unreadCount, enabled])
return null
}
function BrowserExtensionBadgeUnreadCountHandler() {
const root = useAppSelector(state => state.tree.rootCategory)
const { setBadgeUnreadCount } = useBrowserExtension()
useEffect(() => {
if (!root) return
const unreadCount = categoryUnreadCount(root)
setBadgeUnreadCount(unreadCount)
}, [root, setBadgeUnreadCount])
return null
}
function CustomJsHandler() {
const [scriptLoaded, setScriptLoaded] = useState(false)
const { loading } = useAppLoading()
useEffect(() => {
if (scriptLoaded || loading) {
return
}
const script = document.createElement("script")
script.src = "custom_js.js"
script.async = true
document.body.appendChild(script)
setScriptLoaded(true)
return () => script.remove()
}, [scriptLoaded, loading])
return null
}
function CustomCssHandler() {
useEffect(() => {
const link = document.createElement("link")
link.rel = "stylesheet"
link.type = "text/css"
link.href = "custom_css.css"
document.head.appendChild(link)
return () => link.remove()
}, [])
return null
}
export function App() {
useI18n()
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
const disablePullToRefresh = useAppSelector(state => state.user.settings?.disablePullToRefresh)
const dispatch = useAppDispatch()
useEffect(() => {
@@ -229,13 +151,6 @@ export function App() {
return (
<Providers>
<UnreadCountTitleHandler enabled={unreadCountTitle} />
<UnreadCountFaviconHandler enabled={unreadCountFavicon} />
<BrowserExtensionBadgeUnreadCountHandler />
<CustomJsHandler />
<CustomCssHandler />
<DisablePullToRefresh enabled={disablePullToRefresh} />
<HashRouter>
<InitialSetupHandler />
<RedirectHandler />

View File

@@ -1,6 +1,6 @@
import type { IconType } from "react-icons"
import { FaAt } from "react-icons/fa"
import { SiBuffer, SiFacebook, SiGmail, SiInstapaper, SiPocket, SiTumblr, SiX } from "react-icons/si"
import { SiBuffer, SiFacebook, SiGmail, SiInstapaper, SiTumblr, SiX } from "react-icons/si"
import type { Category, Entry, SharingSettings } from "./types"
const categories: Record<string, Omit<Category, "name">> = {
@@ -58,12 +58,6 @@ const sharing: {
color: "#375672",
url: (url, desc) => `https://www.tumblr.com/share/link?url=${url}&name=${desc}`,
},
pocket: {
label: "Pocket",
icon: SiPocket,
color: "#EF4154",
url: (url, desc) => `https://getpocket.com/save?url=${url}&title=${desc}`,
},
instapaper: {
label: "Instapaper",
icon: SiInstapaper,

View File

@@ -1,5 +1,5 @@
import { configureStore } from "@reduxjs/toolkit"
import { type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
import { shallowEqual, type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
import { entriesSlice } from "@/app/entries/slice"
import { redirectSlice } from "@/app/redirect/slice"
import { serverSlice } from "@/app/server/slice"
@@ -41,3 +41,4 @@ export type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
export const useShallowEqualAppSelector: TypedUseSelectorHook<RootState> = selector => useSelector(selector, shallowEqual)

View File

@@ -249,7 +249,6 @@ export interface SharingSettings {
facebook: boolean
twitter: boolean
tumblr: boolean
pocket: boolean
instapaper: boolean
buffer: boolean
}

View File

@@ -1,32 +1,8 @@
import { t } from "@lingui/core/macro"
import { showNotification } from "@mantine/notifications"
import { createSlice, isAnyOf, type PayloadAction } from "@reduxjs/toolkit"
import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import type { LocalSettings, Settings, UserModel, ViewMode } from "@/app/types"
import {
changeCustomContextMenu,
changeDisablePullToRefresh,
changeEntriesToKeepOnTopWhenScrolling,
changeExternalLinkIconDisplayMode,
changeLanguage,
changeMarkAllAsReadConfirmation,
changeMarkAllAsReadNavigateToUnread,
changeMobileFooter,
changePrimaryColor,
changePushNotificationSettings,
changeReadingMode,
changeReadingOrder,
changeScrollMarks,
changeScrollMode,
changeScrollSpeed,
changeSharingSetting,
changeShowRead,
changeStarIconDisplayMode,
changeUnreadCountFavicon,
changeUnreadCountTitle,
reloadProfile,
reloadSettings,
reloadTags,
} from "./thunks"
import { changeSettings, reloadProfile, reloadSettings, reloadTags } from "./thunks"
interface UserState {
settings?: Settings
@@ -73,115 +49,16 @@ export const userSlice = createSlice({
builder.addCase(reloadTags.fulfilled, (state, action) => {
state.tags = action.payload
})
builder.addCase(changeReadingMode.pending, (state, action) => {
builder.addCase(changeSettings.pending, (state, action) => {
if (!state.settings) return
state.settings.readingMode = action.meta.arg
state.settings = { ...state.settings, ...action.meta.arg }
})
builder.addCase(changeReadingOrder.pending, (state, action) => {
if (!state.settings) return
state.settings.readingOrder = action.meta.arg
builder.addCase(changeSettings.fulfilled, () => {
showNotification({
message: t`Settings saved.`,
color: "green",
})
})
builder.addCase(changeLanguage.pending, (state, action) => {
if (!state.settings) return
state.settings.language = action.meta.arg
})
builder.addCase(changeScrollSpeed.pending, (state, action) => {
if (!state.settings) return
state.settings.scrollSpeed = action.meta.arg ? 400 : 0
})
builder.addCase(changeShowRead.pending, (state, action) => {
if (!state.settings) return
state.settings.showRead = action.meta.arg
})
builder.addCase(changeScrollMarks.pending, (state, action) => {
if (!state.settings) return
state.settings.scrollMarks = action.meta.arg
})
builder.addCase(changeScrollMode.pending, (state, action) => {
if (!state.settings) return
state.settings.scrollMode = action.meta.arg
})
builder.addCase(changeEntriesToKeepOnTopWhenScrolling.pending, (state, action) => {
if (!state.settings) return
state.settings.entriesToKeepOnTopWhenScrolling = action.meta.arg
})
builder.addCase(changeStarIconDisplayMode.pending, (state, action) => {
if (!state.settings) return
state.settings.starIconDisplayMode = action.meta.arg
})
builder.addCase(changeExternalLinkIconDisplayMode.pending, (state, action) => {
if (!state.settings) return
state.settings.externalLinkIconDisplayMode = action.meta.arg
})
builder.addCase(changeMarkAllAsReadConfirmation.pending, (state, action) => {
if (!state.settings) return
state.settings.markAllAsReadConfirmation = action.meta.arg
})
builder.addCase(changeMarkAllAsReadNavigateToUnread.pending, (state, action) => {
if (!state.settings) return
state.settings.markAllAsReadNavigateToNextUnread = action.meta.arg
})
builder.addCase(changeCustomContextMenu.pending, (state, action) => {
if (!state.settings) return
state.settings.customContextMenu = action.meta.arg
})
builder.addCase(changeMobileFooter.pending, (state, action) => {
if (!state.settings) return
state.settings.mobileFooter = action.meta.arg
})
builder.addCase(changeUnreadCountTitle.pending, (state, action) => {
if (!state.settings) return
state.settings.unreadCountTitle = action.meta.arg
})
builder.addCase(changeUnreadCountFavicon.pending, (state, action) => {
if (!state.settings) return
state.settings.unreadCountFavicon = action.meta.arg
})
builder.addCase(changeDisablePullToRefresh.pending, (state, action) => {
if (!state.settings) return
state.settings.disablePullToRefresh = action.meta.arg
})
builder.addCase(changePrimaryColor.pending, (state, action) => {
if (!state.settings) return
state.settings.primaryColor = action.meta.arg
})
builder.addCase(changeSharingSetting.pending, (state, action) => {
if (!state.settings) return
state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value
})
builder.addCase(changePushNotificationSettings.pending, (state, action) => {
if (!state.settings) return
state.settings.pushNotificationSettings = action.meta.arg
})
builder.addMatcher(
isAnyOf(
changeLanguage.fulfilled,
changeScrollSpeed.fulfilled,
changeShowRead.fulfilled,
changeScrollMarks.fulfilled,
changeScrollMode.fulfilled,
changeEntriesToKeepOnTopWhenScrolling.fulfilled,
changeStarIconDisplayMode.fulfilled,
changeExternalLinkIconDisplayMode.fulfilled,
changeMarkAllAsReadConfirmation.fulfilled,
changeMarkAllAsReadNavigateToUnread.fulfilled,
changeCustomContextMenu.fulfilled,
changeMobileFooter.fulfilled,
changeUnreadCountTitle.fulfilled,
changeUnreadCountFavicon.fulfilled,
changeDisablePullToRefresh.fulfilled,
changePrimaryColor.fulfilled,
changeSharingSetting.fulfilled,
changePushNotificationSettings.fulfilled
),
() => {
showNotification({
message: t`Settings saved.`,
color: "green",
})
}
)
},
})

View File

@@ -1,7 +1,6 @@
import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client"
import { reloadEntries } from "@/app/entries/thunks"
import type { IconDisplayMode, PushNotificationSettings, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } from "@/app/types"
import type { Settings } from "@/app/types"
export const reloadSettings = createAppAsyncThunk("settings/reload", async () => await client.user.getSettings().then(r => r.data))
@@ -9,163 +8,8 @@ export const reloadProfile = createAppAsyncThunk("profile/reload", async () => a
export const reloadTags = createAppAsyncThunk("entries/tags", async () => await client.entry.getTags().then(r => r.data))
export const changeReadingMode = createAppAsyncThunk("settings/readingMode", (readingMode: ReadingMode, thunkApi) => {
export const changeSettings = createAppAsyncThunk("settings/change", async (newSettings: Partial<Settings>, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, readingMode })
thunkApi.dispatch(reloadEntries())
await client.user.saveSettings({ ...settings, ...newSettings })
})
export const changeReadingOrder = createAppAsyncThunk("settings/readingOrder", (readingOrder: ReadingOrder, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, readingOrder })
thunkApi.dispatch(reloadEntries())
})
export const changeLanguage = createAppAsyncThunk("settings/language", (language: string, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, language })
})
export const changeScrollSpeed = createAppAsyncThunk("settings/scrollSpeed", (speed: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, scrollSpeed: speed ? 400 : 0 })
})
export const changeShowRead = createAppAsyncThunk("settings/showRead", (showRead: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, showRead })
})
export const changeScrollMarks = createAppAsyncThunk("settings/scrollMarks", (scrollMarks: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, scrollMarks })
})
export const changeScrollMode = createAppAsyncThunk("settings/scrollMode", (scrollMode: ScrollMode, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, scrollMode })
})
export const changeEntriesToKeepOnTopWhenScrolling = createAppAsyncThunk(
"settings/entriesToKeepOnTopWhenScrolling",
(entriesToKeepOnTopWhenScrolling: number, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, entriesToKeepOnTopWhenScrolling })
}
)
export const changeStarIconDisplayMode = createAppAsyncThunk(
"settings/starIconDisplayMode",
(starIconDisplayMode: IconDisplayMode, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, starIconDisplayMode })
}
)
export const changeExternalLinkIconDisplayMode = createAppAsyncThunk(
"settings/externalLinkIconDisplayMode",
(externalLinkIconDisplayMode: IconDisplayMode, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, externalLinkIconDisplayMode })
}
)
export const changeMarkAllAsReadConfirmation = createAppAsyncThunk(
"settings/markAllAsReadConfirmation",
(markAllAsReadConfirmation: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, markAllAsReadConfirmation })
}
)
export const changeMarkAllAsReadNavigateToUnread = createAppAsyncThunk(
"settings/markAllAsReadNavigateToUnread",
(markAllAsReadNavigateToNextUnread: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, markAllAsReadNavigateToNextUnread })
}
)
export const changeCustomContextMenu = createAppAsyncThunk("settings/customContextMenu", (customContextMenu: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, customContextMenu })
})
export const changeMobileFooter = createAppAsyncThunk("settings/mobileFooter", (mobileFooter: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, mobileFooter })
})
export const changeUnreadCountTitle = createAppAsyncThunk("settings/unreadCountTitle", (unreadCountTitle: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, unreadCountTitle })
})
export const changeUnreadCountFavicon = createAppAsyncThunk("settings/unreadCountFavicon", (unreadCountFavicon: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, unreadCountFavicon })
})
export const changeDisablePullToRefresh = createAppAsyncThunk(
"settings/disablePullToRefresh",
(disablePullToRefresh: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, disablePullToRefresh })
}
)
export const changePrimaryColor = createAppAsyncThunk("settings/primaryColor", (primaryColor: string, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, primaryColor })
})
export const changeSharingSetting = createAppAsyncThunk(
"settings/sharingSetting",
(
sharingSetting: {
site: keyof SharingSettings
value: boolean
},
thunkApi
) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({
...settings,
sharingSettings: {
...settings.sharingSettings,
[sharingSetting.site]: sharingSetting.value,
},
})
}
)
export const changePushNotificationSettings = createAppAsyncThunk(
"settings/pushNotificationSettings",
(pushNotificationSettings: PushNotificationSettings, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({
...settings,
pushNotificationSettings,
})
}
)

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg height="512" width="512" viewBox="0 0 6.5625 6.5625" xmlns="http://www.w3.org/2000/svg">
<title>CommaFeed logo</title>
<rect fill="#f88a14" rx="0.7" ry="0.7" height="6.5625" width="6.5625" />
<path d="m1.9761,1.5289c2.9002,0,2.9002,2.9101,2.9002,2.9101" fill="none" stroke="#FFF" stroke-linecap="round"
stroke-width="0.78125" />
stroke-width="0.78125" />
<path d="m1.9688,2.875c1.5705-0.00908,1.5705,1.5639,1.5705,1.5639" fill="none" stroke="#FFF" stroke-linecap="round"
stroke-width="0.78125" />
stroke-width="0.78125" />
<path d="m2.6503,4.4062c0,0.23366-0.10712,0.47418-0.24663,0.6537-0.1814,0.2333-0.5705,0.5618-0.6913,0.5653,0.0402-0.0662,0.263-0.5654,0.2563-0.5654-0.36423,0-0.6595-0.29265-0.6595-0.65365s0.29527-0.65365,0.6595-0.65365,0.68159,0.29265,0.68159,0.65365z"
fill="#FFF" />
fill="#FFF" />
</svg>

Before

Width:  |  Height:  |  Size: 791 B

After

Width:  |  Height:  |  Size: 831 B

View File

@@ -1,7 +1,6 @@
import type { MessageDescriptor } from "@lingui/core"
import { useLingui } from "@lingui/react"
import { ActionIcon, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon"
import { ActionIcon, type ActionIconVariant, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
import { forwardRef, type MouseEventHandler, type ReactNode } from "react"
import { Constants } from "@/app/constants"
import { useActionButton } from "@/hooks/useActionButton"

View File

@@ -14,6 +14,7 @@ interface ImageWithPlaceholderWhileLoadingProps {
placeholderHeight?: number
placeholderBackgroundColor?: string
placeholderIconSize?: number
onError?: (e: React.SyntheticEvent<HTMLImageElement, Event>) => void
}
const useStyles = tss
@@ -44,6 +45,7 @@ export function ImageWithPlaceholderWhileLoading({
title,
width,
style,
onError,
}: Readonly<ImageWithPlaceholderWhileLoadingProps>) {
const { classes } = useStyles({
placeholderWidth,
@@ -70,6 +72,7 @@ export function ImageWithPlaceholderWhileLoading({
width={width}
height={height}
onLoad={() => setLoading(false)}
onError={onError}
style={{
...style,
display: loading ? "none" : (style?.display ?? "initial"),

View File

@@ -7,11 +7,11 @@ const init = async () => {
async getWorker(_, label) {
let worker: typeof import("*?worker")
if (label === "css") {
worker = await import("monaco-editor/esm/vs/language/css/css.worker?worker")
worker = await import("monaco-editor/language/css/css.worker?worker")
} else if (label === "javascript") {
worker = await import("monaco-editor/esm/vs/language/typescript/ts.worker?worker")
worker = await import("monaco-editor/language/typescript/ts.worker?worker")
} else {
worker = await import("monaco-editor/esm/vs/editor/editor.worker?worker")
worker = await import("monaco-editor/editor/editor.worker?worker")
}
return new worker.default()
},

View File

@@ -1,3 +1,5 @@
import { useTimeout } from "@mantine/hooks"
import { useState } from "react"
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
export interface FeedFaviconProps {
@@ -6,9 +8,23 @@ export interface FeedFaviconProps {
}
export function FeedFavicon({ url, size = 18 }: Readonly<FeedFaviconProps>) {
// the backend always returns a favicon except when the feed has never been fetched
// this can happen when the user subscribes to a feed, the feed is added to the tree but the feed has not been fetched yet
// in this case we retry every second up to 3 times until the feed is fetched and the favicon is available
const [timestamp, setTimestamp] = useState(0)
const [retryCount, setRetryCount] = useState(0)
const { start: retry } = useTimeout(() => setTimestamp(Date.now()), 1000)
const urlWithTimestamp = url + (timestamp === 0 ? "" : `?t=${timestamp}`)
const handleError = () => {
if (retryCount < 3) {
setRetryCount(c => c + 1)
retry()
}
}
return (
<ImageWithPlaceholderWhileLoading
src={url}
src={urlWithTimestamp}
alt="feed favicon"
width={size}
height={size}
@@ -16,6 +32,7 @@ export function FeedFavicon({ url, size = 18 }: Readonly<FeedFaviconProps>) {
placeholderHeight={size}
placeholderBackgroundColor="inherit"
placeholderIconSize={size}
onError={handleError}
/>
)
}

View File

@@ -1,7 +1,6 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Select, type SelectProps } from "@mantine/core"
import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { type ComboboxItem, Select, type SelectProps } from "@mantine/core"
import { Constants } from "@/app/constants"
import { useAppSelector } from "@/app/store"
import type { Category } from "@/app/types"

View File

@@ -1,20 +1,30 @@
import { Highlight } from "@mantine/core"
import { useAppSelector } from "@/app/store"
import type { Entry } from "@/app/types"
import { tss } from "@/tss"
export interface FeedEntryTitleProps {
entry: Entry
}
const useStyles = tss.create(() => ({
content: {
textWrap: "inherit",
},
}))
export function FeedEntryTitle(props: Readonly<FeedEntryTitleProps>) {
const search = useAppSelector(state => state.entries.search)
const keywords = search?.split(" ")
const { classes } = useStyles()
return (
<Highlight
inherit
highlight={keywords ?? ""}
// make sure ellipsis is shown when title is too long
span
className={classes.content}
>
{props.entry.title}
</Highlight>

View File

@@ -19,7 +19,7 @@ import {
} from "react-icons/tb"
import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { changeReadingMode, changeReadingOrder } from "@/app/user/thunks"
import { changeSettings } from "@/app/user/thunks"
import { ActionButton } from "@/components/ActionButton"
import { Loader } from "@/components/Loader"
import { useActionButton } from "@/hooks/useActionButton"
@@ -73,6 +73,25 @@ export function Header() {
}, [setValues, searchFromStore])
if (!settings) return <Loader />
const toggleReadingMode = async () => {
await dispatch(
changeSettings({
readingMode: settings.readingMode === "all" ? "unread" : "all",
})
)
dispatch(reloadEntries())
}
const toggleReadingOrder = async () => {
await dispatch(
changeSettings({
readingOrder: settings.readingOrder === "asc" ? "desc" : "asc",
})
)
dispatch(reloadEntries())
}
return (
<Center className="cf-toolbar-wrapper">
<HeaderToolbar>
@@ -121,12 +140,12 @@ export function Header() {
<ActionButton
icon={settings.readingMode === "all" ? <TbEye size={iconSize} /> : <TbEyeOff size={iconSize} />}
label={settings.readingMode === "all" ? msg`All` : msg`Unread`}
onClick={async () => await dispatch(changeReadingMode(settings.readingMode === "all" ? "unread" : "all"))}
onClick={toggleReadingMode}
/>
<ActionButton
icon={settings.readingOrder === "asc" ? <TbSortAscending size={iconSize} /> : <TbSortDescending size={iconSize} />}
label={settings.readingOrder === "asc" ? msg`Asc` : msg`Desc`}
onClick={async () => await dispatch(changeReadingOrder(settings.readingOrder === "asc" ? "desc" : "asc"))}
onClick={toggleReadingOrder}
/>
<Popover>

View File

@@ -124,7 +124,7 @@ export function ProfileMenu(props: Readonly<ProfileMenuProps>) {
}
return (
<Menu position="bottom-end" closeOnItemClick={false} opened={opened} onChange={setOpened}>
<Menu position="bottom-end" closeOnItemClick={false} opened={opened} onChange={setOpened} preventPositionChangeWhenVisible={false}>
<Menu.Target>{props.control}</Menu.Target>
<Menu.Dropdown>
{profile && <Menu.Label>{profile.name}</Menu.Label>}

View File

@@ -1,51 +1,29 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Box, Divider, Group, NumberInput, Radio, Select, type SelectProps, SimpleGrid, Stack, Switch } from "@mantine/core"
import type { ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types"
import {
Box,
type ComboboxData,
Divider,
Group,
Loader,
NumberInput,
Radio,
Select,
type SelectProps,
SimpleGrid,
Stack,
Switch,
} from "@mantine/core"
import type { ReactNode } from "react"
import { Constants } from "@/app/constants"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { IconDisplayMode, ScrollMode, SharingSettings } from "@/app/types"
import {
changeCustomContextMenu,
changeDisablePullToRefresh,
changeEntriesToKeepOnTopWhenScrolling,
changeExternalLinkIconDisplayMode,
changeLanguage,
changeMarkAllAsReadConfirmation,
changeMarkAllAsReadNavigateToUnread,
changeMobileFooter,
changePrimaryColor,
changeScrollMarks,
changeScrollMode,
changeScrollSpeed,
changeSharingSetting,
changeShowRead,
changeStarIconDisplayMode,
changeUnreadCountFavicon,
changeUnreadCountTitle,
} from "@/app/user/thunks"
import { changeSettings } from "@/app/user/thunks"
import { locales } from "@/i18n"
export function DisplaySettings() {
const language = useAppSelector(state => state.user.settings?.language)
const scrollSpeed = useAppSelector(state => state.user.settings?.scrollSpeed)
const showRead = useAppSelector(state => state.user.settings?.showRead)
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
const scrollMode = useAppSelector(state => state.user.settings?.scrollMode)
const entriesToKeepOnTop = useAppSelector(state => state.user.settings?.entriesToKeepOnTopWhenScrolling)
const starIconDisplayMode = useAppSelector(state => state.user.settings?.starIconDisplayMode)
const externalLinkIconDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
const markAllAsReadNavigateToNextUnread = useAppSelector(state => state.user.settings?.markAllAsReadNavigateToNextUnread)
const customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
const disablePullToRefresh = useAppSelector(state => state.user.settings?.disablePullToRefresh)
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
const primaryColor = useAppSelector(state => state.user.settings?.primaryColor) || Constants.theme.defaultPrimaryColor
const settings = useAppSelector(state => state.user.settings)
const { _ } = useLingui()
const dispatch = useAppDispatch()
@@ -97,50 +75,51 @@ export function DisplaySettings() {
</Group>
)
if (!settings) return <Loader />
return (
<Stack>
<Divider label={<Trans>Display</Trans>} labelPosition="center" />
<Select
label={<Trans>Language</Trans>}
value={language}
value={settings.language}
data={locales.map(l => ({
value: l.key,
label: l.label,
}))}
onChange={async s => await (s && dispatch(changeLanguage(s)))}
onChange={language => language && dispatch(changeSettings({ language }))}
/>
<Select
label={<Trans>Primary color</Trans>}
data={colorData}
value={primaryColor}
onChange={async value => value && (await dispatch(changePrimaryColor(value)))}
value={settings.primaryColor}
onChange={primaryColor => primaryColor && dispatch(changeSettings({ primaryColor }))}
renderOption={colorRenderer}
/>
<Switch
label={<Trans>Show feeds and categories with no unread entries</Trans>}
checked={showRead}
onChange={async e => await dispatch(changeShowRead(e.currentTarget.checked))}
checked={settings.showRead}
onChange={e => dispatch(changeSettings({ showRead: e.currentTarget.checked }))}
/>
<Switch
label={<Trans>Show confirmation when marking all entries as read</Trans>}
checked={markAllAsReadConfirmation}
onChange={async e => await dispatch(changeMarkAllAsReadConfirmation(e.currentTarget.checked))}
checked={settings.markAllAsReadConfirmation}
onChange={e => dispatch(changeSettings({ markAllAsReadConfirmation: e.currentTarget.checked }))}
/>
<Switch
label={<Trans>Navigate to the next category/feed with unread entries when marking all entries as read</Trans>}
checked={markAllAsReadNavigateToNextUnread}
onChange={async e => await dispatch(changeMarkAllAsReadNavigateToUnread(e.currentTarget.checked))}
checked={settings.markAllAsReadNavigateToNextUnread}
onChange={e => dispatch(changeSettings({ markAllAsReadNavigateToNextUnread: e.currentTarget.checked }))}
/>
<Switch
label={<Trans>On mobile, show action buttons at the bottom of the screen</Trans>}
checked={mobileFooter}
onChange={async e => await dispatch(changeMobileFooter(e.currentTarget.checked))}
checked={settings.mobileFooter}
onChange={e => dispatch(changeSettings({ mobileFooter: e.currentTarget.checked }))}
/>
<Divider label={<Trans>Scrolling</Trans>} labelPosition="center" />
@@ -148,14 +127,14 @@ export function DisplaySettings() {
<Switch
label={<Trans>Disable "Pull to refresh" browser behavior</Trans>}
description={<Trans>This setting can cause scrolling issues on some browsers (e.g. Safari)</Trans>}
checked={disablePullToRefresh}
onChange={async e => await dispatch(changeDisablePullToRefresh(e.currentTarget.checked))}
checked={settings.disablePullToRefresh}
onChange={e => dispatch(changeSettings({ disablePullToRefresh: e.currentTarget.checked }))}
/>
<Radio.Group
label={<Trans>Scroll selected entry to the top of the page</Trans>}
value={scrollMode}
onChange={async value => await dispatch(changeScrollMode(value as ScrollMode))}
value={settings.scrollMode}
onChange={value => dispatch(changeSettings({ scrollMode: value }))}
>
<Group mt="xs">
{Object.entries(scrollModeOptions).map(e => (
@@ -168,56 +147,56 @@ export function DisplaySettings() {
label={<Trans>Entries to keep above the selected entry when scrolling</Trans>}
description={<Trans>Only applies to compact, cozy and detailed modes</Trans>}
min={0}
value={entriesToKeepOnTop}
onChange={async value => await dispatch(changeEntriesToKeepOnTopWhenScrolling(+value))}
value={settings.entriesToKeepOnTopWhenScrolling}
onChange={value => dispatch(changeSettings({ entriesToKeepOnTopWhenScrolling: +value }))}
/>
<Switch
label={<Trans>Scroll smoothly when navigating between entries</Trans>}
checked={scrollSpeed ? scrollSpeed > 0 : false}
onChange={async e => await dispatch(changeScrollSpeed(e.currentTarget.checked))}
checked={settings.scrollSpeed ? settings.scrollSpeed > 0 : false}
onChange={e => dispatch(changeSettings({ scrollSpeed: e.currentTarget.checked ? 400 : 0 }))}
/>
<Switch
label={<Trans>In expanded view, scrolling through entries mark them as read</Trans>}
checked={scrollMarks}
onChange={async e => await dispatch(changeScrollMarks(e.currentTarget.checked))}
checked={settings.scrollMarks}
onChange={e => dispatch(changeSettings({ scrollMarks: e.currentTarget.checked }))}
/>
<Divider label={<Trans>Browser tab</Trans>} labelPosition="center" />
<Switch
label={<Trans>Show unread count in tab title</Trans>}
checked={unreadCountTitle}
onChange={async e => await dispatch(changeUnreadCountTitle(e.currentTarget.checked))}
checked={settings.unreadCountTitle}
onChange={e => dispatch(changeSettings({ unreadCountTitle: e.currentTarget.checked }))}
/>
<Switch
label={<Trans>Show unread count in tab favicon</Trans>}
checked={unreadCountFavicon}
onChange={async e => await dispatch(changeUnreadCountFavicon(e.currentTarget.checked))}
checked={settings.unreadCountFavicon}
onChange={e => dispatch(changeSettings({ unreadCountFavicon: e.currentTarget.checked }))}
/>
<Divider label={<Trans>Entry headers</Trans>} labelPosition="center" />
<Select
label={<Trans>Show star icon</Trans>}
value={starIconDisplayMode}
value={settings.starIconDisplayMode}
data={displayModeData}
onChange={async s => await dispatch(changeStarIconDisplayMode(s as IconDisplayMode))}
onChange={value => dispatch(changeSettings({ starIconDisplayMode: value as IconDisplayMode }))}
/>
<Select
label={<Trans>Show external link icon</Trans>}
value={externalLinkIconDisplayMode}
value={settings.externalLinkIconDisplayMode}
data={displayModeData}
onChange={async s => await dispatch(changeExternalLinkIconDisplayMode(s as IconDisplayMode))}
onChange={value => dispatch(changeSettings({ externalLinkIconDisplayMode: value as IconDisplayMode }))}
/>
<Switch
label={<Trans>Show CommaFeed's own context menu on right click</Trans>}
checked={customContextMenu}
onChange={async e => await dispatch(changeCustomContextMenu(e.currentTarget.checked))}
checked={settings.customContextMenu}
onChange={e => dispatch(changeSettings({ customContextMenu: e.currentTarget.checked }))}
/>
<Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" />
@@ -227,12 +206,14 @@ export function DisplaySettings() {
<Switch
key={site}
label={Constants.sharing[site].label}
checked={sharingSettings?.[site]}
onChange={async e =>
await dispatch(
changeSharingSetting({
site,
value: e.currentTarget.checked,
checked={settings.sharingSettings[site]}
onChange={e =>
dispatch(
changeSettings({
sharingSettings: {
...settings.sharingSettings,
[site]: e.currentTarget.checked,
},
})
)
}

View File

@@ -91,8 +91,8 @@ export function ProfileSettings() {
label={<Trans>API key</Trans>}
description={
<Trans>
This is your API key. It can be used for some read-only API operations and grants access to the Fever API.
Use the form at the bottom of the page to generate a new API key
This is your API key. It can be used for some read-only API operations and grants access to the Fever API
and the Google Reader API. Use the form at the bottom of the page to generate a new API key
</Trans>
}
readOnly
@@ -130,6 +130,22 @@ export function ProfileSettings() {
</Box>
</Input.Wrapper>
<Input.Wrapper
label={<Trans>Google Reader API</Trans>}
description={
<Trans>
CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible
mobile client. Login with your username and your <u>API key</u>.
</Trans>
}
>
<Box>
<Anchor href="rest/googlereader" target="_blank">
<Trans>Google Reader API URL</Trans>
</Anchor>
</Box>
</Input.Wrapper>
<Divider />
<PasswordInput

View File

@@ -10,7 +10,7 @@ import { client, errorToStrings } from "@/app/client"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { PushNotificationSettings as PushNotificationSettingsModel } from "@/app/types"
import { changePushNotificationSettings } from "@/app/user/thunks"
import { changeSettings } from "@/app/user/thunks"
import { Alert } from "@/components/Alert"
export function PushNotificationSettings() {
@@ -25,7 +25,7 @@ export function PushNotificationSettings() {
}, [form.initialize, notificationSettings])
const handleSubmit = (values: PushNotificationSettingsModel) => {
dispatch(changePushNotificationSettings(values))
dispatch(changeSettings({ pushNotificationSettings: values }))
}
const sendTestPushNotification = useAsyncCallback(client.user.sendTestPushNotification)

View File

@@ -6,7 +6,11 @@ const useStyles = tss.create(() => ({
badge: {
width: "3.2rem",
// for some reason, mantine Badge has "cursor: 'default'"
cursor: "pointer",
cursor: "inherit",
},
indicator: {
// ensure the indicator is not shown above the app header
zIndex: 0,
},
}))
@@ -23,7 +27,15 @@ export function UnreadCount(
const count = props.unreadCount >= 10000 ? "10k+" : props.unreadCount
return (
<Tooltip label={props.unreadCount} disabled={props.unreadCount === count} openDelay={Constants.tooltip.delay}>
<Indicator disabled={!props.showIndicator} size={4} offset={10} position="middle-start">
<Indicator
disabled={!props.showIndicator}
size={4}
offset={10}
position="middle-start"
classNames={{
indicator: classes.indicator,
}}
>
<Badge className={`${classes.badge} cf-badge`} variant="light" fullWidth>
{count}
</Badge>

3
commafeed-client/src/globals.d.ts vendored Normal file
View File

@@ -0,0 +1,3 @@
// required because of noUncheckedSideEffectImports
// https://www.typescriptlang.org/tsconfig/#noUncheckedSideEffectImports
declare module "@fontsource/open-sans"

View File

@@ -60,7 +60,6 @@ msgstr "إضافة مستخدم"
msgid "Admin"
msgstr "إداري"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "اسم مستخدم المسؤول"
@@ -73,7 +72,6 @@ msgstr "اسم مستخدم المسؤول"
msgid "All"
msgstr "الكل"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "دائمًا"
@@ -173,18 +171,14 @@ msgstr "قم ببناء تعبير تصفية لتحديد ما تريد قرا
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "إلغاء"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "إصدار إضافة متصفح CommaFeed هو {browserExtensionVersion
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed متوافق مع Fever API. استخدم عنوان URL التالي في تطبيق الهاتف المتوافق مع Fever. قم بتسجيل الدخول باستخدام اسم المستخدم و <0>مفتاح API</0> الخاص بك."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed متوافق مع Google Reader API. استخدم عنوان URL التالي في تطبيق الهاتف المتوافق مع Google Reader. قم بتسجيل الدخول باستخدام اسم المستخدم و <0>مفتاح API</0> الخاص بك."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed التالي العنصر غير المقروء"
@@ -240,7 +238,6 @@ msgstr "تأكيد"
msgid "Confirm password"
msgstr "تأكيد كلمة المرور"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "تأكيد كلمة المرور"
@@ -294,7 +291,6 @@ msgstr "أيام"
msgid "Delete"
msgstr "حذف"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "حذف الحساب"
@@ -342,13 +338,10 @@ msgstr "اسحب الرابط إلى شريط الإشارات"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "البريد الإلكتروني"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "عنوان البريد الإلكتروني"
@@ -399,7 +392,6 @@ msgstr "خيارات الإضافة"
msgid "Feed name"
msgstr "اسم الخلاصة"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "انتقل إلى وثائق API."
msgid "Goodies"
msgstr "الأشياء الجيدة"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "عنبي"
@@ -570,7 +570,6 @@ msgstr "تحميل الاشتراكات ..."
msgid "Loading tags..."
msgstr "تحميل العلامات ..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "انتقل إلى اشتراك بإدخال اسمه"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "انتقل إلى الفئة/الخلاصة التالية التي تحتوي على إدخالات غير مقروءة عند تمييز الكل كمقروء"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "أبدا"
@@ -657,7 +655,6 @@ msgstr "أبدا"
msgid "New password"
msgstr "كلمة مرور جديدة"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "كلمة مرور جديدة"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "تصدير OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "ملف OPML"
@@ -791,10 +787,7 @@ msgstr "الفئة الأصل"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "كلمة المرور"
@@ -874,7 +867,6 @@ msgstr "تحديث"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "تم إغلاق التسجيلات في مثيل CommaFeed هذا"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "إعادة تعيين كلمة المرور"
@@ -883,7 +875,6 @@ msgstr "إعادة تعيين كلمة المرور"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "نقرة بالزر الأيمن"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "التمرير"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "بحث"
@@ -924,7 +913,6 @@ msgstr "تحديد الخلاصة/الفئة التالية غير المقرو
msgid "Select previous unread feed/category"
msgstr "تحديد الخلاصة/الفئة السابقة غير المقروءة"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "رابط الخادم"
@@ -953,8 +941,6 @@ msgstr "مشاركة"
msgid "Sharing sites"
msgstr "مشاركة المواقع"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "الحلقة"
@@ -1003,7 +989,6 @@ msgstr "إظهار عدد غير المقروء في أيقونة التبويب
msgid "Show unread count in tab title"
msgstr "إظهار عدد غير المقروء في عنوان التبويب"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "قم بالتسجيل"
msgid "Something bad just happened..."
msgstr "شيء سيء حدث للتو ..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "فضاء"
@@ -1030,7 +1014,6 @@ msgstr "النجم"
msgid "Starred"
msgstr "مميز بنجمة"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "قم بالتبديل إلى النسق الفاتح"
msgid "System"
msgstr "النظام"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "الكلمات"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "تحتوي هذه الخلاصة على فلتر قديم لا يمكن تحريره ولا يتم تطبيقه. يرجى إعادة إنشاء الفلتر باستخدام محرر التعبيرات الجديد. كان تعبير الفلتر القديم هو: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "هذا هو مفتاح API الخاص بك. يمكن استخدامه لبعض عمليات API للقراءة فقط ويمنح الوصول إلى Fever API. استخدم النموذج الموجود في أسفل الصفحة لإنشاء مفتاح API جديد"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "هذا هو مفتاح API الخاص بك. يمكن استخدامه لبعض عمليات API للقراءة فقط ويمنح حق الوصول إلى Fever API و Google Reader API. استخدم النموذج الموجود أسفل الصفحة لإنشاء مفتاح API جديد"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "غير مقروءة"
msgid "Unstar"
msgstr "إلغاء النجم"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "إلغاء الاشتراك"
@@ -1149,7 +1130,6 @@ msgstr "مفتاح المستخدم"
msgid "User name"
msgstr "اسم المستخدم"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "اسم المستخدم أو البريد الإلكتروني"

View File

@@ -60,7 +60,6 @@ msgstr "Afegeix usuari"
msgid "Admin"
msgstr "Administrador"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nom d'usuari de l'administrador"
@@ -73,7 +72,6 @@ msgstr "Nom d'usuari de l'administrador"
msgid "All"
msgstr "Tot"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Sempre"
@@ -173,18 +171,14 @@ msgstr "Creeu una expressió de filtratge per indicar què voleu llegir. Les ent
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Cancel·la"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Versió de l'extensió del navegador CommaFeed {browserExtensionVersion}
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed és compatible amb l'API Fever. Utilitzeu l'URL següent al vostre client mòbil compatible amb Fever. Inicieu sessió amb el vostre nom d'usuari i la vostra <0>clau API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed és compatible amb l'API de Google Reader. Utilitzeu l'URL següent al vostre client mòbil compatible amb Google Reader. Inicieu sessió amb el vostre nom d'usuari i la vostra <0>clau API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed següent element no llegit"
@@ -240,7 +238,6 @@ msgstr "Confirma"
msgid "Confirm password"
msgstr "Confirmeu la contrasenya"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirma la contrasenya"
@@ -294,7 +291,6 @@ msgstr "dies"
msgid "Delete"
msgstr "Eliminar"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Suprimeix el compte"
@@ -342,13 +338,10 @@ msgstr "Arrossegueu l'enllaç a la barra d'adreces d'interès"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "Correu electrònic"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Adreça de correu electrònic"
@@ -399,7 +392,6 @@ msgstr "Opcions de l'extensió"
msgid "Feed name"
msgstr "Nom del canal"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Vés a la documentació de l'API."
msgid "Goodies"
msgstr "Extres"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Raïm"
@@ -570,7 +570,6 @@ msgstr "S'estan carregant les subscripcions..."
msgid "Loading tags..."
msgstr "Carregant les etiquetes..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navegueu a una subscripció introduint-ne el nom"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navega a la següent categoria/canal amb entrades no llegides quan es marquen totes les entrades com a llegides"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Mai"
@@ -657,7 +655,6 @@ msgstr "Mai"
msgid "New password"
msgstr "Contrasenya nova"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Contrasenya nova"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Exportació OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Fitxer OPML"
@@ -791,10 +787,7 @@ msgstr "Categoria pare"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Contrasenya"
@@ -874,7 +867,6 @@ msgstr "Actualitzar"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Els registres estan tancats en aquesta instància de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Restableix la contrasenya"
@@ -883,7 +875,6 @@ msgstr "Restableix la contrasenya"
msgid "REST API"
msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Clic dret"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Desplaçament"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Cerca"
@@ -924,7 +913,6 @@ msgstr "Selecciona el següent canal/categoria no llegit"
msgid "Select previous unread feed/category"
msgstr "Selecciona el canal/categoria anterior sense llegir"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL del servidor"
@@ -953,8 +941,6 @@ msgstr "Comparteix"
msgid "Sharing sites"
msgstr "Compartir a altres llocs web"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "canvi"
@@ -1003,7 +989,6 @@ msgstr "Mostra el recompte de no llegits a la icona de favorits de la pestanya"
msgid "Show unread count in tab title"
msgstr "Mostra el recompte de no llegits al títol de la pestanya"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Registra't"
msgid "Something bad just happened..."
msgstr "Acaba de passar una cosa dolenta..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Espai"
@@ -1030,7 +1014,6 @@ msgstr "Estrella"
msgid "Starred"
msgstr "Estrellat"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Canvia al tema clar"
msgid "System"
msgstr "Sistema"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Etiquetes"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Aquest feed té un filtre heretat que no es pot editar i no s'aplica. Torneu a crear el filtre amb el nou editor d'expressions. L'expressió del filtre heretat era: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Aquesta és la vostra clau de l'API. Es pot utilitzar per a algunes operacions de l'API de només lectura i permet accedir a l'API Fever. Utilitzeu el formulari de la part inferior de la pàgina per generar una nova clau d'API."
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Aquesta és la vostra clau API. Es pot utilitzar per a algunes operacions de l'API de només lectura i dona accés a l'API Fever i a l'API de Google Reader. Utilitzeu el formulari a la part inferior de la pàgina per generar una nova clau API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Sense llegir"
msgid "Unstar"
msgstr "Desestrellar"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Donar-se de baixa"
@@ -1149,7 +1130,6 @@ msgstr "Clau d'usuari"
msgid "User name"
msgstr "Nom d'usuari"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nom d'usuari o correu electrònic"

View File

@@ -60,7 +60,6 @@ msgstr "Přidat uživatele"
msgid "Admin"
msgstr "Správce"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Uživatelské jméno správce"
@@ -73,7 +72,6 @@ msgstr "Uživatelské jméno správce"
msgid "All"
msgstr "Všechny"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Vždy"
@@ -173,18 +171,14 @@ msgstr "Vytvořte výraz filtru pro určení toho, co chcete číst. Položky, k
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Zrušit"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Verze rozšíření prohlížeče CommaFeed {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed je kompatibilní s Fever API. Použijte následující URL ve svém mobilním klientovi kompatibilním s Fever. Přihlaste se pomocí svého uživatelského jména a svého <0>klíče API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed je kompatibilní s Google Reader API. Použijte následující URL ve svém mobilním klientovi kompatibilním s Google Reader. Přihlaste se pomocí svého uživatelského jména a svého <0>klíče API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed další nepřečtená položka"
@@ -240,7 +238,6 @@ msgstr "Potvrdit"
msgid "Confirm password"
msgstr "Potvrďte heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Potvrdit heslo"
@@ -294,7 +291,6 @@ msgstr "dní"
msgid "Delete"
msgstr "Smazat"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Smazat účet"
@@ -342,13 +338,10 @@ msgstr "Přetáhněte odkaz na lištu záložek"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-mailová adresa"
@@ -399,7 +392,6 @@ msgstr "Možnosti rozšíření"
msgid "Feed name"
msgstr "Název zdroje"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Přejděte na dokumentaci API."
msgid "Goodies"
msgstr "Dobroty"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Hroznová"
@@ -570,7 +570,6 @@ msgstr "Načítání odběrů..."
msgid "Loading tags..."
msgstr "Načítání značek..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Přejděte na předplatné zadáním jeho názvu"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Po označení všech položek jako přečtených přejít na další kategorii/zdroj s nepřečtenými položkami"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nikdy"
@@ -657,7 +655,6 @@ msgstr "Nikdy"
msgid "New password"
msgstr "Nové heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nové heslo"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Export OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "soubor OPML"
@@ -791,10 +787,7 @@ msgstr "Rodičovská kategorie"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Heslo"
@@ -874,7 +867,6 @@ msgstr "Obnovit"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "V této instanci CommaFeed jsou registrace uzavřeny"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Obnovit heslo"
@@ -883,7 +875,6 @@ msgstr "Obnovit heslo"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Kliknutí pravým tlačítkem"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Posouvání"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Hledej"
@@ -924,7 +913,6 @@ msgstr "Vybrat další nepřečtený zdroj/kategorii"
msgid "Select previous unread feed/category"
msgstr "Vybrat předchozí nepřečtený zdroj/kategorii"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL serveru"
@@ -953,8 +941,6 @@ msgstr "Sdílejte"
msgid "Sharing sites"
msgstr "Stránky pro sdílení"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Směna"
@@ -1003,7 +989,6 @@ msgstr "Zobrazovat počet nepřečtených v ikoně karty"
msgid "Show unread count in tab title"
msgstr "Zobrazovat počet nepřečtených v názvu karty"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Zaregistrovat se"
msgid "Something bad just happened..."
msgstr "Právě se stalo něco špatného..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Vesmír"
@@ -1030,7 +1014,6 @@ msgstr "Hvězda"
msgid "Starred"
msgstr "S hvězdičkou"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Přepnout na světlý motiv"
msgid "System"
msgstr "Systémový"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Štítky"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Tento zdroj má zastaralý filtr, který nelze upravovat a není aplikován. Znovu vytvořte filtr pomocí nového editoru výrazů. Původní výraz filtru byl: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Toto je váš klíč API. Lze jej použít pro některé operace API pouze pro čtení a poskytuje přístup k Fever API. Pro vygenerování nového klíče API použijte formulář v dolní části stránky."
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Toto je váš klíč API. Lze jej použít pro některé operace API pouze pro čtení a uděluje přístup k Fever API a Google Reader API. Použijte formulář v dolní části stránky k vygenerování nového klíče API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Nepřečteno"
msgid "Unstar"
msgstr "Odstranit hvězdu"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Odhlásit odběr"
@@ -1149,7 +1130,6 @@ msgstr "Uživatelský klíč"
msgid "User name"
msgstr "Uživatelské jméno"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Uživatelské jméno nebo e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "Ychwanegu defnyddiwr"
msgid "Admin"
msgstr "Gweinyddol"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Enw defnyddiwr gweinyddwr"
@@ -73,7 +72,6 @@ msgstr "Enw defnyddiwr gweinyddwr"
msgid "All"
msgstr "Pawb"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Bob amser"
@@ -173,18 +171,14 @@ msgstr "Adeiladwch fynegiant hidlo i nodi'r hyn rydych chi am ei ddarllen. Bydd
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Diddymu"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Fersiwn estyniad porwr CommaFeed {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "Mae CommaFeed yn gydnaws â'r Fever API. Defnyddiwch y URL canlynol yn eich cleient symudol sy'n gydnaws â Fever. Mewngofnodwch gyda'ch enw defnyddiwr a'ch <0>allwedd API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "Mae CommaFeed yn gydnaws â Google Reader API. Defnyddiwch y URL canlynol yn eich cleient symudol sy'n gydnaws â Google Reader. Mewngofnodwch gyda'ch enw defnyddiwr a'ch <0>allwedd API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed eitem nesaf heb ei darllen"
@@ -240,7 +238,6 @@ msgstr "Cadarnhau"
msgid "Confirm password"
msgstr "Cadarnhau'r cyfrinair"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Cadarnhau Cyfrinair"
@@ -294,7 +291,6 @@ msgstr "diwrnodau"
msgid "Delete"
msgstr "Dileu"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Dileu cyfrif"
@@ -342,13 +338,10 @@ msgstr "Llusgwch y ddolen i'r bar nod tudalen"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-bost"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "cyfeiriad e-bost"
@@ -399,7 +392,6 @@ msgstr "Opsiynau estyniad"
msgid "Feed name"
msgstr "Enw porthiant"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Ewch i'r ddogfennaeth API."
msgid "Goodies"
msgstr "nwyddau"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Grawnwin"
@@ -570,7 +570,6 @@ msgstr "Yn llwytho tanysgrifiadau..."
msgid "Loading tags..."
msgstr "Wrthi'n llwytho tagiau..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Llywiwch i danysgrifiad trwy nodi ei enw"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Llywiwch i'r categori/porthiant nesaf gyda chofnodion heb eu darllen wrth farcio pob cofnod fel un wedi'i ddarllen"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Byth"
@@ -657,7 +655,6 @@ msgstr "Byth"
msgid "New password"
msgstr "Cyfrinair newydd"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Cyfrinair Newydd"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Allforio OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Ffeil OPML"
@@ -791,10 +787,7 @@ msgstr "Categori Rhiant"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "cyfrinair"
@@ -874,7 +867,6 @@ msgstr "Adnewyddu"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Mae cofrestriadau ar gau ar yr achos CommaFeed hwn"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Ailosod Cyfrinair"
@@ -883,7 +875,6 @@ msgstr "Ailosod Cyfrinair"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Clic dde"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Sgrolio"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Chwilio"
@@ -924,7 +913,6 @@ msgstr "Dewiswch y porthiant/categori nesaf heb ei ddarllen"
msgid "Select previous unread feed/category"
msgstr "Dewiswch y porthiant/categori blaenorol heb ei ddarllen"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL y gweinydd"
@@ -953,8 +941,6 @@ msgstr "Rhannu"
msgid "Sharing sites"
msgstr "Rhannu gwefannau"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "shifft"
@@ -1003,7 +989,6 @@ msgstr "Dangos nifer heb ei ddarllen yn ffaficon y tab"
msgid "Show unread count in tab title"
msgstr "Dangos nifer heb ei ddarllen yn nheitl y tab"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Cofrestrwch"
msgid "Something bad just happened..."
msgstr "Mae rhywbeth drwg newydd ddigwydd ..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Gofod"
@@ -1030,7 +1014,6 @@ msgstr "seren"
msgid "Starred"
msgstr "serennog"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Newid i thema golau"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tagiau"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Mae gan y porthiant hwn hen hidlydd na ellir ei olygu ac nid yw'n cael ei gymhwyso. Crëwch yr hidlydd eto gan ddefnyddio'r golygydd mynegiant newydd. Yr hen fynegiant hidlo oedd: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dyma'ch allwedd API. Gellir ei ddefnyddio ar gyfer rhai gweithrediadau API darllen-yn-unig ac mae'n caniatáu mynediad i'r Fever API. Defnyddiwch y ffurflen ar waelod y dudalen i gynhyrchu allwedd API newydd"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dyma eich allwedd API. Gellir ei defnyddio ar gyfer rhai gweithrediadau API darllen-yn-unig ac mae'n rhoi mynediad i'r Fever API a'r Google Reader API. Defnyddiwch y ffurflen ar waelod y dudalen i gynhyrchu allwedd API newydd"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Heb ei ddarllen"
msgid "Unstar"
msgstr "dad-seren"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Dad-danysgrifio"
@@ -1149,7 +1130,6 @@ msgstr "Allwedd defnyddiwr"
msgid "User name"
msgstr "Enw defnyddiwr"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Enw Defnyddiwr neu E-bost"

View File

@@ -60,7 +60,6 @@ msgstr "Tilføj bruger"
msgid "Admin"
msgstr "Administrator"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Administrator brugernavn"
@@ -73,7 +72,6 @@ msgstr "Administrator brugernavn"
msgid "All"
msgstr "Alle"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Altid"
@@ -173,18 +171,14 @@ msgstr "Byg et filterudtryk for at angive, hvad du vil læse. Indlæg, der ikke
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Annuller"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed browserudvidelse version {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed er kompatibel med Fever API. Brug følgende URL i din Fever-kompatible mobilklient. Log ind med dit brugernavn og din <0>API-nøgle</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed er kompatibel med Google Reader API. Brug følgende URL i din Google Reader-kompatible mobilklient. Log ind med dit brugernavn og din <0>API-nøgle</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed næste ulæste element"
@@ -240,7 +238,6 @@ msgstr "Bekræft"
msgid "Confirm password"
msgstr "Bekræft adgangskode"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Bekræft adgangskode"
@@ -294,7 +291,6 @@ msgstr "dage"
msgid "Delete"
msgstr "Slet"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Slet konto"
@@ -342,13 +338,10 @@ msgstr "Træk linket til bogmærkelinjen"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-mailadresse"
@@ -399,7 +392,6 @@ msgstr "Indstillinger for udvidelse"
msgid "Feed name"
msgstr "Feednavn"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Gå til API-dokumentationen."
msgid "Goodies"
msgstr "Godbidder"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Grape"
@@ -570,7 +570,6 @@ msgstr "Indlæser abonnementer..."
msgid "Loading tags..."
msgstr "Indlæser tags..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Naviger til et abonnement ved at indtaste dets navn"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Naviger til næste kategori/feed med ulæste indlæg, når alle indlæg markeres som læst"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Aldrig"
@@ -657,7 +655,6 @@ msgstr "Aldrig"
msgid "New password"
msgstr "Ny adgangskode"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Ny adgangskode"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML eksport"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML fil"
@@ -791,10 +787,7 @@ msgstr "Forældrekategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Adgangskode"
@@ -874,7 +867,6 @@ msgstr "Opdater"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er lukket på denne CommaFeed-instans"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Nulstil adgangskode"
@@ -883,7 +875,6 @@ msgstr "Nulstil adgangskode"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Højreklik"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Rulning"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Søg"
@@ -924,7 +913,6 @@ msgstr "Vælg næste ulæste feed/kategori"
msgid "Select previous unread feed/category"
msgstr "Vælg forrige ulæste feed/kategori"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server-URL"
@@ -953,8 +941,6 @@ msgstr "Del"
msgid "Sharing sites"
msgstr "Delingssider"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Skift"
@@ -1003,7 +989,6 @@ msgstr "Vis ulæst antal i fanens favicon"
msgid "Show unread count in tab title"
msgstr "Vis ulæst antal i fanens titel"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Tilmeld dig"
msgid "Something bad just happened..."
msgstr "Der er lige sket noget slemt..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Rum"
@@ -1030,7 +1014,6 @@ msgstr "Stjerne"
msgid "Starred"
msgstr "Medvirkende"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Skift til lystema"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tags"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Dette feed har et ældre filter, der ikke kan redigeres og ikke anvendes. Genopret venligst filteret ved hjælp af den nye udtrykseditor. Det ældre filterudtryk var: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dette er din API-nøgle. Den kan bruges til visse skrivebeskyttede API-operationer og giver adgang til Fever API. Brug formularen nederst på siden til at generere en ny API-nøgle"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dette er din API-nøgle. Den kan bruges til nogle skrivebeskyttede API-operationer og giver adgang til Fever API og Google Reader API. Brug formularen nederst på siden for at generere en ny API-nøgle"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Ulæst"
msgid "Unstar"
msgstr "Fjern stjerne"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Afmeld"
@@ -1149,7 +1130,6 @@ msgstr "Brugernøgle"
msgid "User name"
msgstr "Brugernavn"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Brugernavn eller e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "Benutzer hinzufügen"
msgid "Admin"
msgstr "Verwaltung"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Administrator-Benutzername"
@@ -73,7 +72,6 @@ msgstr "Administrator-Benutzername"
msgid "All"
msgstr "Alle"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Immer"
@@ -173,18 +171,14 @@ msgstr "Erstellen Sie einen Filterausdruck, um anzugeben, was Sie lesen möchten
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Abbrechen"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed Browser Erweiterung Version {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed ist mit der Fever-API kompatibel. Verwenden Sie die folgende URL in Ihrem Fever-kompatiblen mobilen Client. Melden Sie sich mit Ihrem Benutzernamen und Ihrem <0>API-Schlüssel</0> an."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed ist mit der Google-Reader-API kompatibel. Verwenden Sie die folgende URL in Ihrem Google-Reader-kompatiblen mobilen Client. Melden Sie sich mit Ihrem Benutzernamen und Ihrem <0>API-Schlüssel</0> an."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed nächstes ungelesenes Element"
@@ -240,7 +238,6 @@ msgstr "Bestätigen"
msgid "Confirm password"
msgstr "Passwort bestätigen"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Passwort bestätigen"
@@ -294,7 +291,6 @@ msgstr "Tage"
msgid "Delete"
msgstr "Löschen"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Konto löschen"
@@ -342,13 +338,10 @@ msgstr "Link in Lesezeichenleiste ziehen"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-Mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-Mail-Adresse"
@@ -399,7 +392,6 @@ msgstr "Erweiterungsoptionen"
msgid "Feed name"
msgstr "Feedname"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Gehen Sie zur API-Dokumentation."
msgid "Goodies"
msgstr "Goodies"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Traube"
@@ -570,7 +570,6 @@ msgstr "Abonnements werden geladen..."
msgid "Loading tags..."
msgstr "Tags werden geladen..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigieren Sie zu einem Abonnement, indem Sie seinen Namen eingeben"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigieren Sie zum nächsten Feed/Kategorie mit ungelesenen Einträgen, wenn Sie alle Einträge als gelesen markieren"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Niemals"
@@ -657,7 +655,6 @@ msgstr "Niemals"
msgid "New password"
msgstr "Neues Passwort"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Neues Passwort"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML-Export"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML-Datei"
@@ -791,10 +787,7 @@ msgstr "Übergeordnete Kategorie"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Passwort"
@@ -874,7 +867,6 @@ msgstr "Aktualisieren"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registrierungen sind für diese CommaFeed-Instanz geschlossen"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Passwort zurücksetzen"
@@ -883,7 +875,6 @@ msgstr "Passwort zurücksetzen"
msgid "REST API"
msgstr "REST-API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Rechtsklick"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Scrollen"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Suche"
@@ -924,7 +913,6 @@ msgstr "Nächsten ungelesenen Feed/Kategorie auswählen"
msgid "Select previous unread feed/category"
msgstr "Vorherigen ungelesenen Feed/Kategorie auswählen"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server-URL"
@@ -953,8 +941,6 @@ msgstr "Teilen"
msgid "Sharing sites"
msgstr "Seiten teilen"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Verschiebung"
@@ -1003,7 +989,6 @@ msgstr "Anzahl ungelesener Nachrichten im Tab-Favicon anzeigen"
msgid "Show unread count in tab title"
msgstr "Anzahl ungelesener Nachrichten im Tab-Titel anzeigen"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Melden Sie sich an"
msgid "Something bad just happened..."
msgstr "Etwas Schlimmes ist gerade passiert..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Raum"
@@ -1030,7 +1014,6 @@ msgstr "Stern"
msgid "Starred"
msgstr "Markiert"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Zum Lightmode wechseln"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tags"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Dieser Feed verfügt über einen veralteten Filter, der nicht bearbeitet werden kann und nicht angewendet wird. Bitte erstellen Sie den Filter mit dem neuen Ausdruckseditor neu. Der veraltete Filterausdruck war: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dies ist Ihr API-Schlüssel. Er kann für einige schreibgeschützte API-Vorgänge verwendet werden und ermöglicht den Zugriff auf die Fever-API. Verwenden Sie das Formular unten auf der Seite, um einen neuen API-Schlüssel zu generieren"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dies ist Ihr API-Schlüssel. Er kann für einige schreibgeschützte API-Vorgänge verwendet werden und gewährt Zugriff auf die Fever-API und die Google-Reader-API. Verwenden Sie das Formular am Ende der Seite, um einen neuen API-Schlüssel zu generieren"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Ungelesen"
msgid "Unstar"
msgstr "Stern entfernen"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Abbestellen"
@@ -1149,7 +1130,6 @@ msgstr "Benutzerschlüssel"
msgid "User name"
msgstr "Benutzername"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Benutzername oder E-Mail"

View File

@@ -60,7 +60,6 @@ msgstr "Add user"
msgid "Admin"
msgstr "Admin"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Admin user name"
@@ -73,7 +72,6 @@ msgstr "Admin user name"
msgid "All"
msgstr "All"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Always"
@@ -173,18 +171,14 @@ msgstr "Build a filter expression to indicate what you want to read. Entries tha
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Cancel"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed browser extension version {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed next unread item"
@@ -240,7 +238,6 @@ msgstr "Confirm"
msgid "Confirm password"
msgstr "Confirm password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirm Password"
@@ -294,7 +291,6 @@ msgstr "days"
msgid "Delete"
msgstr "Delete"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Delete account"
@@ -342,13 +338,10 @@ msgstr "Drag link to bookmark bar"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-mail address"
@@ -399,7 +392,6 @@ msgstr "Extension options"
msgid "Feed name"
msgstr "Feed name"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Go to the API documentation."
msgid "Goodies"
msgstr "Goodies"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr "Google Reader API"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr "Google Reader API URL"
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Grape"
@@ -570,7 +570,6 @@ msgstr "Loading subscriptions..."
msgid "Loading tags..."
msgstr "Loading tags..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigate to a subscription by entering its name"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigate to the next category/feed with unread entries when marking all entries as read"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Never"
@@ -657,7 +655,6 @@ msgstr "Never"
msgid "New password"
msgstr "New password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "New Password"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML export"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML file"
@@ -791,10 +787,7 @@ msgstr "Parent Category"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Password"
@@ -874,7 +867,6 @@ msgstr "Refresh"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registrations are closed on this CommaFeed instance"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Reset Password"
@@ -883,7 +875,6 @@ msgstr "Reset Password"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Right click"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Scrolling"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Search"
@@ -924,7 +913,6 @@ msgstr "Select next unread feed/category"
msgid "Select previous unread feed/category"
msgstr "Select previous unread feed/category"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server URL"
@@ -953,8 +941,6 @@ msgstr "Share"
msgid "Sharing sites"
msgstr "Sharing sites"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "Show unread count in tab favicon"
msgid "Show unread count in tab title"
msgstr "Show unread count in tab title"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Sign up"
msgid "Something bad just happened..."
msgstr "Something bad just happened..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Space"
@@ -1030,7 +1014,6 @@ msgstr "Star"
msgid "Starred"
msgstr "Starred"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Switch to light theme"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tags"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "This feed has a legacy filter that cannot be edited and is not applied. Please recreate the filter using the new expression editor. The legacy filter expression was: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Unread"
msgid "Unstar"
msgstr "Unstar"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Unsubscribe"
@@ -1149,7 +1130,6 @@ msgstr "User key"
msgid "User name"
msgstr "User name"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "User Name or E-mail"

View File

@@ -61,7 +61,6 @@ msgstr "Añadir usuario"
msgid "Admin"
msgstr "Administrador"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nombre de usuario administrador"
@@ -74,7 +73,6 @@ msgstr "Nombre de usuario administrador"
msgid "All"
msgstr "Todo"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Siempre"
@@ -174,18 +172,14 @@ msgstr "Crea una expresión de filtrado para indicar lo que quieres leer. Las en
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Cancelar"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -217,6 +211,10 @@ msgstr "Versión de la extensión del navegador CommaFeed {browserExtensionVersi
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed es compatible con Fever API. Utilice la siguiente URL en su cliente móvil compatible con Fever. Inicie sesión con su nombre de usuario y su <0>clave API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed es compatible con Google Reader API. Utilice la siguiente URL en su cliente móvil compatible con Google Reader. Inicie sesión con su nombre de usuario y su <0>clave API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed siguiente elemento no leído"
@@ -241,7 +239,6 @@ msgstr "Confirmar"
msgid "Confirm password"
msgstr "Confirmar contraseña"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirmar contraseña"
@@ -295,7 +292,6 @@ msgstr "días"
msgid "Delete"
msgstr "Borrar"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Borrar cuenta"
@@ -343,13 +339,10 @@ msgstr "Arrastra el enlace a la barra de marcadores"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "Correo electrónico"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Dirección de correo electrónico"
@@ -400,7 +393,6 @@ msgstr "Opciones de la extensión"
msgid "Feed name"
msgstr "Nombre del feed"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -467,6 +459,14 @@ msgstr "Ir a la documentación de la API."
msgid "Goodies"
msgstr "Golosinas"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Uva"
@@ -571,7 +571,6 @@ msgstr "Cargando suscripciones..."
msgid "Loading tags..."
msgstr "Cargando etiquetas..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -649,7 +648,6 @@ msgstr "Navegar a una suscripción introduciendo su nombre"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navegar a la siguiente categoría/feed con entradas no leídas al marcar todas como leídas"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nunca"
@@ -658,7 +656,6 @@ msgstr "Nunca"
msgid "New password"
msgstr "Nueva contraseña"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nueva contraseña"
@@ -765,7 +762,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Exportar OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Archivo OPML"
@@ -792,10 +788,7 @@ msgstr "Categoría principal"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Contraseña"
@@ -875,7 +868,6 @@ msgstr "Actualizar"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Los registros están cerrados en esta instancia de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Restablecer contraseña"
@@ -884,7 +876,6 @@ msgstr "Restablecer contraseña"
msgid "REST API"
msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Clic derecho"
@@ -911,8 +902,6 @@ msgid "Scrolling"
msgstr "Desplazarse"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Buscar"
@@ -925,7 +914,6 @@ msgstr "Seleccionar el siguiente feed/categoría no leído"
msgid "Select previous unread feed/category"
msgstr "Seleccionar el anterior feed/categoría no leído"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL del servidor"
@@ -954,8 +942,6 @@ msgstr "Compartir"
msgid "Sharing sites"
msgstr "Compartir sitios"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1004,7 +990,6 @@ msgstr "Mostrar recuento de no leídos en la pestaña favicon"
msgid "Show unread count in tab title"
msgstr "Mostrar recuento de no leídos en el título de la pestaña"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1014,7 +999,6 @@ msgstr "Registrarse"
msgid "Something bad just happened..."
msgstr "Algo malo acaba de pasar..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Espacio"
@@ -1031,7 +1015,6 @@ msgstr "Estrella"
msgid "Starred"
msgstr "Destacado"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1065,7 +1048,6 @@ msgstr "Cambiar a tema claro"
msgid "System"
msgstr "Sistema"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Etiquetas"
@@ -1096,8 +1078,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Este feed tiene un filtro heredado que no se puede editar y no se aplica. Por favor, vuelve a crear el filtro usando el nuevo editor de expresiones. La expresión del filtro heredado era: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Esta es su clave API. Se puede utilizar para algunas operaciones API de solo lectura y otorga acceso a Fever API. Utilice el formulario en la parte inferior de la página para generar una nueva clave API"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Esta es su clave API. Se puede utilizar para algunas operaciones API de solo lectura y otorga acceso a la API Fever y a la API de Google Reader. Utilice el formulario en la parte inferior de la página para generar una nueva clave API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1137,7 +1119,6 @@ msgstr "No leído"
msgid "Unstar"
msgstr "Desmarcar"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Cancelar suscripción"
@@ -1150,7 +1131,6 @@ msgstr "Clave de usuario"
msgid "User name"
msgstr "Nombre de usuario"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nombre de usuario o correo electrónico"

View File

@@ -60,7 +60,6 @@ msgstr "افزودن کاربر"
msgid "Admin"
msgstr "مدیر"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "نام کاربری مدیر"
@@ -73,7 +72,6 @@ msgstr "نام کاربری مدیر"
msgid "All"
msgstr "همه"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "همیشه"
@@ -173,18 +171,14 @@ msgstr "یک عبارت فیلتر بسازید تا نشان دهید چه چی
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "لغو"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "نسخه افزونه مرورگر CommaFeed {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed با Fever API سازگار است. از آدرس زیر در کلاینت موبایل سازگار با Fever استفاده کنید. با نام کاربری و <0>کلید API</0> خود وارد شوید."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed با Google Reader API سازگار است. از آدرس زیر در کلاینت موبایل سازگار با Google Reader استفاده کنید. با نام کاربری و <0>کلید API</0> خود وارد شوید."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "مورد خوانده نشده بعدی CommaFeed"
@@ -240,7 +238,6 @@ msgstr "تأیید کنید"
msgid "Confirm password"
msgstr "رمز عبور را تأیید کنید"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "تأیید رمز عبور"
@@ -294,7 +291,6 @@ msgstr "روز"
msgid "Delete"
msgstr "حذف کنید"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "حذف حساب"
@@ -342,13 +338,10 @@ msgstr "پیوند را به نوار نشانک بکشید"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "ایمیل"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "آدرس ایمیل"
@@ -399,7 +392,6 @@ msgstr "گزینه های افزونه"
msgid "Feed name"
msgstr "نام فید"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "به مستندات API بروید."
msgid "Goodies"
msgstr "خوبی ها"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "عنبی"
@@ -570,7 +570,6 @@ msgstr "بارگیری اشتراک ها..."
msgid "Loading tags..."
msgstr "بارگیری برچسب ها..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "با وارد کردن نام اشتراک، به آن بروید"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "هنگام علامت‌گذاری همه ورودی‌ها به‌عنوان خوانده‌شده، به دسته‌بندی/فید بعدی با ورودی‌های خوانده‌نشده بروید"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "هرگز"
@@ -657,7 +655,6 @@ msgstr "هرگز"
msgid "New password"
msgstr "رمز عبور جدید"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "رمز عبور جدید"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "خروجی OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "فایل OPML"
@@ -791,10 +787,7 @@ msgstr "دسته والد"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "رمز عبور"
@@ -874,7 +867,6 @@ msgstr "تازه کردن"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "ثبت نام در این نمونه CommaFeed بسته شده است"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "بازنشانی رمز عبور"
@@ -883,7 +875,6 @@ msgstr "بازنشانی رمز عبور"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "راست کلیک"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "پیمایش"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "جستجو"
@@ -924,7 +913,6 @@ msgstr "انتخاب فید/دسته بعدی خوانده نشده"
msgid "Select previous unread feed/category"
msgstr "انتخاب فید/دسته قبلی خوانده نشده"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "آدرس سرور"
@@ -953,8 +941,6 @@ msgstr "به اشتراک بگذارید"
msgid "Sharing sites"
msgstr "اشتراک گذاری سایت ها"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "شیفت"
@@ -1003,7 +989,6 @@ msgstr "نمایش تعداد خوانده نشده در فاویکون زبان
msgid "Show unread count in tab title"
msgstr "نمایش تعداد خوانده نشده در عنوان زبانه"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "ثبت نام کنید"
msgid "Something bad just happened..."
msgstr "اتفاق بدی افتاد..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "فضا"
@@ -1030,7 +1014,6 @@ msgstr "ستاره"
msgid "Starred"
msgstr "ستاره دار"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "تغییر به تم روشن"
msgid "System"
msgstr "سیستم"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "برچسب‌ها"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "این فید دارای یک فیلتر قدیمی است که قابل ویرایش نیست و اعمال نمی‌شود. لطفاً فیلتر را با استفاده از ویرایشگر جدید بازسازی کنید. عبارت فیلتر قدیمی: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "این کلید API شماست. می‌توان از آن برای برخی عملیات API فقط خواندنی استفاده کرد و دسترسی به Fever API را فراهم می‌کند. از فرم پایین صفحه برای تولید کلید جدید استفاده کنید"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "این کلید API شماست. می‌توان از آن برای برخی عملیات فقط خواندنی API استفاده کرد و دسترسی به Fever API و Google Reader API را می‌دهد. از فرم پایین صفحه برای ایجاد کلید API جدید استفاده کنید"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "خوانده نشده"
msgid "Unstar"
msgstr "برداشتن ستاره"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "لغو اشتراک"
@@ -1149,7 +1130,6 @@ msgstr "کلید کاربر"
msgid "User name"
msgstr "نام کاربری"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "نام کاربری یا ایمیل"

View File

@@ -60,7 +60,6 @@ msgstr "Lisää käyttäjä"
msgid "Admin"
msgstr "Järjestelmänvalvoja"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Järjestelmänvalvojan käyttäjänimi"
@@ -73,7 +72,6 @@ msgstr "Järjestelmänvalvojan käyttäjänimi"
msgid "All"
msgstr "Kaikki"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Aina"
@@ -173,18 +171,14 @@ msgstr "Rakenna suodatinlauseke määrittääksesi, mitä haluat lukea. Merkinn
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Peruuta"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed-selainlaajennuksen versio {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed on yhteensopiva Fever-API:n kanssa. Käytä seuraavaa URL-osoitetta Fever-yhteensopivassa mobiiliohjelmassasi. Kirjaudu sisään käyttäjätunnuksellasi ja <0>API-avaimellasi</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed on yhteensopiva Google Reader -API:n kanssa. Käytä seuraavaa URL-osoitetta Google Reader -yhteensopivassa mobiiliohjelmassasi. Kirjaudu sisään käyttäjätunnuksellasi ja <0>API-avaimellasi</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed seuraava lukematon kohde"
@@ -240,7 +238,6 @@ msgstr "Vahvista"
msgid "Confirm password"
msgstr "Vahvista salasana"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Vahvista salasana"
@@ -294,7 +291,6 @@ msgstr "päivää"
msgid "Delete"
msgstr "Poista"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Poista tili"
@@ -342,13 +338,10 @@ msgstr "Vedä linkki kirjanmerkkipalkkiin"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "Sähköposti"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Sähköpostiosoite"
@@ -399,7 +392,6 @@ msgstr "Laajennuksen asetukset"
msgid "Feed name"
msgstr "Syötteen nimi"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Siirry API-dokumentaatioon."
msgid "Goodies"
msgstr "Hyvää"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Viinirypäle"
@@ -570,7 +570,6 @@ msgstr "Ladataan tilauksia..."
msgid "Loading tags..."
msgstr "Ladataan tunnisteita..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Siirry tilaukseen kirjoittamalla sen nimi"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Siirry seuraavaan luokkaan/syötteeseen, jossa on lukemattomia merkintöjä, kun kaikki merkinnät merkitään luetuiksi"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Ei koskaan"
@@ -657,7 +655,6 @@ msgstr "Ei koskaan"
msgid "New password"
msgstr "Uusi salasana"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Uusi salasana"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML-vienti"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML-tiedosto"
@@ -791,10 +787,7 @@ msgstr "Pääluokka"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Salasana"
@@ -874,7 +867,6 @@ msgstr "Päivitä"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Tämän CommaFeed-esiintymän rekisteröinnit on suljettu"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Nollaa salasana"
@@ -883,7 +875,6 @@ msgstr "Nollaa salasana"
msgid "REST API"
msgstr "REST-API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Oikea napsautus"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Vieritys"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Etsi"
@@ -924,7 +913,6 @@ msgstr "Valitse seuraava lukematon syöte/luokka"
msgid "Select previous unread feed/category"
msgstr "Valitse edellinen lukematon syöte/luokka"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Palvelimen URL-osoite"
@@ -953,8 +941,6 @@ msgstr "Jaa"
msgid "Sharing sites"
msgstr "Sivustojen jakaminen"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Vaihto"
@@ -1003,7 +989,6 @@ msgstr "Näytä lukemattomien määrä välilehden kuvakkeessa"
msgid "Show unread count in tab title"
msgstr "Näytä lukemattomien määrä välilehden otsikossa"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Rekisteröidy"
msgid "Something bad just happened..."
msgstr "Jotain pahaa tapahtui juuri..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Avaruus"
@@ -1030,7 +1014,6 @@ msgstr "Tähti"
msgid "Starred"
msgstr "Tähdellä merkitty"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Vaihda vaaleaan teemaan"
msgid "System"
msgstr "Järjestelmä"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tunnisteet"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Tässä syötteessä on vanha suodatin, jota ei voi muokata ja jota ei sovelleta. Luo suodatin uudelleen käyttämällä uutta lauseke-editoria. Vanha suodatinlauseke oli: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Tämä on API-avaimesi. Sitä voidaan käyttää joihinkin vain luku -API-toimintoihin ja se antaa pääsyn Fever-API:iin. Käytä sivun alareunassa olevaa lomaketta uuden API-avaimen luomiseen"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Tämä on API-avaimesi. Sitä voidaan käyttää joihinkin vain luku -API-toimintoihin, ja se myöntää pääsyn Fever-API:hin ja Google Reader -API:hin. Käytä sivun alareunassa olevaa lomaketta luodaksesi uuden API-avaimen"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Lukematon"
msgid "Unstar"
msgstr "Poista tähti"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Peruuta tilaus"
@@ -1149,7 +1130,6 @@ msgstr "Käyttäjäavain"
msgid "User name"
msgstr "Käyttäjänimi"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Käyttäjänimi tai sähköpostiosoite"

View File

@@ -60,7 +60,6 @@ msgstr "Ajouter un utilisateur"
msgid "Admin"
msgstr "Administrateur"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nom de l'administrateur"
@@ -73,7 +72,6 @@ msgstr "Nom de l'administrateur"
msgid "All"
msgstr "Tout"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Toujours"
@@ -173,18 +171,14 @@ msgstr "Créez une expression régulière pour filtrer ce que vous voulez lire.
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Annuler"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Extension CommaFeed pour navigateur version {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "Commafeed est compatible avec l'API Fever, en inscrivant l'URL suivante dans votre client mobile compatible. Entrez votre nom d'utilisateur habituel, et votre <0>clef API</0> comme mot de passe."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "Commafeed est compatible avec l'API Google Reader, en inscrivant l'URL suivante dans votre client mobile compatible. Entrez votre nom d'utilisateur habituel, et votre <0>clef API</0> comme mot de passe."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed prochain article non lu"
@@ -240,7 +238,6 @@ msgstr "Confirmer"
msgid "Confirm password"
msgstr "Confirmer le mot de passe"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirmer le mot de passe"
@@ -294,7 +291,6 @@ msgstr "jours"
msgid "Delete"
msgstr "Effacer"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Effacer le compte"
@@ -342,13 +338,10 @@ msgstr "Déplacez le lien vers la barre de favoris"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Adresse e-mail"
@@ -399,7 +392,6 @@ msgstr "Options de l'extension"
msgid "Feed name"
msgstr "Nom du flux"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Aller à la documentation de l'API."
msgid "Goodies"
msgstr "Extensions"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Bordeaux"
@@ -570,7 +570,6 @@ msgstr "Chargement des abonnements..."
msgid "Loading tags..."
msgstr "Chargement des marqueurs..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Naviguer vers un abonnement en entrant son nom"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Aller vers la catégorie/le flux comportant des entrées non lues suivant après avoir marqué toutes les entrées lues"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Jamais"
@@ -657,7 +655,6 @@ msgstr "Jamais"
msgid "New password"
msgstr "Nouveau mot de passe"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nouveau mot de passe"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Export du fichier OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Fichier OPML"
@@ -791,10 +787,7 @@ msgstr "Catégorie parente"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Mot de passe"
@@ -874,7 +867,6 @@ msgstr "Rafraîchir"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Les inscriptions sont fermées sur cette instance de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Réinitialiser le mot de passe"
@@ -883,7 +875,6 @@ msgstr "Réinitialiser le mot de passe"
msgid "REST API"
msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Clic droit"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Défilement"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Rechercher"
@@ -924,7 +913,6 @@ msgstr "Sélectionner l'article non lu suivant/la catégorie non lue suivante"
msgid "Select previous unread feed/category"
msgstr "Sélectionner l'article non lu précédent/la catégorie non lue précédente"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL du serveur"
@@ -953,8 +941,6 @@ msgstr "Partager"
msgid "Sharing sites"
msgstr "Sites de partage"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Maj"
@@ -1003,7 +989,6 @@ msgstr "Afficher le nombre d'entrées non lues dans la favicône de l'onglet"
msgid "Show unread count in tab title"
msgstr "Afficher le nombre d'entrées non lues dans le titre de l'onglet"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Créer un compte"
msgid "Something bad just happened..."
msgstr "Quelque chose s'est mal passé..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Espace"
@@ -1030,7 +1014,6 @@ msgstr "Ajouter aux favoris"
msgid "Starred"
msgstr "Favoris"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Activer le mode clair"
msgid "System"
msgstr "Système"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Marqueurs"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Ce flux est configuré avec un filtre obsolète, qui ne peut être ni modifié ni appliqué. Vous devez recréer ce filtre à l'aide du nouvel éditeur d'expressions régulières. Le filtre configuré était : <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ceci est votre clef API. Elle peut être utilisée pour certaines opérations en lecture seule et donne accès à l'API Fever. Utilisez le formulaire en bas de la page pour générer une nouvelle clef API"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ceci est votre clef API. Elle peut être utilisée pour certaines opérations en lecture seule et donne accès à l'API Fever et à l'API Google Reader. Utilisez le formulaire en bas de la page pour générer une nouvelle clef API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Non lu"
msgid "Unstar"
msgstr "Retirer des favoris"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Se désabonner"
@@ -1149,7 +1130,6 @@ msgstr "Clé utilisateur"
msgid "User name"
msgstr "Nom"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nom d'utilisateur ou e-mail"

View File

@@ -61,7 +61,6 @@ msgstr "Engadir persoa usuaria"
msgid "Admin"
msgstr "Administración"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Identificador de admin"
@@ -74,7 +73,6 @@ msgstr "Identificador de admin"
msgid "All"
msgstr "Todos"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Sempre"
@@ -174,18 +172,14 @@ msgstr "Crea unha expresión de filtrado para indicar o que queres ler. Os artig
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Cancelar"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -217,6 +211,10 @@ msgstr "Versión do complemento do navegador para CommaFeed {browserExtensionVer
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed é compatible coa API de Fever. Usa o seguinte URL no teu cliente móbil compatible con Fever. Accede co identificador e a túa <0>clave API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed é compatible coa API de Google Reader. Usa o seguinte URL no teu cliente móbil compatible con Google Reader. Accede co identificador e a túa <0>clave API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "Seguinte elemento non lido en CommaFeed"
@@ -241,7 +239,6 @@ msgstr "Confirmar"
msgid "Confirm password"
msgstr "Confirmar contrasinal"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirmar contrasinal"
@@ -295,7 +292,6 @@ msgstr "días"
msgid "Delete"
msgstr "Eliminar"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Eliminar conta"
@@ -343,13 +339,10 @@ msgstr "Arrastra a ligazón á barra de marcadores"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "Correo electrónico"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Enderezo de correo electrónico"
@@ -400,7 +393,6 @@ msgstr "Opcións do complemento"
msgid "Feed name"
msgstr "Nome da canle"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -467,6 +459,14 @@ msgstr "Ir á documentación da API."
msgid "Goodies"
msgstr "Agasallos"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Bago"
@@ -571,7 +571,6 @@ msgstr "Cargando subscricións..."
msgid "Loading tags..."
msgstr "Cargando etiquetas..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -649,7 +648,6 @@ msgstr "Vai a unha subscrición ao escribir o seu nome"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Vai á seguinte categoría/canle con artigos sen ler cando marques todos artigos como lidos"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nunca"
@@ -658,7 +656,6 @@ msgstr "Nunca"
msgid "New password"
msgstr "Novo contrasinal"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Novo contrasinal"
@@ -765,7 +762,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Exportación OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Ficheiro OPML"
@@ -792,10 +788,7 @@ msgstr "Categoría superior"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Contrasinal"
@@ -875,7 +868,6 @@ msgstr "Actualizar"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Non se admiten novas contas nesta instancia de CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Restablecer contrasinal"
@@ -884,7 +876,6 @@ msgstr "Restablecer contrasinal"
msgid "REST API"
msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Botón dereito"
@@ -911,8 +902,6 @@ msgid "Scrolling"
msgstr "Desprazamento"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Busca"
@@ -925,7 +914,6 @@ msgstr "Seleccionar a seguinte canle/categoría sen ler"
msgid "Select previous unread feed/category"
msgstr "Seleccionar a canle/categoría anterior sen ler"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL do servidor"
@@ -954,8 +942,6 @@ msgstr "Compartir"
msgid "Sharing sites"
msgstr "Servizos para compartir"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Maiúsculas"
@@ -1004,7 +990,6 @@ msgstr "Mostrar a conta de artigos sen ler na icona da pestana"
msgid "Show unread count in tab title"
msgstr "Mostar a conta de artigos sen ler no título da pestana"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1014,7 +999,6 @@ msgstr "Crear conta"
msgid "Something bad just happened..."
msgstr "Algo malo pasou..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Espazo"
@@ -1031,7 +1015,6 @@ msgstr "Pór estrela"
msgid "Starred"
msgstr "Con estrela"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1065,7 +1048,6 @@ msgstr "Cambiar ao decorado claro"
msgid "System"
msgstr "Sistema"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Etiquetas"
@@ -1096,8 +1078,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Esta canle ten un filtro antigo que non se pode editar e que non se aplica. Volve crear o filtro usando o novo editor de expresións. A expresión do filtro antigo era: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Esta é a túa clave da API. Pode usarse para algunhas operacións da API de só-lectura e concede acceso á API Fever. Usa o formulario da parte inferior da páxina para crear unha nova clave da API"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Esta é a túa clave da API. Pode usarse para algunhas operacións da API de só-lectura e concede acceso á API Fever e á API de Google Reader. Usa o formulario da parte inferior da páxina para crear unha nova clave da API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1137,7 +1119,6 @@ msgstr "Sen ler"
msgid "Unstar"
msgstr "Retirar estrela"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Cancelar a subscrición"
@@ -1150,7 +1131,6 @@ msgstr "Clave de usuario"
msgid "User name"
msgstr "Identificador"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Identificador ou correo electrónico"

View File

@@ -60,7 +60,6 @@ msgstr "Felhasználó hozzáadása"
msgid "Admin"
msgstr "Adminisztráció"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Adminisztrátor felhasználóneve"
@@ -73,7 +72,6 @@ msgstr "Adminisztrátor felhasználóneve"
msgid "All"
msgstr "Mind"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Mindig"
@@ -173,18 +171,14 @@ msgstr "Hozzon létre egy szűrőkifejezést annak megjelölésére, hogy mit sz
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Mégse"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed böngészőbővítmény {browserExtensionVersion} verzió."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "A CommaFeed kompatibilis a Fever API-val. Használja a következő URL-t Fever-kompatibilis mobilkliensében. Jelentkezzen be felhasználónevével és <0>API-kulcsával</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "A CommaFeed kompatibilis a Google Reader API-val. Használja a következő URL-t Google Reader-kompatibilis mobilkliensében. Jelentkezzen be felhasználónevével és <0>API-kulcsával</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed következő olvasatlan elem"
@@ -240,7 +238,6 @@ msgstr "Erősítse meg"
msgid "Confirm password"
msgstr "Erősítse meg a jelszót"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Jelszó megerősítése"
@@ -294,7 +291,6 @@ msgstr "nap"
msgid "Delete"
msgstr "Törlés"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Fiók törlése"
@@ -342,13 +338,10 @@ msgstr "Húzza a hivatkozást a könyvjelzősávra"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-mail cím"
@@ -399,7 +392,6 @@ msgstr "Bővítmény beállításai"
msgid "Feed name"
msgstr "Hírcsatorna neve"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Nyissa meg az API dokumentációját."
msgid "Goodies"
msgstr "Jók"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Szőlő"
@@ -570,7 +570,6 @@ msgstr "Előfizetések betöltése..."
msgid "Loading tags..."
msgstr "Címkék betöltése..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigáljon egy előfizetéshez a nevének megadásával"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigálás a következő olvasatlan bejegyzéseket tartalmazó kategóriára/hírcsatornára, ha minden bejegyzést olvasottnak jelöl"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Soha"
@@ -657,7 +655,6 @@ msgstr "Soha"
msgid "New password"
msgstr "Új jelszó"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Új jelszó"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML exportálás"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML fájl"
@@ -791,10 +787,7 @@ msgstr "Szülő kategória"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Jelszó"
@@ -874,7 +867,6 @@ msgstr "Frissítés"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "A regisztrációk le vannak zárva ezen a CommaFeed példányon"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Jelszó visszaállítása"
@@ -883,7 +875,6 @@ msgstr "Jelszó visszaállítása"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Jobb klikk"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Görgetés"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Keresés"
@@ -924,7 +913,6 @@ msgstr "Következő olvasatlan feed/kategória kiválasztása"
msgid "Select previous unread feed/category"
msgstr "Előző olvasatlan feed/kategória kiválasztása"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Szerver URL"
@@ -953,8 +941,6 @@ msgstr "Oszd meg"
msgid "Sharing sites"
msgstr "Webhelyek megosztása"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "Olvasatlan darabszám megjelenítése a tab faviconján"
msgid "Show unread count in tab title"
msgstr "Olvasatlan darabszám megjelenítése a tab címében"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Regisztráció"
msgid "Something bad just happened..."
msgstr "Valami rossz történt..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Szóköz"
@@ -1030,7 +1014,6 @@ msgstr "Csillag"
msgid "Starred"
msgstr "Csillaggal megjelölve"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Váltás világos témára"
msgid "System"
msgstr "Rendszer"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Címkék"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Ez a feed egy régi szűrővel rendelkezik, amely nem szerkeszthető és nem kerül alkalmazásra. Kérjük, hozza létre újra a szűrőt az új kifejezésszerkesztővel. A régi szűrőkifejezés a következő volt: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ez az Ön API-kulcsa. Felhasználható néhány írásvédett API-művelethez, és hozzáférést biztosít a Fever API-hoz. Használja az oldal alján található űrlapot egy új API-kulcs létrehozásához"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ez az Ön API-kulcsa. Néhány csak olvasható API-művelethez használható, és hozzáférést biztosít a Fever API-hoz és a Google Reader API-hoz. Használja az oldal alján található űrlapot új API-kulcs létrehozásához"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Olvasatlan"
msgid "Unstar"
msgstr "Csillag eltávolítása"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Leiratkozás"
@@ -1149,7 +1130,6 @@ msgstr "Felhasználói kulcs"
msgid "User name"
msgstr "Felhasználónév"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Felhasználói név vagy e-mail cím"

View File

@@ -60,7 +60,6 @@ msgstr "Tambahkan pengguna"
msgid "Admin"
msgstr "Admin"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nama pengguna admin"
@@ -73,7 +72,6 @@ msgstr "Nama pengguna admin"
msgid "All"
msgstr "Semua"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Selalu"
@@ -173,18 +171,14 @@ msgstr "Bangun ekspresi filter untuk menunjukkan apa yang ingin Anda baca. Entri
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Batal"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Ekstensi browser CommaFeed versi {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed kompatibel dengan Fever API. Gunakan URL berikut di klien seluler Anda yang kompatibel dengan Fever. Login dengan nama pengguna dan <0>kunci API</0> Anda."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed kompatibel dengan Google Reader API. Gunakan URL berikut di klien seluler Anda yang kompatibel dengan Google Reader. Login dengan nama pengguna dan <0>kunci API</0> Anda."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed item yang belum dibaca berikutnya"
@@ -240,7 +238,6 @@ msgstr "Konfirmasi"
msgid "Confirm password"
msgstr "Konfirmasi kata sandi"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Konfirmasi Kata Sandi"
@@ -294,7 +291,6 @@ msgstr "hari"
msgid "Delete"
msgstr "Hapus"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Hapus akun"
@@ -342,13 +338,10 @@ msgstr "Seret tautan ke bilah bookmark"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "Email"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Alamat email"
@@ -399,7 +392,6 @@ msgstr "Opsi ekstensi"
msgid "Feed name"
msgstr "Nama umpan"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Buka dokumentasi API."
msgid "Goodies"
msgstr "Pernak-pernik"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Anggur"
@@ -570,7 +570,6 @@ msgstr "Memuat langganan..."
msgid "Loading tags..."
msgstr "Memuat tag..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigasikan ke langganan dengan memasukkan namanya"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigasikan ke kategori/umpan berikutnya dengan entri yang belum dibaca saat menandai semua entri sebagai telah dibaca"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Tidak pernah"
@@ -657,7 +655,6 @@ msgstr "Tidak pernah"
msgid "New password"
msgstr "Kata sandi baru"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Kata Sandi Baru"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "ekspor OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "file OPML"
@@ -791,10 +787,7 @@ msgstr "Kategori Induk"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Kata Sandi"
@@ -874,7 +867,6 @@ msgstr "Segarkan"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Pendaftaran ditutup pada instans CommaFeed ini"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Atur Ulang Kata Sandi"
@@ -883,7 +875,6 @@ msgstr "Atur Ulang Kata Sandi"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Klik kanan"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Menggulir"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Pencarian"
@@ -924,7 +913,6 @@ msgstr "Pilih umpan/kategori belum dibaca berikutnya"
msgid "Select previous unread feed/category"
msgstr "Pilih umpan/kategori belum dibaca sebelumnya"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL Server"
@@ -953,8 +941,6 @@ msgstr "Bagikan"
msgid "Sharing sites"
msgstr "Berbagi situs"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Pergeseran"
@@ -1003,7 +989,6 @@ msgstr "Tampilkan jumlah yang belum dibaca di favicon tab"
msgid "Show unread count in tab title"
msgstr "Tampilkan jumlah yang belum dibaca di judul tab"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Daftar"
msgid "Something bad just happened..."
msgstr "Sesuatu yang buruk baru saja terjadi..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Spasi"
@@ -1030,7 +1014,6 @@ msgstr "Bintang"
msgid "Starred"
msgstr "Berbintang"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Beralih ke tema terang"
msgid "System"
msgstr "Sistem"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tag"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Umpan ini memiliki filter lama yang tidak dapat diedit dan tidak diterapkan. Silakan buat ulang filter menggunakan editor ekspresi baru. Ekspresi filter lama adalah: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ini adalah kunci API Anda. Ini dapat digunakan untuk beberapa operasi API baca-saja dan memberikan akses ke Fever API. Gunakan formulir di bagian bawah halaman untuk membuat kunci API baru"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ini adalah kunci API Anda. Ini dapat digunakan untuk beberapa operasi API yang hanya-baca dan memberikan akses ke Fever API dan Google Reader API. Gunakan formulir di bagian bawah halaman untuk membuat kunci API baru"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Belum Dibaca"
msgid "Unstar"
msgstr "Hapus bintang"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Berhenti berlangganan"
@@ -1149,7 +1130,6 @@ msgstr "Kunci pengguna"
msgid "User name"
msgstr "Nama pengguna"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nama Pengguna atau Email"

View File

@@ -60,7 +60,6 @@ msgstr "Aggiungi utente"
msgid "Admin"
msgstr "Ammin"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nome utente amministratore"
@@ -73,7 +72,6 @@ msgstr "Nome utente amministratore"
msgid "All"
msgstr "Tutto"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Sempre"
@@ -173,18 +171,14 @@ msgstr "Crea un'espressione di filtro per indicare cosa vuoi leggere. Le voci ch
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Annulla"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Versione dell'estensione del browser CommaFeed {browserExtensionVersion}
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed è compatibile con l'API Fever. Utilizza il seguente URL nel tuo client mobile compatibile con Fever. Accedi con il tuo nome utente e la tua <0>chiave API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed è compatibile con l'API Google Reader. Utilizza il seguente URL nel tuo client mobile compatibile con Google Reader. Accedi con il tuo nome utente e la tua <0>chiave API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed successivo elemento non letto"
@@ -240,7 +238,6 @@ msgstr "Conferma"
msgid "Confirm password"
msgstr "Conferma password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Conferma password"
@@ -294,7 +291,6 @@ msgstr "giorni"
msgid "Delete"
msgstr "Elimina"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Elimina account"
@@ -342,13 +338,10 @@ msgstr "Trascina il collegamento sulla barra dei preferiti"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Indirizzo e-mail"
@@ -399,7 +392,6 @@ msgstr "Opzioni estensione"
msgid "Feed name"
msgstr "Nome del feed"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Vai alla documentazione dell'API."
msgid "Goodies"
msgstr "Chicche"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Uva"
@@ -570,7 +570,6 @@ msgstr "Caricamento abbonamenti..."
msgid "Loading tags..."
msgstr "Caricamento tag..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigare verso un abbonamento inserendo il suo nome"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Passa alla categoria/feed successivo con voci non lette quando contrassegni tutte le voci come lette"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Mai"
@@ -657,7 +655,6 @@ msgstr "Mai"
msgid "New password"
msgstr "Nuova password"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nuova password"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Esportazione OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "File OPML"
@@ -791,10 +787,7 @@ msgstr "Categoria padre"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Password"
@@ -874,7 +867,6 @@ msgstr "Aggiorna"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Le registrazioni sono chiuse su questa istanza CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Reimposta password"
@@ -883,7 +875,6 @@ msgstr "Reimposta password"
msgid "REST API"
msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Clic destro"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Scorrimento"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Cerca"
@@ -924,7 +913,6 @@ msgstr "Seleziona il feed/categoria non letto successivo"
msgid "Select previous unread feed/category"
msgstr "Seleziona il feed/categoria non letto precedente"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL del server"
@@ -953,8 +941,6 @@ msgstr "Condividi"
msgid "Sharing sites"
msgstr "Condivisione di siti"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Cambio"
@@ -1003,7 +989,6 @@ msgstr "Mostra il numero di non letti nella favicon della scheda"
msgid "Show unread count in tab title"
msgstr "Mostra il numero di non letti nel titolo della scheda"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Iscriviti"
msgid "Something bad just happened..."
msgstr "È appena successo qualcosa di brutto..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Spazio"
@@ -1030,7 +1014,6 @@ msgstr "Stella"
msgid "Starred"
msgstr "Speciali"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Passa al tema chiaro"
msgid "System"
msgstr "Sistema"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tag"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Questo feed ha un filtro legacy che non può essere modificato e non viene applicato. Ricrea il filtro utilizzando il nuovo editor di espressioni. L'espressione del filtro legacy era: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Questa è la tua chiave API. Può essere utilizzata per alcune operazioni API di sola lettura e garantisce l'accesso all'API Fever. Utilizza il modulo in fondo alla pagina per generare una nuova chiave API"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Questa è la tua chiave API. Può essere utilizzata per alcune operazioni API di sola lettura e concede l'accesso all'API Fever e all'API Google Reader. Usa il modulo in fondo alla pagina per generare una nuova chiave API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Non letto"
msgid "Unstar"
msgstr "Elimina le stelle"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Annulla iscrizione"
@@ -1149,7 +1130,6 @@ msgstr "Chiave utente"
msgid "User name"
msgstr "Nome utente"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nome utente o e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "ユーザー追加"
msgid "Admin"
msgstr "管理者"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "管理者ユーザー名"
@@ -73,7 +72,6 @@ msgstr "管理者ユーザー名"
msgid "All"
msgstr "すべて"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "常に"
@@ -173,18 +171,14 @@ msgstr "読みたい内容を指定するためのフィルタ式を作成しま
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "キャンセル"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed ブラウザー拡張機能のバージョンは {browserExten
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeedはFever APIと互換性があります。Fever互換のモバイルクライアントで次のURLを使用してください。ユーザー名と <0>API キー</0> でログインしてください。"
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeedはGoogle Reader APIと互換性があります。Google Reader互換のモバイルクライアントで次のURLを使用してください。ユーザー名と <0>API キー</0> でログインしてください。"
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed 次の未読アイテム"
@@ -240,7 +238,6 @@ msgstr "確認"
msgid "Confirm password"
msgstr "パスワード確認"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "パスワードの確認"
@@ -294,7 +291,6 @@ msgstr "日"
msgid "Delete"
msgstr "削除"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "アカウント削除"
@@ -342,13 +338,10 @@ msgstr "リンクをブックマークバーにドラッグ"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "メール"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "メールアドレス"
@@ -399,7 +392,6 @@ msgstr "拡張機能オプション"
msgid "Feed name"
msgstr "フィード名"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "APIドキュメントに移動します。"
msgid "Goodies"
msgstr "グッズ"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "グレープ"
@@ -570,7 +570,6 @@ msgstr "サブスクリプションを読み込んでいます..."
msgid "Loading tags..."
msgstr "タグを読み込んでいます..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "名前を入力してサブスクリプションに移動します"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "すべてのエントリーを既読にするとき、未読エントリーのある次のカテゴリ/フィードに移動する"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "しない"
@@ -657,7 +655,6 @@ msgstr "しない"
msgid "New password"
msgstr "新しいパスワード"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "新しいパスワード"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML エクスポート"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPMLファイル"
@@ -791,10 +787,7 @@ msgstr "親カテゴリ"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "パスワード"
@@ -874,7 +867,6 @@ msgstr "リフレッシュ"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "このCommaFeedインスタンスの登録は終了しています"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "パスワードをリセット"
@@ -883,7 +875,6 @@ msgstr "パスワードをリセット"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "右クリック"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "スクロール"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "検索"
@@ -924,7 +913,6 @@ msgstr "次の未読フィード/カテゴリを選択"
msgid "Select previous unread feed/category"
msgstr "前の未読フィード/カテゴリを選択"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "サーバーURL"
@@ -953,8 +941,6 @@ msgstr "シェア"
msgid "Sharing sites"
msgstr "共有サイト"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "未読数をタブのアイコンに表示する"
msgid "Show unread count in tab title"
msgstr "未読数をタブのタイトルに表示する"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "サインアップ"
msgid "Something bad just happened..."
msgstr "何か悪いことが起きました..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Space"
@@ -1030,7 +1014,6 @@ msgstr "スター"
msgid "Starred"
msgstr "スター付き"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "ライトテーマに切り替え"
msgid "System"
msgstr "システム"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "タグ"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "このフィードには、編集できず適用もされない古い形式のフィルタがあります。新しい式エディタを使用してフィルタを再作成してください。古いフィルタ式は次のとおりでした: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "これはあなたのAPIキーです。いくつかの読み取り専用API操作に使用できます。これにより、Fever APIへのアクセスが可能になります。ページの下部のフォームを使用して新しいAPIキーを生成します。"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "これはあなたのAPIキーです。一部の読み取り専用API操作に使用でき、Fever APIおよびGoogle Reader APIへのアクセスを許可します。新しいAPIキーを生成するには、ページ下部のフォームを使用してください"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "未読"
msgid "Unstar"
msgstr "スターを外す"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "退会"
@@ -1149,7 +1130,6 @@ msgstr "ユーザーキー"
msgid "User name"
msgstr "ユーザー名"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "ユーザー名またはメールアドレス"

View File

@@ -60,7 +60,6 @@ msgstr "사용자 추가"
msgid "Admin"
msgstr "관리자"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "관리자 사용자 이름"
@@ -73,7 +72,6 @@ msgstr "관리자 사용자 이름"
msgid "All"
msgstr "전체"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "항상"
@@ -173,18 +171,14 @@ msgstr "읽고 싶은 항목을 지정하기 위한 필터 표현식을 작성
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "취소"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed 브라우저 확장 프로그램 버전 {browserExtensionVersi
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed는 Fever API와 호환됩니다. Fever 호환 모바일 클라이언트에서 다음 URL을 사용하십시오. 사용자 이름과 <0>API 키</0>로 로그인하십시오."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed는 Google Reader API와 호환됩니다. Google Reader 호환 모바일 클라이언트에서 다음 URL을 사용하십시오. 사용자 이름과 <0>API 키</0>로 로그인하십시오."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "다음 읽지 않은 항목을 쉼표로 피드"
@@ -240,7 +238,6 @@ msgstr "확인"
msgid "Confirm password"
msgstr "비밀번호 확인"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "비밀번호 확인"
@@ -294,7 +291,6 @@ msgstr "일"
msgid "Delete"
msgstr "삭제"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "계정 삭제"
@@ -342,13 +338,10 @@ msgstr "링크를 북마크바로 드래그"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "이메일"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "이메일 주소"
@@ -399,7 +392,6 @@ msgstr "확장 프로그램 옵션"
msgid "Feed name"
msgstr "피드 이름"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "API 문서로 이동합니다."
msgid "Goodies"
msgstr "굿즈"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "그레이프"
@@ -570,7 +570,6 @@ msgstr "구독 로드 중..."
msgid "Loading tags..."
msgstr "태그 로드 중..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "이름을 입력하여 구독으로 이동"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "모든 항목을 읽음으로 표시할 때 읽지 않은 항목이 있는 다음 카테고리/피드로 이동"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "안 함"
@@ -657,7 +655,6 @@ msgstr "안 함"
msgid "New password"
msgstr "새 비밀번호"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "새 비밀번호"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML 내보내기"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML 파일"
@@ -791,10 +787,7 @@ msgstr "부모 카테고리"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "비밀번호"
@@ -874,7 +867,6 @@ msgstr "새로 고침"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "이 CommaFeed 인스턴스에 대한 등록이 마감되었습니다."
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "비밀번호 재설정"
@@ -883,7 +875,6 @@ msgstr "비밀번호 재설정"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "오른쪽 클릭"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "스크롤"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "검색"
@@ -924,7 +913,6 @@ msgstr "다음 읽지 않은 피드/카테고리 선택"
msgid "Select previous unread feed/category"
msgstr "이전 읽지 않은 피드/카테고리 선택"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "서버 URL"
@@ -953,8 +941,6 @@ msgstr "공유"
msgid "Sharing sites"
msgstr "사이트 공유"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "시프트"
@@ -1003,7 +989,6 @@ msgstr "탭 파비콘에 읽지 않은 개수 표시"
msgid "Show unread count in tab title"
msgstr "탭 제목에 읽지 않은 개수 표시"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "가입"
msgid "Something bad just happened..."
msgstr "뭔가 안 좋은 일이 일어났어..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "우주"
@@ -1030,7 +1014,6 @@ msgstr "스타"
msgid "Starred"
msgstr "별표"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "밝은 테마로 전환"
msgid "System"
msgstr "시스템"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "태그"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "이 피드에는 편집할 수 없고 적용되지 않는 기존 필터가 있습니다. 새 표현식 편집기를 사용하여 필터를 다시 만드십시오. 기존 필터 표현식은 <0>{0}</0>이었습니다."
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "이것은 귀하의 API 키입니다. 일부 읽기 전용 API 작업에 사용할 수 있으며 Fever API에 대한 액세스 권한을 부여합니다. 새 API 키를 생성하려면 페이지 하단의 양식을 사용하십시오."
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "이것은 회원님의 API 키입니다. 일부 읽기 전용 API 작업에 사용할 수 있으며 Fever API 및 Google Reader API에 대한 액세스 권한을 부여합니다. 새 API 키를 생성하려면 페이지 하단의 양식을 사용하십시오"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "읽지 않음"
msgid "Unstar"
msgstr "별표 제거"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "구독 취소"
@@ -1149,7 +1130,6 @@ msgstr "사용자 키"
msgid "User name"
msgstr "사용자 이름"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "사용자 이름 또는 이메일"

View File

@@ -60,7 +60,6 @@ msgstr "Tambah pengguna"
msgid "Admin"
msgstr "Pentadbir"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nama pengguna pentadbir"
@@ -73,7 +72,6 @@ msgstr "Nama pengguna pentadbir"
msgid "All"
msgstr "Semua"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Sentiasa"
@@ -173,18 +171,14 @@ msgstr "Bina ungkapan penapis untuk menunjukkan perkara yang anda mahu baca. Ent
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Batal"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Ekstensi penyemak imbas CommaFeed versi {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed serasi dengan Fever API. Gunakan URL berikut dalam klien mudah alih serasi Fever anda. Log masuk dengan nama pengguna anda dan <0>kunci API</0> anda."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed serasi dengan Google Reader API. Gunakan URL berikut dalam klien mudah alih serasi Google Reader anda. Log masuk dengan nama pengguna anda dan <0>kunci API</0> anda."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed item belum dibaca seterusnya"
@@ -240,7 +238,6 @@ msgstr "Sahkan"
msgid "Confirm password"
msgstr "Sahkan kata laluan"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Sahkan Kata Laluan"
@@ -294,7 +291,6 @@ msgstr "hari"
msgid "Delete"
msgstr "Padam"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Padam akaun"
@@ -342,13 +338,10 @@ msgstr "Seret pautan ke bar penanda halaman"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mel"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Alamat e-mel"
@@ -399,7 +392,6 @@ msgstr "Pilihan ekstensi"
msgid "Feed name"
msgstr "Nama suapan"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Pergi ke dokumentasi API."
msgid "Goodies"
msgstr "Barangan"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Anggur"
@@ -570,7 +570,6 @@ msgstr "Memuatkan langganan..."
msgid "Loading tags..."
msgstr "Memuatkan tag..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigasi ke langganan dengan memasukkan namanya"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigasi ke kategori/suapan seterusnya dengan entri yang belum dibaca apabila menandakan semua entri sebagai dibaca"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Tidak pernah"
@@ -657,7 +655,6 @@ msgstr "Tidak pernah"
msgid "New password"
msgstr "Kata laluan baharu"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Kata Laluan Baharu"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML eksport"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Fail OPML"
@@ -791,10 +787,7 @@ msgstr "Kategori Induk"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Kata Laluan"
@@ -874,7 +867,6 @@ msgstr "Muat semula"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Pendaftaran ditutup pada contoh CommaFeed ini"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Tetap Semula Kata Laluan"
@@ -883,7 +875,6 @@ msgstr "Tetap Semula Kata Laluan"
msgid "REST API"
msgstr "REHAT API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Klik kanan"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Menatal"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Cari"
@@ -924,7 +913,6 @@ msgstr "Pilih suapan/kategori belum dibaca seterusnya"
msgid "Select previous unread feed/category"
msgstr "Pilih suapan/kategori belum dibaca sebelumnya"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL Pelayan"
@@ -953,8 +941,6 @@ msgstr "Kongsi"
msgid "Sharing sites"
msgstr "Berkongsi tapak"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Anjakan"
@@ -1003,7 +989,6 @@ msgstr "Tunjukkan kiraan belum dibaca dalam favicon tab"
msgid "Show unread count in tab title"
msgstr "Tunjukkan kiraan belum dibaca dalam tajuk tab"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Daftar"
msgid "Something bad just happened..."
msgstr "Sesuatu yang buruk baru saja berlaku..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Angkasa"
@@ -1030,7 +1014,6 @@ msgstr "Bintang"
msgid "Starred"
msgstr "Dibintangi"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Tukar kepada tema cahaya"
msgid "System"
msgstr "Sistem"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tag"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Suapan ini mempunyai penapis warisan yang tidak boleh diedit dan tidak digunakan. Sila buat semula penapis menggunakan editor ungkapan baharu. Ungkapan penapis warisan ialah: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ini ialah kunci API anda. Ia boleh digunakan untuk beberapa operasi API baca sahaja dan memberikan akses kepada Fever API. Gunakan borang di bahagian bawah halaman untuk menjana kunci API baharu"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Ini ialah kunci API anda. Ia boleh digunakan untuk sesetengah operasi API baca sahaja dan memberikan akses kepada Fever API dan Google Reader API. Gunakan borang di bahagian bawah halaman untuk menjana kunci API baharu"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Belum dibaca"
msgid "Unstar"
msgstr "Nyahbintang"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Nyahlanggan"
@@ -1149,7 +1130,6 @@ msgstr "Kunci pengguna"
msgid "User name"
msgstr "Nama pengguna"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nama Pengguna atau E-mel"

View File

@@ -60,7 +60,6 @@ msgstr "Legg til bruker"
msgid "Admin"
msgstr "Administrator"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Administrator brukernavn"
@@ -73,7 +72,6 @@ msgstr "Administrator brukernavn"
msgid "All"
msgstr "Alle"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Alltid"
@@ -173,18 +171,14 @@ msgstr "Bygg et filteruttrykk for å angi hva du vil lese. Oppføringer som ikke
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Avbryt"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed nettleserutvidelse versjon {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed er kompatibel med Fever API. Bruk følgende URL i din Fever-kompatible mobilklient. Logg inn med brukernavn og din <0>API-nøkkel</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed er kompatibel med Google Reader API. Bruk følgende URL i din Google Reader-kompatible mobilklient. Logg inn med brukernavn og din <0>API-nøkkel</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed neste uleste element"
@@ -240,7 +238,6 @@ msgstr "Bekreft"
msgid "Confirm password"
msgstr "Bekreft passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Bekreft passord"
@@ -294,7 +291,6 @@ msgstr "dager"
msgid "Delete"
msgstr "Slett"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Slett konto"
@@ -342,13 +338,10 @@ msgstr "Dra lenken til bokmerkelinjen"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-post"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-postadresse"
@@ -399,7 +392,6 @@ msgstr "Utvidelsesvalg"
msgid "Feed name"
msgstr "Feednavn"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Gå til API-dokumentasjonen."
msgid "Goodies"
msgstr "Godbiter"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Drue"
@@ -570,7 +570,6 @@ msgstr "Laster abonnementer..."
msgid "Loading tags..."
msgstr "Laster tagger..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Naviger til et abonnement ved å skrive inn navnet"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Naviger til neste kategori/feed med uleste oppføringer når du markerer alle oppføringer som lest"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Aldri"
@@ -657,7 +655,6 @@ msgstr "Aldri"
msgid "New password"
msgstr "Nytt passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nytt passord"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML-eksport"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML-fil"
@@ -791,10 +787,7 @@ msgstr "Overordnet kategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Passord"
@@ -874,7 +867,6 @@ msgstr "Oppdater"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Tilbakestill passord"
@@ -883,7 +875,6 @@ msgstr "Tilbakestill passord"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Høyreklikk"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Rulling"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Søk"
@@ -924,7 +913,6 @@ msgstr "Velg neste uleste feed/kategori"
msgid "Select previous unread feed/category"
msgstr "Velg forrige uleste feed/kategori"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server-URL"
@@ -953,8 +941,6 @@ msgstr "Del"
msgid "Sharing sites"
msgstr "Delingssider"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Skift"
@@ -1003,7 +989,6 @@ msgstr "Vis antall uleste i fanens favicon"
msgid "Show unread count in tab title"
msgstr "Vis antall uleste i fanetittelen"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Meld deg på"
msgid "Something bad just happened..."
msgstr "Noe ille skjedde akkurat..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Mellomrom"
@@ -1030,7 +1014,6 @@ msgstr "Stjerne"
msgid "Starred"
msgstr "Stjerne"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Bytt til lystema"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tagger"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Denne feeden har et utdatert filter som ikke kan redigeres og ikke blir brukt. Vennligst opprett filteret på nytt med den nye uttrykkseditoren. Det utdaterte filteruttrykket var: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dette er API-nøkkelen din. Den kan brukes til enkelte skrivebeskyttede API-operasjoner og gir tilgang til Fever API. Bruk skjemaet nederst på siden for å generere en ny API-nøkkel"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dette er din API-nøkkel. Den kan brukes til noen skrivebeskyttede API-operasjoner og gir tilgang til Fever API og Google Reader API. Bruk skjemaet nederst på siden for å generere en ny API-nøkkel"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Ulest"
msgid "Unstar"
msgstr "Fjern stjerne"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Avslutt abonnement"
@@ -1149,7 +1130,6 @@ msgstr "Brukernøkkel"
msgid "User name"
msgstr "Brukernavn"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Brukernavn eller e-post"

View File

@@ -60,7 +60,6 @@ msgstr "Gebruiker toevoegen"
msgid "Admin"
msgstr "Beheerder"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Gebruikersnaam beheerder"
@@ -73,7 +72,6 @@ msgstr "Gebruikersnaam beheerder"
msgid "All"
msgstr "Alles"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Altijd"
@@ -173,18 +171,14 @@ msgstr "Maak een filterexpressie om aan te geven wat u wilt lezen. Items die nie
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Annuleren"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed browser extensie versie {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed is compatibel met de Fever API. Gebruik de volgende URL in uw Fever-compatibele mobiele client. Log in met uw gebruikersnaam en uw <0>API-sleutel</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed is compatibel met de Google Reader API. Gebruik de volgende URL in uw Google Reader-compatibele mobiele client. Log in met uw gebruikersnaam en uw <0>API-sleutel</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed volgende ongelezen item"
@@ -240,7 +238,6 @@ msgstr "Bevestigen"
msgid "Confirm password"
msgstr "Bevestig wachtwoord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Bevestig wachtwoord"
@@ -294,7 +291,6 @@ msgstr "dagen"
msgid "Delete"
msgstr "Verwijderen"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Account verwijderen"
@@ -342,13 +338,10 @@ msgstr "Link naar bladwijzerbalk slepen"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-mailadres"
@@ -399,7 +392,6 @@ msgstr "Extensie opties"
msgid "Feed name"
msgstr "Feednaam"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Ga naar de API-documentatie."
msgid "Goodies"
msgstr "Goederen"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Druif"
@@ -570,7 +570,6 @@ msgstr "Abonnementen laden..."
msgid "Loading tags..."
msgstr "Tags laden..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigeer naar een abonnement door de naam in te voeren"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigeer naar de volgende categorie/feed met ongelezen items wanneer alle items als gelezen worden gemarkeerd"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nooit"
@@ -657,7 +655,6 @@ msgstr "Nooit"
msgid "New password"
msgstr "Nieuw wachtwoord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nieuw wachtwoord"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML-export"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML-bestand"
@@ -791,10 +787,7 @@ msgstr "Oudercategorie"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Wachtwoord"
@@ -874,7 +867,6 @@ msgstr "Vernieuwen"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registraties zijn gesloten op deze CommaFeed-instantie"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Wachtwoord herstellen"
@@ -883,7 +875,6 @@ msgstr "Wachtwoord herstellen"
msgid "REST API"
msgstr "REST-API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Rechtermuisklik"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Scrollen"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Zoeken"
@@ -924,7 +913,6 @@ msgstr "Selecteer volgende ongelezen feed/categorie"
msgid "Select previous unread feed/category"
msgstr "Selecteer vorige ongelezen feed/categorie"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server-URL"
@@ -953,8 +941,6 @@ msgstr "Delen"
msgid "Sharing sites"
msgstr "Sites delen"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "Toon aantal ongelezen in tabblad-favicon"
msgid "Show unread count in tab title"
msgstr "Toon aantal ongelezen in tabbladtitel"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Aanmelden"
msgid "Something bad just happened..."
msgstr "Er is net iets ergs gebeurd..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Ruimte"
@@ -1030,7 +1014,6 @@ msgstr "Ster"
msgid "Starred"
msgstr "Met ster"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Schakel over naar licht thema"
msgid "System"
msgstr "Systeem"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tags"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Deze feed heeft een legacy-filter dat niet kan worden bewerkt en niet wordt toegepast. Maak het filter opnieuw met de nieuwe expressie-editor. De legacy-filterexpressie was: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dit is je API-sleutel. Het kan worden gebruikt voor sommige alleen-lezen API-bewerkingen en geeft toegang tot de Fever API. Gebruik het formulier onderaan de pagina om een nieuwe API-sleutel te genereren"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dit is uw API-sleutel. Deze kan worden gebruikt voor sommige alleen-lezen API-bewerkingen en geeft toegang tot de Fever API en de Google Reader API. Gebruik het formulier onderaan de pagina om een nieuwe API-sleutel te genereren"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Ongelezen"
msgid "Unstar"
msgstr "Sterren uit"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Afmelden"
@@ -1149,7 +1130,6 @@ msgstr "Gebruikerssleutel"
msgid "User name"
msgstr "Gebruikersnaam"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Gebruikersnaam of e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "Legg til brukar"
msgid "Admin"
msgstr "Administrator"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Brukarnamn for administrator"
@@ -73,7 +72,6 @@ msgstr "Brukarnamn for administrator"
msgid "All"
msgstr "Alle"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Alltid"
@@ -173,18 +171,14 @@ msgstr "Bygg eit filteruttrykk for å angis kva du vil lesa. Innlegg som ikkje s
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Avbryt"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed nettlesarutviding versjon {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed er kompatibel med Fever API. Bruk følgjande URL i din Fever-kompatible mobilklient. Logg inn med brukarnamn og din <0>API-nøkkel</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed er kompatibel med Google Reader API. Bruk følgjande URL i din Google Reader-kompatible mobilklient. Logg inn med brukarnamn og din <0>API-nøkkel</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed neste ulesne element"
@@ -240,7 +238,6 @@ msgstr "Bekreft"
msgid "Confirm password"
msgstr "Bekreft passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Stadfest passord"
@@ -294,7 +291,6 @@ msgstr "dagar"
msgid "Delete"
msgstr "Slett"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Slett konto"
@@ -342,13 +338,10 @@ msgstr "Dra lenken til bokmerkelinjen"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-post"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-postadresse"
@@ -399,7 +392,6 @@ msgstr "Val for utviding"
msgid "Feed name"
msgstr "Feednavn"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Gå til API-dokumentasjonen."
msgid "Goodies"
msgstr "Godbitar"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Drue"
@@ -570,7 +570,6 @@ msgstr "Laster abonnementer..."
msgid "Loading tags..."
msgstr "Laster tagger..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Naviger til et abonnement ved å skrive inn navnet"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Naviger til neste kategori/suap med ulesne innlegg når du markerer alle innlegg som lesne"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Aldri"
@@ -657,7 +655,6 @@ msgstr "Aldri"
msgid "New password"
msgstr "Nytt passord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nytt passord"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML-eksport"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML-fil"
@@ -791,10 +787,7 @@ msgstr "Overordnet kategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Passord"
@@ -874,7 +867,6 @@ msgstr "Oppdater"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringer er stengt på denne CommaFeed-forekomsten"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Tilbakestill passord"
@@ -883,7 +875,6 @@ msgstr "Tilbakestill passord"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Høgreklikk"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Rulling"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Søk"
@@ -924,7 +913,6 @@ msgstr "Vel neste ulesne suap/kategori"
msgid "Select previous unread feed/category"
msgstr "Vel førre ulesne suap/kategori"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server-URL"
@@ -953,8 +941,6 @@ msgstr "Del"
msgid "Sharing sites"
msgstr "Delingssider"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Skift"
@@ -1003,7 +989,6 @@ msgstr "Vis talet på ulesne i favicon for fana"
msgid "Show unread count in tab title"
msgstr "Vis talet på ulesne i tittelen på fana"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Meld deg på"
msgid "Something bad just happened..."
msgstr "Noe ille skjedde akkurat..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Mellomrom"
@@ -1030,7 +1014,6 @@ msgstr "Stjerne"
msgid "Starred"
msgstr "Stjernemerka"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Bytt til lyst tema"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tagger"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Denne suapen har eit gamalt filter som ikkje kan redigerast og som ikkje vert brukt. Ver venleg og lag filteret på nytt med den nye uttrykkseditoren. Det gamle filteruttrykket var: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dette er API-nøkkelen din. Han kan brukast til enkelte skrivebeskytta API-handlingar og gjev tilgang til Fever API. Bruk skjemaet nedst på sida for å laga ein ny API-nøkkel"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Dette er din API-nøkkel. Ho kan brukast til nokre skrivebeskytta API-operasjonar og gir tilgang til Fever API og Google Reader API. Bruk skjemaet nedst på sida for å generere ein ny API-nøkkel"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Ulesen"
msgid "Unstar"
msgstr "Fjern stjerne"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Avslutt abonnement"
@@ -1149,7 +1130,6 @@ msgstr "Brukarnøkkel"
msgid "User name"
msgstr "Brukarnamn"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Brukarnamn eller e-post"

View File

@@ -60,7 +60,6 @@ msgstr "Dodaj użytkownika"
msgid "Admin"
msgstr "Administracja"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nazwa użytkownika administratora"
@@ -73,7 +72,6 @@ msgstr "Nazwa użytkownika administratora"
msgid "All"
msgstr "Wszystkie"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Zawsze"
@@ -173,18 +171,14 @@ msgstr "Zbuduj wyrażenie filtrujące, aby wskazać, co chcesz czytać. Wpisy, k
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Anuluj"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Wersja rozszerzenia przeglądarki CommaFeed {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed jest kompatybilny z API Fever. Użyj następującego adresu URL w swoim kliencie mobilnym zgodnym z Fever. Zaloguj się przy użyciu swojej nazwy użytkownika i <0>klucza API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed jest kompatybilny z API Google Reader. Użyj następującego adresu URL w swoim kliencie mobilnym zgodnym z Google Reader. Zaloguj się przy użyciu swojej nazwy użytkownika i <0>klucza API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "Przecinek następny nieprzeczytany element"
@@ -240,7 +238,6 @@ msgstr "Potwierdź"
msgid "Confirm password"
msgstr "Potwierdź hasło"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Potwierdź hasło"
@@ -294,7 +291,6 @@ msgstr "dni"
msgid "Delete"
msgstr "Usuń"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Usuń konto"
@@ -342,13 +338,10 @@ msgstr "Przeciągnij link do paska zakładek"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Adres e-mail"
@@ -399,7 +392,6 @@ msgstr "Opcje rozszerzenia"
msgid "Feed name"
msgstr "nazwa kanału"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Przejdź do dokumentacji API."
msgid "Goodies"
msgstr "Gadżety"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Winogronowy"
@@ -570,7 +570,6 @@ msgstr "Ładowanie subskrypcji..."
msgid "Loading tags..."
msgstr "Ładowanie tagów..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Przejdź do subskrypcji, wpisując jej nazwę"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Przejdź do następnej kategorii/kanału z nieprzeczytanymi wpisami po oznaczeniu wszystkich wpisów jako przeczytane"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nigdy"
@@ -657,7 +655,6 @@ msgstr "Nigdy"
msgid "New password"
msgstr "Nowe hasło"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nowe hasło"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Eksport OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Plik OPML"
@@ -791,10 +787,7 @@ msgstr "Kategoria nadrzędna"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Hasło"
@@ -874,7 +867,6 @@ msgstr "Odśwież"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Rejestracje są zamknięte w tej instancji CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Zresetuj hasło"
@@ -883,7 +875,6 @@ msgstr "Zresetuj hasło"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Prawe kliknięcie"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Przewijanie"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Szukaj"
@@ -924,7 +913,6 @@ msgstr "Wybierz następny nieprzeczytany kanał/kategorię"
msgid "Select previous unread feed/category"
msgstr "Wybierz poprzedni nieprzeczytany kanał/kategorię"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL serwera"
@@ -953,8 +941,6 @@ msgstr "Udostępnij"
msgid "Sharing sites"
msgstr "Udostępnianie witryn"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "zmiana"
@@ -1003,7 +989,6 @@ msgstr "Pokazuj liczbę nieprzeczytanych na ikonie karty"
msgid "Show unread count in tab title"
msgstr "Pokazuj liczbę nieprzeczytanych w tytule karty"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Zarejestruj się"
msgid "Something bad just happened..."
msgstr "Coś złego właśnie się stało..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Przestrzeń"
@@ -1030,7 +1014,6 @@ msgstr "Gwiazda"
msgid "Starred"
msgstr "Oznaczone gwiazdką"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Przełącz na jasny motyw"
msgid "System"
msgstr "Systemowy"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Tagi"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Ten kanał ma stary filtr, którego nie można edytować i który nie jest stosowany. Proszę utworzyć filtr ponownie, korzystając z nowego edytora wyrażeń. Stare wyrażenie filtra to: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "To jest Twój klucz API. Może być używany do niektórych operacji API tylko do odczytu i zapewnia dostęp do API Fever. Użyj formularza na dole strony, aby wygenerować nowy klucz API"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "To jest Twój klucz API. Może być używany do niektórych operacji API tylko do odczytu i zapewnia dostęp do API Fever i API Google Reader. Użyj formularza na dole strony, aby wygenerować nowy klucz API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Nieprzeczytane"
msgid "Unstar"
msgstr "Usuń gwiazdkę"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Anuluj subskrypcję"
@@ -1149,7 +1130,6 @@ msgstr "Klucz użytkownika"
msgid "User name"
msgstr "Nazwa użytkownika"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nazwa użytkownika lub e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "Adicionar usuário"
msgid "Admin"
msgstr "Administrador"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Nome de usuário do administrador"
@@ -73,7 +72,6 @@ msgstr "Nome de usuário do administrador"
msgid "All"
msgstr "Todos"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Sempre"
@@ -173,18 +171,14 @@ msgstr "Crie uma expressão de filtro para indicar o que você deseja ler. As en
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Cancelar"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Extensão de navegador CommaFeed versão {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed é compatível com a API Fever. Use a URL a seguir no seu aplicativo móvel compatível com Fever. Entre com seu nome de usuário e sua <0>chave de API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed é compatível com a API Google Reader. Use a URL a seguir no seu aplicativo móvel compatível com Google Reader. Entre com seu nome de usuário e sua <0>chave de API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed próximo item não lido"
@@ -240,7 +238,6 @@ msgstr "Confirmar"
msgid "Confirm password"
msgstr "Confirmar senha"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Confirmar senha"
@@ -294,7 +291,6 @@ msgstr "dias"
msgid "Delete"
msgstr "Excluir"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Excluir conta"
@@ -342,13 +338,10 @@ msgstr "Arraste o link para a barra de favoritos"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Endereço de e-mail"
@@ -399,7 +392,6 @@ msgstr "Opções da extensão"
msgid "Feed name"
msgstr "Nome do feed"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Vá para a documentação da API."
msgid "Goodies"
msgstr "Brindes"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Uva"
@@ -570,7 +570,6 @@ msgstr "Carregando assinaturas..."
msgid "Loading tags..."
msgstr "Carregando tags..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navegue até uma assinatura digitando seu nome"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navegar para a próxima categoria/feed com entradas não lidas quando todas as entrafas forem lidas"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nunca"
@@ -657,7 +655,6 @@ msgstr "Nunca"
msgid "New password"
msgstr "Nova senha"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nova senha"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Exportação OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Arquivo OPML"
@@ -791,10 +787,7 @@ msgstr "Categoria Pai"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Senha"
@@ -874,7 +867,6 @@ msgstr "Atualizar"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Os registros estão fechados nesta instância do CommaFeed"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Redefinir senha"
@@ -883,7 +875,6 @@ msgstr "Redefinir senha"
msgid "REST API"
msgstr "API REST"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Clique com o botão direito"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Deslizar"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Pesquisar"
@@ -924,7 +913,6 @@ msgstr "Selecionar próximo feed/categoria não lido"
msgid "Select previous unread feed/category"
msgstr "Selecionar feed/categoria não lido anterior"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL do servidor"
@@ -953,8 +941,6 @@ msgstr "Compartilhar"
msgid "Sharing sites"
msgstr "Compartilhando sites"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "Mostrar contagem de não lidos no favion da aba"
msgid "Show unread count in tab title"
msgstr "Mostrar contagem de não lidos no título da aba"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Inscreva-se"
msgid "Something bad just happened..."
msgstr "Algo ruim acabou de acontecer..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Espaço"
@@ -1030,7 +1014,6 @@ msgstr "Estrela"
msgid "Starred"
msgstr "Com estrela"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Mudar para tema claro"
msgid "System"
msgstr "Sistema"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Etiquetas"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Este feed tem um filtro legado que não pode ser editado e não é aplicado. Recrie o filtro usando o novo editor de expressões. A expressão do filtro legado era: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Esta é sua chave de API. Ela pode ser usada para algumas operações somente leitura da API e concede acesso à API do Fever. Use o formulário abaixo para gerar uma nova chave de API"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Esta é sua chave de API. Ela pode ser usada para algumas operações de API somente leitura e concede acesso à API Fever e à API Google Reader. Use o formulário na parte inferior da página para gerar uma nova chave de API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Não lido"
msgid "Unstar"
msgstr "Desestrelar"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Cancelar inscrição"
@@ -1149,7 +1130,6 @@ msgstr "Chave do usuário"
msgid "User name"
msgstr "Nome de usuário"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Nome de usuário ou e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "Добавить пользователя"
msgid "Admin"
msgstr "Админ"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Имя администратора"
@@ -73,7 +72,6 @@ msgstr "Имя администратора"
msgid "All"
msgstr "Все"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Всегда"
@@ -173,18 +171,14 @@ msgstr "Создайте выражение фильтра, чтобы указ
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Отмена"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Версия расширения браузера CommaFeed {browserExt
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed совместим с API Fever. Используйте следующий URL-адрес в мобильном клиенте, совместимом с Fever. Войдите в систему, указав свое имя пользователя и <0>API-ключ</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed совместим с API Google Reader. Используйте следующий URL-адрес в мобильном клиенте, совместимом с Google Reader. Войдите в систему, указав свое имя пользователя и <0>API-ключ</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed следующий непрочитанный элемент"
@@ -240,7 +238,6 @@ msgstr "Подтвердить"
msgid "Confirm password"
msgstr "Подтвердить пароль"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Подтвердите пароль"
@@ -294,7 +291,6 @@ msgstr "дней"
msgid "Delete"
msgstr "Удалить"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Удалить учетную запись"
@@ -342,13 +338,10 @@ msgstr "Перетащите ссылку на панель закладок"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "Электронная почта"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "Адрес электронной почты"
@@ -399,7 +392,6 @@ msgstr "Параметры расширения"
msgid "Feed name"
msgstr "Имя фида"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Перейдите к документации по API."
msgid "Goodies"
msgstr "Сладости"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Виноградный"
@@ -570,7 +570,6 @@ msgstr "Загрузка подписок..."
msgid "Loading tags..."
msgstr "Загрузка тегов..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Перейдите к подписке, введя ее имя."
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Переходить к следующей категории/фиду с непрочитанными записями при пометке всех записей как прочитанных"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Никогда"
@@ -657,7 +655,6 @@ msgstr "Никогда"
msgid "New password"
msgstr "Новый пароль"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Новый пароль"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Экспорт OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML файл"
@@ -791,10 +787,7 @@ msgstr "Родительская категория"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Пароль"
@@ -874,7 +867,6 @@ msgstr "Обновить"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Регистрация закрыта для этого экземпляра CommaFeed."
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Сброс пароля"
@@ -883,7 +875,6 @@ msgstr "Сброс пароля"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Правый клик"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Прокрутка"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Поиск"
@@ -924,7 +913,6 @@ msgstr "Выбрать следующий непрочитанный фид/ка
msgid "Select previous unread feed/category"
msgstr "Выбрать предыдущий непрочитанный фид/категорию"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL сервера"
@@ -953,8 +941,6 @@ msgstr "Поделиться"
msgid "Sharing sites"
msgstr "Обмен сайтами"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "Показывать количество непрочитанных в
msgid "Show unread count in tab title"
msgstr "Показывать количество непрочитанных в заголовке вкладки"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Зарегистрироваться"
msgid "Something bad just happened..."
msgstr "Только что случилось что-то плохое..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Пробел"
@@ -1030,7 +1014,6 @@ msgstr "В избранное"
msgid "Starred"
msgstr "Избранное"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Переключиться на светлую тему"
msgid "System"
msgstr "Системная"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Теги"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Этот фид имеет устаревший фильтр, который нельзя редактировать и который не применяется. Пожалуйста, создайте фильтр заново, используя новый редактор выражений. Устаревшее выражение фильтра было: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Это ваш ключ API. Он может использоваться для некоторых операций API только для чтения и предоставляет доступ к API Fever. Чтобы сгенерировать новый ключ API, воспользуйтесь формой в нижней части страницы"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Это ваш ключ API. Он может использоваться для некоторых операций API только для чтения и предоставляет доступ к API Fever и API Google Reader. Используйте форму внизу страницы, чтобы сгенерировать новый ключ API"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Не прочитано"
msgid "Unstar"
msgstr "Удалить из избранного"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Отписаться"
@@ -1149,7 +1130,6 @@ msgstr "Ключ пользователя"
msgid "User name"
msgstr "Имя пользователя"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Имя пользователя или адрес электронной почты"

View File

@@ -60,7 +60,6 @@ msgstr "Pridať užívateľa"
msgid "Admin"
msgstr "Správca"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Meno správcu"
@@ -73,7 +72,6 @@ msgstr "Meno správcu"
msgid "All"
msgstr "Všetky"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Vždy"
@@ -173,18 +171,14 @@ msgstr "Vytvorte výraz filtra na určenie toho, čo chcete čítať. Záznamy,
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Zrušiť"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Rozšírenie prehliadača CommaFeed verzie {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed je kompatibilný s Fever API. Vo svojom mobilnom klientovi kompatibilnom s Fever použite nasledujúcu adresu URL. Prihláste sa pomocou svojho používateľského mena a <0>kľúča API</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed je kompatibilný s Google Reader API. Vo svojom mobilnom klientovi kompatibilnom s Google Reader použite nasledujúcu adresu URL. Prihláste sa pomocou svojho používateľského mena a <0>kľúča API</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed ďalšia neprečítaná položka"
@@ -240,7 +238,6 @@ msgstr "Potvrdiť"
msgid "Confirm password"
msgstr "Potvrďte heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Potvrdiť heslo"
@@ -294,7 +291,6 @@ msgstr "dni"
msgid "Delete"
msgstr "Vymazať"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Vymažte účet"
@@ -342,13 +338,10 @@ msgstr "Presuňte odkaz na lištu so záložkami"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-mail"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-mailová adresa"
@@ -399,7 +392,6 @@ msgstr "Možnosti rozšírenia"
msgid "Feed name"
msgstr "Názov informačného kanála"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Prejdite na dokumentáciu API."
msgid "Goodies"
msgstr "Dobrôtky"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Hroznová"
@@ -570,7 +570,6 @@ msgstr "Načítavam odbery..."
msgid "Loading tags..."
msgstr "Načítavam značky..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Prejdite na predplatné zadaním jeho názvu"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Pri označovaní všetkých položiek ako prečítaných prejdite na ďalšiu kategóriu/kanál s neprečítanými položkami"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Nikdy"
@@ -657,7 +655,6 @@ msgstr "Nikdy"
msgid "New password"
msgstr "Nové heslo"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nové heslo"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "Export OPML"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "Súbor OPML"
@@ -791,10 +787,7 @@ msgstr "Rodičovská kategória"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Heslo"
@@ -874,7 +867,6 @@ msgstr "Obnoviť"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "V tejto inštancii CommaFeed sú registrácie uzavreté"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Obnoviť heslo"
@@ -883,7 +875,6 @@ msgstr "Obnoviť heslo"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Kliknutie pravým tlačidlom"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Rolovanie"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Hľadaj"
@@ -924,7 +913,6 @@ msgstr "Vybrať ďalší neprečítaný kanál/kategóriu"
msgid "Select previous unread feed/category"
msgstr "Vybrať predchádzajúci neprečítaný kanál/kategóriu"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "URL servera"
@@ -953,8 +941,6 @@ msgstr "Zdieľať"
msgid "Sharing sites"
msgstr "Zdieľanie stránok"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Smena"
@@ -1003,7 +989,6 @@ msgstr "Zobraziť počet neprečítaných v ikone karty"
msgid "Show unread count in tab title"
msgstr "Zobraziť počet neprečítaných v názve karty"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Zaregistrujte sa"
msgid "Something bad just happened..."
msgstr "Práve sa stalo niečo zlé..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Medzera"
@@ -1030,7 +1014,6 @@ msgstr "Hviezda"
msgid "Starred"
msgstr "S hviezdičkou"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Prepnite na svetlú tému"
msgid "System"
msgstr "Systém"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Značky"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Tento kanál má starší filter, ktorý sa nedá upravovať a nepoužíva sa. Znova vytvorte filter pomocou nového editora výrazov. Pôvodný výraz filtra bol: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Toto je váš kľúč API. Môže sa použiť na niektoré operácie rozhrania API len na čítanie a poskytuje prístup k rozhraniu Fever API. Na vygenerovanie nového kľúča API použite formulár v dolnej časti stránky."
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Toto je váš kľúč API. Môže sa použiť pre niektoré operácie API iba na čítanie a udeľuje prístup k Fever API a Google Reader API. Na vygenerovanie nového kľúča API použite formulár v spodnej časti stránky"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Neprečítané"
msgid "Unstar"
msgstr "Odobrať hviezdičku"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Zrušte odber"
@@ -1149,7 +1130,6 @@ msgstr "Používateľský kľúč"
msgid "User name"
msgstr "Meno používateľa"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Meno používateľa alebo e-mail"

View File

@@ -60,7 +60,6 @@ msgstr "Lägg till användare"
msgid "Admin"
msgstr "Admin"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Administratörsanvändarnamn"
@@ -73,7 +72,6 @@ msgstr "Administratörsanvändarnamn"
msgid "All"
msgstr "Alla"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Alltid"
@@ -173,18 +171,14 @@ msgstr "Skapa ett filteruttryck för att ange vad du vill läsa. Poster som inte
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "Avbryt"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "Webbläsartillägget CommaFeed version {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed är kompatibelt med Fever API. Använd följande URL i din Fever-kompatibla mobilklient. Logga in med ditt användarnamn och din <0>API-nyckel</0>."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed är kompatibelt med Google Reader API. Använd följande URL i din Google Reader-kompatibla mobilklient. Logga in med ditt användarnamn och din <0>API-nyckel</0>."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed nästa olästa objekt"
@@ -240,7 +238,6 @@ msgstr "Bekräfta"
msgid "Confirm password"
msgstr "Bekräfta lösenord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Bekräfta lösenord"
@@ -294,7 +291,6 @@ msgstr "dagar"
msgid "Delete"
msgstr "Ta bort"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Radera konto"
@@ -342,13 +338,10 @@ msgstr "Dra länken till bokmärkesfältet"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-post"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-postadress"
@@ -399,7 +392,6 @@ msgstr "Tilläggsalternativ"
msgid "Feed name"
msgstr "Flödesnamn"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "Gå till API-dokumentationen."
msgid "Goodies"
msgstr "Godsaker"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Lila"
@@ -570,7 +570,6 @@ msgstr "Laddar prenumerationer..."
msgid "Loading tags..."
msgstr "Laddar taggar..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Navigera till ett abonnemang genom att ange dess namn"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Navigera till nästa kategori/flöde med olästa poster när alla poster markeras som lästa"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Aldrig"
@@ -657,7 +655,6 @@ msgstr "Aldrig"
msgid "New password"
msgstr "Nytt lösenord"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Nytt lösenord"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML-export"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML-fil"
@@ -791,10 +787,7 @@ msgstr "Föräldrakategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Lösenord"
@@ -874,7 +867,6 @@ msgstr "Uppdatera"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Registreringar är stängda på denna CommaFeed-instans"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Återställ lösenord"
@@ -883,7 +875,6 @@ msgstr "Återställ lösenord"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Högerklick"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Rullning"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Sök"
@@ -924,7 +913,6 @@ msgstr "Välj nästa olästa flöde/kategori"
msgid "Select previous unread feed/category"
msgstr "Välj föregående olästa flöde/kategori"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Server-URL"
@@ -953,8 +941,6 @@ msgstr "Dela"
msgid "Sharing sites"
msgstr "Delningssajter"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Skift"
@@ -1003,7 +989,6 @@ msgstr "Visa antal olästa i flikikonen"
msgid "Show unread count in tab title"
msgstr "Visa antal olästa i fliktiteln"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Anmäl dig"
msgid "Something bad just happened..."
msgstr "Något dåligt hände precis..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Rymden"
@@ -1030,7 +1014,6 @@ msgstr "Stjärna"
msgid "Starred"
msgstr "Starmed"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Byt till ljustema"
msgid "System"
msgstr "System"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Taggar"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Detta flöde har ett gammalt filter som inte kan redigeras och inte tillämpas. Skapa filtret på nytt med den nya uttryckseditorern. Det gamla filteruttrycket var: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Detta är din API-nyckel. Den kan användas för vissa skrivskyddade API-operationer och ger åtkomst till Fever API. Använd formuläret längst ner på sidan för att generera en ny API-nyckel"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Det här är din API-nyckel. Den kan användas för vissa skrivskyddade API-åtgärder och ger åtkomst till Fever API och Google Reader API. Använd formuläret längst ner på sidan för att generera en ny API-nyckel"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Oläst"
msgid "Unstar"
msgstr "Ta bort stjärna"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Avsluta prenumeration"
@@ -1149,7 +1130,6 @@ msgstr "Användarnyckel"
msgid "User name"
msgstr "Användarnamn"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Användarnamn eller e-post"

View File

@@ -60,7 +60,6 @@ msgstr "Kullanıcı ekle"
msgid "Admin"
msgstr "Yönetici"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Yönetici kullanıcı adı"
@@ -73,7 +72,6 @@ msgstr "Yönetici kullanıcı adı"
msgid "All"
msgstr "Tümü"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "Her zaman"
@@ -173,18 +171,14 @@ msgstr "Ne okumak istediğinizi belirtmek için bir filtre ifadesi oluşturun. E
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "İptal"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed tarayıcı eklentisi sürüm {browserExtensionVersion}."
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed, Fever API ile uyumludur. Fever uyumlu mobil istemcinizde aşağıdaki URL'yi kullanın. Kullanıcı adınız ve <0>API anahtarınız</0> ile giriş yapın."
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed, Google Reader API ile uyumludur. Google Reader uyumlu mobil istemcinizde aşağıdaki URL'yi kullanın. Kullanıcı adınız ve <0>API anahtarınız</0> ile giriş yapın."
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed sonraki okunmamış öğe"
@@ -240,7 +238,6 @@ msgstr "Onayla"
msgid "Confirm password"
msgstr "Şifreyi onayla"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "Şifreyi Onayla"
@@ -294,7 +291,6 @@ msgstr "gün"
msgid "Delete"
msgstr "Sil"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "Hesabı sil"
@@ -342,13 +338,10 @@ msgstr "Bağlantıyı yer işareti çubuğuna sürükleyin"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "E-posta"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "E-posta adresi"
@@ -399,7 +392,6 @@ msgstr "Eklenti ayarları"
msgid "Feed name"
msgstr "Yayın adı"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "API belgelerine gidin."
msgid "Goodies"
msgstr "İyilikler"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "Üzüm"
@@ -570,7 +570,6 @@ msgstr "Abonelikler yükleniyor..."
msgid "Loading tags..."
msgstr "Etiketler yükleniyor..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "Adını girerek bir aboneliğe gidin"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "Tüm girişleri okundu olarak işaretlerken okunmamış girişleri olan bir sonraki kategoriye/beslemeye git"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "Asla"
@@ -657,7 +655,6 @@ msgstr "Asla"
msgid "New password"
msgstr "Yeni şifre"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "Yeni Şifre"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML dışa aktarma"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML dosyası"
@@ -791,10 +787,7 @@ msgstr "Üst Kategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "Şifre"
@@ -874,7 +867,6 @@ msgstr "Yenile"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "Bu CommaFeed örneğinde kayıtlar kapalı"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "Şifreyi Sıfırla"
@@ -883,7 +875,6 @@ msgstr "Şifreyi Sıfırla"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "Sağ tık"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "Kaydırma"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "Ara"
@@ -924,7 +913,6 @@ msgstr "Sonraki okunmamış beslemeyi/kategoriyi seç"
msgid "Select previous unread feed/category"
msgstr "Önceki okunmamış beslemeyi/kategoriyi seç"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "Sunucu URL'si"
@@ -953,8 +941,6 @@ msgstr "Paylaş"
msgid "Sharing sites"
msgstr "Siteleri paylaşma"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Vardiya"
@@ -1003,7 +989,6 @@ msgstr "Sekme simgesinde okunmamış sayısını göster"
msgid "Show unread count in tab title"
msgstr "Sekme başlığında okunmamış sayısını göster"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "Kaydolun"
msgid "Something bad just happened..."
msgstr "Az önce kötü bir şey oldu..."
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "Uzay"
@@ -1030,7 +1014,6 @@ msgstr "Yıldız"
msgid "Starred"
msgstr "Yıldızlı"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "Aydınlık temaya geç"
msgid "System"
msgstr "Sistem"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "Etiketler"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "Bu beslemenin düzenlenemeyen ve uygulanmayan eski bir filtresi var. Lütfen yeni ifade düzenleyiciyi kullanarak filtreyi yeniden oluşturun. Eski filtre ifadesi şuydu: <0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "Bu sizin API anahtarınızdır. Bazı salt okunur API işlemleri için kullanılabilir ve Fever API'sine erişim sağlar. Yeni bir API anahtarı oluşturmak için sayfanın altındaki formu kullanın"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "Bu, API anahtarınızdır. Bazı salt okunur API işlemleri için kullanılabilir ve Fever API ile Google Reader API'ye erişim sağlar. Yeni bir API anahtarı oluşturmak için sayfanın altındaki formu kullanın"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "Okunmamış"
msgid "Unstar"
msgstr "Yıldızı kaldır"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "Aboneliği iptal et"
@@ -1149,7 +1130,6 @@ msgstr "Kullanıcı anahtarı"
msgid "User name"
msgstr "Kullanıcı adı"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "Kullanıcı Adı veya E-posta"

View File

@@ -60,7 +60,6 @@ msgstr "添加用户"
msgid "Admin"
msgstr "管理员"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "管理员用户名"
@@ -73,7 +72,6 @@ msgstr "管理员用户名"
msgid "All"
msgstr "全部"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Always"
msgstr "总是"
@@ -173,18 +171,14 @@ msgstr "构建过滤表达式以指示您想阅读的内容。不匹配的条目
#: src/components/MarkAllAsReadConfirmationDialog.tsx
#: src/components/settings/CustomCodeSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/CategoryDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/TagDetailsPage.tsx
msgid "Cancel"
msgstr "取消"
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/AddCategory.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AboutPage.tsx
@@ -216,6 +210,10 @@ msgstr "CommaFeed的浏览器扩展版本:{browserExtensionVersion}。"
msgid "CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed 和 Fever API 兼容,可以在您Fever兼容的移动客户端中使用以下网址,通过用户名和<0>API密钥</0>登录。"
#: src/components/settings/ProfileSettings.tsx
msgid "CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible mobile client. Login with your username and your <0>API key</0>."
msgstr "CommaFeed 和 Google Reader API 兼容,可以在您Google Reader兼容的移动客户端中使用以下网址,通过用户名和<0>API密钥</0>登录。"
#: src/pages/app/AboutPage.tsx
msgid "CommaFeed next unread item"
msgstr "CommaFeed 下一个未读项目"
@@ -240,7 +238,6 @@ msgstr "确认"
msgid "Confirm password"
msgstr "确认密码"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Confirm Password"
msgstr "确认密码"
@@ -294,7 +291,6 @@ msgstr "天"
msgid "Delete"
msgstr "删除"
#: src/components/settings/ProfileSettings.tsx
#: src/components/settings/ProfileSettings.tsx
msgid "Delete account"
msgstr "删除帐号"
@@ -342,13 +338,10 @@ msgstr "拖动链接到书签栏"
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
msgstr "电子邮件"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "E-mail address"
msgstr "电子邮件地址"
@@ -399,7 +392,6 @@ msgstr "扩展选项"
msgid "Feed name"
msgstr "信息流名称"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Feed URL"
@@ -466,6 +458,14 @@ msgstr "API 文档"
msgid "Goodies"
msgstr "好东西"
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API"
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "Google Reader API URL"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Grape"
msgstr "深紫"
@@ -570,7 +570,6 @@ msgstr "正在加载订阅..."
msgid "Loading tags..."
msgstr "正在加载标签..."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Log in"
@@ -648,7 +647,6 @@ msgstr "通过输入订阅名称导航到订阅"
msgid "Navigate to the next category/feed with unread entries when marking all entries as read"
msgstr "将所有条目标记为已读时导航到下一个包含未读条目的类别/信息流"
#: src/components/settings/DisplaySettings.tsx
#: src/components/settings/DisplaySettings.tsx
msgid "Never"
msgstr "从不"
@@ -657,7 +655,6 @@ msgstr "从不"
msgid "New password"
msgstr "新密码"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "New Password"
msgstr "新密码"
@@ -764,7 +761,6 @@ msgstr "OPML"
msgid "OPML export"
msgstr "OPML 导出"
#: src/components/content/add/ImportOpml.tsx
#: src/components/content/add/ImportOpml.tsx
msgid "OPML file"
msgstr "OPML 文件"
@@ -791,10 +787,7 @@ msgstr "父类别"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
msgid "Password"
msgstr "密码"
@@ -874,7 +867,6 @@ msgstr "刷新"
msgid "Registrations are closed on this CommaFeed instance"
msgstr "此 CommaFeed 实例上的注册已关闭"
#: src/pages/auth/PasswordResetPage.tsx
#: src/pages/auth/PasswordResetPage.tsx
msgid "Reset Password"
msgstr "重置密码"
@@ -883,7 +875,6 @@ msgstr "重置密码"
msgid "REST API"
msgstr "REST API"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Right click"
msgstr "右键单击"
@@ -910,8 +901,6 @@ msgid "Scrolling"
msgstr "滚动"
#: src/components/header/Header.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/TreeSearch.tsx
#: src/components/sidebar/TreeSearch.tsx
msgid "Search"
msgstr "搜索"
@@ -924,7 +913,6 @@ msgstr "选择下一个未读信息流/类别"
msgid "Select previous unread feed/category"
msgstr "选择上一个未读信息流/类别"
#: src/components/settings/PushNotificationSettings.tsx
#: src/components/settings/PushNotificationSettings.tsx
msgid "Server URL"
msgstr "服务器网址"
@@ -953,8 +941,6 @@ msgstr "分享"
msgid "Sharing sites"
msgstr "共享站点"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Shift"
msgstr "Shift"
@@ -1003,7 +989,6 @@ msgstr "在标签页图标上显示未读数量"
msgid "Show unread count in tab title"
msgstr "在标签页标题中显示未读数量"
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/pages/WelcomePage.tsx
msgid "Sign up"
@@ -1013,7 +998,6 @@ msgstr "注册"
msgid "Something bad just happened..."
msgstr "刚刚发生了不好的事情……"
#: src/components/KeyboardShortcutsHelp.tsx
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Space"
msgstr "空格"
@@ -1030,7 +1014,6 @@ msgstr "星标"
msgid "Starred"
msgstr "已加星标"
#: src/components/content/add/Subscribe.tsx
#: src/components/content/add/Subscribe.tsx
#: src/pages/app/AddPage.tsx
msgid "Subscribe"
@@ -1064,7 +1047,6 @@ msgstr "切换到浅色主题"
msgid "System"
msgstr "跟随系统"
#: src/components/content/FeedEntryFooter.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Tags"
msgstr "标签"
@@ -1095,8 +1077,8 @@ msgid "This feed has a legacy filter that cannot be edited and is not applied. P
msgstr "此信息流具有无法编辑且未应用的旧版过滤器。请使用新的表达式编辑器重新创建过滤器。旧版过滤器表达式为:<0>{0}</0>"
#: src/components/settings/ProfileSettings.tsx
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API. Use the form at the bottom of the page to generate a new API key"
msgstr "这是您的API 密钥,它可以被用于Fever API的只读操作及访问授权。使用页面底部的表单生成一个新的API密钥。"
msgid "This is your API key. It can be used for some read-only API operations and grants access to the Fever API and the Google Reader API. Use the form at the bottom of the page to generate a new API key"
msgstr "这是您的 API 密钥。它可用于某些只读 API 操作,并授予对 Fever API 和 Google Reader API 的访问权限。使用页面底部的表单生成新的 API 密钥"
#: src/components/settings/DisplaySettings.tsx
msgid "This setting can cause scrolling issues on some browsers (e.g. Safari)"
@@ -1136,7 +1118,6 @@ msgstr "未读"
msgid "Unstar"
msgstr "取消星标"
#: src/pages/app/FeedDetailsPage.tsx
#: src/pages/app/FeedDetailsPage.tsx
msgid "Unsubscribe"
msgstr "取消订阅"
@@ -1149,7 +1130,6 @@ msgstr "用户密钥"
msgid "User name"
msgstr "用户名"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
msgid "User Name or E-mail"
msgstr "用户名或电子邮件"

View File

@@ -15,9 +15,10 @@ dayjs.extend(relativeTime)
dayjs.extend(duration)
const root = document.getElementById("root")
root &&
ReactDOM.createRoot(root).render(
<Provider store={store}>
<App />
</Provider>
)
if (!root) throw new Error("root element not found")
ReactDOM.createRoot(root).render(
<Provider store={store}>
<App />
</Provider>
)

View File

@@ -8,9 +8,11 @@ import { Constants } from "@/app/constants"
import type { EntrySourceType } from "@/app/entries/slice"
import { loadEntries } from "@/app/entries/thunks"
import { redirectToCategoryDetails, redirectToFeedDetails, redirectToTagDetails } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { flattenCategoryTree } from "@/app/utils"
import { useAppDispatch, useAppSelector, useShallowEqualAppSelector } from "@/app/store"
import { categoryHasNewEntries, categoryUnreadCount, flattenCategoryTree } from "@/app/utils"
import { FeedEntries } from "@/components/content/FeedEntries"
import { UnreadCount } from "@/components/sidebar/UnreadCount"
import { useMobile } from "@/hooks/useMobile"
import { tss } from "@/tss"
function NoSubscriptionHelp() {
@@ -33,6 +35,12 @@ const useStyles = tss.create(() => ({
sourceWebsiteLink: {
color: "inherit",
textDecoration: "none",
overflow: "hidden",
},
titleText: {
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
}))
@@ -48,6 +56,33 @@ export function FeedEntriesPage(props: Readonly<FeedEntriesPageProps>) {
const sourceLabel = useAppSelector(state => state.entries.sourceLabel)
const sourceWebsiteUrl = useAppSelector(state => state.entries.sourceWebsiteUrl)
const hasMore = useAppSelector(state => state.entries.hasMore)
const mobile = useMobile()
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)
const { unreadCount, hasNewEntries } = useShallowEqualAppSelector(state => {
const root = state.tree.rootCategory
if (!root) return { unreadCount: 0, hasNewEntries: false }
if (props.sourceType === "category") {
const category = id === Constants.categories.all.id ? root : flattenCategoryTree(root).find(c => c.id === id)
return {
unreadCount: categoryUnreadCount(category),
hasNewEntries: categoryHasNewEntries(category),
}
}
if (props.sourceType === "feed") {
const feed = flattenCategoryTree(root)
.flatMap(c => c.feeds)
.find(f => f.id === +id)
return {
unreadCount: feed?.unread ?? 0,
hasNewEntries: !!feed?.hasNewEntries,
}
}
return { unreadCount: 0, hasNewEntries: false }
})
const showUnreadCount = mobile || !sidebarVisible
const dispatch = useAppDispatch()
let title: React.ReactNode = sourceLabel
@@ -89,16 +124,23 @@ export function FeedEntriesPage(props: Readonly<FeedEntriesPageProps>) {
return (
// add some room at the bottom of the page in order to be able to scroll the current entry at the top of the page when expanding
<Box mb={viewport.height * 0.7}>
<Group gap="xl" className="cf-entries-title">
<Group className="cf-entries-title" wrap="nowrap">
{sourceWebsiteUrl && (
<a href={sourceWebsiteUrl} target="_blank" rel="noreferrer" className={classes.sourceWebsiteLink}>
<Title order={3}>{title}</Title>
<Title order={3} className={classes.titleText}>
{title}
</Title>
</a>
)}
{!sourceWebsiteUrl && <Title order={3}>{title}</Title>}
{!sourceWebsiteUrl && (
<Title order={3} className={classes.titleText}>
{title}
</Title>
)}
<ActionIcon onClick={titleClicked} variant="subtle" color={theme.primaryColor}>
<TbEdit size={18} />
</ActionIcon>
{showUnreadCount && <UnreadCount unreadCount={unreadCount} showIndicator={hasNewEntries} />}
</Group>
<FeedEntries />

View File

@@ -1,10 +1,11 @@
import { msg } from "@lingui/core/macro"
import { ActionIcon, AppShell, Box, Center, Group, ScrollArea, Title, useMantineTheme } from "@mantine/core"
import { type ReactNode, type RefObject, Suspense, useEffect, useRef } from "react"
import { type ReactNode, type RefObject, Suspense, useEffect, useRef, useState } from "react"
import Draggable from "react-draggable"
import { TbMenu2, TbPlus, TbX } from "react-icons/tb"
import { Outlet } from "react-router-dom"
import { useSwipeable } from "react-swipeable"
import Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectToAdd, redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
@@ -12,8 +13,10 @@ import { setMobileMenuOpen } from "@/app/tree/slice"
import { reloadTree } from "@/app/tree/thunks"
import { setSidebarWidth } from "@/app/user/slice"
import { reloadProfile, reloadSettings, reloadTags } from "@/app/user/thunks"
import { categoryUnreadCount } from "@/app/utils"
import { ActionButton } from "@/components/ActionButton"
import { AnnouncementDialog } from "@/components/AnnouncementDialog"
import { DisablePullToRefresh } from "@/components/DisablePullToRefresh"
import { Loader } from "@/components/Loader"
import { Logo } from "@/components/Logo"
import { MarkAllAsReadConfirmationDialog } from "@/components/MarkAllAsReadConfirmationDialog"
@@ -51,6 +54,78 @@ function LogoAndTitle() {
)
}
function UnreadCountTitleHandler({
enabled,
}: Readonly<{
enabled?: boolean
}>) {
const root = useAppSelector(state => state.tree.rootCategory)
const unreadCount = categoryUnreadCount(root)
return <title>{enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}</title>
}
function UnreadCountFaviconHandler({ enabled }: { enabled?: boolean }) {
const root = useAppSelector(state => state.tree.rootCategory)
const unreadCount = categoryUnreadCount(root)
useEffect(() => {
if (enabled && unreadCount > 0) {
Tinycon.setBubble(unreadCount)
} else {
Tinycon.reset()
}
}, [unreadCount, enabled])
return null
}
function BrowserExtensionBadgeUnreadCountHandler() {
const root = useAppSelector(state => state.tree.rootCategory)
const { setBadgeUnreadCount } = useBrowserExtension()
useEffect(() => {
if (!root) return
const unreadCount = categoryUnreadCount(root)
setBadgeUnreadCount(unreadCount)
}, [root, setBadgeUnreadCount])
return null
}
function CustomJsHandler() {
const [scriptLoaded, setScriptLoaded] = useState(false)
const { loading } = useAppLoading()
useEffect(() => {
if (scriptLoaded || loading) {
return
}
const script = document.createElement("script")
script.src = "custom_js.js"
script.async = true
document.body.appendChild(script)
setScriptLoaded(true)
return () => script.remove()
}, [scriptLoaded, loading])
return null
}
function CustomCssHandler() {
useEffect(() => {
const link = document.createElement("link")
link.rel = "stylesheet"
link.type = "text/css"
link.href = "custom_css.css"
document.head.appendChild(link)
return () => link.remove()
}, [])
return null
}
const useStyles = tss
.withParams<{
sidebarWidth: number
@@ -75,6 +150,9 @@ export default function Layout(props: Readonly<LayoutProps>) {
const draggableSeparator = useRef<HTMLDivElement>(null)
const { loading } = useAppLoading()
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
const disablePullToRefresh = useAppSelector(state => state.user.settings?.disablePullToRefresh)
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
@@ -173,61 +251,69 @@ export default function Layout(props: Readonly<LayoutProps>) {
if (loading) return <LoadingPage />
return (
<Box {...swipeHandlers}>
<AppShell
header={{ height: Constants.layout.headerHeight, collapsed: headerInFooter }}
footer={{ height: Constants.layout.headerHeight, collapsed: !headerInFooter }}
navbar={{
width: sidebarWidth,
breakpoint: Constants.layout.mobileBreakpoint,
collapsed: { mobile: !mobileMenuOpen, desktop: !props.sidebarVisible },
}}
padding={{ base: 6, [Constants.layout.mobileBreakpointName]: "md" }}
>
<AppShell.Header>{!headerInFooter && header}</AppShell.Header>
<AppShell.Footer>{headerInFooter && header}</AppShell.Footer>
<AppShell.Navbar p={sidebarPadding}>
<AppShell.Section grow component={ScrollArea} mx="-sm" px="sm">
<Box className={classes.sidebarContent}>{props.sidebar}</Box>
</AppShell.Section>
</AppShell.Navbar>
<OnDesktop>
<Draggable
nodeRef={draggableSeparator as RefObject<HTMLElement>}
axis="x"
defaultPosition={{
x: sidebarWidth,
y: 0,
}}
bounds={{
left: 120,
right: 1000,
}}
grid={[30, 30]}
onDrag={(_e, data) => {
dispatch(setSidebarWidth(data.x))
}}
>
<Box
ref={draggableSeparator}
style={{
position: "fixed",
height: "100%",
width: "10px",
cursor: "ew-resize",
<>
<UnreadCountTitleHandler enabled={unreadCountTitle} />
<UnreadCountFaviconHandler enabled={unreadCountFavicon} />
<BrowserExtensionBadgeUnreadCountHandler />
<CustomJsHandler />
<CustomCssHandler />
<DisablePullToRefresh enabled={disablePullToRefresh} />
<Box {...swipeHandlers}>
<AppShell
header={{ height: Constants.layout.headerHeight, collapsed: headerInFooter }}
footer={{ height: Constants.layout.headerHeight, collapsed: !headerInFooter }}
navbar={{
width: sidebarWidth,
breakpoint: Constants.layout.mobileBreakpoint,
collapsed: { mobile: !mobileMenuOpen, desktop: !props.sidebarVisible },
}}
padding={{ base: 6, [Constants.layout.mobileBreakpointName]: "md" }}
>
<AppShell.Header>{!headerInFooter && header}</AppShell.Header>
<AppShell.Footer>{headerInFooter && header}</AppShell.Footer>
<AppShell.Navbar p={sidebarPadding}>
<AppShell.Section grow component={ScrollArea} mx="-sm" px="sm">
<Box className={classes.sidebarContent}>{props.sidebar}</Box>
</AppShell.Section>
</AppShell.Navbar>
<OnDesktop>
<Draggable
nodeRef={draggableSeparator as RefObject<HTMLElement>}
axis="x"
defaultPosition={{
x: sidebarWidth,
y: 0,
}}
/>
</Draggable>
</OnDesktop>
bounds={{
left: 120,
right: 1000,
}}
grid={[30, 30]}
onDrag={(_e, data) => {
dispatch(setSidebarWidth(data.x))
}}
>
<Box
ref={draggableSeparator}
style={{
position: "fixed",
height: "100%",
width: "10px",
cursor: "ew-resize",
}}
/>
</Draggable>
</OnDesktop>
<AppShell.Main>
<Suspense fallback={<Loader />}>
<AnnouncementDialog />
<MarkAllAsReadConfirmationDialog />
<Outlet />
</Suspense>
</AppShell.Main>
</AppShell>
</Box>
<AppShell.Main>
<Suspense fallback={<Loader />}>
<AnnouncementDialog />
<MarkAllAsReadConfirmationDialog />
<Outlet />
</Suspense>
</AppShell.Main>
</AppShell>
</Box>
</>
)
}

View File

@@ -8,12 +8,11 @@
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,

View File

@@ -1,27 +1,20 @@
import { lingui } from "@lingui/vite-plugin"
import react from "@vitejs/plugin-react"
import babel from "@rolldown/plugin-babel"
import react, { reactCompilerPreset } from "@vitejs/plugin-react"
import { defineConfig } from "vite"
import checker from "vite-plugin-checker"
import tsconfigPaths from "vite-tsconfig-paths"
// https://vitejs.dev/config/
export default defineConfig(() => ({
plugins: [
react({
babel: {
plugins: [
// support for lingui macros
// needs to be before the react compiler plugin
"@lingui/babel-plugin-lingui-macro",
// react compiler
["babel-plugin-react-compiler", { target: "19" }],
],
},
react(),
babel({
presets: [reactCompilerPreset()],
plugins: ["@lingui/babel-plugin-lingui-macro"],
}),
lingui(),
tsconfigPaths(),
checker({
typescript: true,
// temporary disabled until TypeScript 7 exposes a stable api
// typescript: true,
biome: {
command: "check",
flags: "--error-on-warnings",
@@ -43,22 +36,32 @@ export default defineConfig(() => ({
"/logout": "http://localhost:8083",
},
},
build: {
chunkSizeWarningLimit: 3500,
rollupOptions: {
output: {
manualChunks: id => {
// output mantine as its own chunk because it is quite large
if (id.includes("@mantine")) {
return "mantine"
}
},
},
},
resolve: {
tsconfigPaths: true,
},
legacy: {
// required for websocket-heartbeat-js
inconsistentCjsInterop: true,
},
test: {
environment: "jsdom",
globals: true,
setupFiles: "./src/setupTests.ts",
},
build: {
chunkSizeWarningLimit: 4000,
rolldownOptions: {
output: {
codeSplitting: {
groups: [
// output mantine as its own chunk because it is quite large
{
name: "mantine",
test: "@mantine",
},
],
},
},
},
},
}))

View File

@@ -1,401 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="23">
<profile kind="CodeFormatterProfile" name="CommaFeed" version="23">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="140"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="140"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8" />
<property name="fileExtensions" value="java" />
@@ -40,11 +40,6 @@
<module name="AvoidStarImport" />
<module name="AvoidStaticImport" />
<module name="IllegalImport" />
<module name="ImportOrder">
<property name="groups" value="/^java\./,javax,jakarta,org,com" />
<property name="ordered" value="true" />
<property name="separated" value="true" />
</module>
<module name="RedundantImport" />
<module name="UnusedImports" />
@@ -60,7 +55,7 @@
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="ignore|ignored" />
<message key="catch.block.empty"
value="Empty catch block. You can use the name 'ignore' or 'ignored' for the exception variable if you really want an empty catch block, but you should strongly consider at the very least logging something." />
value="Empty catch block. You can use the name 'ignore' or 'ignored' for the exception variable if you really want an empty catch block, but you should strongly consider at the very least logging something." />
</module>
<module name="LeftCurly" />
<module name="NeedBraces" />
@@ -76,7 +71,7 @@
<module name="FallThrough" />
<module name="IllegalInstantiation">
<property name="classes"
value="java.lang.Boolean, java.lang.Byte, java.lang.Character, java.lang.Double, java.lang.Float, java.lang.Integer, java.lang.Long, java.lang.Short" />
value="java.lang.Boolean, java.lang.Byte, java.lang.Character, java.lang.Double, java.lang.Float, java.lang.Integer, java.lang.Long, java.lang.Short" />
</module>
<module name="IllegalType" />
<module name="ModifiedControlVariable">
@@ -113,12 +108,6 @@
<module name="NoLineWrap" />
<module name="NoWhitespaceBefore" />
<module name="ParenPad" />
<module name="RegexpSinglelineJava">
<property name="format" value="^\t* +\t*\S" />
<property name="message" value="Line has leading space characters; indentation should be performed with tabs only." />
<property name="ignoreComments" value="true" />
</module>
<module name="WhitespaceAround" />
</module>
</module>

View File

@@ -1,7 +0,0 @@
#Organize Import Order
#Wed Jan 29 15:15:04 CET 2025
0=java
1=javax
2=jakarta
3=org
4=com

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
FROM ibm-semeru-runtimes:open-jdk-25.0.2_10-jre@sha256:b02e4cd184d9ece59b01129af1d0a069fa01e4f0f798f0bc4f3ff1a8391ba694
FROM ibm-semeru-runtimes:open-jdk-25.0.4.0-jre@sha256:02888a2306c2cb8af22a05b2c41be90fdc9df5f4611861ddf6ea90c874de65e6
EXPOSE 8082
RUN mkdir -p /commafeed/data

View File

@@ -1,4 +1,4 @@
FROM debian:13.3@sha256:2c91e484d93f0830a7e05a2b9d92a7b102be7cab562198b984a84fdbc7806d91
FROM debian:13.6@sha256:34cd9e9fd437c0a095ec39cb2e73422c9f30821b0d0848ed74fd0d43bae4d958
ARG TARGETARCH
EXPOSE 8082

View File

@@ -93,3 +93,12 @@ Tags are of the form `<version>-<database>[-jvm]` where:
- `master` (always points to the latest git commit)
- `<database>` is the database to use (`h2`, `postgresql`, `mysql` or `mariadb`)
- `-jvm` is optional and indicates that CommaFeed is running on a JVM, and not compiled natively.
## FAQ
### Getting "Access to local address blocked" when adding a feed
CommaFeed blocks access to local resources by default to prevent [SSRF](https://en.wikipedia.org/wiki/Server-side_request_forgery) attacks.
If you want to subscribe to feeds that are only available on your local network, you can disable this security measure by setting the `COMMAFEED_HTTP_CLIENT_BLOCK_LOCAL_ADDRESSES` variable to `false`.
Do this only if you trust all users of your CommaFeed instance not to access private resources.

View File

@@ -1,34 +1,54 @@
package com.commafeed;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Singleton;
import com.commafeed.backend.feed.FeedRefreshEngine;
import com.commafeed.backend.feed.ImageProxyUrl;
import com.commafeed.backend.task.TaskScheduler;
import com.commafeed.security.password.PasswordConstraintValidator;
import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import lombok.RequiredArgsConstructor;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Singleton;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SystemProperties;
@Slf4j
@Singleton
@RequiredArgsConstructor
public class CommaFeedApplication {
private final FeedRefreshEngine feedRefreshEngine;
private final TaskScheduler taskScheduler;
private final CommaFeedConfiguration config;
private final FeedRefreshEngine feedRefreshEngine;
private final TaskScheduler taskScheduler;
private final CommaFeedConfiguration config;
public void start(@Observes StartupEvent ev) {
PasswordConstraintValidator.setMinimumPasswordLength(config.users().minimumPasswordLength());
public void start(@Observes StartupEvent ev) {
log.info("starting up...");
feedRefreshEngine.start();
taskScheduler.start();
}
// disable entity expansion limits added in JDK24+ (#1961)
// we already strip doctype declarations in XMLCleaner to prevent xxe attacks
// we also already limit the size of feeds we download in HttpGetter
System.setProperty(SystemProperties.JDK_XML_MAX_GENERAL_ENTITY_SIZE_LIMIT, "0");
System.setProperty(SystemProperties.JDK_XML_TOTAL_ENTITY_SIZE_LIMIT, "0");
public void stop(@Observes ShutdownEvent ev) {
feedRefreshEngine.stop();
taskScheduler.stop();
}
PasswordConstraintValidator.setMinimumPasswordLength(
config.users().minimumPasswordLength());
if (config.imageProxyEnabled()) {
ImageProxyUrl.generateKey();
}
feedRefreshEngine.start();
taskScheduler.start();
}
public void stop(@Observes ShutdownEvent ev) {
log.info("shutting down...");
feedRefreshEngine.stop();
taskScheduler.stop();
}
}

View File

@@ -1,12 +1,5 @@
package com.commafeed;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Positive;
import com.commafeed.backend.feed.FeedRefreshIntervalCalculator;
import io.quarkus.runtime.annotations.ConfigDocSection;
@@ -16,385 +9,355 @@ import io.quarkus.runtime.configuration.MemorySize;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Positive;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
/**
* CommaFeed configuration
*
* Default values are for production, they can be overridden in application.properties for other profiles
* <p>Default values are for production, they can be overridden in application.properties for other
* profiles
*/
@ConfigMapping(prefix = "commafeed")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface CommaFeedConfiguration {
/**
* Whether to expose a robots.txt file that disallows web crawlers and search engine indexers.
*/
@WithDefault("true")
boolean hideFromWebCrawlers();
/**
* Whether to expose a robots.txt file that disallows web crawlers and search engine indexers.
*/
@WithDefault("true")
boolean hideFromWebCrawlers();
/**
* If enabled, images in feed entries will be proxied through the server instead of accessed directly by the browser.
*
* This is useful if commafeed is accessed through a restricting proxy that blocks some feeds that are followed.
*/
@WithDefault("false")
boolean imageProxyEnabled();
/**
* If enabled, images in feed entries will be proxied through the server instead of accessed
* directly by the browser.
*
* <p>This is useful if commafeed is accessed through a restricting proxy that blocks some feeds
* that are followed.
*/
@WithDefault("false")
boolean imageProxyEnabled();
/**
* Enable password recovery via email.
*
* Quarkus mailer will need to be configured.
*/
@WithDefault("false")
boolean passwordRecoveryEnabled();
/**
* Enable password recovery via email.
*
* <p>Quarkus mailer will need to be configured.
*/
@WithDefault("false")
boolean passwordRecoveryEnabled();
/**
* Message displayed in a notification at the bottom of the page.
*/
Optional<String> announcement();
/** Message displayed in a notification at the bottom of the page. */
Optional<String> announcement();
/**
* Google Auth key for fetching Youtube channel favicons.
*/
Optional<String> googleAuthKey();
/** Google Auth key for fetching Youtube channel favicons. */
Optional<String> googleAuthKey();
/**
* HTTP client configuration
*/
@ConfigDocSection
HttpClient httpClient();
/** HTTP client configuration */
@ConfigDocSection
HttpClient httpClient();
/**
* Feed refresh engine settings.
*/
@ConfigDocSection
FeedRefresh feedRefresh();
/** Feed refresh engine settings. */
@ConfigDocSection
FeedRefresh feedRefresh();
/**
* Push notification settings.
*/
@ConfigDocSection
PushNotifications pushNotifications();
/** Push notification settings. */
@ConfigDocSection
PushNotifications pushNotifications();
/**
* Database settings.
*/
@ConfigDocSection
Database database();
/** Database settings. */
@ConfigDocSection
Database database();
/**
* Users settings.
*/
@ConfigDocSection
Users users();
/** Users settings. */
@ConfigDocSection
Users users();
/**
* Websocket settings.
*/
@ConfigDocSection
Websocket websocket();
/** Websocket settings. */
@ConfigDocSection
Websocket websocket();
interface HttpClient {
/**
* User-Agent string that will be used by the http client, leave empty for the default one.
*/
Optional<String> userAgent();
/**
* Duration to wait for the feed refresh engine and the task scheduler to stop when the
* application is shutting down.
*/
@WithDefault("2s")
Duration shutdownTimeout();
/**
* Time to wait for a connection to be established.
*/
@WithDefault("5s")
Duration connectTimeout();
interface HttpClient {
/**
* User-Agent string that will be used by the http client, leave empty for the default one.
*/
Optional<String> userAgent();
/**
* Time to wait for SSL handshake to complete.
*/
@WithDefault("5s")
Duration sslHandshakeTimeout();
/** Time to wait for a connection to be established. */
@WithDefault("5s")
Duration connectTimeout();
/**
* Time to wait between two packets before timeout.
*/
@WithDefault("10s")
Duration socketTimeout();
/** Time to wait for SSL handshake to complete. */
@WithDefault("5s")
Duration sslHandshakeTimeout();
/**
* Time to wait for the full response to be received.
*/
@WithDefault("10s")
Duration responseTimeout();
/** Time to wait between two packets before timeout. */
@WithDefault("10s")
Duration socketTimeout();
/**
* Time to live for a connection in the pool.
*/
@WithDefault("30s")
Duration connectionTimeToLive();
/** Time to wait for the full response to be received. */
@WithDefault("10s")
Duration responseTimeout();
/**
* Time between eviction runs for idle connections.
*/
@WithDefault("1m")
Duration idleConnectionsEvictionInterval();
/** Time to live for a connection in the pool. */
@WithDefault("30s")
Duration connectionTimeToLive();
/**
* If a feed is larger than this, it will be discarded to prevent memory issues while parsing the feed.
*/
@WithDefault("5M")
MemorySize maxResponseSize();
/** Time between eviction runs for idle connections. */
@WithDefault("1m")
Duration idleConnectionsEvictionInterval();
/**
* Prevent access to local addresses to mitigate server-side request forgery (SSRF) attacks, which could potentially expose internal
* resources.
*
* You may want to enable this if you host a public instance of CommaFeed with regisration open.
*/
@WithDefault("false")
boolean blockLocalAddresses();
/**
* If a feed is larger than this, it will be discarded to prevent memory issues while
* parsing the feed.
*/
@WithDefault("5M")
MemorySize maxResponseSize();
/**
* HTTP client cache configuration
*/
@ConfigDocSection
HttpClientCache cache();
}
/**
* Prevent access to local addresses to mitigate server-side request forgery (SSRF) attacks,
* which could potentially expose internal resources.
*
* <p>You may want to enable this if you host a public instance of CommaFeed with
* registrations open.
*/
@WithDefault("true")
boolean blockLocalAddresses();
interface HttpClientCache {
/**
* Whether to enable the cache. This cache is used to avoid spamming feeds in short bursts (e.g. when subscribing to a feed for the
* first time or when clicking "fetch all my feeds now").
*/
@WithDefault("true")
boolean enabled();
/** HTTP client cache configuration */
@ConfigDocSection
HttpClientCache cache();
}
/**
* Maximum amount of memory the cache can use.
*/
@WithDefault("10M")
MemorySize maximumMemorySize();
interface HttpClientCache {
/**
* Whether to enable the cache. This cache is used to avoid spamming feeds in short bursts
* (e.g. when subscribing to a feed for the first time or when clicking "fetch all my feeds
* now").
*/
@WithDefault("true")
boolean enabled();
/**
* Duration after which an entry is removed from the cache.
*/
@WithDefault("1m")
Duration expiration();
}
/** Maximum amount of memory the cache can use. */
@WithDefault("10M")
MemorySize maximumMemorySize();
interface FeedRefresh {
/**
* Default amount of time CommaFeed will wait before refreshing a feed.
*/
@WithDefault("5m")
Duration interval();
/** Duration after which an entry is removed from the cache. */
@WithDefault("1m")
Duration expiration();
}
/**
* Maximum amount of time CommaFeed will wait before refreshing a feed. This is used as an upper bound when:
*
* <ul>
* <li>an error occurs while refreshing a feed and we're backing off exponentially</li>
* <li>we receive a Cache-Control header from the feed</li>
* <li>we receive a Retry-After header from the feed</li>
* </ul>
*/
@WithDefault("4h")
Duration maxInterval();
interface FeedRefresh {
/** Default amount of time CommaFeed will wait before refreshing a feed. */
@WithDefault("5m")
Duration interval();
/**
* If enabled, CommaFeed will calculate the next refresh time based on the feed's average time between entries and the time since
* the last entry was published. The interval will be sometimes between the default refresh interval
* (`commafeed.feed-refresh.interval`) and the maximum refresh interval (`commafeed.feed-refresh.max-interval`).
*
* See {@link FeedRefreshIntervalCalculator} for details.
*/
@WithDefault("true")
boolean intervalEmpirical();
/**
* Maximum amount of time CommaFeed will wait before refreshing a feed. This is used as an
* upper bound when:
*
* <ul>
* <li>an error occurs while refreshing a feed and we're backing off exponentially
* <li>we receive a Cache-Control header from the feed
* <li>we receive a Retry-After header from the feed
* </ul>
*/
@WithDefault("4h")
Duration maxInterval();
/**
* Feed refresh engine error handling settings.
*/
@ConfigDocSection
FeedRefreshErrorHandling errors();
/**
* If enabled, CommaFeed will calculate the next refresh time based on the feed's average
* time between entries and the time since the last entry was published. The interval will
* be sometimes between the default refresh interval (`commafeed.feed-refresh.interval`) and
* the maximum refresh interval (`commafeed.feed-refresh.max-interval`).
*
* <p>See {@link FeedRefreshIntervalCalculator} for details.
*/
@WithDefault("true")
boolean intervalEmpirical();
/**
* Amount of http threads used to fetch feeds.
*/
@Min(1)
@WithDefault("3")
int httpThreads();
/** Feed refresh engine error handling settings. */
@ConfigDocSection
FeedRefreshErrorHandling errors();
/**
* Amount of threads used to insert new entries in the database.
*/
@Min(1)
@WithDefault("1")
int databaseThreads();
/** Amount of http threads used to fetch feeds. */
@Min(1)
@WithDefault("3")
int httpThreads();
/**
* Duration after which a user is considered inactive. Feeds for inactive users are not refreshed until they log in again.
*
* 0 to disable.
*/
@WithDefault("0")
Duration userInactivityPeriod();
/** Amount of threads used to insert new entries in the database. */
@Min(1)
@WithDefault("1")
int databaseThreads();
/**
* Duration after which the evaluation of a filtering expresion to mark an entry as read is considered to have timed out.
*/
@WithDefault("500ms")
Duration filteringExpressionEvaluationTimeout();
/**
* Duration after which a user is considered inactive. Feeds for inactive users are not
* refreshed until they log in again.
*
* <p>0 to disable.
*/
@WithDefault("0")
Duration userInactivityPeriod();
/**
* Duration after which the "Fetch all my feeds now" action is available again after use to avoid spamming feeds.
*/
@WithDefault("0")
Duration forceRefreshCooldownDuration();
}
/**
* Duration after which the evaluation of a filtering expresion to mark an entry as read is
* considered to have timed out.
*/
@WithDefault("500ms")
Duration filteringExpressionEvaluationTimeout();
interface PushNotifications {
/**
* Whether to enable push notifications to notify users of new entries in their feeds.
*/
@WithDefault("true")
boolean enabled();
/**
* Duration after which the "Fetch all my feeds now" action is available again after use to
* avoid spamming feeds.
*/
@WithDefault("0")
Duration forceRefreshCooldownDuration();
}
/**
* Amount of threads used to send external notifications about new entries.
*/
@Min(1)
@WithDefault("5")
int threads();
interface PushNotifications {
/** Whether to enable push notifications to notify users of new entries in their feeds. */
@WithDefault("true")
boolean enabled();
/**
* Maximum amount of notifications that can be queued before new notifications are discarded.
*/
@Min(1)
@WithDefault("100")
int queueCapacity();
}
/** Amount of threads used to send external notifications about new entries. */
@Min(1)
@WithDefault("5")
int threads();
interface FeedRefreshErrorHandling {
/**
* Number of retries before backoff is applied.
*/
@Min(0)
@WithDefault("3")
int retriesBeforeBackoff();
/**
* Maximum amount of notifications that can be queued before new notifications are
* discarded.
*/
@Min(1)
@WithDefault("100")
int queueCapacity();
}
/**
* Duration to wait before retrying after an error. Will be multiplied by the number of errors since the last successful fetch.
*/
@WithDefault("1h")
Duration backoffInterval();
}
interface FeedRefreshErrorHandling {
/** Number of retries before backoff is applied. */
@Min(0)
@WithDefault("3")
int retriesBeforeBackoff();
interface Database {
/**
* Timeout applied to all database queries.
*
* 0 to disable.
*/
@WithDefault("0")
Duration queryTimeout();
/**
* Duration to wait before retrying after an error. Will be multiplied by the number of
* errors since the last successful fetch.
*/
@WithDefault("1h")
Duration backoffInterval();
}
/**
* Database cleanup settings.
*/
@ConfigDocSection
Cleanup cleanup();
interface Database {
/**
* Timeout applied to all database queries.
*
* <p>0 to disable.
*/
@WithDefault("0")
Duration queryTimeout();
interface Cleanup {
/**
* Maximum age of feed entries in the database. Older entries will be deleted.
*
* 0 to disable.
*/
@WithDefault("365d")
Duration entriesMaxAge();
/** Database cleanup settings. */
@ConfigDocSection
Cleanup cleanup();
/**
* Maximum age of feed entry statuses (read/unread) in the database. Older statuses will be deleted.
*
* 0 to disable.
*/
@WithDefault("0")
Duration statusesMaxAge();
interface Cleanup {
/**
* Maximum age of feed entries in the database. Older entries will be deleted.
*
* <p>0 to disable.
*/
@WithDefault("365d")
Duration entriesMaxAge();
/**
* Maximum number of entries per feed to keep in the database.
*
* 0 to disable.
*/
@WithDefault("500")
int maxFeedCapacity();
/**
* Maximum age of feed entry statuses (read/unread) in the database. Older statuses will
* be deleted.
*
* <p>0 to disable.
*/
@WithDefault("0")
Duration statusesMaxAge();
/**
* Limit the number of feeds a user can subscribe to.
*
* 0 to disable.
*/
@WithDefault("0")
int maxFeedsPerUser();
/**
* Maximum number of entries per feed to keep in the database.
*
* <p>0 to disable.
*/
@WithDefault("500")
int maxFeedCapacity();
/**
* Rows to delete per query while cleaning up old entries.
*/
@Positive
@WithDefault("100")
int batchSize();
/**
* Limit the number of feeds a user can subscribe to.
*
* <p>0 to disable.
*/
@WithDefault("0")
int maxFeedsPerUser();
/**
* Whether to keep starred entries when cleaning up old entries.
*/
@WithDefault("true")
boolean keepStarredEntries();
/** Rows to delete per query while cleaning up old entries. */
@Positive
@WithDefault("100")
int batchSize();
default Instant statusesInstantThreshold() {
return statusesMaxAge().toMillis() > 0 ? Instant.now().minus(statusesMaxAge()) : null;
}
}
}
/** Whether to keep starred entries when cleaning up old entries. */
@WithDefault("true")
boolean keepStarredEntries();
interface Users {
/**
* Whether to let users create accounts for themselves.
*/
@WithDefault("false")
boolean allowRegistrations();
default Instant statusesInstantThreshold() {
return statusesMaxAge().toMillis() > 0
? Instant.now().minus(statusesMaxAge())
: null;
}
}
}
/**
* Minimum password length for user accounts.
*/
@WithDefault("4")
int minimumPasswordLength();
interface Users {
/** Whether to let users create accounts for themselves. */
@WithDefault("false")
boolean allowRegistrations();
/**
* Whether an email address is required when creating a user account.
*/
@WithDefault("false")
boolean emailAddressRequired();
/** Minimum password length for user accounts. */
@WithDefault("4")
int minimumPasswordLength();
/**
* Whether to create a demo account the first time the app starts.
*/
@WithDefault("false")
boolean createDemoAccount();
}
/** Whether an email address is required when creating a user account. */
@WithDefault("false")
boolean emailAddressRequired();
interface Websocket {
/**
* Enable websocket connection so the server can notify web clients that there are new entries for feeds.
*/
@WithDefault("true")
boolean enabled();
/** Whether to create a demo account the first time the app starts. */
@WithDefault("false")
boolean createDemoAccount();
}
/**
* Interval at which the client will send a ping message on the websocket to keep the connection alive.
*/
@WithDefault("15m")
Duration pingInterval();
interface Websocket {
/**
* Enable websocket connection so the server can notify web clients that there are new
* entries for feeds.
*/
@WithDefault("true")
boolean enabled();
/**
* If the websocket connection is disabled or the connection is lost, the client will reload the feed tree at this interval.
*/
@WithDefault("30s")
Duration treeReloadInterval();
}
/**
* Interval at which the client will send a ping message on the websocket to keep the
* connection alive.
*/
@WithDefault("15m")
Duration pingInterval();
/**
* If the websocket connection is disabled or the connection is lost, the client will reload
* the feed tree at this interval.
*/
@WithDefault("30s")
Duration treeReloadInterval();
}
}

View File

@@ -4,5 +4,5 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public class CommaFeedConstants {
public static final String USERNAME_DEMO = "demo";
public static final String USERNAME_DEMO = "demo";
}

View File

@@ -1,25 +1,24 @@
package com.commafeed;
import java.time.InstantSource;
import com.codahale.metrics.MetricRegistry;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Singleton;
import com.codahale.metrics.MetricRegistry;
import java.time.InstantSource;
@Singleton
public class CommaFeedProducers {
@Produces
@Singleton
public InstantSource instantSource() {
return InstantSource.system();
}
@Produces
@Singleton
public MetricRegistry metricRegistry() {
return new MetricRegistry();
}
@Produces
@Singleton
public InstantSource instantSource() {
return InstantSource.system();
}
@Produces
@Singleton
public MetricRegistry metricRegistry() {
return new MetricRegistry();
}
}

View File

@@ -1,30 +1,29 @@
package com.commafeed;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import jakarta.inject.Singleton;
import lombok.Getter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@Singleton
@Getter
public class CommaFeedVersion {
private final String version;
private final String gitCommit;
private final String version;
private final String gitCommit;
public CommaFeedVersion() throws IOException {
Properties properties = new Properties();
try (InputStream stream = getClass().getResourceAsStream("/git.properties")) {
if (stream != null) {
properties.load(stream);
}
}
this.version = properties.getProperty("git.build.version", "unknown");
this.gitCommit = properties.getProperty("git.commit.id.abbrev", "unknown");
}
public CommaFeedVersion() throws IOException {
Properties properties = new Properties();
try (InputStream stream = getClass().getResourceAsStream("/git.properties")) {
if (stream != null) {
properties.load(stream);
}
}
this.version = properties.getProperty("git.build.version", "unknown");
this.gitCommit = properties.getProperty("git.commit.id.abbrev", "unknown");
}
}

View File

@@ -1,55 +1,58 @@
package com.commafeed;
import jakarta.annotation.Priority;
import jakarta.validation.ValidationException;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.ext.Provider;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
import org.jboss.resteasy.reactive.RestResponse.Status;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import com.commafeed.security.CookieService;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.UnauthorizedException;
import jakarta.annotation.Priority;
import jakarta.validation.ValidationException;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.ext.Provider;
import lombok.RequiredArgsConstructor;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
import org.jboss.resteasy.reactive.RestResponse.Status;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
@RequiredArgsConstructor
@Provider
@Priority(1)
public class ExceptionMappers {
private final CookieService cookieService;
private final CommaFeedConfiguration config;
private final CookieService cookieService;
private final CommaFeedConfiguration config;
@ServerExceptionMapper(UnauthorizedException.class)
public RestResponse<UnauthorizedResponse> unauthorized(UnauthorizedException e) {
return RestResponse.status(Status.UNAUTHORIZED, new UnauthorizedResponse(e.getMessage(), config.users().allowRegistrations()));
}
@ServerExceptionMapper(UnauthorizedException.class)
public RestResponse<UnauthorizedResponse> unauthorized(UnauthorizedException e) {
return RestResponse.status(
Status.UNAUTHORIZED,
new UnauthorizedResponse(e.getMessage(), config.users().allowRegistrations()));
}
@ServerExceptionMapper(AuthenticationFailedException.class)
public RestResponse<AuthenticationFailed> authenticationFailed(AuthenticationFailedException e) {
NewCookie logoutCookie = cookieService.buildLogoutCookie();
return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage())).cookie(logoutCookie).build();
}
@ServerExceptionMapper(AuthenticationFailedException.class)
public RestResponse<AuthenticationFailed> authenticationFailed(
AuthenticationFailedException e) {
NewCookie logoutCookie = cookieService.buildLogoutCookie();
return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage()))
.cookie(logoutCookie)
.build();
}
@ServerExceptionMapper(ValidationException.class)
public RestResponse<ValidationFailed> validationFailed(ValidationException e) {
return RestResponse.status(Status.BAD_REQUEST, new ValidationFailed(e.getMessage()));
}
@ServerExceptionMapper(ValidationException.class)
public RestResponse<ValidationFailed> validationFailed(ValidationException e) {
return RestResponse.status(Status.BAD_REQUEST, new ValidationFailed(e.getMessage()));
}
@RegisterForReflection
public record UnauthorizedResponse(String message, boolean allowRegistrations) {
}
@RegisterForReflection
public record UnauthorizedResponse(String message, boolean allowRegistrations) {}
@RegisterForReflection
public record AuthenticationFailed(String message) {
}
@RegisterForReflection
public record AuthenticationFailed(String message) {}
@RegisterForReflection
public record ValidationFailed(String message) {
}
@RegisterForReflection
public record ValidationFailed(String message) {}
}

View File

@@ -1,9 +1,5 @@
package com.commafeed;
import java.util.concurrent.TimeUnit;
import jakarta.inject.Singleton;
import com.codahale.metrics.json.MetricsModule;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -12,18 +8,23 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.quarkus.jackson.ObjectMapperCustomizer;
import jakarta.inject.Singleton;
import java.util.concurrent.TimeUnit;
@Singleton
public class JacksonCustomizer implements ObjectMapperCustomizer {
@Override
public void customize(ObjectMapper objectMapper) {
objectMapper.registerModule(new JavaTimeModule());
@Override
public void customize(ObjectMapper objectMapper) {
objectMapper.registerModule(new JavaTimeModule());
// read and write instants as milliseconds instead of nanoseconds
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
// read and write instants as milliseconds instead of nanoseconds
objectMapper
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
// add support for serializing metrics
objectMapper.registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.SECONDS, false));
}
// add support for serializing metrics
objectMapper.registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.SECONDS, false));
}
}

View File

@@ -10,217 +10,340 @@ import com.codahale.metrics.Timer;
import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection(
targets = {
// metrics
MetricRegistry.class, Meter.class, Gauge.class, Counter.class, Timer.class, Histogram.class,
targets = {
// metrics
MetricRegistry.class,
Meter.class,
Gauge.class,
Counter.class,
Timer.class,
Histogram.class,
// rome
java.util.Date.class, com.rometools.opml.feed.synd.impl.TreeCategoryImpl.class,
com.rometools.rome.feed.synd.SyndFeedImpl.class, com.rometools.rome.feed.module.DCSubjectImpl.class,
com.rometools.rome.feed.synd.SyndEntryImpl.class, com.rometools.modules.psc.types.SimpleChapter.class,
com.rometools.rome.feed.synd.SyndCategoryImpl.class, com.rometools.rome.feed.synd.SyndImageImpl.class,
com.rometools.rome.feed.synd.SyndContentImpl.class, com.rometools.rome.feed.synd.SyndEnclosureImpl.class,
// rome
java.util.Date.class,
com.rometools.opml.feed.synd.impl.TreeCategoryImpl.class,
com.rometools.rome.feed.synd.SyndFeedImpl.class,
com.rometools.rome.feed.module.DCSubjectImpl.class,
com.rometools.rome.feed.synd.SyndEntryImpl.class,
com.rometools.modules.psc.types.SimpleChapter.class,
com.rometools.rome.feed.synd.SyndCategoryImpl.class,
com.rometools.rome.feed.synd.SyndImageImpl.class,
com.rometools.rome.feed.synd.SyndContentImpl.class,
com.rometools.rome.feed.synd.SyndEnclosureImpl.class,
// rome cloneable
com.rometools.modules.activitystreams.types.Article.class, com.rometools.modules.activitystreams.types.Audio.class,
com.rometools.modules.activitystreams.types.Bookmark.class, com.rometools.modules.activitystreams.types.Comment.class,
com.rometools.modules.activitystreams.types.Event.class, com.rometools.modules.activitystreams.types.File.class,
com.rometools.modules.activitystreams.types.Folder.class, com.rometools.modules.activitystreams.types.List.class,
com.rometools.modules.activitystreams.types.Note.class, com.rometools.modules.activitystreams.types.Person.class,
com.rometools.modules.activitystreams.types.Photo.class, com.rometools.modules.activitystreams.types.PhotoAlbum.class,
com.rometools.modules.activitystreams.types.Place.class, com.rometools.modules.activitystreams.types.Playlist.class,
com.rometools.modules.activitystreams.types.Product.class, com.rometools.modules.activitystreams.types.Review.class,
com.rometools.modules.activitystreams.types.Service.class, com.rometools.modules.activitystreams.types.Song.class,
com.rometools.modules.activitystreams.types.Status.class, com.rometools.modules.base.types.DateTimeRange.class,
com.rometools.modules.base.types.FloatUnit.class, com.rometools.modules.base.types.GenderEnumeration.class,
com.rometools.modules.base.types.IntUnit.class, com.rometools.modules.base.types.PriceTypeEnumeration.class,
com.rometools.modules.base.types.ShippingType.class, com.rometools.modules.base.types.ShortDate.class,
com.rometools.modules.base.types.Size.class, com.rometools.modules.base.types.YearType.class,
com.rometools.modules.content.ContentItem.class, com.rometools.modules.georss.GeoRSSPoint.class,
com.rometools.modules.georss.geometries.Envelope.class, com.rometools.modules.georss.geometries.LineString.class,
com.rometools.modules.georss.geometries.LinearRing.class, com.rometools.modules.georss.geometries.Point.class,
com.rometools.modules.georss.geometries.Polygon.class, com.rometools.modules.georss.geometries.Position.class,
com.rometools.modules.georss.geometries.PositionList.class, com.rometools.modules.mediarss.types.MediaGroup.class,
com.rometools.modules.mediarss.types.Metadata.class, com.rometools.modules.mediarss.types.Thumbnail.class,
com.rometools.modules.opensearch.entity.OSQuery.class, com.rometools.modules.photocast.types.PhotoDate.class,
com.rometools.modules.sle.types.DateValue.class, com.rometools.modules.sle.types.Group.class,
com.rometools.modules.sle.types.NumberValue.class, com.rometools.modules.sle.types.Sort.class,
com.rometools.modules.sle.types.StringValue.class, com.rometools.modules.yahooweather.types.Astronomy.class,
com.rometools.modules.yahooweather.types.Atmosphere.class, com.rometools.modules.yahooweather.types.Condition.class,
com.rometools.modules.yahooweather.types.Forecast.class, com.rometools.modules.yahooweather.types.Location.class,
com.rometools.modules.yahooweather.types.Units.class, com.rometools.modules.yahooweather.types.Wind.class,
com.rometools.opml.feed.opml.Attribute.class, com.rometools.opml.feed.opml.Opml.class,
com.rometools.opml.feed.opml.Outline.class, com.rometools.rome.feed.atom.Category.class,
com.rometools.rome.feed.atom.Content.class, com.rometools.rome.feed.atom.Entry.class,
com.rometools.rome.feed.atom.Feed.class, com.rometools.rome.feed.atom.Generator.class,
com.rometools.rome.feed.atom.Link.class, com.rometools.rome.feed.atom.Person.class,
com.rometools.rome.feed.rss.Category.class, com.rometools.rome.feed.rss.Channel.class,
com.rometools.rome.feed.rss.Cloud.class, com.rometools.rome.feed.rss.Content.class,
com.rometools.rome.feed.rss.Description.class, com.rometools.rome.feed.rss.Enclosure.class,
com.rometools.rome.feed.rss.Guid.class, com.rometools.rome.feed.rss.Image.class, com.rometools.rome.feed.rss.Item.class,
com.rometools.rome.feed.rss.Source.class, com.rometools.rome.feed.rss.TextInput.class,
com.rometools.rome.feed.synd.SyndLinkImpl.class, com.rometools.rome.feed.synd.SyndPersonImpl.class,
java.util.ArrayList.class,
// rome cloneable
com.rometools.modules.activitystreams.types.Article.class,
com.rometools.modules.activitystreams.types.Audio.class,
com.rometools.modules.activitystreams.types.Bookmark.class,
com.rometools.modules.activitystreams.types.Comment.class,
com.rometools.modules.activitystreams.types.Event.class,
com.rometools.modules.activitystreams.types.File.class,
com.rometools.modules.activitystreams.types.Folder.class,
com.rometools.modules.activitystreams.types.List.class,
com.rometools.modules.activitystreams.types.Note.class,
com.rometools.modules.activitystreams.types.Person.class,
com.rometools.modules.activitystreams.types.Photo.class,
com.rometools.modules.activitystreams.types.PhotoAlbum.class,
com.rometools.modules.activitystreams.types.Place.class,
com.rometools.modules.activitystreams.types.Playlist.class,
com.rometools.modules.activitystreams.types.Product.class,
com.rometools.modules.activitystreams.types.Review.class,
com.rometools.modules.activitystreams.types.Service.class,
com.rometools.modules.activitystreams.types.Song.class,
com.rometools.modules.activitystreams.types.Status.class,
com.rometools.modules.base.types.DateTimeRange.class,
com.rometools.modules.base.types.FloatUnit.class,
com.rometools.modules.base.types.GenderEnumeration.class,
com.rometools.modules.base.types.IntUnit.class,
com.rometools.modules.base.types.PriceTypeEnumeration.class,
com.rometools.modules.base.types.ShippingType.class,
com.rometools.modules.base.types.ShortDate.class,
com.rometools.modules.base.types.Size.class,
com.rometools.modules.base.types.YearType.class,
com.rometools.modules.content.ContentItem.class,
com.rometools.modules.georss.GeoRSSPoint.class,
com.rometools.modules.georss.geometries.Envelope.class,
com.rometools.modules.georss.geometries.LineString.class,
com.rometools.modules.georss.geometries.LinearRing.class,
com.rometools.modules.georss.geometries.Point.class,
com.rometools.modules.georss.geometries.Polygon.class,
com.rometools.modules.georss.geometries.Position.class,
com.rometools.modules.georss.geometries.PositionList.class,
com.rometools.modules.mediarss.types.MediaGroup.class,
com.rometools.modules.mediarss.types.Metadata.class,
com.rometools.modules.mediarss.types.Thumbnail.class,
com.rometools.modules.opensearch.entity.OSQuery.class,
com.rometools.modules.photocast.types.PhotoDate.class,
com.rometools.modules.sle.types.DateValue.class,
com.rometools.modules.sle.types.Group.class,
com.rometools.modules.sle.types.NumberValue.class,
com.rometools.modules.sle.types.Sort.class,
com.rometools.modules.sle.types.StringValue.class,
com.rometools.modules.yahooweather.types.Astronomy.class,
com.rometools.modules.yahooweather.types.Atmosphere.class,
com.rometools.modules.yahooweather.types.Condition.class,
com.rometools.modules.yahooweather.types.Forecast.class,
com.rometools.modules.yahooweather.types.Location.class,
com.rometools.modules.yahooweather.types.Units.class,
com.rometools.modules.yahooweather.types.Wind.class,
com.rometools.opml.feed.opml.Attribute.class,
com.rometools.opml.feed.opml.Opml.class,
com.rometools.opml.feed.opml.Outline.class,
com.rometools.rome.feed.atom.Category.class,
com.rometools.rome.feed.atom.Content.class,
com.rometools.rome.feed.atom.Entry.class,
com.rometools.rome.feed.atom.Feed.class,
com.rometools.rome.feed.atom.Generator.class,
com.rometools.rome.feed.atom.Link.class,
com.rometools.rome.feed.atom.Person.class,
com.rometools.rome.feed.rss.Category.class,
com.rometools.rome.feed.rss.Channel.class,
com.rometools.rome.feed.rss.Cloud.class,
com.rometools.rome.feed.rss.Content.class,
com.rometools.rome.feed.rss.Description.class,
com.rometools.rome.feed.rss.Enclosure.class,
com.rometools.rome.feed.rss.Guid.class,
com.rometools.rome.feed.rss.Image.class,
com.rometools.rome.feed.rss.Item.class,
com.rometools.rome.feed.rss.Source.class,
com.rometools.rome.feed.rss.TextInput.class,
com.rometools.rome.feed.synd.SyndLinkImpl.class,
com.rometools.rome.feed.synd.SyndPersonImpl.class,
java.util.ArrayList.class,
// rome modules
com.rometools.modules.sse.modules.Conflict.class, com.rometools.modules.sse.modules.Conflicts.class,
com.rometools.modules.cc.CreativeCommonsImpl.class, com.rometools.modules.feedpress.modules.FeedpressModuleImpl.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleImpl.class, com.rometools.modules.sse.modules.Sharing.class,
com.rometools.modules.georss.SimpleModuleImpl.class, com.rometools.modules.atom.modules.AtomLinkModuleImpl.class,
com.rometools.modules.itunes.EntryInformationImpl.class, com.rometools.modules.sse.modules.Update.class,
com.rometools.modules.photocast.PhotocastModuleImpl.class, com.rometools.modules.itunes.FeedInformationImpl.class,
com.rometools.modules.yahooweather.YWeatherModuleImpl.class, com.rometools.modules.feedburner.FeedBurnerImpl.class,
com.rometools.modules.sse.modules.Related.class, com.rometools.modules.fyyd.modules.FyydModuleImpl.class,
com.rometools.modules.psc.modules.PodloveSimpleChapterModuleImpl.class, com.rometools.modules.thr.ThreadingModuleImpl.class,
com.rometools.modules.sse.modules.Sync.class, com.rometools.modules.sle.SimpleListExtensionImpl.class,
com.rometools.modules.slash.SlashImpl.class, com.rometools.modules.sse.modules.History.class,
com.rometools.modules.georss.GMLModuleImpl.class, com.rometools.modules.base.CustomTagsImpl.class,
com.rometools.modules.base.GoogleBaseImpl.class, com.rometools.modules.sle.SleEntryImpl.class,
com.rometools.modules.mediarss.MediaEntryModuleImpl.class, com.rometools.modules.content.ContentModuleImpl.class,
com.rometools.modules.georss.W3CGeoModuleImpl.class, com.rometools.rome.feed.module.DCModuleImpl.class,
com.rometools.modules.mediarss.MediaModuleImpl.class, com.rometools.rome.feed.module.SyModuleImpl.class,
// rome modules
com.rometools.modules.sse.modules.Conflict.class,
com.rometools.modules.sse.modules.Conflicts.class,
com.rometools.modules.cc.CreativeCommonsImpl.class,
com.rometools.modules.feedpress.modules.FeedpressModuleImpl.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleImpl.class,
com.rometools.modules.sse.modules.Sharing.class,
com.rometools.modules.georss.SimpleModuleImpl.class,
com.rometools.modules.atom.modules.AtomLinkModuleImpl.class,
com.rometools.modules.itunes.EntryInformationImpl.class,
com.rometools.modules.sse.modules.Update.class,
com.rometools.modules.photocast.PhotocastModuleImpl.class,
com.rometools.modules.itunes.FeedInformationImpl.class,
com.rometools.modules.yahooweather.YWeatherModuleImpl.class,
com.rometools.modules.feedburner.FeedBurnerImpl.class,
com.rometools.modules.sse.modules.Related.class,
com.rometools.modules.fyyd.modules.FyydModuleImpl.class,
com.rometools.modules.psc.modules.PodloveSimpleChapterModuleImpl.class,
com.rometools.modules.thr.ThreadingModuleImpl.class,
com.rometools.modules.sse.modules.Sync.class,
com.rometools.modules.sle.SimpleListExtensionImpl.class,
com.rometools.modules.slash.SlashImpl.class,
com.rometools.modules.sse.modules.History.class,
com.rometools.modules.georss.GMLModuleImpl.class,
com.rometools.modules.base.CustomTagsImpl.class,
com.rometools.modules.base.GoogleBaseImpl.class,
com.rometools.modules.sle.SleEntryImpl.class,
com.rometools.modules.mediarss.MediaEntryModuleImpl.class,
com.rometools.modules.content.ContentModuleImpl.class,
com.rometools.modules.georss.W3CGeoModuleImpl.class,
com.rometools.rome.feed.module.DCModuleImpl.class,
com.rometools.modules.mediarss.MediaModuleImpl.class,
com.rometools.rome.feed.module.SyModuleImpl.class,
// extracted from all 3 rome.properties files of rome library
com.rometools.rome.io.impl.RSS090Parser.class, com.rometools.rome.io.impl.RSS091NetscapeParser.class,
com.rometools.rome.io.impl.RSS091UserlandParser.class, com.rometools.rome.io.impl.RSS092Parser.class,
com.rometools.rome.io.impl.RSS093Parser.class, com.rometools.rome.io.impl.RSS094Parser.class,
com.rometools.rome.io.impl.RSS10Parser.class, com.rometools.rome.io.impl.RSS20wNSParser.class,
com.rometools.rome.io.impl.RSS20Parser.class, com.rometools.rome.io.impl.Atom10Parser.class,
com.rometools.rome.io.impl.Atom03Parser.class,
com.rometools.rome.io.impl.SyModuleParser.class, com.rometools.rome.io.impl.DCModuleParser.class,
com.rometools.rome.io.impl.RSS090Generator.class, com.rometools.rome.io.impl.RSS091NetscapeGenerator.class,
com.rometools.rome.io.impl.RSS091UserlandGenerator.class, com.rometools.rome.io.impl.RSS092Generator.class,
com.rometools.rome.io.impl.RSS093Generator.class, com.rometools.rome.io.impl.RSS094Generator.class,
com.rometools.rome.io.impl.RSS10Generator.class, com.rometools.rome.io.impl.RSS20Generator.class,
com.rometools.rome.io.impl.Atom10Generator.class, com.rometools.rome.io.impl.Atom03Generator.class,
com.rometools.rome.feed.synd.impl.ConverterForAtom10.class, com.rometools.rome.feed.synd.impl.ConverterForAtom03.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS090.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS091Netscape.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS091Userland.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS092.class, com.rometools.rome.feed.synd.impl.ConverterForRSS093.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS094.class, com.rometools.rome.feed.synd.impl.ConverterForRSS10.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS20.class,
com.rometools.modules.mediarss.io.RSS20YahooParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.itunes.io.ITunesParser.class, com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class, com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class, com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class, com.rometools.modules.atom.io.AtomModuleParser.class,
com.rometools.modules.itunes.io.ITunesParserOldNamespace.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class, com.rometools.modules.sle.io.ModuleParser.class,
com.rometools.modules.yahooweather.io.WeatherModuleParser.class, com.rometools.modules.feedpress.io.FeedpressParser.class,
com.rometools.modules.fyyd.io.FyydParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.itunes.io.ITunesParser.class, com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class, com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class, com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class, com.rometools.modules.atom.io.AtomModuleParser.class,
com.rometools.modules.itunes.io.ITunesParserOldNamespace.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class, com.rometools.modules.sle.io.ModuleParser.class,
com.rometools.modules.yahooweather.io.WeatherModuleParser.class, com.rometools.modules.feedpress.io.FeedpressParser.class,
com.rometools.modules.fyyd.io.FyydParser.class,
com.rometools.modules.cc.io.ModuleParserRSS1.class, com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class, com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class, com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class, com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class, com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.feedpress.io.FeedpressParser.class, com.rometools.modules.fyyd.io.FyydParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.content.io.ContentModuleParser.class, com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.itunes.io.ITunesParser.class, com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.atom.io.AtomModuleParser.class, com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class, com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class, com.rometools.modules.itunes.io.ITunesParserOldNamespace.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class, com.rometools.modules.sle.io.ItemParser.class,
com.rometools.modules.yahooweather.io.WeatherModuleParser.class,
com.rometools.modules.psc.io.PodloveSimpleChapterParser.class,
com.rometools.modules.cc.io.ModuleParserRSS1.class, com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.base.io.CustomTagParser.class, com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.base.io.CustomTagParser.class, com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class, com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class, com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class, com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.base.io.CustomTagParser.class, com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class, com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class, com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.thr.io.ThreadingModuleParser.class, com.rometools.modules.psc.io.PodloveSimpleChapterParser.class,
com.rometools.modules.cc.io.CCModuleGenerator.class, com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.itunes.io.ITunesGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class, com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class, com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class, com.rometools.modules.atom.io.AtomModuleGenerator.class,
com.rometools.modules.sle.io.ModuleGenerator.class, com.rometools.modules.yahooweather.io.WeatherModuleGenerator.class,
com.rometools.modules.feedpress.io.FeedpressGenerator.class, com.rometools.modules.fyyd.io.FyydGenerator.class,
com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class, com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class, com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class, com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class, com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class, com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class, com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.feedpress.io.FeedpressGenerator.class, com.rometools.modules.fyyd.io.FyydGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class, com.rometools.modules.base.io.GoogleBaseGenerator.class,
com.rometools.modules.base.io.CustomTagGenerator.class, com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class, com.rometools.modules.itunes.io.ITunesGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class, com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class, com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class, com.rometools.modules.atom.io.AtomModuleGenerator.class,
com.rometools.modules.yahooweather.io.WeatherModuleGenerator.class,
com.rometools.modules.psc.io.PodloveSimpleChapterGenerator.class,
com.rometools.modules.base.io.GoogleBaseGenerator.class, com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class, com.rometools.modules.base.io.GoogleBaseGenerator.class,
com.rometools.modules.base.io.CustomTagGenerator.class, com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class, com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class, com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class, com.rometools.modules.base.io.CustomTagGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class, com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class, com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class, com.rometools.modules.thr.io.ThreadingModuleGenerator.class,
com.rometools.modules.psc.io.PodloveSimpleChapterGenerator.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.opml.io.impl.OPML10Generator.class, com.rometools.opml.io.impl.OPML20Generator.class,
com.rometools.opml.io.impl.OPML10Parser.class, com.rometools.opml.io.impl.OPML20Parser.class,
com.rometools.opml.feed.synd.impl.ConverterForOPML10.class, com.rometools.opml.feed.synd.impl.ConverterForOPML20.class, })
public class NativeImageClasses {
}
// extracted from all 3 rome.properties files of rome library
com.rometools.rome.io.impl.RSS090Parser.class,
com.rometools.rome.io.impl.RSS091NetscapeParser.class,
com.rometools.rome.io.impl.RSS091UserlandParser.class,
com.rometools.rome.io.impl.RSS092Parser.class,
com.rometools.rome.io.impl.RSS093Parser.class,
com.rometools.rome.io.impl.RSS094Parser.class,
com.rometools.rome.io.impl.RSS10Parser.class,
com.rometools.rome.io.impl.RSS20wNSParser.class,
com.rometools.rome.io.impl.RSS20Parser.class,
com.rometools.rome.io.impl.Atom10Parser.class,
com.rometools.rome.io.impl.Atom03Parser.class,
com.rometools.rome.io.impl.SyModuleParser.class,
com.rometools.rome.io.impl.DCModuleParser.class,
com.rometools.rome.io.impl.RSS090Generator.class,
com.rometools.rome.io.impl.RSS091NetscapeGenerator.class,
com.rometools.rome.io.impl.RSS091UserlandGenerator.class,
com.rometools.rome.io.impl.RSS092Generator.class,
com.rometools.rome.io.impl.RSS093Generator.class,
com.rometools.rome.io.impl.RSS094Generator.class,
com.rometools.rome.io.impl.RSS10Generator.class,
com.rometools.rome.io.impl.RSS20Generator.class,
com.rometools.rome.io.impl.Atom10Generator.class,
com.rometools.rome.io.impl.Atom03Generator.class,
com.rometools.rome.feed.synd.impl.ConverterForAtom10.class,
com.rometools.rome.feed.synd.impl.ConverterForAtom03.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS090.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS091Netscape.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS091Userland.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS092.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS093.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS094.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS10.class,
com.rometools.rome.feed.synd.impl.ConverterForRSS20.class,
com.rometools.modules.mediarss.io.RSS20YahooParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.itunes.io.ITunesParser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.atom.io.AtomModuleParser.class,
com.rometools.modules.itunes.io.ITunesParserOldNamespace.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.sle.io.ModuleParser.class,
com.rometools.modules.yahooweather.io.WeatherModuleParser.class,
com.rometools.modules.feedpress.io.FeedpressParser.class,
com.rometools.modules.fyyd.io.FyydParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.itunes.io.ITunesParser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.atom.io.AtomModuleParser.class,
com.rometools.modules.itunes.io.ITunesParserOldNamespace.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.sle.io.ModuleParser.class,
com.rometools.modules.yahooweather.io.WeatherModuleParser.class,
com.rometools.modules.feedpress.io.FeedpressParser.class,
com.rometools.modules.fyyd.io.FyydParser.class,
com.rometools.modules.cc.io.ModuleParserRSS1.class,
com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.feedpress.io.FeedpressParser.class,
com.rometools.modules.fyyd.io.FyydParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.itunes.io.ITunesParser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.atom.io.AtomModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.itunes.io.ITunesParserOldNamespace.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.sle.io.ItemParser.class,
com.rometools.modules.yahooweather.io.WeatherModuleParser.class,
com.rometools.modules.psc.io.PodloveSimpleChapterParser.class,
com.rometools.modules.cc.io.ModuleParserRSS1.class,
com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.base.io.CustomTagParser.class,
com.rometools.modules.content.io.ContentModuleParser.class,
com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.base.io.CustomTagParser.class,
com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.cc.io.ModuleParserRSS2.class,
com.rometools.modules.base.io.GoogleBaseParser.class,
com.rometools.modules.base.io.CustomTagParser.class,
com.rometools.modules.slash.io.SlashModuleParser.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleParser.class,
com.rometools.modules.georss.SimpleParser.class,
com.rometools.modules.georss.W3CGeoParser.class,
com.rometools.modules.photocast.io.Parser.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.AlternateMediaModuleParser.class,
com.rometools.modules.thr.io.ThreadingModuleParser.class,
com.rometools.modules.psc.io.PodloveSimpleChapterParser.class,
com.rometools.modules.cc.io.CCModuleGenerator.class,
com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.itunes.io.ITunesGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.atom.io.AtomModuleGenerator.class,
com.rometools.modules.sle.io.ModuleGenerator.class,
com.rometools.modules.yahooweather.io.WeatherModuleGenerator.class,
com.rometools.modules.feedpress.io.FeedpressGenerator.class,
com.rometools.modules.fyyd.io.FyydGenerator.class,
com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.feedpress.io.FeedpressGenerator.class,
com.rometools.modules.fyyd.io.FyydGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class,
com.rometools.modules.base.io.GoogleBaseGenerator.class,
com.rometools.modules.base.io.CustomTagGenerator.class,
com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.itunes.io.ITunesGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.atom.io.AtomModuleGenerator.class,
com.rometools.modules.yahooweather.io.WeatherModuleGenerator.class,
com.rometools.modules.psc.io.PodloveSimpleChapterGenerator.class,
com.rometools.modules.base.io.GoogleBaseGenerator.class,
com.rometools.modules.content.io.ContentModuleGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class,
com.rometools.modules.base.io.GoogleBaseGenerator.class,
com.rometools.modules.base.io.CustomTagGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.cc.io.CCModuleGenerator.class,
com.rometools.modules.base.io.CustomTagGenerator.class,
com.rometools.modules.slash.io.SlashModuleGenerator.class,
com.rometools.modules.opensearch.impl.OpenSearchModuleGenerator.class,
com.rometools.modules.georss.SimpleGenerator.class,
com.rometools.modules.georss.W3CGeoGenerator.class,
com.rometools.modules.photocast.io.Generator.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.modules.thr.io.ThreadingModuleGenerator.class,
com.rometools.modules.psc.io.PodloveSimpleChapterGenerator.class,
com.rometools.modules.mediarss.io.MediaModuleParser.class,
com.rometools.modules.mediarss.io.MediaModuleGenerator.class,
com.rometools.opml.io.impl.OPML10Generator.class,
com.rometools.opml.io.impl.OPML20Generator.class,
com.rometools.opml.io.impl.OPML10Parser.class,
com.rometools.opml.io.impl.OPML20Parser.class,
com.rometools.opml.feed.synd.impl.ConverterForOPML10.class,
com.rometools.opml.feed.synd.impl.ConverterForOPML20.class,
})
public class NativeImageClasses {}

View File

@@ -1,29 +1,29 @@
package com.commafeed.backend;
import java.nio.charset.StandardCharsets;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import lombok.experimental.UtilityClass;
import java.nio.charset.StandardCharsets;
@UtilityClass
@SuppressWarnings("deprecation")
public class Digests {
public static String sha1Hex(byte[] input) {
return hashBytesToHex(Hashing.sha1(), input);
}
public static String sha1Hex(byte[] input) {
return hashBytesToHex(Hashing.sha1(), input);
}
public static String sha1Hex(String input) {
return hashBytesToHex(Hashing.sha1(), input.getBytes(StandardCharsets.UTF_8));
}
public static String sha1Hex(String input) {
return hashBytesToHex(Hashing.sha1(), input.getBytes(StandardCharsets.UTF_8));
}
public static String md5Hex(String input) {
return hashBytesToHex(Hashing.md5(), input.getBytes(StandardCharsets.UTF_8));
}
public static String md5Hex(String input) {
return hashBytesToHex(Hashing.md5(), input.getBytes(StandardCharsets.UTF_8));
}
private static String hashBytesToHex(HashFunction function, byte[] input) {
return function.hashBytes(input).toString();
}
private static String hashBytesToHex(HashFunction function, byte[] input) {
return function.hashBytes(input).toString();
}
}

View File

@@ -1,16 +1,22 @@
package com.commafeed.backend;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.SequencedMap;
import java.util.zip.GZIPInputStream;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedVersion;
import com.google.common.net.HttpHeaders;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressNetwork;
import inet.ipaddr.IPAddressString;
import inet.ipaddr.ipv4.IPv4Address;
import inet.ipaddr.ipv6.IPv6Address;
import jakarta.inject.Singleton;
import lombok.RequiredArgsConstructor;
import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.apache5.util.Apache5SslUtils;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.SystemDefaultDnsResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
@@ -35,132 +41,187 @@ import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.brotli.dec.BrotliInputStream;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedVersion;
import com.google.common.net.HttpHeaders;
import lombok.RequiredArgsConstructor;
import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.apache5.util.Apache5SslUtils;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.SequencedMap;
import java.util.zip.GZIPInputStream;
@Singleton
@RequiredArgsConstructor
public class HttpClientFactory {
private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE;
private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE;
private static final IPAddress CGNAT_RANGE = new IPAddressString("100.64.0.0/10").getAddress();
private final CommaFeedConfiguration config;
private final CommaFeedVersion version;
private final CommaFeedConfiguration config;
private final CommaFeedVersion version;
public CloseableHttpClient newClient(int poolSize) {
PoolingHttpClientConnectionManager connectionManager = newConnectionManager(config, poolSize);
String userAgent = config.httpClient()
.userAgent()
.orElseGet(() -> String.format("CommaFeed/%s (https://github.com/Athou/commafeed)", version.getVersion()));
return newClient(config, connectionManager, userAgent);
}
public CloseableHttpClient newClient(int poolSize) {
PoolingHttpClientConnectionManager connectionManager =
newConnectionManager(config, poolSize);
String userAgent =
config.httpClient()
.userAgent()
.orElseGet(
() ->
String.format(
"CommaFeed/%s (https://github.com/Athou/commafeed)",
version.getVersion()));
return newClient(config, connectionManager, userAgent);
}
private CloseableHttpClient newClient(CommaFeedConfiguration config, HttpClientConnectionManager connectionManager, String userAgent) {
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "en"));
headers.add(new BasicHeader(HttpHeaders.PRAGMA, "No-cache"));
headers.add(new BasicHeader(HttpHeaders.CACHE_CONTROL, "no-cache"));
private CloseableHttpClient newClient(
CommaFeedConfiguration config,
HttpClientConnectionManager connectionManager,
String userAgent) {
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "en"));
headers.add(new BasicHeader(HttpHeaders.PRAGMA, "No-cache"));
headers.add(new BasicHeader(HttpHeaders.CACHE_CONTROL, "no-cache"));
SequencedMap<String, InputStreamFactory> contentDecoderMap = new LinkedHashMap<>();
contentDecoderMap.put(ContentCoding.GZIP.token(), GZIPInputStream::new);
contentDecoderMap.put(ContentCoding.DEFLATE.token(), DeflateInputStream::new);
contentDecoderMap.put(ContentCoding.BROTLI.token(), BrotliInputStream::new);
SequencedMap<String, InputStreamFactory> contentDecoderMap = new LinkedHashMap<>();
contentDecoderMap.put(ContentCoding.GZIP.token(), GZIPInputStream::new);
contentDecoderMap.put(ContentCoding.DEFLATE.token(), DeflateInputStream::new);
contentDecoderMap.put(ContentCoding.BROTLI.token(), BrotliInputStream::new);
RedirectStrategy redirectStrategy = config.httpClient().blockLocalAddresses()
? new BlockLocalAddressesRedirectStrategy(DNS_RESOLVER)
: new DefaultRedirectStrategy();
RedirectStrategy redirectStrategy =
config.httpClient().blockLocalAddresses()
? new BlockLocalAddressesRedirectStrategy(DNS_RESOLVER)
: new DefaultRedirectStrategy();
return HttpClientBuilder.create()
.useSystemProperties()
.disableAutomaticRetries()
.disableCookieManagement()
.setUserAgent(userAgent)
.setDefaultHeaders(headers)
.setConnectionManager(connectionManager)
.evictExpiredConnections()
.evictIdleConnections(TimeValue.of(config.httpClient().idleConnectionsEvictionInterval()))
.setContentDecoderRegistry(new LinkedHashMap<>(contentDecoderMap))
.setRedirectStrategy(redirectStrategy)
.build();
}
return HttpClientBuilder.create()
.disableConnectionState()
.useSystemProperties()
.disableAutomaticRetries()
.disableCookieManagement()
.setUserAgent(userAgent)
.setDefaultHeaders(headers)
.setConnectionManager(connectionManager)
.evictExpiredConnections()
.evictIdleConnections(
TimeValue.of(config.httpClient().idleConnectionsEvictionInterval()))
.setContentDecoderRegistry(new LinkedHashMap<>(contentDecoderMap))
.setRedirectStrategy(redirectStrategy)
.build();
}
private PoolingHttpClientConnectionManager newConnectionManager(CommaFeedConfiguration config, int poolSize) {
SSLFactory sslFactory = SSLFactory.builder().withUnsafeTrustMaterial().withUnsafeHostnameVerifier().build();
DnsResolver dnsResolver = config.httpClient().blockLocalAddresses() ? new BlockLocalAddressesDnsResolver(DNS_RESOLVER)
: DNS_RESOLVER;
private PoolingHttpClientConnectionManager newConnectionManager(
CommaFeedConfiguration config, int poolSize) {
SSLFactory sslFactory =
SSLFactory.builder().withUnsafeTrustMaterial().withUnsafeHostnameVerifier().build();
DnsResolver dnsResolver =
config.httpClient().blockLocalAddresses()
? new BlockLocalAddressesDnsResolver(DNS_RESOLVER)
: DNS_RESOLVER;
return PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(Apache5SslUtils.toTlsSocketStrategy(sslFactory))
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(Timeout.of(config.httpClient().connectTimeout()))
.setSocketTimeout(Timeout.of(config.httpClient().socketTimeout()))
.setTimeToLive(Timeout.of(config.httpClient().connectionTimeToLive()))
.build())
.setDefaultTlsConfig(TlsConfig.custom().setHandshakeTimeout(Timeout.of(config.httpClient().sslHandshakeTimeout())).build())
.setMaxConnPerRoute(poolSize)
.setMaxConnTotal(poolSize)
.setDnsResolver(dnsResolver)
.build();
return PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(Apache5SslUtils.toTlsSocketStrategy(sslFactory))
.setDefaultConnectionConfig(
ConnectionConfig.custom()
.setConnectTimeout(Timeout.of(config.httpClient().connectTimeout()))
.setSocketTimeout(Timeout.of(config.httpClient().socketTimeout()))
.setTimeToLive(
Timeout.of(config.httpClient().connectionTimeToLive()))
.build())
.setDefaultTlsConfig(
TlsConfig.custom()
.setHandshakeTimeout(
Timeout.of(config.httpClient().sslHandshakeTimeout()))
.build())
.setMaxConnPerRoute(poolSize)
.setMaxConnTotal(poolSize)
.setDnsResolver(dnsResolver)
.build();
}
}
private static boolean isLocalAddress(InetAddress address) {
return isLocalAddress(new IPAddressNetwork.IPAddressGenerator().from(address));
}
private static boolean isLocalAddress(InetAddress address) {
return address.isSiteLocalAddress() || address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress()
|| address.isMulticastAddress();
}
private static boolean isLocalAddress(IPAddress ip) {
if (ip.isLocal() || ip.isLoopback() || ip.isMulticast() || CGNAT_RANGE.contains(ip)) {
return true;
}
private record BlockLocalAddressesDnsResolver(DnsResolver delegate) implements DnsResolver {
@Override
public InetAddress[] resolve(String host) throws UnknownHostException {
InetAddress[] addresses = delegate.resolve(host);
for (InetAddress addr : addresses) {
if (isLocalAddress(addr)) {
throw new UnknownHostException("Access to local address blocked: " + addr.getHostAddress());
}
}
return addresses;
}
if (!ip.isIPv6()) {
return false;
}
@Override
public String resolveCanonicalHostname(String host) throws UnknownHostException {
return delegate.resolveCanonicalHostname(host);
}
}
// IPv6 transition mechanisms embed an IPv4 address that must be validated too, otherwise
// they could be used to smuggle a blocked IPv4 target past the IPv6-only checks above
IPv6Address ipv6 = ip.toIPv6();
if (ipv6.isIPv4Mapped() || ipv6.isIPv4Compatible() || ipv6.isWellKnownIPv4Translatable()) {
// IPv4-mapped (::ffff:x.x.x.x), IPv4-compatible (::x.x.x.x) and NAT64
// (64:ff9b::/96, RFC 6052) addresses all embed the IPv4 address in the lowest 32 bits
return isLocalAddress(ipv6.getEmbeddedIPv4Address());
}
if (ipv6.is6To4()) {
// 6to4 (2002::/16, RFC 3056) embeds the IPv4 address in bits 16-47
return isLocalAddress(ipv6.get6To4IPv4Address());
}
if (ipv6.isTeredo()) {
// Teredo (2001::/32, RFC 4380) embeds the IPv4 address in the lowest 32 bits,
// obfuscated with a bitwise complement
IPv4Address obfuscated = ipv6.getEmbeddedIPv4Address();
return isLocalAddress(new IPv4Address(~obfuscated.intValue()));
}
@RequiredArgsConstructor
private static class BlockLocalAddressesRedirectStrategy extends DefaultRedirectStrategy {
return false;
}
private final DnsResolver delegate;
private record BlockLocalAddressesDnsResolver(DnsResolver delegate) implements DnsResolver {
@Override
public InetAddress[] resolve(String host) throws UnknownHostException {
InetAddress[] addresses = delegate.resolve(host);
for (InetAddress addr : addresses) {
if (isLocalAddress(addr)) {
throw new UnknownHostException(
"Access to local address blocked: " + addr.getHostAddress());
}
}
return addresses;
}
@Override
public URI getLocationURI(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException {
URI redirectUri = super.getLocationURI(request, response, context);
@Override
public String resolveCanonicalHostname(String host) throws UnknownHostException {
return delegate.resolveCanonicalHostname(host);
}
}
String host = redirectUri.getHost();
if (host == null) {
throw new HttpException("Redirect URI does not have a host: " + redirectUri);
}
@RequiredArgsConstructor
private static class BlockLocalAddressesRedirectStrategy extends DefaultRedirectStrategy {
InetAddress[] addresses;
try {
addresses = delegate.resolve(host);
} catch (UnknownHostException e) {
throw new HttpException("Unknown host: " + host);
}
private final DnsResolver delegate;
for (InetAddress addr : addresses) {
if (isLocalAddress(addr)) {
throw new HttpException("Access to local address blocked: " + addr.getHostAddress());
}
}
@Override
public URI getLocationURI(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException {
URI redirectUri = super.getLocationURI(request, response, context);
return redirectUri;
}
}
String host = redirectUri.getHost();
if (host == null) {
throw new HttpException("Redirect URI does not have a host: " + redirectUri);
}
InetAddress[] addresses;
try {
addresses = delegate.resolve(host);
} catch (UnknownHostException e) {
throw new HttpException("Unknown host: " + host);
}
for (InetAddress addr : addresses) {
if (isLocalAddress(addr)) {
throw new HttpException(
"Access to local address blocked: " + addr.getHostAddress());
}
}
return redirectUri;
}
}
}

View File

@@ -1,17 +1,24 @@
package com.commafeed.backend;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConfiguration.HttpClientCache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.net.HttpHeaders;
import jakarta.inject.Singleton;
import jakarta.ws.rs.core.CacheControl;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Lombok;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.config.RequestConfig;
@@ -27,284 +34,335 @@ import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.util.Timeout;
import org.jboss.resteasy.reactive.common.headers.CacheControlDelegate;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConfiguration.HttpClientCache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.net.HttpHeaders;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Lombok;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* Smart HTTP getter: handles gzip, ssl, last modified and etag headers
*/
/** Smart HTTP getter: handles gzip, ssl, last modified and etag headers */
@Singleton
@Slf4j
public class HttpGetter {
private final CommaFeedConfiguration config;
private final InstantSource instantSource;
private final CloseableHttpClient client;
private final Cache<HttpRequest, HttpResponse> cache;
private final CommaFeedConfiguration config;
private final InstantSource instantSource;
private final CloseableHttpClient client;
private final Cache<HttpRequest, HttpResponse> cache;
public HttpGetter(CommaFeedConfiguration config, InstantSource instantSource, HttpClientFactory httpClientFactory,
MetricRegistry metrics) {
this.config = config;
this.instantSource = instantSource;
this.client = httpClientFactory.newClient(config.feedRefresh().httpThreads());
this.cache = newCache(config);
public HttpGetter(
CommaFeedConfiguration config,
InstantSource instantSource,
HttpClientFactory httpClientFactory,
MetricRegistry metrics) {
this.config = config;
this.instantSource = instantSource;
this.client = httpClientFactory.newClient(config.feedRefresh().httpThreads());
this.cache = newCache(config);
metrics.registerGauge(MetricRegistry.name(getClass(), "cache", "size"), () -> cache == null ? 0 : cache.size());
metrics.registerGauge(MetricRegistry.name(getClass(), "cache", "memoryUsage"),
() -> cache == null ? 0 : cache.asMap().values().stream().mapToInt(e -> ArrayUtils.getLength(e.content)).sum());
}
metrics.registerGauge(
MetricRegistry.name(getClass(), "cache", "size"),
() -> cache == null ? 0 : cache.size());
metrics.registerGauge(
MetricRegistry.name(getClass(), "cache", "memoryUsage"),
() ->
cache == null
? 0
: cache.asMap().values().stream()
.mapToInt(e -> ArrayUtils.getLength(e.content))
.sum());
}
public HttpResult get(String url) throws IOException, NotModifiedException, TooManyRequestsException, SchemeNotAllowedException {
return get(HttpRequest.builder(url).build());
}
public HttpResult get(String url)
throws IOException,
NotModifiedException,
TooManyRequestsException,
SchemeNotAllowedException {
return get(HttpRequest.builder(url).build());
}
public HttpResult get(HttpRequest request)
throws IOException, NotModifiedException, TooManyRequestsException, SchemeNotAllowedException {
URI uri = URI.create(request.getUrl());
ensureHttpScheme(uri.getScheme());
public HttpResult get(HttpRequest request)
throws IOException,
NotModifiedException,
TooManyRequestsException,
SchemeNotAllowedException {
URI uri = URI.create(request.getUrl());
ensureHttpScheme(uri.getScheme());
final HttpResponse response;
if (cache == null) {
response = invoke(request);
} else {
try {
response = cache.get(request, () -> invoke(request));
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException ioe) {
throw ioe;
} else {
throw Lombok.sneakyThrow(e);
}
}
}
final HttpResponse response;
if (cache == null) {
response = invoke(request);
} else {
try {
response = cache.get(request, () -> invoke(request));
} catch (ExecutionException e) {
if (e.getCause() instanceof IOException ioe) {
throw ioe;
} else {
throw Lombok.sneakyThrow(e);
}
}
}
int code = response.code();
if (code == HttpStatus.SC_TOO_MANY_REQUESTS || code == HttpStatus.SC_SERVICE_UNAVAILABLE && response.retryAfter() != null) {
throw new TooManyRequestsException(response.retryAfter());
}
int code = response.code();
if (code == HttpStatus.SC_TOO_MANY_REQUESTS
|| code == HttpStatus.SC_SERVICE_UNAVAILABLE && response.retryAfter() != null) {
throw new TooManyRequestsException(response.retryAfter());
}
if (code == HttpStatus.SC_NOT_MODIFIED) {
throw new NotModifiedException("'304 - not modified' http code received");
}
if (code == HttpStatus.SC_NOT_MODIFIED) {
throw new NotModifiedException("'304 - not modified' http code received");
}
if (code >= 300) {
throw new HttpResponseException(code, "Server returned HTTP error code " + code);
}
if (code >= 300) {
throw new HttpResponseException(code, "Server returned HTTP error code " + code);
}
String lastModifiedHeader = response.lastModifiedHeader();
String eTagHeader = response.eTagHeader();
String lastModifiedHeader = response.lastModifiedHeader();
String eTagHeader = response.eTagHeader();
Duration validFor = Optional.ofNullable(response.cacheControl())
.filter(cc -> cc.getMaxAge() >= 0)
.map(cc -> Duration.ofSeconds(cc.getMaxAge()))
.orElse(Duration.ZERO);
Duration validFor =
Optional.ofNullable(response.cacheControl())
.filter(cc -> cc.getMaxAge() >= 0)
.map(cc -> Duration.ofSeconds(cc.getMaxAge()))
.orElse(Duration.ZERO);
return new HttpResult(response.content(), response.contentType(), lastModifiedHeader, eTagHeader, response.urlAfterRedirect(),
validFor);
}
return new HttpResult(
response.content(),
response.contentType(),
lastModifiedHeader,
eTagHeader,
response.urlAfterRedirect(),
validFor);
}
private void ensureHttpScheme(String scheme) throws SchemeNotAllowedException {
if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
throw new SchemeNotAllowedException(scheme);
}
}
private void ensureHttpScheme(String scheme) throws SchemeNotAllowedException {
if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
throw new SchemeNotAllowedException(scheme);
}
}
private HttpResponse invoke(HttpRequest request) throws IOException {
log.debug("fetching {}", request.getUrl());
private HttpResponse invoke(HttpRequest request) throws IOException {
log.debug("fetching {}", request.getUrl());
HttpClientContext context = HttpClientContext.create();
context.setRequestConfig(RequestConfig.custom()
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
// causes issues with some feeds
// see https://github.com/Athou/commafeed/issues/1572
// and https://issues.apache.org/jira/browse/HTTPCLIENT-2344
.setProtocolUpgradeEnabled(false)
.build());
HttpClientContext context = HttpClientContext.create();
context.setRequestConfig(
RequestConfig.custom()
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
// causes issues with some feeds
// see https://github.com/Athou/commafeed/issues/1572
// and https://issues.apache.org/jira/browse/HTTPCLIENT-2344
.setProtocolUpgradeEnabled(false)
.build());
return client.execute(request.toClassicHttpRequest(), context, resp -> {
byte[] content = resp.getEntity() == null ? null
: toByteArray(resp.getEntity(), config.httpClient().maxResponseSize().asLongValue());
int code = resp.getCode();
String lastModifiedHeader = Optional.ofNullable(resp.getFirstHeader(HttpHeaders.LAST_MODIFIED))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.orElse(null);
String eTagHeader = Optional.ofNullable(resp.getFirstHeader(HttpHeaders.ETAG))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.orElse(null);
return client.execute(
request.toClassicHttpRequest(),
context,
resp -> {
byte[] content =
resp.getEntity() == null
? null
: toByteArray(
resp.getEntity(),
config.httpClient().maxResponseSize().asLongValue());
int code = resp.getCode();
String lastModifiedHeader =
Optional.ofNullable(resp.getFirstHeader(HttpHeaders.LAST_MODIFIED))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.orElse(null);
String eTagHeader =
Optional.ofNullable(resp.getFirstHeader(HttpHeaders.ETAG))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.orElse(null);
CacheControl cacheControl = Optional.ofNullable(resp.getFirstHeader(HttpHeaders.CACHE_CONTROL))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.map(HttpGetter::toCacheControl)
.orElse(null);
CacheControl cacheControl =
Optional.ofNullable(resp.getFirstHeader(HttpHeaders.CACHE_CONTROL))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.map(HttpGetter::toCacheControl)
.orElse(null);
Instant retryAfter = Optional.ofNullable(resp.getFirstHeader(HttpHeaders.RETRY_AFTER))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.map(this::toInstant)
.orElse(null);
Instant retryAfter =
Optional.ofNullable(resp.getFirstHeader(HttpHeaders.RETRY_AFTER))
.map(NameValuePair::getValue)
.map(StringUtils::trimToNull)
.map(this::toInstant)
.orElse(null);
String contentType = Optional.ofNullable(resp.getEntity()).map(HttpEntity::getContentType).orElse(null);
String urlAfterRedirect = Optional.ofNullable(context.getRedirectLocations())
.map(RedirectLocations::getAll)
.map(l -> Iterables.getLast(l, null))
.map(URI::toString)
.orElse(request.getUrl());
String contentType =
Optional.ofNullable(resp.getEntity())
.map(HttpEntity::getContentType)
.orElse(null);
String urlAfterRedirect =
Optional.ofNullable(context.getRedirectLocations())
.map(RedirectLocations::getAll)
.map(l -> Iterables.getLast(l, null))
.map(URI::toString)
.orElse(request.getUrl());
return new HttpResponse(code, lastModifiedHeader, eTagHeader, cacheControl, retryAfter, content, contentType, urlAfterRedirect);
});
}
return new HttpResponse(
code,
lastModifiedHeader,
eTagHeader,
cacheControl,
retryAfter,
content,
contentType,
urlAfterRedirect);
});
}
private static CacheControl toCacheControl(String headerValue) {
try {
return CacheControlDelegate.INSTANCE.fromString(headerValue);
} catch (Exception e) {
log.debug("Invalid Cache-Control header: {}", headerValue);
return null;
}
}
private static CacheControl toCacheControl(String headerValue) {
try {
return CacheControlDelegate.INSTANCE.fromString(headerValue);
} catch (Exception e) {
log.debug("Invalid Cache-Control header: {}", headerValue);
return null;
}
}
private Instant toInstant(String headerValue) {
if (headerValue == null) {
return null;
}
private Instant toInstant(String headerValue) {
if (headerValue == null) {
return null;
}
if (StringUtils.isNumeric(headerValue)) {
return instantSource.instant().plusSeconds(Long.parseLong(headerValue));
}
if (StringUtils.isNumeric(headerValue)) {
return instantSource.instant().plusSeconds(Long.parseLong(headerValue));
}
return DateUtils.parseStandardDate(headerValue);
}
return DateUtils.parseStandardDate(headerValue);
}
private static byte[] toByteArray(HttpEntity entity, long maxBytes) throws IOException {
if (entity.getContentLength() > maxBytes) {
throw new IOException(
"Response size (%s bytes) exceeds the maximum allowed size (%s bytes)".formatted(entity.getContentLength(), maxBytes));
}
private static byte[] toByteArray(HttpEntity entity, long maxBytes) throws IOException {
if (entity.getContentLength() > maxBytes) {
throw new IOException(
"Response size (%s bytes) exceeds the maximum allowed size (%s bytes)"
.formatted(entity.getContentLength(), maxBytes));
}
try (InputStream input = entity.getContent()) {
if (input == null) {
return null;
}
try (InputStream input = entity.getContent()) {
if (input == null) {
return null;
}
byte[] bytes = ByteStreams.limit(input, maxBytes + 1).readAllBytes();
if (bytes.length > maxBytes) {
throw new IOException("Response size exceeds the maximum allowed size (%s bytes)".formatted(maxBytes));
}
return bytes;
}
}
byte[] bytes = ByteStreams.limit(input, maxBytes + 1).readAllBytes();
if (bytes.length > maxBytes) {
throw new IOException(
"Response size exceeds the maximum allowed size (%s bytes)"
.formatted(maxBytes));
}
return bytes;
}
}
private static Cache<HttpRequest, HttpResponse> newCache(CommaFeedConfiguration config) {
HttpClientCache cacheConfig = config.httpClient().cache();
if (!cacheConfig.enabled()) {
return null;
}
private static Cache<HttpRequest, HttpResponse> newCache(CommaFeedConfiguration config) {
HttpClientCache cacheConfig = config.httpClient().cache();
if (!cacheConfig.enabled()) {
return null;
}
return CacheBuilder.newBuilder()
.weigher((HttpRequest key, HttpResponse value) -> value.content() != null ? value.content().length : 0)
.maximumWeight(cacheConfig.maximumMemorySize().asLongValue())
.expireAfterWrite(cacheConfig.expiration())
.build();
}
return CacheBuilder.newBuilder()
.weigher(
(HttpRequest key, HttpResponse value) ->
value.content() != null ? value.content().length : 0)
.maximumWeight(cacheConfig.maximumMemorySize().asLongValue())
.expireAfterWrite(cacheConfig.expiration())
.build();
}
public static class SchemeNotAllowedException extends Exception {
private static final long serialVersionUID = 1L;
public static class SchemeNotAllowedException extends Exception {
private static final long serialVersionUID = 1L;
public SchemeNotAllowedException(String scheme) {
super("Scheme not allowed: " + scheme);
}
}
public SchemeNotAllowedException(String scheme) {
super("Scheme not allowed: " + scheme);
}
}
@Getter
public static class NotModifiedException extends Exception {
private static final long serialVersionUID = 1L;
@Getter
public static class NotModifiedException extends Exception {
private static final long serialVersionUID = 1L;
/**
* if the value of this header changed, this is its new value
*/
private final String newLastModifiedHeader;
/** if the value of this header changed, this is its new value */
private final String newLastModifiedHeader;
/**
* if the value of this header changed, this is its new value
*/
private final String newEtagHeader;
/** if the value of this header changed, this is its new value */
private final String newEtagHeader;
public NotModifiedException(String message) {
this(message, null, null);
}
public NotModifiedException(String message) {
this(message, null, null);
}
public NotModifiedException(String message, String newLastModifiedHeader, String newEtagHeader) {
super(message);
this.newLastModifiedHeader = newLastModifiedHeader;
this.newEtagHeader = newEtagHeader;
}
}
public NotModifiedException(
String message, String newLastModifiedHeader, String newEtagHeader) {
super(message);
this.newLastModifiedHeader = newLastModifiedHeader;
this.newEtagHeader = newEtagHeader;
}
}
@RequiredArgsConstructor
@Getter
public static class TooManyRequestsException extends Exception {
private static final long serialVersionUID = 1L;
@RequiredArgsConstructor
@Getter
public static class TooManyRequestsException extends Exception {
private static final long serialVersionUID = 1L;
private final Instant retryAfter;
}
private final Instant retryAfter;
}
@Getter
public static class HttpResponseException extends IOException {
private static final long serialVersionUID = 1L;
@Getter
public static class HttpResponseException extends IOException {
private static final long serialVersionUID = 1L;
private final int code;
private final int code;
public HttpResponseException(int code, String message) {
super(message);
this.code = code;
}
}
public HttpResponseException(int code, String message) {
super(message);
this.code = code;
}
}
@Builder(builderMethodName = "")
@EqualsAndHashCode
@Getter
public static class HttpRequest {
private String url;
private String lastModified;
private String eTag;
@Builder(builderMethodName = "")
@EqualsAndHashCode
@Getter
public static class HttpRequest {
private String url;
private String lastModified;
private String eTag;
public static HttpRequestBuilder builder(String url) {
return new HttpRequestBuilder().url(url);
}
public static HttpRequestBuilder builder(String url) {
return new HttpRequestBuilder().url(url);
}
public ClassicHttpRequest toClassicHttpRequest() {
ClassicHttpRequest req = ClassicRequestBuilder.get(url).build();
if (lastModified != null) {
req.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified);
}
if (eTag != null) {
req.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
}
return req;
}
}
public ClassicHttpRequest toClassicHttpRequest() {
ClassicHttpRequest req = ClassicRequestBuilder.get(url).build();
if (lastModified != null) {
req.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified);
}
if (eTag != null) {
req.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
}
return req;
}
}
private record HttpResponse(int code, String lastModifiedHeader, String eTagHeader, CacheControl cacheControl, Instant retryAfter,
byte[] content, String contentType, String urlAfterRedirect) {
}
public record HttpResult(byte[] content, String contentType, String lastModifiedSince, String eTag, String urlAfterRedirect,
Duration validFor) {
}
private record HttpResponse(
int code,
String lastModifiedHeader,
String eTagHeader,
CacheControl cacheControl,
Instant retryAfter,
byte[] content,
String contentType,
String urlAfterRedirect) {}
public record HttpResult(
byte[] content,
String contentType,
String lastModifiedSince,
String eTag,
String urlAfterRedirect,
Duration validFor) {}
}

View File

@@ -1,106 +1,129 @@
package com.commafeed.backend;
import java.net.URI;
import java.util.regex.Pattern;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Strings;
import org.netpreserve.urlcanon.Canonicalizer;
import org.netpreserve.urlcanon.ParsedUrl;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import java.net.URI;
import java.util.Locale;
import java.util.regex.Pattern;
@UtilityClass
@Slf4j
public class Urls {
private static final String ESCAPED_QUESTION_MARK = Pattern.quote("?");
private static final Pattern QUESTION_MARK = Pattern.compile(Pattern.quote("?"));
public static boolean isHttp(String url) {
return url.startsWith("http://");
}
public static boolean isHttp(String url) {
if (url == null) {
return false;
}
public static boolean isHttps(String url) {
return url.startsWith("https://");
}
return url.toLowerCase(Locale.ROOT).startsWith("http://");
}
public static boolean isAbsolute(String url) {
return isHttp(url) || isHttps(url);
}
public static boolean isHttps(String url) {
if (url == null) {
return false;
}
/**
*
* @param relativeUrl
* the url of the entry
* @param feedLink
* the url of the feed as described in the feed
* @param feedUrl
* the url of the feed that we used to fetch the feed
* @return an absolute url pointing to the entry
*/
public static String toAbsolute(String relativeUrl, String feedLink, String feedUrl) {
String baseUrl = (feedLink != null && isAbsolute(feedLink)) ? feedLink : feedUrl;
if (baseUrl == null) {
return null;
}
return url.toLowerCase(Locale.ROOT).startsWith("https://");
}
try {
return URI.create(baseUrl).resolve(relativeUrl).toString();
} catch (IllegalArgumentException e) {
log.debug("Unable to create absolute url from relative url: {} base: {}", relativeUrl, baseUrl, e);
return null;
}
}
public static boolean isAbsolute(String url) {
return isHttp(url) || isHttps(url);
}
public static String removeTrailingSlash(String url) {
if (url == null) {
return null;
}
/** remove malicious 'javascript: 'URLs * */
public static String sanitize(String url) {
if (url == null) {
return null;
}
if (url.endsWith("/")) {
url = url.substring(0, url.length() - 1);
}
return url;
}
if (!isHttp(url) && !isHttps(url)) {
return null;
}
/**
* Normalize the url. The resulting url is not meant to be fetched but rather used as a mean to identify a feed and avoid duplicates
*/
public static String normalize(String url) {
if (url == null) {
return null;
}
return url;
}
ParsedUrl parsedUrl = ParsedUrl.parseUrl(url);
Canonicalizer.AGGRESSIVE.canonicalize(parsedUrl);
String normalized = parsedUrl.toString();
if (normalized == null) {
normalized = url;
}
/**
* @param relativeUrl the url of the entry
* @param feedLink the url of the feed as described in the feed
* @param feedUrl the url of the feed that we used to fetch the feed
* @return an absolute url pointing to the entry
*/
public static String toAbsolute(String relativeUrl, String feedLink, String feedUrl) {
String baseUrl = (feedLink != null && isAbsolute(feedLink)) ? feedLink : feedUrl;
if (baseUrl == null) {
return null;
}
// convert to lower case, the url probably won't work in some cases
// after that but we don't care we just want to compare urls to avoid
// duplicates
normalized = normalized.toLowerCase();
try {
return URI.create(baseUrl).resolve(relativeUrl).toString();
} catch (IllegalArgumentException e) {
log.debug(
"Unable to create absolute url from relative url: {} base: {}",
relativeUrl,
baseUrl,
e);
return null;
}
}
// store all urls as http
if (normalized.startsWith("https")) {
normalized = "http" + normalized.substring(5);
}
public static String removeTrailingSlash(String url) {
if (url == null) {
return null;
}
// remove the www. part
normalized = normalized.replace("//www.", "//");
if (url.endsWith("/")) {
url = url.substring(0, url.length() - 1);
}
return url;
}
// feedproxy redirects to feedburner
normalized = normalized.replace("feedproxy.google.com", "feeds.feedburner.com");
/**
* Normalize the url. The resulting url is not meant to be fetched but rather used as a mean to
* identify a feed and avoid duplicates
*/
public static String normalize(String url) {
if (url == null) {
return null;
}
// feedburner feeds have a special treatment
if (normalized.split(ESCAPED_QUESTION_MARK)[0].contains("feedburner.com")) {
normalized = normalized.replace("feeds2.feedburner.com", "feeds.feedburner.com");
normalized = normalized.split(ESCAPED_QUESTION_MARK)[0];
normalized = Strings.CS.removeEnd(normalized, "/");
}
ParsedUrl parsedUrl = ParsedUrl.parseUrl(url);
Canonicalizer.AGGRESSIVE.canonicalize(parsedUrl);
String normalized = parsedUrl.toString();
if (normalized == null) {
normalized = url;
}
return normalized;
}
// convert to lower case, the url probably won't work in some cases
// after that but we don't care we just want to compare urls to avoid
// duplicates
normalized = normalized.toLowerCase();
// store all urls as http
if (normalized.startsWith("https")) {
normalized = "http" + normalized.substring(5);
}
// remove the www. part
normalized = normalized.replace("//www.", "//");
// feedproxy redirects to feedburner
normalized = normalized.replace("feedproxy.google.com", "feeds.feedburner.com");
// feedburner feeds have a special treatment
if (QUESTION_MARK.split(normalized)[0].contains("feedburner.com")) {
normalized = normalized.replace("feeds2.feedburner.com", "feeds.feedburner.com");
normalized = QUESTION_MARK.split(normalized)[0];
normalized = Strings.CS.removeEnd(normalized, "/");
}
return normalized;
}
}

View File

@@ -1,71 +1,78 @@
package com.commafeed.backend.dao;
import java.util.List;
import java.util.Objects;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.QFeedCategory;
import com.commafeed.backend.model.QUser;
import com.commafeed.backend.model.User;
import com.querydsl.core.types.Predicate;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import java.util.List;
import java.util.Objects;
@Singleton
public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
private static final QFeedCategory CATEGORY = QFeedCategory.feedCategory;
private static final QFeedCategory CATEGORY = QFeedCategory.feedCategory;
public FeedCategoryDAO(EntityManager entityManager) {
super(entityManager, FeedCategory.class);
}
public FeedCategoryDAO(EntityManager entityManager) {
super(entityManager, FeedCategory.class);
}
public List<FeedCategory> findAll(User user) {
return query().selectFrom(CATEGORY).where(CATEGORY.user.eq(user)).join(CATEGORY.user, QUser.user).fetchJoin().fetch();
}
public List<FeedCategory> findAll(User user) {
return query().selectFrom(CATEGORY)
.where(CATEGORY.user.eq(user))
.join(CATEGORY.user, QUser.user)
.fetchJoin()
.fetch();
}
public FeedCategory findById(User user, Long id) {
return query().selectFrom(CATEGORY).where(CATEGORY.user.eq(user), CATEGORY.id.eq(id)).fetchOne();
}
public FeedCategory findById(User user, Long id) {
return query().selectFrom(CATEGORY)
.where(CATEGORY.user.eq(user), CATEGORY.id.eq(id))
.fetchOne();
}
public FeedCategory findByName(User user, String name, FeedCategory parent) {
Predicate parentPredicate;
if (parent == null) {
parentPredicate = CATEGORY.parent.isNull();
} else {
parentPredicate = CATEGORY.parent.eq(parent);
}
return query().selectFrom(CATEGORY).where(CATEGORY.user.eq(user), CATEGORY.name.eq(name), parentPredicate).fetchOne();
}
public FeedCategory findByName(User user, String name, FeedCategory parent) {
Predicate parentPredicate;
if (parent == null) {
parentPredicate = CATEGORY.parent.isNull();
} else {
parentPredicate = CATEGORY.parent.eq(parent);
}
return query().selectFrom(CATEGORY)
.where(CATEGORY.user.eq(user), CATEGORY.name.eq(name), parentPredicate)
.fetchOne();
}
public List<FeedCategory> findByParent(User user, FeedCategory parent) {
Predicate parentPredicate;
if (parent == null) {
parentPredicate = CATEGORY.parent.isNull();
} else {
parentPredicate = CATEGORY.parent.eq(parent);
}
return query().selectFrom(CATEGORY).where(CATEGORY.user.eq(user), parentPredicate).fetch();
}
public List<FeedCategory> findByParent(User user, FeedCategory parent) {
Predicate parentPredicate;
if (parent == null) {
parentPredicate = CATEGORY.parent.isNull();
} else {
parentPredicate = CATEGORY.parent.eq(parent);
}
return query().selectFrom(CATEGORY).where(CATEGORY.user.eq(user), parentPredicate).fetch();
}
public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) {
return findAll(user).stream().filter(c -> isChild(c, parent)).toList();
}
private boolean isChild(FeedCategory child, FeedCategory parent) {
if (parent == null) {
return true;
}
boolean isChild = false;
while (child != null) {
if (Objects.equals(child.getId(), parent.getId())) {
isChild = true;
break;
}
child = child.getParent();
}
return isChild;
}
public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) {
return findAll(user).stream().filter(c -> isChild(c, parent)).toList();
}
private boolean isChild(FeedCategory child, FeedCategory parent) {
if (parent == null) {
return true;
}
boolean isChild = false;
while (child != null) {
if (Objects.equals(child.getId(), parent.getId())) {
isChild = true;
break;
}
child = child.getParent();
}
return isChild;
}
}

View File

@@ -1,64 +1,72 @@
package com.commafeed.backend.dao;
import java.time.Instant;
import java.util.List;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import org.apache.commons.lang3.Strings;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.QFeed;
import com.commafeed.backend.model.QFeedSubscription;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQuery;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import org.apache.commons.lang3.Strings;
import java.time.Instant;
import java.util.List;
@Singleton
public class FeedDAO extends GenericDAO<Feed> {
private static final QFeed FEED = QFeed.feed;
private static final QFeedSubscription SUBSCRIPTION = QFeedSubscription.feedSubscription;
private static final QFeed FEED = QFeed.feed;
private static final QFeedSubscription SUBSCRIPTION = QFeedSubscription.feedSubscription;
public FeedDAO(EntityManager entityManager) {
super(entityManager, Feed.class);
}
public FeedDAO(EntityManager entityManager) {
super(entityManager, Feed.class);
}
public List<Feed> findByIds(List<Long> id) {
return query().selectFrom(FEED).where(FEED.id.in(id)).fetch();
}
public List<Feed> findByIds(List<Long> id) {
return query().selectFrom(FEED).where(FEED.id.in(id)).fetch();
}
public List<Feed> findNextUpdatable(int count, Instant lastLoginThreshold) {
JPAQuery<Feed> query = query().selectFrom(FEED)
.distinct()
// join on subscriptions to only refresh feeds that have subscribers
.join(SUBSCRIPTION)
.on(SUBSCRIPTION.feed.eq(FEED))
.where(FEED.disabledUntil.isNull().or(FEED.disabledUntil.lt(Instant.now())));
public List<Feed> findNextUpdatable(int count, Instant lastLoginThreshold) {
JPAQuery<Feed> query =
query().selectFrom(FEED)
.distinct()
// join on subscriptions to only refresh feeds that have subscribers
.join(SUBSCRIPTION)
.on(SUBSCRIPTION.feed.eq(FEED))
.where(
FEED.disabledUntil
.isNull()
.or(FEED.disabledUntil.lt(Instant.now())));
if (lastLoginThreshold != null) {
query.join(SUBSCRIPTION.user).where(SUBSCRIPTION.user.lastLogin.gt(lastLoginThreshold));
}
if (lastLoginThreshold != null) {
query.join(SUBSCRIPTION.user).where(SUBSCRIPTION.user.lastLogin.gt(lastLoginThreshold));
}
return query.orderBy(FEED.disabledUntil.asc()).limit(count).fetch();
}
return query.orderBy(FEED.disabledUntil.asc()).limit(count).fetch();
}
public void setDisabledUntil(List<Long> feedIds, Instant date) {
updateQuery(FEED).set(FEED.disabledUntil, date).where(FEED.id.in(feedIds)).execute();
}
public void setDisabledUntil(List<Long> feedIds, Instant date) {
updateQuery(FEED).set(FEED.disabledUntil, date).where(FEED.id.in(feedIds)).execute();
}
public Feed findByUrl(String normalizedUrl, String normalizedUrlHash) {
return query().selectFrom(FEED)
.where(FEED.normalizedUrlHash.eq(normalizedUrlHash))
.fetch()
.stream()
.filter(f -> Strings.CS.equals(normalizedUrl, f.getNormalizedUrl()))
.findFirst()
.orElse(null);
}
public Feed findByUrl(String normalizedUrl, String normalizedUrlHash) {
return query()
.selectFrom(FEED)
.where(FEED.normalizedUrlHash.eq(normalizedUrlHash))
.fetch()
.stream()
.filter(f -> Strings.CS.equals(normalizedUrl, f.getNormalizedUrl()))
.findFirst()
.orElse(null);
}
public List<Feed> findWithoutSubscriptions(int max) {
QFeedSubscription sub = QFeedSubscription.feedSubscription;
return query().selectFrom(FEED).where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(FEED)).notExists()).limit(max).fetch();
}
public List<Feed> findWithoutSubscriptions(int max) {
QFeedSubscription sub = QFeedSubscription.feedSubscription;
return query().selectFrom(FEED)
.where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(FEED)).notExists())
.limit(max)
.fetch();
}
}

View File

@@ -1,36 +1,40 @@
package com.commafeed.backend.dao;
import java.util.List;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.FeedEntryContent;
import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedEntryContent;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import java.util.List;
@Singleton
public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
private static final QFeedEntryContent CONTENT = QFeedEntryContent.feedEntryContent;
private static final QFeedEntry ENTRY = QFeedEntry.feedEntry;
private static final QFeedEntryContent CONTENT = QFeedEntryContent.feedEntryContent;
private static final QFeedEntry ENTRY = QFeedEntry.feedEntry;
public FeedEntryContentDAO(EntityManager entityManager) {
super(entityManager, FeedEntryContent.class);
}
public FeedEntryContentDAO(EntityManager entityManager) {
super(entityManager, FeedEntryContent.class);
}
public List<FeedEntryContent> findExisting(String contentHash, String titleHash) {
return query().select(CONTENT).from(CONTENT).where(CONTENT.contentHash.eq(contentHash), CONTENT.titleHash.eq(titleHash)).fetch();
}
public List<FeedEntryContent> findExisting(String contentHash, String titleHash) {
return query().select(CONTENT)
.from(CONTENT)
.where(CONTENT.contentHash.eq(contentHash), CONTENT.titleHash.eq(titleHash))
.fetch();
}
public long deleteWithoutEntries(int max) {
List<Long> ids = query().select(CONTENT.id)
.from(CONTENT)
.leftJoin(ENTRY)
.on(ENTRY.content.id.eq(CONTENT.id))
.where(ENTRY.id.isNull())
.limit(max)
.fetch();
return deleteQuery(CONTENT).where(CONTENT.id.in(ids)).execute();
}
public long deleteWithoutEntries(int max) {
List<Long> ids =
query().select(CONTENT.id)
.from(CONTENT)
.leftJoin(ENTRY)
.on(ENTRY.content.id.eq(CONTENT.id))
.where(ENTRY.id.isNull())
.limit(max)
.fetch();
return deleteQuery(CONTENT).where(CONTENT.id.in(ids)).execute();
}
}

View File

@@ -1,82 +1,106 @@
package com.commafeed.backend.dao;
import java.time.Instant;
import java.util.List;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.QFeedEntry;
import com.google.common.collect.Lists;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQuery;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Singleton
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
private static final QFeedEntry ENTRY = QFeedEntry.feedEntry;
private static final QFeedEntry ENTRY = QFeedEntry.feedEntry;
private static final int IN_CLAUSE_BATCH_SIZE = 1000;
public FeedEntryDAO(EntityManager entityManager) {
super(entityManager, FeedEntry.class);
}
public FeedEntryDAO(EntityManager entityManager) {
super(entityManager, FeedEntry.class);
}
public FeedEntry findExisting(String guidHash, Feed feed) {
return query().select(ENTRY).from(ENTRY).where(ENTRY.guidHash.eq(guidHash), ENTRY.feed.eq(feed)).limit(1).fetchOne();
}
public FeedEntry findExisting(String guidHash, Feed feed) {
return query().select(ENTRY)
.from(ENTRY)
.where(ENTRY.guidHash.eq(guidHash), ENTRY.feed.eq(feed))
.limit(1)
.fetchOne();
}
public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max, boolean keepStarredEntries) {
NumberExpression<Long> count = ENTRY.id.count();
JPAQuery<Tuple> query = query().select(ENTRY.feed.id, count).from(ENTRY);
public Set<String> findExistingGuidHashes(Set<String> guidHashes, Feed feed) {
if (guidHashes.isEmpty()) {
return Set.of();
}
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
Set<String> result = new HashSet<>();
for (List<String> batch :
Lists.partition(new ArrayList<>(guidHashes), IN_CLAUSE_BATCH_SIZE)) {
result.addAll(
query().select(ENTRY.guidHash)
.from(ENTRY)
.where(ENTRY.feed.eq(feed), ENTRY.guidHash.in(batch))
.fetch());
}
return result;
}
return query.groupBy(ENTRY.feed)
.having(count.gt(maxCapacity))
.limit(max)
.fetch()
.stream()
.map(t -> new FeedCapacity(t.get(ENTRY.feed.id), t.get(count)))
.toList();
}
public List<FeedCapacity> findFeedsExceedingCapacity(
long maxCapacity, long max, boolean keepStarredEntries) {
NumberExpression<Long> count = ENTRY.id.count();
JPAQuery<Tuple> query = query().select(ENTRY.feed.id, count).from(ENTRY);
public int delete(Long feedId, long max) {
List<FeedEntry> list = query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).limit(max).fetch();
return delete(list);
}
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
/**
* Delete entries older than a certain date
*/
public int deleteEntriesOlderThan(Instant olderThan, long max, boolean keepStarredEntries) {
JPAQuery<FeedEntry> query = query().selectFrom(ENTRY)
.where(ENTRY.published.lt(olderThan))
.orderBy(ENTRY.published.asc())
.limit(max);
return query.groupBy(ENTRY.feed).having(count.gt(maxCapacity)).limit(max).fetch().stream()
.map(t -> new FeedCapacity(t.get(ENTRY.feed.id), t.get(count)))
.toList();
}
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
public int delete(Long feedId, long max) {
List<FeedEntry> list =
query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).limit(max).fetch();
return delete(list);
}
return delete(query.fetch());
}
/** Delete entries older than a certain date */
public int deleteEntriesOlderThan(Instant olderThan, long max, boolean keepStarredEntries) {
JPAQuery<FeedEntry> query =
query().selectFrom(ENTRY)
.where(ENTRY.published.lt(olderThan))
.orderBy(ENTRY.published.asc())
.limit(max);
/**
* Delete the oldest entries of a feed
*/
public int deleteOldEntries(Long feedId, long max, boolean keepStarredEntries) {
JPAQuery<FeedEntry> query = query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).orderBy(ENTRY.published.asc()).limit(max);
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
return delete(query.fetch());
}
return delete(query.fetch());
}
/** Delete the oldest entries of a feed */
public int deleteOldEntries(Long feedId, long max, boolean keepStarredEntries) {
JPAQuery<FeedEntry> query =
query().selectFrom(ENTRY)
.where(ENTRY.feed.id.eq(feedId))
.orderBy(ENTRY.published.asc())
.limit(max);
public record FeedCapacity(Long id, Long capacity) {
}
if (keepStarredEntries) {
query.where(Predicates.isNotStarred(ENTRY));
}
return delete(query.fetch());
}
public record FeedCapacity(Long id, Long capacity) {}
}

View File

@@ -1,16 +1,5 @@
package com.commafeed.backend.dao;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import org.apache.commons.collections4.CollectionUtils;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.feed.FeedEntryKeyword;
import com.commafeed.backend.feed.FeedEntryKeyword.Mode;
@@ -33,263 +22,318 @@ import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQuery;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import org.apache.commons.collections4.CollectionUtils;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Singleton
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
private static final QFeedEntryStatus STATUS = QFeedEntryStatus.feedEntryStatus;
private static final QFeedEntry ENTRY = QFeedEntry.feedEntry;
private static final QFeed FEED = QFeed.feed;
private static final QFeedEntryContent CONTENT = QFeedEntryContent.feedEntryContent;
private static final QFeedEntryTag TAG = QFeedEntryTag.feedEntryTag;
private static final QFeedSubscription SUBSCRIPTION = QFeedSubscription.feedSubscription;
private static final QFeedEntryStatus STATUS = QFeedEntryStatus.feedEntryStatus;
private static final QFeedEntry ENTRY = QFeedEntry.feedEntry;
private static final QFeed FEED = QFeed.feed;
private static final QFeedEntryContent CONTENT = QFeedEntryContent.feedEntryContent;
private static final QFeedEntryTag TAG = QFeedEntryTag.feedEntryTag;
private static final QFeedSubscription SUBSCRIPTION = QFeedSubscription.feedSubscription;
private final FeedEntryTagDAO feedEntryTagDAO;
private final CommaFeedConfiguration config;
private final FeedEntryTagDAO feedEntryTagDAO;
private final CommaFeedConfiguration config;
public FeedEntryStatusDAO(EntityManager entityManager, FeedEntryTagDAO feedEntryTagDAO, CommaFeedConfiguration config) {
super(entityManager, FeedEntryStatus.class);
this.feedEntryTagDAO = feedEntryTagDAO;
this.config = config;
}
public FeedEntryStatusDAO(
EntityManager entityManager,
FeedEntryTagDAO feedEntryTagDAO,
CommaFeedConfiguration config) {
super(entityManager, FeedEntryStatus.class);
this.feedEntryTagDAO = feedEntryTagDAO;
this.config = config;
}
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
List<FeedEntryStatus> statuses = query().selectFrom(STATUS).where(STATUS.entry.eq(entry), STATUS.subscription.eq(sub)).fetch();
FeedEntryStatus status = statuses.stream().findFirst().orElse(null);
return handleStatus(user, status, sub, entry);
}
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
List<FeedEntryStatus> statuses =
query().selectFrom(STATUS)
.where(STATUS.entry.eq(entry), STATUS.subscription.eq(sub))
.fetch();
FeedEntryStatus status = statuses.stream().findFirst().orElse(null);
return handleStatus(user, status, sub, entry);
}
/**
* creates an artificial "unread" status if status is null
*/
private FeedEntryStatus handleStatus(User user, FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
if (status == null) {
Instant statusesInstantThreshold = config.database().cleanup().statusesInstantThreshold();
boolean read = statusesInstantThreshold != null && entry.getPublished().isBefore(statusesInstantThreshold);
status = new FeedEntryStatus(user, sub, entry);
status.setRead(read);
status.setMarkable(!read);
} else {
status.setMarkable(true);
}
return status;
}
/** creates an artificial "unread" status if status is null */
private FeedEntryStatus handleStatus(
User user, FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
if (status == null) {
Instant statusesInstantThreshold =
config.database().cleanup().statusesInstantThreshold();
boolean read =
statusesInstantThreshold != null
&& entry.getPublished().isBefore(statusesInstantThreshold);
status = new FeedEntryStatus(user, sub, entry);
status.setRead(read);
status.setMarkable(!read);
} else {
status.setMarkable(true);
}
return status;
}
private void fetchTags(User user, List<FeedEntryStatus> statuses) {
Map<Long, List<FeedEntryTag>> tagsByEntryIds = feedEntryTagDAO.findByEntries(user,
statuses.stream().map(FeedEntryStatus::getEntry).toList());
for (FeedEntryStatus status : statuses) {
List<FeedEntryTag> tags = tagsByEntryIds.get(status.getEntry().getId());
status.setTags(tags == null ? List.of() : tags);
}
}
private void fetchTags(User user, List<FeedEntryStatus> statuses) {
Map<Long, List<FeedEntryTag>> tagsByEntryIds =
feedEntryTagDAO.findByEntries(
user, statuses.stream().map(FeedEntryStatus::getEntry).toList());
for (FeedEntryStatus status : statuses) {
List<FeedEntryTag> tags = tagsByEntryIds.get(status.getEntry().getId());
status.setTags(tags == null ? List.of() : tags);
}
}
public List<FeedEntryStatus> findStarred(User user, Instant newerThan, int offset, int limit, ReadingOrder order,
boolean includeContent) {
JPAQuery<FeedEntryStatus> query = query().selectFrom(STATUS).where(STATUS.user.eq(user), STATUS.starred.isTrue());
if (includeContent) {
query.join(STATUS.entry).fetchJoin();
query.join(STATUS.entry.content).fetchJoin();
}
public List<FeedEntryStatus> findStarred(
User user,
List<FeedEntryKeyword> keywords,
Instant newerThan,
int offset,
int limit,
ReadingOrder order,
boolean includeContent) {
JPAQuery<FeedEntryStatus> query =
query().selectFrom(STATUS).where(STATUS.user.eq(user), STATUS.starred.isTrue());
if (includeContent || CollectionUtils.isNotEmpty(keywords)) {
query.join(STATUS.entry).fetchJoin();
query.join(STATUS.entry.content, CONTENT).fetchJoin();
}
if (newerThan != null) {
query.where(STATUS.entryInserted.gt(newerThan));
}
if (CollectionUtils.isNotEmpty(keywords)) {
applyKeywordsFilter(query, keywords);
}
if (order == ReadingOrder.ASC) {
query.orderBy(STATUS.entryPublished.asc(), STATUS.id.asc());
} else {
query.orderBy(STATUS.entryPublished.desc(), STATUS.id.desc());
}
if (newerThan != null) {
query.where(STATUS.entryInserted.gt(newerThan));
}
if (offset > -1) {
query.offset(offset);
}
if (order == ReadingOrder.ASC) {
query.orderBy(STATUS.entryPublished.asc(), STATUS.id.asc());
} else {
query.orderBy(STATUS.entryPublished.desc(), STATUS.id.desc());
}
if (limit > -1) {
query.limit(limit);
}
if (offset > -1) {
query.offset(offset);
}
setTimeout(query, config.database().queryTimeout());
if (limit > -1) {
query.limit(limit);
}
List<FeedEntryStatus> statuses = query.fetch();
statuses.forEach(s -> s.setMarkable(true));
if (includeContent) {
fetchTags(user, statuses);
}
setTimeout(query, config.database().queryTimeout());
return statuses;
}
List<FeedEntryStatus> statuses = query.fetch();
statuses.forEach(s -> s.setMarkable(true));
if (includeContent) {
fetchTags(user, statuses);
}
public List<FeedEntryStatus> findBySubscriptions(User user, List<FeedSubscription> subs, boolean unreadOnly,
List<FeedEntryKeyword> keywords, Instant newerThan, int offset, int limit, ReadingOrder order, boolean includeContent,
String tag, Long minEntryId, Long maxEntryId) {
Map<Long, List<FeedSubscription>> subsByFeedId = subs.stream().collect(Collectors.groupingBy(s -> s.getFeed().getId()));
return statuses;
}
JPAQuery<Tuple> query = query().select(ENTRY, STATUS).from(ENTRY);
query.leftJoin(ENTRY.statuses, STATUS).on(STATUS.subscription.in(subs));
query.where(ENTRY.feed.id.in(subsByFeedId.keySet()));
public List<FeedEntryStatus> findBySubscriptions(
User user,
List<FeedSubscription> subs,
boolean unreadOnly,
List<FeedEntryKeyword> keywords,
Instant newerThan,
int offset,
int limit,
ReadingOrder order,
boolean includeContent,
String tag,
Long minEntryId,
Long maxEntryId) {
Map<Long, List<FeedSubscription>> subsByFeedId =
subs.stream().collect(Collectors.groupingBy(s -> s.getFeed().getId()));
if (includeContent || CollectionUtils.isNotEmpty(keywords)) {
query.join(ENTRY.content, CONTENT).fetchJoin();
}
if (CollectionUtils.isNotEmpty(keywords)) {
for (FeedEntryKeyword keyword : keywords) {
BooleanBuilder or = new BooleanBuilder();
or.or(CONTENT.content.containsIgnoreCase(keyword.keyword()));
or.or(CONTENT.title.containsIgnoreCase(keyword.keyword()));
if (keyword.mode() == Mode.EXCLUDE) {
or.not();
}
query.where(or);
}
}
JPAQuery<Tuple> query = query().select(ENTRY, STATUS).from(ENTRY);
query.leftJoin(ENTRY.statuses, STATUS).on(STATUS.subscription.in(subs));
query.where(ENTRY.feed.id.in(subsByFeedId.keySet()));
if (unreadOnly && tag == null) {
query.where(buildUnreadPredicate());
}
if (includeContent || CollectionUtils.isNotEmpty(keywords)) {
query.join(ENTRY.content, CONTENT).fetchJoin();
}
if (tag != null) {
BooleanBuilder and = new BooleanBuilder();
and.and(TAG.user.id.eq(user.getId()));
and.and(TAG.name.eq(tag));
query.join(ENTRY.tags, TAG).on(and);
}
if (CollectionUtils.isNotEmpty(keywords)) {
applyKeywordsFilter(query, keywords);
}
if (newerThan != null) {
query.where(ENTRY.inserted.goe(newerThan));
}
if (unreadOnly && tag == null) {
query.where(buildUnreadPredicate());
}
if (minEntryId != null) {
query.where(ENTRY.id.gt(minEntryId));
}
if (tag != null) {
BooleanBuilder and = new BooleanBuilder();
and.and(TAG.user.id.eq(user.getId()));
and.and(TAG.name.eq(tag));
query.join(ENTRY.tags, TAG).on(and);
}
if (maxEntryId != null) {
query.where(ENTRY.id.lt(maxEntryId));
}
if (newerThan != null) {
query.where(ENTRY.inserted.goe(newerThan));
}
if (order != null) {
if (order == ReadingOrder.ASC) {
query.orderBy(ENTRY.published.asc(), ENTRY.id.asc());
} else {
query.orderBy(ENTRY.published.desc(), ENTRY.id.desc());
}
}
if (minEntryId != null) {
query.where(ENTRY.id.gt(minEntryId));
}
if (offset > -1) {
query.offset(offset);
}
if (maxEntryId != null) {
query.where(ENTRY.id.lt(maxEntryId));
}
if (limit > -1) {
query.limit(limit);
}
if (order != null) {
if (order == ReadingOrder.ASC) {
query.orderBy(ENTRY.published.asc(), ENTRY.id.asc());
} else {
query.orderBy(ENTRY.published.desc(), ENTRY.id.desc());
}
}
setTimeout(query, config.database().queryTimeout());
if (offset > -1) {
query.offset(offset);
}
List<FeedEntryStatus> statuses = new ArrayList<>();
List<Tuple> tuples = query.fetch();
for (Tuple tuple : tuples) {
FeedEntry e = tuple.get(ENTRY);
FeedEntryStatus s = tuple.get(STATUS);
for (FeedSubscription sub : subsByFeedId.get(e.getFeed().getId())) {
statuses.add(handleStatus(user, s, sub, e));
}
}
if (limit > -1) {
query.limit(limit);
}
if (includeContent) {
fetchTags(user, statuses);
}
setTimeout(query, config.database().queryTimeout());
return statuses;
}
List<FeedEntryStatus> statuses = new ArrayList<>();
List<Tuple> tuples = query.fetch();
for (Tuple tuple : tuples) {
FeedEntry e = tuple.get(ENTRY);
FeedEntryStatus s = tuple.get(STATUS);
for (FeedSubscription sub : subsByFeedId.get(e.getFeed().getId())) {
statuses.add(handleStatus(user, s, sub, e));
}
}
public UnreadCount getUnreadCount(FeedSubscription sub) {
JPAQuery<Tuple> query = query().select(ENTRY.count(), ENTRY.published.max())
.from(ENTRY)
.leftJoin(ENTRY.statuses, STATUS)
.on(STATUS.subscription.eq(sub))
.where(ENTRY.feed.eq(sub.getFeed()))
.where(buildUnreadPredicate());
if (includeContent) {
fetchTags(user, statuses);
}
Tuple tuple = query.fetchOne();
Long count = tuple.get(ENTRY.count());
Instant published = tuple.get(ENTRY.published.max());
return new UnreadCount(sub.getId(), count == null ? 0 : count, published);
}
return statuses;
}
private BooleanBuilder buildUnreadPredicate() {
BooleanBuilder or = new BooleanBuilder();
or.or(STATUS.read.isNull());
or.or(STATUS.read.isFalse());
private void applyKeywordsFilter(JPAQuery<?> query, List<FeedEntryKeyword> keywords) {
for (FeedEntryKeyword keyword : keywords) {
BooleanBuilder or = new BooleanBuilder();
or.or(CONTENT.content.containsIgnoreCase(keyword.keyword()));
or.or(CONTENT.title.containsIgnoreCase(keyword.keyword()));
if (keyword.mode() == Mode.EXCLUDE) {
or.not();
}
query.where(or);
}
}
Instant statusesInstantThreshold = config.database().cleanup().statusesInstantThreshold();
if (statusesInstantThreshold != null) {
return or.and(ENTRY.published.goe(statusesInstantThreshold));
} else {
return or;
}
}
public UnreadCount getUnreadCount(FeedSubscription sub) {
JPAQuery<Tuple> query =
query().select(ENTRY.count(), ENTRY.published.max())
.from(ENTRY)
.leftJoin(ENTRY.statuses, STATUS)
.on(STATUS.subscription.eq(sub))
.where(ENTRY.feed.eq(sub.getFeed()))
.where(buildUnreadPredicate());
public long deleteOldStatuses(Instant olderThan, int limit) {
List<Long> ids = query().select(STATUS.id)
.from(STATUS)
.where(STATUS.entryInserted.lt(olderThan), STATUS.starred.isFalse())
.limit(limit)
.fetch();
return deleteQuery(STATUS).where(STATUS.id.in(ids)).execute();
}
Tuple tuple = query.fetchOne();
Long count = tuple.get(ENTRY.count());
Instant published = tuple.get(ENTRY.published.max());
return new UnreadCount(sub.getId(), count == null ? 0 : count, published);
}
public long autoMarkAsRead(int limit) {
Instant now = Instant.now();
private BooleanBuilder buildUnreadPredicate() {
BooleanBuilder or = new BooleanBuilder();
or.or(STATUS.read.isNull());
or.or(STATUS.read.isFalse());
BooleanBuilder where = new BooleanBuilder();
where.and(SUBSCRIPTION.autoMarkAsReadAfterDays.isNotNull());
where.and(SUBSCRIPTION.autoMarkAsReadAfterDays.gt(0));
Instant statusesInstantThreshold = config.database().cleanup().statusesInstantThreshold();
if (statusesInstantThreshold != null) {
return or.and(ENTRY.published.goe(statusesInstantThreshold));
} else {
return or;
}
}
NumberExpression<Integer> daysDiff = Expressions.numberTemplate(Integer.class, "TIMESTAMPDIFF(DAY, {0}, {1})", ENTRY.published,
now);
where.and(daysDiff.goe(SUBSCRIPTION.autoMarkAsReadAfterDays));
public long deleteOldStatuses(Instant olderThan, int limit) {
List<Long> ids =
query().select(STATUS.id)
.from(STATUS)
.where(STATUS.entryInserted.lt(olderThan), STATUS.starred.isFalse())
.limit(limit)
.fetch();
return deleteQuery(STATUS).where(STATUS.id.in(ids)).execute();
}
where.and(buildUnreadPredicate());
public long autoMarkAsRead(int limit) {
Instant now = Instant.now();
List<Tuple> tuples = query().select(ENTRY, STATUS, SUBSCRIPTION)
.from(ENTRY)
.join(ENTRY.feed, FEED)
.join(SUBSCRIPTION)
.on(SUBSCRIPTION.feed.eq(FEED))
.leftJoin(ENTRY.statuses, STATUS)
.on(STATUS.subscription.eq(SUBSCRIPTION))
.where(where)
.limit(limit)
.fetch();
BooleanBuilder where = new BooleanBuilder();
where.and(SUBSCRIPTION.autoMarkAsReadAfterDays.isNotNull());
where.and(SUBSCRIPTION.autoMarkAsReadAfterDays.gt(0));
long updated = 0;
NumberExpression<Integer> daysDiff =
Expressions.numberTemplate(
Integer.class, "TIMESTAMPDIFF(DAY, {0}, {1})", ENTRY.published, now);
where.and(daysDiff.goe(SUBSCRIPTION.autoMarkAsReadAfterDays));
// Update existing statuses
List<Long> statusIdsToUpdate = tuples.stream()
.map(t -> t.get(STATUS))
.filter(s -> s != null && s.getId() != null)
.map(FeedEntryStatus::getId)
.distinct()
.toList();
where.and(buildUnreadPredicate());
if (!statusIdsToUpdate.isEmpty()) {
updated += updateQuery(STATUS).where(STATUS.id.in(statusIdsToUpdate)).set(STATUS.read, true).execute();
}
List<Tuple> tuples =
query().select(ENTRY, STATUS, SUBSCRIPTION)
.from(ENTRY)
.join(ENTRY.feed, FEED)
.join(SUBSCRIPTION)
.on(SUBSCRIPTION.feed.eq(FEED))
.leftJoin(ENTRY.statuses, STATUS)
.on(STATUS.subscription.eq(SUBSCRIPTION))
.where(where)
.limit(limit)
.fetch();
// Insert new statuses for entries without existing status
for (Tuple tuple : tuples) {
FeedEntryStatus status = tuple.get(STATUS);
if (status == null || status.getId() == null) {
FeedEntry entry = tuple.get(ENTRY);
FeedSubscription sub = tuple.get(SUBSCRIPTION);
FeedEntryStatus newStatus = new FeedEntryStatus(sub.getUser(), sub, entry);
newStatus.setRead(true);
persist(newStatus);
updated++;
}
}
long updated = 0;
return updated;
}
// Update existing statuses
List<Long> statusIdsToUpdate =
tuples.stream()
.map(t -> t.get(STATUS))
.filter(s -> s != null && s.getId() != null)
.map(FeedEntryStatus::getId)
.distinct()
.toList();
if (!statusIdsToUpdate.isEmpty()) {
updated +=
updateQuery(STATUS)
.where(STATUS.id.in(statusIdsToUpdate))
.set(STATUS.read, true)
.execute();
}
// Insert new statuses for entries without existing status
for (Tuple tuple : tuples) {
FeedEntryStatus status = tuple.get(STATUS);
if (status == null || status.getId() == null) {
FeedEntry entry = tuple.get(ENTRY);
FeedSubscription sub = tuple.get(SUBSCRIPTION);
FeedEntryStatus newStatus = new FeedEntryStatus(sub.getUser(), sub, entry);
newStatus.setRead(true);
persist(newStatus);
updated++;
}
}
return updated;
}
}

View File

@@ -1,39 +1,40 @@
package com.commafeed.backend.dao;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryTag;
import com.commafeed.backend.model.QFeedEntryTag;
import com.commafeed.backend.model.User;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Singleton
public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
private static final QFeedEntryTag TAG = QFeedEntryTag.feedEntryTag;
private static final QFeedEntryTag TAG = QFeedEntryTag.feedEntryTag;
public FeedEntryTagDAO(EntityManager entityManager) {
super(entityManager, FeedEntryTag.class);
}
public FeedEntryTagDAO(EntityManager entityManager) {
super(entityManager, FeedEntryTag.class);
}
public List<String> findByUser(User user) {
return query().selectDistinct(TAG.name).from(TAG).where(TAG.user.eq(user)).fetch();
}
public List<String> findByUser(User user) {
return query().selectDistinct(TAG.name).from(TAG).where(TAG.user.eq(user)).fetch();
}
public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) {
return query().selectFrom(TAG).where(TAG.user.eq(user), TAG.entry.eq(entry)).fetch();
}
public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) {
return query().selectFrom(TAG).where(TAG.user.eq(user), TAG.entry.eq(entry)).fetch();
}
public Map<Long, List<FeedEntryTag>> findByEntries(User user, List<FeedEntry> entries) {
return query().selectFrom(TAG)
.where(TAG.user.eq(user), TAG.entry.in(entries))
.fetch()
.stream()
.collect(Collectors.groupingBy(t -> t.getEntry().getId()));
}
public Map<Long, List<FeedEntryTag>> findByEntries(User user, List<FeedEntry> entries) {
return query()
.selectFrom(TAG)
.where(TAG.user.eq(user), TAG.entry.in(entries))
.fetch()
.stream()
.collect(Collectors.groupingBy(t -> t.getEntry().getId()));
}
}

View File

@@ -1,9 +1,13 @@
package com.commafeed.backend.dao;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import com.commafeed.backend.model.AbstractModel;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.Models;
import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.User;
import com.querydsl.jpa.JPQLQuery;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
@@ -15,117 +19,128 @@ import org.hibernate.event.spi.PostCommitInsertEventListener;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.persister.entity.EntityPersister;
import com.commafeed.backend.model.AbstractModel;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.Models;
import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.User;
import com.querydsl.jpa.JPQLQuery;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@Singleton
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
private static final QFeedSubscription SUBSCRIPTION = QFeedSubscription.feedSubscription;
private static final QFeedSubscription SUBSCRIPTION = QFeedSubscription.feedSubscription;
private final EntityManager entityManager;
private final EntityManager entityManager;
public FeedSubscriptionDAO(EntityManager entityManager) {
super(entityManager, FeedSubscription.class);
this.entityManager = entityManager;
}
public FeedSubscriptionDAO(EntityManager entityManager) {
super(entityManager, FeedSubscription.class);
this.entityManager = entityManager;
}
public void onPostCommitInsert(Consumer<FeedSubscription> consumer) {
entityManager.unwrap(SharedSessionContractImplementor.class)
.getFactory()
.getServiceRegistry()
.getService(EventListenerRegistry.class)
.getEventListenerGroup(EventType.POST_COMMIT_INSERT)
.appendListener(new PostCommitInsertEventListener() {
@Override
public void onPostInsert(PostInsertEvent event) {
if (event.getEntity() instanceof FeedSubscription s) {
consumer.accept(s);
}
}
public void onPostCommitInsert(Consumer<FeedSubscription> consumer) {
entityManager
.unwrap(SharedSessionContractImplementor.class)
.getFactory()
.getServiceRegistry()
.getService(EventListenerRegistry.class)
.getEventListenerGroup(EventType.POST_COMMIT_INSERT)
.appendListener(
new PostCommitInsertEventListener() {
@Override
public void onPostInsert(PostInsertEvent event) {
if (event.getEntity() instanceof FeedSubscription s) {
consumer.accept(s);
}
}
@Override
public boolean requiresPostCommitHandling(EntityPersister persister) {
return true;
}
@Override
public boolean requiresPostCommitHandling(EntityPersister persister) {
return true;
}
@Override
public void onPostInsertCommitFailed(PostInsertEvent event) {
// do nothing
}
});
}
@Override
public void onPostInsertCommitFailed(PostInsertEvent event) {
// do nothing
}
});
}
public FeedSubscription findById(User user, Long id) {
List<FeedSubscription> subs = query().selectFrom(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user), SUBSCRIPTION.id.eq(id))
.leftJoin(SUBSCRIPTION.feed)
.fetchJoin()
.leftJoin(SUBSCRIPTION.category)
.fetchJoin()
.fetch();
FeedSubscription sub = subs.stream().findFirst().orElse(null);
return initRelations(sub);
}
public FeedSubscription findById(User user, Long id) {
List<FeedSubscription> subs =
query().selectFrom(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user), SUBSCRIPTION.id.eq(id))
.leftJoin(SUBSCRIPTION.feed)
.fetchJoin()
.leftJoin(SUBSCRIPTION.category)
.fetchJoin()
.fetch();
FeedSubscription sub = subs.stream().findFirst().orElse(null);
return initRelations(sub);
}
public List<FeedSubscription> findByFeed(Feed feed) {
return query().selectFrom(SUBSCRIPTION).where(SUBSCRIPTION.feed.eq(feed)).fetch();
}
public List<FeedSubscription> findByFeed(Feed feed) {
return query().selectFrom(SUBSCRIPTION).where(SUBSCRIPTION.feed.eq(feed)).fetch();
}
public FeedSubscription findByFeed(User user, Feed feed) {
List<FeedSubscription> subs = query().selectFrom(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user), SUBSCRIPTION.feed.eq(feed))
.fetch();
FeedSubscription sub = subs.stream().findFirst().orElse(null);
return initRelations(sub);
}
public FeedSubscription findByFeed(User user, Feed feed) {
List<FeedSubscription> subs =
query().selectFrom(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user), SUBSCRIPTION.feed.eq(feed))
.fetch();
FeedSubscription sub = subs.stream().findFirst().orElse(null);
return initRelations(sub);
}
public List<FeedSubscription> findAll(User user) {
List<FeedSubscription> subs = query().selectFrom(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user))
.leftJoin(SUBSCRIPTION.feed)
.fetchJoin()
.leftJoin(SUBSCRIPTION.category)
.fetchJoin()
.fetch();
return initRelations(subs);
}
public List<FeedSubscription> findAll(User user) {
List<FeedSubscription> subs =
query().selectFrom(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user))
.leftJoin(SUBSCRIPTION.feed)
.fetchJoin()
.leftJoin(SUBSCRIPTION.category)
.fetchJoin()
.fetch();
return initRelations(subs);
}
public Long count(User user) {
return query().select(SUBSCRIPTION.count()).from(SUBSCRIPTION).where(SUBSCRIPTION.user.eq(user)).fetchOne();
}
public Long count(User user) {
return query().select(SUBSCRIPTION.count())
.from(SUBSCRIPTION)
.where(SUBSCRIPTION.user.eq(user))
.fetchOne();
}
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
JPQLQuery<FeedSubscription> query = query().selectFrom(SUBSCRIPTION).where(SUBSCRIPTION.user.eq(user));
if (category == null) {
query.where(SUBSCRIPTION.category.isNull());
} else {
query.where(SUBSCRIPTION.category.eq(category));
}
return initRelations(query.fetch());
}
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
JPQLQuery<FeedSubscription> query =
query().selectFrom(SUBSCRIPTION).where(SUBSCRIPTION.user.eq(user));
if (category == null) {
query.where(SUBSCRIPTION.category.isNull());
} else {
query.where(SUBSCRIPTION.category.eq(category));
}
return initRelations(query.fetch());
}
public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) {
Set<Long> categoryIds = categories.stream().map(AbstractModel::getId).collect(Collectors.toSet());
return findAll(user).stream().filter(s -> s.getCategory() != null && categoryIds.contains(s.getCategory().getId())).toList();
}
public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) {
Set<Long> categoryIds =
categories.stream().map(AbstractModel::getId).collect(Collectors.toSet());
return findAll(user).stream()
.filter(
s ->
s.getCategory() != null
&& categoryIds.contains(s.getCategory().getId()))
.toList();
}
private List<FeedSubscription> initRelations(List<FeedSubscription> list) {
list.forEach(this::initRelations);
return list;
}
private List<FeedSubscription> initRelations(List<FeedSubscription> list) {
list.forEach(this::initRelations);
return list;
}
private FeedSubscription initRelations(FeedSubscription sub) {
if (sub != null) {
Models.initialize(sub.getFeed());
Models.initialize(sub.getCategory());
}
return sub;
}
private FeedSubscription initRelations(FeedSubscription sub) {
if (sub != null) {
Models.initialize(sub.getFeed());
Models.initialize(sub.getCategory());
}
return sub;
}
}

View File

@@ -1,12 +1,5 @@
package com.commafeed.backend.dao;
import java.time.Duration;
import java.util.Collection;
import jakarta.persistence.EntityManager;
import org.hibernate.jpa.SpecHints;
import com.commafeed.backend.model.AbstractModel;
import com.querydsl.core.types.EntityPath;
import com.querydsl.jpa.impl.JPADeleteClause;
@@ -14,53 +7,59 @@ import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.querydsl.jpa.impl.JPAUpdateClause;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.hibernate.jpa.SpecHints;
import java.time.Duration;
import java.util.Collection;
@RequiredArgsConstructor
public abstract class GenericDAO<T extends AbstractModel> {
private final EntityManager entityManager;
private final Class<T> entityClass;
private final EntityManager entityManager;
private final Class<T> entityClass;
protected JPAQueryFactory query() {
return new JPAQueryFactory(entityManager);
}
protected JPAQueryFactory query() {
return new JPAQueryFactory(entityManager);
}
protected JPAUpdateClause updateQuery(EntityPath<T> entityPath) {
return new JPAUpdateClause(entityManager, entityPath);
}
protected JPAUpdateClause updateQuery(EntityPath<T> entityPath) {
return new JPAUpdateClause(entityManager, entityPath);
}
protected JPADeleteClause deleteQuery(EntityPath<T> entityPath) {
return new JPADeleteClause(entityManager, entityPath);
}
protected JPADeleteClause deleteQuery(EntityPath<T> entityPath) {
return new JPADeleteClause(entityManager, entityPath);
}
public void persist(T model) {
entityManager.persist(model);
}
public void persist(T model) {
entityManager.persist(model);
}
public T merge(T model) {
return entityManager.merge(model);
}
public T merge(T model) {
return entityManager.merge(model);
}
public T findById(Long id) {
return entityManager.find(entityClass, id);
}
public T findById(Long id) {
return entityManager.find(entityClass, id);
}
public void delete(T object) {
if (object != null) {
entityManager.remove(object);
}
}
public void delete(T object) {
if (object != null) {
entityManager.remove(object);
}
}
public int delete(Collection<T> objects) {
objects.forEach(this::delete);
return objects.size();
}
protected void setTimeout(JPAQuery<?> query, Duration timeout) {
if (!timeout.isZero()) {
query.setHint(SpecHints.HINT_SPEC_QUERY_TIMEOUT, Math.toIntExact(timeout.toMillis()));
}
}
public int delete(Collection<T> objects) {
objects.forEach(this::delete);
return objects.size();
}
protected void setTimeout(JPAQuery<?> query, Duration timeout) {
if (!timeout.isZero()) {
query.setHint(SpecHints.HINT_SPEC_QUERY_TIMEOUT, Math.toIntExact(timeout.toMillis()));
}
}
}

View File

@@ -10,9 +10,12 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public class Predicates {
private static final QFeedEntryStatus STATUS = QFeedEntryStatus.feedEntryStatus;
private static final QFeedEntryStatus STATUS = QFeedEntryStatus.feedEntryStatus;
public static BooleanExpression isNotStarred(QFeedEntry entry) {
return JPAExpressions.selectOne().from(STATUS).where(STATUS.entry.eq(entry).and(STATUS.starred.isTrue())).notExists();
}
public static BooleanExpression isNotStarred(QFeedEntry entry) {
return JPAExpressions.selectOne()
.from(STATUS)
.where(STATUS.entry.eq(entry).and(STATUS.starred.isTrue()))
.notExists();
}
}

View File

@@ -1,19 +1,19 @@
package com.commafeed.backend.dao;
import java.util.concurrent.Callable;
import io.quarkus.narayana.jta.QuarkusTransaction;
import jakarta.inject.Singleton;
import io.quarkus.narayana.jta.QuarkusTransaction;
import java.util.concurrent.Callable;
@Singleton
public class UnitOfWork {
public void run(Runnable runnable) {
QuarkusTransaction.joiningExisting().run(runnable);
}
public void run(Runnable runnable) {
QuarkusTransaction.joiningExisting().run(runnable);
}
public <T> T call(Callable<T> callable) {
return QuarkusTransaction.joiningExisting().call(callable);
}
public <T> T call(Callable<T> callable) {
return QuarkusTransaction.joiningExisting().call(callable);
}
}

View File

@@ -1,33 +1,33 @@
package com.commafeed.backend.dao;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.QUser;
import com.commafeed.backend.model.User;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
@Singleton
public class UserDAO extends GenericDAO<User> {
private static final QUser USER = QUser.user;
private static final QUser USER = QUser.user;
public UserDAO(EntityManager entityManager) {
super(entityManager, User.class);
}
public UserDAO(EntityManager entityManager) {
super(entityManager, User.class);
}
public User findByName(String name) {
return query().selectFrom(USER).where(USER.name.equalsIgnoreCase(name)).fetchOne();
}
public User findByName(String name) {
return query().selectFrom(USER).where(USER.name.equalsIgnoreCase(name)).fetchOne();
}
public User findByApiKey(String key) {
return query().selectFrom(USER).where(USER.apiKey.equalsIgnoreCase(key)).fetchOne();
}
public User findByApiKey(String key) {
return query().selectFrom(USER).where(USER.apiKey.equalsIgnoreCase(key)).fetchOne();
}
public User findByEmail(String email) {
return query().selectFrom(USER).where(USER.email.equalsIgnoreCase(email)).fetchOne();
}
public User findByEmail(String email) {
return query().selectFrom(USER).where(USER.email.equalsIgnoreCase(email)).fetchOne();
}
public long count() {
return query().select(USER.count()).from(USER).fetchOne();
}
public long count() {
return query().select(USER.count()).from(USER).fetchOne();
}
}

View File

@@ -1,39 +1,39 @@
package com.commafeed.backend.dao;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.QUserRole;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserRole;
import com.commafeed.backend.model.UserRole.Role;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Singleton
public class UserRoleDAO extends GenericDAO<UserRole> {
private static final QUserRole ROLE = QUserRole.userRole;
private static final QUserRole ROLE = QUserRole.userRole;
public UserRoleDAO(EntityManager entityManager) {
super(entityManager, UserRole.class);
}
public UserRoleDAO(EntityManager entityManager) {
super(entityManager, UserRole.class);
}
public List<UserRole> findAll() {
return query().selectFrom(ROLE).leftJoin(ROLE.user).fetchJoin().distinct().fetch();
}
public List<UserRole> findAll() {
return query().selectFrom(ROLE).leftJoin(ROLE.user).fetchJoin().distinct().fetch();
}
public List<UserRole> findAll(User user) {
return query().selectFrom(ROLE).where(ROLE.user.eq(user)).distinct().fetch();
}
public List<UserRole> findAll(User user) {
return query().selectFrom(ROLE).where(ROLE.user.eq(user)).distinct().fetch();
}
public Set<Role> findRoles(User user) {
return findAll(user).stream().map(UserRole::getRole).collect(Collectors.toSet());
}
public Set<Role> findRoles(User user) {
return findAll(user).stream().map(UserRole::getRole).collect(Collectors.toSet());
}
public long countAdmins() {
return query().select(ROLE.count()).from(ROLE).where(ROLE.role.eq(Role.ADMIN)).fetchOne();
}
public long countAdmins() {
return query().select(ROLE.count()).from(ROLE).where(ROLE.role.eq(Role.ADMIN)).fetchOne();
}
}

View File

@@ -1,22 +1,22 @@
package com.commafeed.backend.dao;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import com.commafeed.backend.model.QUserSettings;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserSettings;
import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
@Singleton
public class UserSettingsDAO extends GenericDAO<UserSettings> {
private static final QUserSettings SETTINGS = QUserSettings.userSettings;
private static final QUserSettings SETTINGS = QUserSettings.userSettings;
public UserSettingsDAO(EntityManager entityManager) {
super(entityManager, UserSettings.class);
}
public UserSettingsDAO(EntityManager entityManager) {
super(entityManager, UserSettings.class);
}
public UserSettings findByUser(User user) {
return query().selectFrom(SETTINGS).where(SETTINGS.user.eq(user)).fetchFirst();
}
public UserSettings findByUser(User user) {
return query().selectFrom(SETTINGS).where(SETTINGS.user.eq(user)).fetchFirst();
}
}

View File

@@ -1,49 +0,0 @@
package com.commafeed.backend.favicon;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.commafeed.backend.model.Feed;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class AbstractFaviconFetcher {
private static final List<String> ICON_MIMETYPE_BLACKLIST = Arrays.asList("application/xml", "text/html");
private static final long MIN_ICON_LENGTH = 100;
private static final long MAX_ICON_LENGTH = 100000;
public abstract Favicon fetch(Feed feed);
protected boolean isValidIconResponse(byte[] content, String contentType) {
if (content == null) {
return false;
}
long length = content.length;
if (StringUtils.isNotBlank(contentType)) {
contentType = contentType.split(";")[0];
}
if (ICON_MIMETYPE_BLACKLIST.contains(contentType)) {
log.debug("Content-Type {} is blacklisted", contentType);
return false;
}
if (length < MIN_ICON_LENGTH) {
log.debug("Length {} below MIN_ICON_LENGTH {}", length, MIN_ICON_LENGTH);
return false;
}
if (length > MAX_ICON_LENGTH) {
log.debug("Length {} greater than MAX_ICON_LENGTH {}", length, MAX_ICON_LENGTH);
return false;
}
return true;
}
}

View File

@@ -1,130 +0,0 @@
package com.commafeed.backend.favicon;
import jakarta.annotation.Priority;
import jakarta.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.Urls;
import com.commafeed.backend.model.Feed;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* Inspired/Ported from https://github.com/potatolondon/getfavicon
*
*/
@Slf4j
@RequiredArgsConstructor
@Singleton
@Priority(Integer.MIN_VALUE)
public class DefaultFaviconFetcher extends AbstractFaviconFetcher {
private final HttpGetter getter;
@Override
public Favicon fetch(Feed feed) {
Favicon icon = fetch(feed.getLink());
if (icon == null) {
icon = fetch(feed.getUrl());
}
return icon;
}
private Favicon fetch(String url) {
if (url == null) {
log.debug("url is null");
return null;
}
int doubleSlash = url.indexOf("//");
if (doubleSlash == -1) {
doubleSlash = 0;
} else {
doubleSlash += 2;
}
int firstSlash = url.indexOf('/', doubleSlash);
if (firstSlash != -1) {
url = url.substring(0, firstSlash);
}
Favicon icon = getIconAtRoot(url);
if (icon == null) {
icon = getIconInPage(url);
}
return icon;
}
private Favicon getIconAtRoot(String url) {
byte[] bytes = null;
String contentType = null;
try {
url = Urls.removeTrailingSlash(url) + "/favicon.ico";
log.debug("getting root icon at {}", url);
HttpResult result = getter.get(url);
bytes = result.content();
contentType = result.contentType();
} catch (Exception e) {
log.debug("Failed to retrieve iconAtRoot for url {}: ", url, e);
}
if (!isValidIconResponse(bytes, contentType)) {
return null;
}
return new Favicon(bytes, contentType);
}
private Favicon getIconInPage(String url) {
Document doc;
try {
HttpResult result = getter.get(url);
doc = Jsoup.parse(new String(result.content()), url);
} catch (Exception e) {
log.debug("Failed to retrieve page to find icon", e);
return null;
}
Elements icons = doc.select("link[rel~=(?i)^(shortcut|icon|shortcut icon)$]");
if (icons.isEmpty()) {
log.debug("No icon found in page {}", url);
return null;
}
String href = icons.getFirst().attr("abs:href");
if (StringUtils.isBlank(href)) {
log.debug("No icon found in page");
return null;
}
log.debug("Found unconfirmed iconInPage at {}", href);
byte[] bytes;
String contentType;
try {
HttpResult result = getter.get(href);
bytes = result.content();
contentType = result.contentType();
} catch (Exception e) {
log.debug("Failed to retrieve icon found in page {}", href, e);
return null;
}
if (!isValidIconResponse(bytes, contentType)) {
log.debug("Invalid icon found for {}", href);
return null;
}
return new Favicon(bytes, contentType);
}
}

Some files were not shown because too many files have changed in this diff Show More